From 21f115fdc6314d11e648c9fed5837d68b9d44329 Mon Sep 17 00:00:00 2001 From: JannikStreek <Jannik.streek@gmail.com> Date: Sun, 1 Sep 2024 14:26:38 +0200 Subject: [PATCH] Merge upstream 2.2.2 dev (#22) --- .dockerignore | 2 + .editorconfig | 17 + .env.default | 12 +- .env.dev.default | 18 + .github/workflows/build-and-deploy-docs.yml | 70 + .github/workflows/perform-type-check.yml | 52 + .github/workflows/release.yml | 6 +- .gitignore | 1 + CHANGELOG.md | 83 +- Dockerfile | 59 +- README.md | 139 +- admin/package.json | 51 +- admin/src/App.tsx | 8 +- admin/src/components/ShoutType.ts | 13 + admin/src/index.css | 88 +- admin/src/main.tsx | 2 + admin/src/pages/HelpPage.tsx | 6 +- admin/src/pages/HomePage.tsx | 122 +- admin/src/pages/LoginScreen.tsx | 2 +- admin/src/pages/PadPage.tsx | 21 +- admin/src/pages/Plugin.ts | 2 +- admin/src/pages/SettingsPage.tsx | 6 +- admin/src/pages/ShoutPage.tsx | 76 + admin/src/utils/utils.ts | 14 +- admin/vite.config.ts | 10 +- bin/buildForWindows.sh | 14 +- bin/checkAllPads.ts | 1 + bin/checkPad.ts | 13 +- bin/commonPlugins.ts | 17 + bin/createUserSession.ts | 2 + bin/deleteAllGroupSessions.ts | 1 + bin/deletePad.ts | 1 + bin/extractPadData.ts | 1 + bin/generateReleaseNotes.ts | 39 + bin/importSqlFile.ts | 7 +- bin/installOnWindows.bat | 9 + bin/make_docs.ts | 63 + bin/migrateDirtyDBtoRealDB.ts | 7 +- bin/package.json | 18 +- bin/plugins.ts | 117 + bin/plugins/checkPlugin.ts | 2 +- bin/plugins/lib/backend-tests.yml | 2 +- bin/plugins/lib/frontend-tests.yml | 2 +- bin/plugins/stalePlugins.ts | 2 + bin/push-after-release.sh | 3 +- bin/rebuildPad.ts | 3 + bin/release.ts | 2 +- bin/repairPad.ts | 1 + bin/run.sh | 10 +- bin/tsconfig.json | 2 +- doc/.gitignore | 2 + doc/.vitepress/config.mts | 78 + doc/.vitepress/theme/components/SvgImage.vue | 22 + doc/.vitepress/theme/index.ts | 12 + doc/.vitepress/theme/styles/vars.css | 77 + doc/api/editbar.adoc | 18 +- doc/api/editorInfo.md | 208 + doc/api/embed_parameters.adoc | 44 +- doc/api/hooks_client-side.adoc | 38 +- doc/api/hooks_server-side.adoc | 12 +- doc/api/http_api.adoc | 710 +- doc/api/index.md | 3 + doc/api/toolbar.adoc | 34 +- doc/cookies.md | 18 + doc/demo.md | 19 + doc/docker.adoc | 2 +- doc/docker.md | 331 + doc/documentation.adoc | 2 +- doc/index.md | 39 + doc/package.json | 10 + doc/{ => public}/easysync/README.md | 0 .../easysync/easysync-full-description.pdf | Bin .../easysync/easysync-full-description.tex | 0 doc/{ => public}/easysync/easysync-notes.pdf | Bin doc/{ => public}/easysync/easysync-notes.tex | 0 doc/{ => public}/easysync/easysync-notes.txt | 0 doc/{images => public}/etherpad_basic.png | Bin doc/{images => public}/etherpad_demo.gif | Bin .../etherpad_full_features.png | Bin .../etherpad_skin_variants.gif | Bin doc/public/favicon.ico | Bin 0 -> 130045 bytes doc/stats.adoc | 18 +- docker-compose-prod.yml | 64 - docker-compose.dev.yml | 74 + docker-compose.yml | 73 +- make_docs.js | 58 - package.json | 14 +- pnpm-lock.yaml | 11146 ++++++---- pnpm-workspace.yaml | 2 + settings.json.docker | 43 +- settings.json.template | 49 +- src/ep.json | 15 +- src/locales/az.json | 1 + src/locales/bn.json | 1 + src/locales/fa.json | 23 +- src/locales/id.json | 43 +- src/locales/ko.json | 7 +- src/locales/krc.json | 8 +- src/locales/ms.json | 1 + src/locales/ne.json | 3 + src/locales/pa.json | 7 +- src/locales/sl.json | 2 +- src/locales/th.json | 3 +- src/locales/vec.json | 3 +- src/locales/zh-hans.json | 3 +- src/node/db/API.ts | 16 +- src/node/db/AuthorManager.ts | 6 +- src/node/db/DB.ts | 4 +- src/node/db/GroupManager.ts | 2 +- src/node/db/Pad.ts | 68 +- src/node/db/SecurityManager.ts | 4 +- src/node/db/SessionManager.ts | 4 +- src/node/db/SessionStore.ts | 2 +- src/node/eejs/index.ts | 2 +- src/node/handler/APIHandler.ts | 60 +- src/node/handler/APIKeyHandler.ts | 25 + src/node/handler/ImportHandler.ts | 2 +- src/node/handler/PadMessageHandler.ts | 104 +- src/node/hooks/express.ts | 2 +- src/node/hooks/express/admin.ts | 81 +- src/node/hooks/express/adminplugins.ts | 11 +- src/node/hooks/express/adminsettings.ts | 403 +- src/node/hooks/express/openapi.ts | 27 +- src/node/hooks/express/pwa.ts | 32 + src/node/hooks/express/specialpages.ts | 321 +- src/node/hooks/express/static.ts | 29 +- src/node/hooks/express/webaccess.ts | 8 +- src/node/hooks/i18n.ts | 4 +- src/node/security/OAuth2Provider.ts | 282 + src/node/security/OAuth2User.ts | 5 + src/node/security/OIDCAdapter.ts | 115 + src/node/server.ts | 31 +- src/node/types/PadType.ts | 6 +- src/node/utils/ExportHelper.ts | 13 +- src/node/utils/ExportHtml.ts | 17 +- src/node/utils/ExportTxt.ts | 13 +- src/node/utils/ImportEtherpad.ts | 8 +- src/node/utils/ImportHtml.ts | 7 +- src/node/utils/{Minify.js => Minify.ts} | 176 +- src/node/utils/MinifyWorker.js | 33 - src/node/utils/MinifyWorker.ts | 42 + src/node/utils/Settings.ts | 949 +- src/node/utils/SettingsTree.ts | 112 + src/node/utils/UpdateCheck.ts | 18 +- src/node/utils/caching_middleware.ts | 210 - src/node/utils/padDiff.ts | 65 +- src/node/utils/path_exists.ts | 4 +- src/node/utils/promises.ts | 7 +- src/node/utils/sanitizePathname.ts | 8 +- src/node/utils/toolbar.ts | 29 +- src/package.json | 98 +- src/playwright.config.ts | 2 +- ...ttributeManager.js => AttributeManager.ts} | 50 +- .../js/{AttributeMap.js => AttributeMap.ts} | 24 +- .../js/{AttributePool.js => AttributePool.ts} | 36 +- src/static/js/Builder.ts | 108 + src/static/js/{Changeset.js => Changeset.ts} | 1398 +- .../{ChangesetUtils.js => ChangesetUtils.ts} | 28 +- .../js/{ChatMessage.js => ChatMessage.ts} | 35 +- src/static/js/MergingOpAssembler.ts | 73 + src/static/js/Op.ts | 78 + src/static/js/OpAssembler.ts | 21 + src/static/js/OpIter.ts | 47 + src/static/js/SmartOpAssembler.ts | 115 + src/static/js/StringAssembler.ts | 18 + src/static/js/StringIterator.ts | 54 + src/static/js/TextLinesMutator.ts | 348 + src/static/js/{ace.js => ace.ts} | 35 +- .../js/{ace2_common.js => ace2_common.ts} | 23 +- .../js/{ace2_inner.js => ace2_inner.ts} | 217 +- .../js/{attributes.js => attributes.ts} | 34 +- ...rror_handler.js => basic_error_handler.ts} | 1 + src/static/js/{broadcast.js => broadcast.ts} | 34 +- ...st_revisions.js => broadcast_revisions.ts} | 1 + ...roadcast_slider.js => broadcast_slider.ts} | 2 + .../js/{caretPosition.js => caretPosition.ts} | 42 +- ...hangesettracker.js => changesettracker.ts} | 58 +- src/static/js/{chat.js => chat.ts} | 7 +- .../js/{collab_client.js => collab_client.ts} | 1 + .../js/{colorutils.js => colorutils.ts} | 1 + ...ontentcollector.js => contentcollector.ts} | 21 +- .../js/{cssmanager.js => cssmanager.ts} | 1 + src/static/js/{domline.js => domline.ts} | 1 + src/static/js/{index.js => index.ts} | 10 +- src/static/js/l10n.js | 16 - src/static/js/l10n.ts | 18 + ...{linestylefilter.js => linestylefilter.ts} | 12 +- src/static/js/{pad.js => pad.ts} | 55 +- ...econnect.js => pad_automatic_reconnect.ts} | 2 + ...ctionstatus.js => pad_connectionstatus.ts} | 1 + .../js/{pad_cookie.js => pad_cookie.ts} | 3 +- .../js/{pad_editbar.js => pad_editbar.ts} | 3 +- .../js/{pad_editor.js => pad_editor.ts} | 10 +- .../js/{pad_impexp.js => pad_impexp.ts} | 3 + .../js/{pad_modals.js => pad_modals.ts} | 1 + .../js/{pad_savedrevs.js => pad_savedrevs.ts} | 3 +- .../js/{pad_userlist.js => pad_userlist.ts} | 5 +- src/static/js/{pad_utils.js => pad_utils.ts} | 414 +- src/static/js/pluginfw/LinkInstaller.ts | 12 +- .../{client_plugins.js => client_plugins.ts} | 26 +- src/static/js/pluginfw/{hooks.js => hooks.ts} | 5 +- src/static/js/pluginfw/installer.ts | 7 +- .../{plugin_defs.js => plugin_defs.ts} | 0 .../js/pluginfw/{plugins.js => plugins.ts} | 4 +- .../js/pluginfw/{shared.js => shared.ts} | 15 +- src/static/js/pluginfw/{tsort.js => tsort.ts} | 2 +- src/static/js/qrcode_download.js | 9 +- src/static/js/{rjquery.js => rjquery.ts} | 1 + src/static/js/scroll.js | 351 - src/static/js/scroll.ts | 338 + src/static/js/{security.js => security.ts} | 1 + .../js/{skin_variants.js => skin_variants.ts} | 1 + src/static/js/{skiplist.js => skiplist.ts} | 185 +- src/static/js/{socketio.js => socketio.ts} | 9 +- .../js/{timeslider.js => timeslider.ts} | 16 +- src/static/js/types/AText.ts | 4 + src/static/js/types/Attribute.ts | 1 + src/static/js/types/ChangeSet.ts | 6 + src/static/js/types/ChangeSetBuilder.ts | 7 + src/static/js/types/PadRevision.ts | 7 + src/static/js/types/RepModel.ts | 31 + src/static/js/types/SocketIOMessage.ts | 317 + .../js/{underscore.js => underscore.ts} | 1 + .../js/{undomodule.js => undomodule.ts} | 23 +- .../js/vendors/{browser.js => browser.ts} | 1 + .../vendors/{farbtastic.js => farbtastic.ts} | 17 +- .../js/vendors/{gritter.js => gritter.ts} | 7 +- src/static/js/vendors/html10n.js | 1056 - src/static/js/vendors/html10n.ts | 997 + src/static/js/vendors/jquery.js | 10704 --------- src/static/js/vendors/jquery.ts | 10713 +++++++++ .../{nice-select.js => nice-select.ts} | 9 +- src/templates/export_html.html | 3 +- src/templates/index.html | 25 +- src/templates/indexBootstrap.js | 6 + src/templates/javascript.html | 3 +- src/templates/pad.html | 72 +- src/templates/padBootstrap.js | 45 + src/templates/padViteBootstrap.js | 41 + src/templates/timeSliderBootstrap.js | 37 + src/templates/timeslider.html | 50 +- src/tests/backend-new/easysync-helper.ts | 220 + .../specs/AttributeMap.ts} | 20 +- .../backend-new/specs/StringIteratorTest.ts | 47 + src/tests/backend-new/specs/admin_utils.ts | 38 + .../specs/attributes.ts} | 63 +- .../specs/easysync-assembler.ts} | 104 +- .../backend-new/specs/easysync-compose.ts | 54 + .../specs/easysync-inverseRandom.ts} | 28 +- .../specs/easysync-mutations.ts} | 91 +- .../backend-new/specs/easysync-other.test.ts | 166 + .../specs/easysync-subAttribution.ts} | 8 +- .../specs/pad_utils.ts | 27 +- src/tests/backend-new/specs/path_exists.ts | 22 + .../specs/promises.ts | 34 +- .../specs/sanitizePathname.ts | 7 +- src/tests/backend-new/specs/skiplist.ts | 56 + src/tests/backend/common.ts | 86 +- src/tests/backend/fuzzImportTest.ts | 10 +- src/tests/backend/specs/ImportEtherpad.ts | 2 +- src/tests/backend/specs/api/api.ts | 3 +- .../backend/specs/api/characterEncoding.ts | 27 +- src/tests/backend/specs/api/chat.ts | 26 +- src/tests/backend/specs/api/fuzzImportTest.ts | 1 - src/tests/backend/specs/api/importexport.ts | 15 +- .../backend/specs/api/importexportGetPost.ts | 77 +- src/tests/backend/specs/api/instance.ts | 4 +- src/tests/backend/specs/api/pad.ts | 196 +- .../backend/specs/api/restoreRevision.ts | 3 +- .../backend/specs/api/sessionsAndGroups.ts | 111 +- src/tests/backend/specs/caching_middleware.ts | 125 - src/tests/backend/specs/chat.ts | 16 +- src/tests/backend/specs/contentcollector.ts | 16 +- src/tests/backend/specs/lowerCasePadIds.ts | 6 +- src/tests/backend/specs/settings.ts | 33 +- src/tests/container/specs/api/pad.js | 4 +- .../admin-spec/adminsettings.spec.ts | 8 +- .../admin-spec/admintroubleshooting.spec.ts | 2 +- .../frontend-new/specs/embed_value.spec.ts | 4 +- .../helper/{methods.js => methods.ts} | 2 +- .../{multipleUsers.js => multipleUsers.ts} | 2 +- src/tests/frontend/helper/{ui.js => ui.ts} | 1 + src/tests/frontend/index.html | 1 - src/tests/frontend/lib/expect.js | 1247 -- src/tests/frontend/lib/mocha.js | 18115 ---------------- src/tests/frontend/lib/sendkeys.js | 467 - src/tests/frontend/lib/underscore.js | 1200 - src/tests/frontend/runner.js | 1 - src/tests/frontend/specs/easysync-compose.js | 53 - src/tests/frontend/specs/easysync-other.js | 158 - src/tests/frontend/specs/skiplist.js | 54 - src/tests/frontend/travis/runnerLoadTest.sh | 2 +- src/tests/settings.json | 655 +- src/vitest.config.ts | 7 + ui/.gitignore | 24 + ui/consent.html | 24 + ui/login.html | 38 + ui/package.json | 17 + ui/pad.html | 686 + ui/src/consent.ts | 35 + ui/src/main.ts | 58 + ui/src/style.css | 125 + ui/src/typescript.svg | 1 + ui/src/vite-env.d.ts | 1 + ui/tsconfig.json | 23 + ui/vite.config.ts | 47 + var/js/.gitignore | 2 + 307 files changed, 28875 insertions(+), 42590 deletions(-) create mode 100644 .editorconfig create mode 100644 .env.dev.default create mode 100644 .github/workflows/build-and-deploy-docs.yml create mode 100644 .github/workflows/perform-type-check.yml create mode 100644 admin/src/components/ShoutType.ts create mode 100644 admin/src/pages/ShoutPage.tsx create mode 100644 bin/commonPlugins.ts create mode 100644 bin/generateReleaseNotes.ts create mode 100644 bin/make_docs.ts create mode 100644 bin/plugins.ts create mode 100644 doc/.gitignore create mode 100644 doc/.vitepress/config.mts create mode 100644 doc/.vitepress/theme/components/SvgImage.vue create mode 100644 doc/.vitepress/theme/index.ts create mode 100644 doc/.vitepress/theme/styles/vars.css create mode 100644 doc/api/editorInfo.md create mode 100644 doc/api/index.md create mode 100644 doc/cookies.md create mode 100644 doc/demo.md create mode 100644 doc/docker.md create mode 100644 doc/index.md create mode 100644 doc/package.json rename doc/{ => public}/easysync/README.md (100%) rename doc/{ => public}/easysync/easysync-full-description.pdf (100%) rename doc/{ => public}/easysync/easysync-full-description.tex (100%) rename doc/{ => public}/easysync/easysync-notes.pdf (100%) rename doc/{ => public}/easysync/easysync-notes.tex (100%) rename doc/{ => public}/easysync/easysync-notes.txt (100%) rename doc/{images => public}/etherpad_basic.png (100%) rename doc/{images => public}/etherpad_demo.gif (100%) rename doc/{images => public}/etherpad_full_features.png (100%) rename doc/{images => public}/etherpad_skin_variants.gif (100%) create mode 100644 doc/public/favicon.ico delete mode 100644 docker-compose-prod.yml create mode 100644 docker-compose.dev.yml delete mode 100644 make_docs.js create mode 100644 src/node/handler/APIKeyHandler.ts create mode 100644 src/node/hooks/express/pwa.ts create mode 100644 src/node/security/OAuth2Provider.ts create mode 100644 src/node/security/OAuth2User.ts create mode 100644 src/node/security/OIDCAdapter.ts rename src/node/utils/{Minify.js => Minify.ts} (67%) delete mode 100644 src/node/utils/MinifyWorker.js create mode 100644 src/node/utils/MinifyWorker.ts create mode 100644 src/node/utils/SettingsTree.ts delete mode 100644 src/node/utils/caching_middleware.ts rename src/static/js/{AttributeManager.js => AttributeManager.ts} (90%) rename src/static/js/{AttributeMap.js => AttributeMap.ts} (78%) rename src/static/js/{AttributePool.js => AttributePool.ts} (91%) create mode 100644 src/static/js/Builder.ts rename src/static/js/{Changeset.js => Changeset.ts} (54%) rename src/static/js/{ChangesetUtils.js => ChangesetUtils.ts} (59%) rename src/static/js/{ChatMessage.js => ChatMessage.ts} (66%) create mode 100644 src/static/js/MergingOpAssembler.ts create mode 100644 src/static/js/Op.ts create mode 100644 src/static/js/OpAssembler.ts create mode 100644 src/static/js/OpIter.ts create mode 100644 src/static/js/SmartOpAssembler.ts create mode 100644 src/static/js/StringAssembler.ts create mode 100644 src/static/js/StringIterator.ts create mode 100644 src/static/js/TextLinesMutator.ts rename src/static/js/{ace.js => ace.ts} (93%) rename src/static/js/{ace2_common.js => ace2_common.ts} (76%) rename src/static/js/{ace2_inner.js => ace2_inner.ts} (95%) rename src/static/js/{attributes.js => attributes.ts} (77%) rename src/static/js/{basic_error_handler.js => basic_error_handler.ts} (99%) rename src/static/js/{broadcast.js => broadcast.ts} (94%) rename src/static/js/{broadcast_revisions.js => broadcast_revisions.ts} (99%) rename src/static/js/{broadcast_slider.js => broadcast_slider.ts} (99%) rename src/static/js/{caretPosition.js => caretPosition.ts} (84%) rename src/static/js/{changesettracker.js => changesettracker.ts} (76%) rename src/static/js/{chat.js => chat.ts} (98%) mode change 100755 => 100644 rename src/static/js/{collab_client.js => collab_client.ts} (99%) rename src/static/js/{colorutils.js => colorutils.ts} (99%) rename src/static/js/{contentcollector.js => contentcollector.ts} (97%) rename src/static/js/{cssmanager.js => cssmanager.ts} (99%) rename src/static/js/{domline.js => domline.ts} (99%) rename src/static/js/{index.js => index.ts} (90%) delete mode 100644 src/static/js/l10n.js create mode 100644 src/static/js/l10n.ts rename src/static/js/{linestylefilter.js => linestylefilter.ts} (97%) rename src/static/js/{pad.js => pad.ts} (94%) rename src/static/js/{pad_automatic_reconnect.js => pad_automatic_reconnect.ts} (99%) rename src/static/js/{pad_connectionstatus.js => pad_connectionstatus.ts} (99%) rename src/static/js/{pad_cookie.js => pad_cookie.ts} (97%) rename src/static/js/{pad_editbar.js => pad_editbar.ts} (99%) rename src/static/js/{pad_editor.js => pad_editor.ts} (96%) rename src/static/js/{pad_impexp.js => pad_impexp.ts} (99%) rename src/static/js/{pad_modals.js => pad_modals.ts} (99%) rename src/static/js/{pad_savedrevs.js => pad_savedrevs.ts} (96%) rename src/static/js/{pad_userlist.js => pad_userlist.ts} (99%) rename src/static/js/{pad_utils.js => pad_utils.ts} (58%) rename src/static/js/pluginfw/{client_plugins.js => client_plugins.ts} (64%) rename src/static/js/pluginfw/{hooks.js => hooks.ts} (99%) rename src/static/js/pluginfw/{plugin_defs.js => plugin_defs.ts} (100%) rename src/static/js/pluginfw/{plugins.js => plugins.ts} (98%) rename src/static/js/pluginfw/{shared.js => shared.ts} (89%) rename src/static/js/pluginfw/{tsort.js => tsort.ts} (99%) rename src/static/js/{rjquery.js => rjquery.ts} (93%) delete mode 100644 src/static/js/scroll.js create mode 100644 src/static/js/scroll.ts rename src/static/js/{security.js => security.ts} (97%) rename src/static/js/{skin_variants.js => skin_variants.ts} (99%) rename src/static/js/{skiplist.js => skiplist.ts} (61%) rename src/static/js/{socketio.js => socketio.ts} (90%) rename src/static/js/{timeslider.js => timeslider.ts} (93%) create mode 100644 src/static/js/types/AText.ts create mode 100644 src/static/js/types/Attribute.ts create mode 100644 src/static/js/types/ChangeSet.ts create mode 100644 src/static/js/types/ChangeSetBuilder.ts create mode 100644 src/static/js/types/PadRevision.ts create mode 100644 src/static/js/types/RepModel.ts create mode 100644 src/static/js/types/SocketIOMessage.ts rename src/static/js/{underscore.js => underscore.ts} (78%) rename src/static/js/{undomodule.js => undomodule.ts} (91%) rename src/static/js/vendors/{browser.js => browser.ts} (99%) rename src/static/js/vendors/{farbtastic.js => farbtastic.ts} (97%) rename src/static/js/vendors/{gritter.js => gritter.ts} (98%) delete mode 100644 src/static/js/vendors/html10n.js create mode 100644 src/static/js/vendors/html10n.ts delete mode 100644 src/static/js/vendors/jquery.js create mode 100644 src/static/js/vendors/jquery.ts rename src/static/js/vendors/{nice-select.js => nice-select.ts} (96%) create mode 100644 src/templates/indexBootstrap.js create mode 100644 src/templates/padBootstrap.js create mode 100644 src/templates/padViteBootstrap.js create mode 100644 src/templates/timeSliderBootstrap.js create mode 100644 src/tests/backend-new/easysync-helper.ts rename src/tests/{frontend/specs/AttributeMap.js => backend-new/specs/AttributeMap.ts} (91%) create mode 100644 src/tests/backend-new/specs/StringIteratorTest.ts create mode 100644 src/tests/backend-new/specs/admin_utils.ts rename src/tests/{frontend/specs/attributes.js => backend-new/specs/attributes.ts} (86%) rename src/tests/{frontend/specs/easysync-assembler.js => backend-new/specs/easysync-assembler.ts} (64%) create mode 100644 src/tests/backend-new/specs/easysync-compose.ts rename src/tests/{frontend/specs/easysync-inverseRandom.js => backend-new/specs/easysync-inverseRandom.ts} (51%) rename src/tests/{frontend/specs/easysync-mutations.js => backend-new/specs/easysync-mutations.ts} (75%) create mode 100644 src/tests/backend-new/specs/easysync-other.test.ts rename src/tests/{frontend/specs/easysync-subAttribution.js => backend-new/specs/easysync-subAttribution.ts} (90%) rename src/tests/{backend => backend-new}/specs/pad_utils.ts (57%) create mode 100644 src/tests/backend-new/specs/path_exists.ts rename src/tests/{backend => backend-new}/specs/promises.ts (70%) rename src/tests/{backend => backend-new}/specs/sanitizePathname.ts (92%) create mode 100644 src/tests/backend-new/specs/skiplist.ts delete mode 100644 src/tests/backend/specs/caching_middleware.ts rename src/tests/frontend/helper/{methods.js => methods.ts} (99%) rename src/tests/frontend/helper/{multipleUsers.js => multipleUsers.ts} (99%) rename src/tests/frontend/helper/{ui.js => ui.ts} (99%) delete mode 100644 src/tests/frontend/lib/expect.js delete mode 100644 src/tests/frontend/lib/mocha.js delete mode 100644 src/tests/frontend/lib/sendkeys.js delete mode 100644 src/tests/frontend/lib/underscore.js delete mode 100644 src/tests/frontend/specs/easysync-compose.js delete mode 100644 src/tests/frontend/specs/easysync-other.js delete mode 100644 src/tests/frontend/specs/skiplist.js create mode 100644 src/vitest.config.ts create mode 100644 ui/.gitignore create mode 100644 ui/consent.html create mode 100644 ui/login.html create mode 100644 ui/package.json create mode 100644 ui/pad.html create mode 100644 ui/src/consent.ts create mode 100644 ui/src/main.ts create mode 100644 ui/src/style.css create mode 100644 ui/src/typescript.svg create mode 100644 ui/src/vite-env.d.ts create mode 100644 ui/tsconfig.json create mode 100644 ui/vite.config.ts create mode 100644 var/js/.gitignore diff --git a/.dockerignore b/.dockerignore index 720dece986f..3f84d919bbf 100644 --- a/.dockerignore +++ b/.dockerignore @@ -31,3 +31,5 @@ docker-compose*.yml settings.json src/node_modules admin/node_modules +ui/node_modules +node_modules diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..f521471dcc0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +# editorconfig-tools is unable to ignore longs strings or urls +max_line_length = off + +[CHANGELOG.md] +indent_size = 4 + +[*.bat] +end_of_line = crlf diff --git a/.env.default b/.env.default index 74ac097ee72..e9b560b72cb 100644 --- a/.env.default +++ b/.env.default @@ -4,15 +4,15 @@ # Always ensure to load the env variables in every terminal session. # Otherwise the env variables will not be available -DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED=9001 -DOCKER_COMPOSE_APP_DEV_PORT_TARGET=9001 +DOCKER_COMPOSE_APP_PORT_PUBLISHED=9001 +DOCKER_COMPOSE_APP_PORT_TARGET=9001 # IMPORTANT: When the env var DEFAULT_PAD_TEXT is unset or empty, then the pad is not established (not the landing page). # The env var DEFAULT_PAD_TEXT seems to be mandatory in the latest version of etherpad. DOCKER_COMPOSE_APP_DEV_ENV_DEFAULT_PAD_TEXT="Welcome to etherpad" -DOCKER_COMPOSE_APP_DEV_ENV_ADMIN_PASSWORD= +DOCKER_COMPOSE_APP_ADMIN_PASSWORD= -DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE=db -DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD=etherpad-lite-password -DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER=etherpad-lite-user \ No newline at end of file +DOCKER_COMPOSE_POSTGRES_DATABASE=db +DOCKER_COMPOSE_POSTGRES_PASSWORD=etherpad-lite-password +DOCKER_COMPOSE_POSTGRES_USER=etherpad-lite-user diff --git a/.env.dev.default b/.env.dev.default new file mode 100644 index 00000000000..b78b5599aa1 --- /dev/null +++ b/.env.dev.default @@ -0,0 +1,18 @@ +# Please copy and rename this file. +# +# !Attention! +# Always ensure to load the env variables in every terminal session. +# Otherwise the env variables will not be available + +DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED=9001 +DOCKER_COMPOSE_APP_DEV_PORT_TARGET=9001 + +# IMPORTANT: When the env var DEFAULT_PAD_TEXT is unset or empty, then the pad is not established (not the landing page). +# The env var DEFAULT_PAD_TEXT seems to be mandatory in the latest version of etherpad. +DOCKER_COMPOSE_APP_DEV_ENV_DEFAULT_PAD_TEXT="Welcome to etherpad" + +DOCKER_COMPOSE_APP_DEV_ADMIN_PASSWORD= + +DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE=db +DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD=etherpad-lite-password +DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER=etherpad-lite-user \ No newline at end of file diff --git a/.github/workflows/build-and-deploy-docs.yml b/.github/workflows/build-and-deploy-docs.yml new file mode 100644 index 00000000000..3134de22ae8 --- /dev/null +++ b/.github/workflows/build-and-deploy-docs.yml @@ -0,0 +1,70 @@ +# Workflow for deploying static content to GitHub Pages +name: Deploy Docs to GitHub Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["develop"] + paths: + - doc/** # Only run workflow when changes are made to the doc directory + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + packages: read + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + version: 9.0.4 + run_install: false + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + - name: Only install direct dependencies + run: pnpm config set auto-install-peers false + - name: Install dependencies + run: pnpm install + - name: Build app + working-directory: doc + run: pnpm run docs:build + env: + COMMIT_REF: ${{ github.sha }} + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: './doc/.vitepress/dist' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/perform-type-check.yml b/.github/workflows/perform-type-check.yml new file mode 100644 index 00000000000..ba35dec3219 --- /dev/null +++ b/.github/workflows/perform-type-check.yml @@ -0,0 +1,52 @@ +name: "Perform type checks" + +# any branch is useful for testing before a PR is submitted +on: + push: + paths-ignore: + - "doc/**" + pull_request: + paths-ignore: + - "doc/**" + +permissions: + contents: read + + +jobs: + performTypeCheck: + if: | + (github.event_name != 'pull_request') + || (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id) + name: perform type check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + version: 9.0.4 + run_install: false + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + - name: Only install direct dependencies + run: pnpm config set auto-install-peers false + - + name: Install all dependencies and symlink for ep_etherpad-lite + run: ./bin/installDeps.sh + - name: Perform type check + working-directory: ./src + run: npm run ts-check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ebbc2afcee2..86f22c9900f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,9 @@ name: Create and publish a Docker image on: release: types: [published] + push: + branches: + - 'merge-upstream-2.2.2-dev' env: REGISTRY: ghcr.io @@ -50,7 +53,8 @@ jobs: with: platforms: linux/amd64,linux/arm64/v8 build-args: | - ETHERPAD_PLUGINS=kitsteam/ep_comments_page kitsteam/ep_push2delete ep_image_upload ep_embedded_hyperlinks2 ep_headings2 kitsteam/ep_delete_after_delay kitsteam/ep_delete_empty_pads ep_align ep_font_color ep_helmet ep_font_size ep_disable_imports + ETHERPAD_PLUGINS=ep_image_upload ep_embedded_hyperlinks2 ep_headings2 ep_align ep_font_color ep_helmet ep_font_size ep_disable_imports + ETHERPAD_GITHUB_PLUGINS=kitsteam/ep_comments_page#merge-upstream-1.0.36 kitsteam/ep_push2delete kitsteam/ep_delete_after_delay kitsteam/ep_delete_empty_pads push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index f577330c9ab..71584e76bb5 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ plugin_packages /src/test-results playwright-report state.json +/src/static/oidc diff --git a/CHANGELOG.md b/CHANGELOG.md index b565eed2c36..7772d044752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,58 @@ +# 2.2.2 + +### Notable enhancements and fixes + +- Removal of Etherpad require kernel: We finally managed to include esbuild to bundle our frontend code together. So no matter how many plugins your server has it is always one JavaScript file. This boosts performance dramatically. +- Added log layoutType: This lets you print the log in either colored or basic (black and white text) +- Introduced esbuild for bundling CSS files +- Cache all files to be bundled in memory for faster load speed + + +# 2.1.1 + + +### Notable enhancements and fixes + +- Fixed failing Docker build when checked out as git submodule. Thanks to @neurolabs +- Fixed: Fallback to websocket and polling when unknown(old) config is present for socket io +- Fixed: Next page disabled if zero page by @samyakj023 +- On CTRL+CLICK bring the window back to focus by Helder Sepulveda + +# 2.1.0 + +### Notable enhancements and fixes + +- Added PWA support. You can now add your Etherpad instance to your home screen on your mobile device or desktop. +- Fixed live plugin manager versions clashing. Thanks to @yacchin1205 +- Fixed a bug in the pad panel where pagination was not working correctly when sorting by pad name + +### Compatibility changes + +- Reintroduced APIKey.txt support. You can now switch between APIKey and OAuth2.0 authentication. This can be toggled with the setting authenticationMethod. The default is OAuth2. If you want to use the APIKey method you can set that to `apikey`. + + +# 2.0.3 + +### Notable enhancements and fixes + +- Added documentation for replacing apikeys with oauth2 +- Bumped live plugin manager to 0.20.0. Thanks to @fgreinacher +- Added better documentation for using docker-compose with Etherpad + + + +# 2.0.2 + +### Notable enhancements and fixes + +- Fixed the locale loading in the admin panel +- Added OAuth2.0 support for the Etherpad API. You can now log in into the Etherpad API with your admin user using OAuth2 + +### Compatibility changes + +- The tests now require generating a token from the OAuth secret. You can find the `generateJWTToken` in the common.ts script for plugin endpoint updates. + + # 2.0.1 ### Notable enhancements and fixes @@ -13,7 +68,7 @@ - Socket io has been updated to 4.7.5. This means that the json.send function won't work anymore and needs to be changed to .emit('message', myObj) - Deprecating npm version 6 in favor of pnpm: We have made the decision to switch to the well established pnpm (https://pnpm.io/). It works by symlinking dependencies into a global directory allowing you to have a cleaner and more reliable environment. - Introducing Typescript to the Etherpad core: Etherpad core logic has been rewritten in Typescript allowing for compiler checking of errors. -- Rewritten Admin Panel: The Admin panel has been rewritten in React and now features a more pleasant user experience. It now also features an integrated pad searching with sorting functionality. +- Rewritten Admin Panel: The Admin panel has been rewritten in React and now features a more pleasant user experience. It now also features an integrated pad searching with sorting functionality. ### Notable enhancements and fixes @@ -23,17 +78,17 @@ * Enhancements - pnpm Workspaces: In addition to pnpm we introduced workspaces. A clean way to manage multiple bounded contexts like the admin panel or the bin folder. - Bin folder: The bin folder has been moved from the src folder to the root folder. This change was necessary as the contained scripts do not represent core functionality of the user. - - Starting Etherpad: Etherpad can now be started with a single command: `pnpm run prod` in the root directory. + - Starting Etherpad: Etherpad can now be started with a single command: `pnpm run prod` in the root directory. - Installing Etherpad: Etherpad no longer symlinks itself in the root directory. This is now also taken care by pnpm, and it just creates a node_modules folder with the src directory`s ep_etherpad-lite folder - - Plugins can now be installed simply via the command: `pnpm run install-plugins first-plugin second-plugin` or if you want to install from path you can do: - `pnpm run install-plugins --path ../path-to-plugin` + - Plugins can now be installed simply via the command: `pnpm run plugins i first-plugin second-plugin` or if you want to install from path you can do: + `pnpm run plugins i --path ../path-to-plugin` # 1.9.7 ### Notable enhancements and fixes -* Added Live Plugin Manager: Plugins are now installed into a separate folder on the host system. This folder is called `plugin_packages`. +* Added Live Plugin Manager: Plugins are now installed into a separate folder on the host system. This folder is called `plugin_packages`. That way the plugins are separated from the normal etherpad installation. * Make repairPad.js more verbose * Fixed favicon not being loaded correctly @@ -56,19 +111,19 @@ That way the plugins are separated from the normal etherpad installation. ### Notable enhancements and fixes -* The support for the tidy program to tidy up HTML files has been removed. This decision was made because it hasn't been updated for years and also caused an incompability when exporting a pad with Abiword. +* The support for the tidy program to tidy up HTML files has been removed. This decision was made because it hasn't been updated for years and also caused an incompability when exporting a pad with Abiword. # 1.9.4 ### Compatibility changes -* Log4js has been updated to the latest version. As it involved a bump of 6 major version. +* Log4js has been updated to the latest version. As it involved a bump of 6 major version. A lot has changed since then. Most notably the console appender has been deprecated. You can find out more about it [here](https://github.com/log4js-node/log4js-node) ### Notable enhancements and fixes -* Fix for MySQL: The logger calls were incorrectly configured leading to a crash when e.g. somebody uses a different encoding than standard MySQL encoding. +* Fix for MySQL: The logger calls were incorrectly configured leading to a crash when e.g. somebody uses a different encoding than standard MySQL encoding. # 1.9.3 @@ -76,7 +131,7 @@ That way the plugins are separated from the normal etherpad installation. * express-rate-limit has been bumped to 7.0.0: This involves the breaking change that "max: 0" in the importExportRateLimiting is set to always trigger. So set it to your desired value. -If you haven't changed that value in the settings.json you are all set. +If you haven't changed that value in the settings.json you are all set. ### Notable enhancements and fixes @@ -95,7 +150,7 @@ If you haven't changed that value in the settings.json you are all set. * Enable session key rotation: This setting can be enabled in the settings.json. It changes the signing key for the cookie authentication in a fixed interval. * Bugfixes - * Fix appendRevision when creating a new pad via the API without a text. + * Fix appendRevision when creating a new pad via the API without a text. * Enhancements @@ -104,11 +159,11 @@ If you haven't changed that value in the settings.json you are all set. ### Compatibility changes -* No compability changes as JQuery maintains excellent backwards compatibility. +* No compability changes as JQuery maintains excellent backwards compatibility. #### For plugin authors -* Please update to JQuery 3.7. There is an excellent deprecation guide over [here](https://api.jquery.com/category/deprecated/). Version 3.1 to 3.7 are relevant for the upgrade. +* Please update to JQuery 3.7. There is an excellent deprecation guide over [here](https://api.jquery.com/category/deprecated/). Version 3.1 to 3.7 are relevant for the upgrade. # 1.9.1 @@ -116,7 +171,7 @@ If you haven't changed that value in the settings.json you are all set. * Security * Limit requested revisions in timeslider and export to head revision. (affects v1.9.0) - + * Bugfixes * revisions in `CHANGESET_REQ` (timeslider) and export (txt, html, custom) are now checked to be numbers. @@ -130,7 +185,7 @@ If you haven't changed that value in the settings.json you are all set. * tests: drop windows 7 test coverage & use chrome latest for admin tests * Require Node 16 for Etherpad and target Node 20 for testing - + # 1.9.0 ### Notable enhancements and fixes diff --git a/Dockerfile b/Dockerfile index a80c0d9e69b..7953b98a4fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,14 +4,15 @@ # # Author: muxator -FROM node:alpine as adminBuild - +FROM node:alpine AS adminbuild +RUN npm install -g pnpm@9.0.4 WORKDIR /opt/etherpad-lite -COPY ./admin ./admin -RUN cd ./admin && npm install -g pnpm && pnpm install && pnpm run build --outDir ./dist +COPY . . +RUN pnpm install +RUN pnpm run build:ui -FROM node:alpine as build +FROM node:alpine AS build LABEL maintainer="Etherpad team, https://github.com/ether/etherpad-lite" # Set these arguments when building the image from behind a proxy @@ -40,6 +41,22 @@ ARG SETTINGS=./settings.json.docker # ETHERPAD_PLUGINS="ep_codepad ep_author_neat" ARG ETHERPAD_PLUGINS= +# local plugins to install while building the container. By default no plugins are +# installed. +# If given a value, it has to be a space-separated, quoted list of plugin names. +# +# EXAMPLE: +# ETHERPAD_LOCAL_PLUGINS="../ep_my_plugin ../ep_another_plugin" +ARG ETHERPAD_LOCAL_PLUGINS= + +# github plugins to install while building the container. By default no plugins are +# installed. +# If given a value, it has to be a space-separated, quoted list of plugin names. +# +# EXAMPLE: +# ETHERPAD_GITHUB_PLUGINS="ether/ep_plugin" +ARG ETHERPAD_GITHUB_PLUGINS= + # Control whether abiword will be installed, enabling exports to DOC/PDF/ODT formats. # By default, it is not installed. # If given any value, abiword will be installed. @@ -57,7 +74,7 @@ ARG INSTALL_ABIWORD= ARG INSTALL_SOFFICE= # Install dependencies required for modifying access. -RUN apk add shadow bash +RUN apk add --no-cache shadow bash # Follow the principle of least privilege: run as unprivileged user. # # Running as non-root enables running this image in platforms like OpenShift @@ -82,9 +99,9 @@ RUN mkdir -p "${EP_DIR}" && chown etherpad:etherpad "${EP_DIR}" # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199 RUN \ mkdir -p /usr/share/man/man1 && \ - npm install pnpm -g && \ + npm install pnpm@9.0.4 -g && \ apk update && apk upgrade && \ - apk add \ + apk add --no-cache \ ca-certificates \ curl \ git \ @@ -95,30 +112,36 @@ USER etherpad WORKDIR "${EP_DIR}" -# etherpads version feature requires this. Only copy what is really needed COPY --chown=etherpad:etherpad ${SETTINGS} ./settings.json COPY --chown=etherpad:etherpad ./var ./var COPY --chown=etherpad:etherpad ./bin ./bin COPY --chown=etherpad:etherpad ./pnpm-workspace.yaml ./package.json ./ -COPY --chown=etherpad:etherpad ./src ./src -FROM build as development +FROM build AS development -COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin +COPY --chown=etherpad:etherpad ./src/ ./src/ +COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/ templates/admin./src/templates/admin +COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/static/oidc ./src/static/oidc RUN bin/installDeps.sh && \ - { [ -z "${ETHERPAD_PLUGINS}" ] || pnpm install --workspace-root ${ETHERPAD_PLUGINS}; } + if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ] || [ ! -z "${ETHERPAD_GITHUB_PLUGINS}" ]; then \ + pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_PLUGINS}}; \ + fi -FROM build as production + +FROM build AS production ENV NODE_ENV=production ENV ETHERPAD_PRODUCTION=true -COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin - -RUN bin/installDeps.sh && rm -rf ~/.npm && \ - { [ -z "${ETHERPAD_PLUGINS}" ] || pnpm install --workspace-root ${ETHERPAD_PLUGINS}; } +COPY --chown=etherpad:etherpad ./src ./src +COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/templates/admin ./src/templates/admin +COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/static/oidc ./src/static/oidc +RUN bin/installDeps.sh && rm -rf ~/.npm && rm -rf ~/.local && rm -rf ~/.cache && \ + if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ] || [ ! -z "${ETHERPAD_GITHUB_PLUGINS}" ]; then \ + pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_PLUGINS}}; \ + fi # Copy the configuration file. COPY --chown=etherpad:etherpad ${SETTINGS} "${EP_DIR}"/settings.json diff --git a/README.md b/README.md index 6bfe6a65c7e..50f1d17730f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Etherpad: A real-time collaborative editor for the web - + ## About @@ -21,7 +21,6 @@ We're looking for maintainers and have some funding available. Please contact J ### Code Quality [](https://github.com/ether/etherpad-lite/actions/workflows/codeql-analysis.yml) -[](https://github.com/ether/etherpad-lite/actions/workflows/lint-package-lock.yml) ### Development @@ -53,74 +52,74 @@ docker-compose up -d --build --force-recreate ## Installation -### Requirements - -[Node.js](https://nodejs.org/) >= **18.18.2**. - -### GNU/Linux and other UNIX-like systems - -#### Quick install on Debian/Ubuntu - -Install the latest Node.js LTS per [official install instructions](https://github.com/nodesource/distributions#installation-instructions), then: -```sh -git clone --branch master https://github.com/ether/etherpad-lite.git && -cd etherpad-lite && -bin/run.sh +### Docker-Compose + +```yaml +services: + app: + user: "0:0" + image: etherpad/etherpad:latest + tty: true + stdin_open: true + volumes: + - plugins:/opt/etherpad-lite/src/plugin_packages + - etherpad-var:/opt/etherpad-lite/var + depends_on: + - postgres + environment: + NODE_ENV: production + ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_ADMIN_PASSWORD:-admin} + DB_CHARSET: ${DOCKER_COMPOSE_APP_DB_CHARSET:-utf8mb4} + DB_HOST: postgres + DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad} + DB_PASS: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin} + DB_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432} + DB_TYPE: "postgres" + DB_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin} + # For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad + DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEFAULT_PAD_TEXT:- } + DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DISABLE_IP_LOGGING:-false} + SOFFICE: ${DOCKER_COMPOSE_APP_SOFFICE:-null} + TRUST_PROXY: ${DOCKER_COMPOSE_APP_TRUST_PROXY:-true} + restart: always + ports: + - "${DOCKER_COMPOSE_APP_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_PORT_TARGET:-9001}" + + postgres: + image: postgres:15-alpine + environment: + POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad} + POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin} + POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432} + POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin} + PGDATA: /var/lib/postgresql/data/pgdata + restart: always + # Exposing the port is not needed unless you want to access this database instance from the host. + # Be careful when other postgres docker container are running on the same port + # ports: + # - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data/pgdata + +volumes: + postgres_data: + plugins: + etherpad-var: ``` -#### Manual install - -You'll need Git and [Node.js](https://nodejs.org/) installed. - -**As any user (we recommend creating a separate user called etherpad):** - - 1. Move to a folder where you want to install Etherpad. - 2. Clone the Git repository: `git clone --branch master - https://github.com/ether/etherpad-lite.git` - 3. Change into the new directory containing the cloned source code: `cd - etherpad-lite` - 4. Run `bin/run.sh` and open http://127.0.0.1:9001 in your browser. - -To update to the latest released version, execute `git pull origin`. The next -start with `bin/run.sh` will update the dependencies. - -### Windows - -#### Prebuilt Windows package - -This package runs on any Windows machine. You can perform a manual installation -via git for development purposes, but as this uses symlinks which performs -unreliably on Windows, please stick to the prebuilt package if possible. - - 1. [Download the latest Windows package](https://etherpad.org/#download) - 2. Extract the folder - -Run `start.bat` and open <http://localhost:9001> in your browser. - -#### Manually install on Windows - -You'll need [Node.js](https://nodejs.org) and (optionally, though recommended) -git. - - 1. Grab the source, either: - * download <https://github.com/ether/etherpad-lite/zipball/master> - * or `git clone --branch master - https://github.com/ether/etherpad-lite.git` - 2. With a "Run as administrator" command prompt execute - `bin\installOnWindows.bat` - -Now, run `start.bat` and open http://localhost:9001 in your browser. +### Requirements -Update to the latest version with `git pull origin`, then run -`bin\installOnWindows.bat`, again. +[Node.js](https://nodejs.org/) >= **18.18.2**. -If cloning to a subdirectory within another project, you may need to do the -following: +### Windows, macOS, Linux - 1. Start the server manually (e.g. `node src/node/server.ts`) - 2. Edit the db `filename` in `settings.json` to the relative directory with - the file (e.g. `application/lib/etherpad-lite/var/dirty.db`) - 3. Add auto-generated files to the main project `.gitignore` +1. Download the latest Node.js runtime from [nodejs.org](https://nodejs.org/). +2. Install pnpm: `npm install -g pnpm` (Administrator privileges may be required). +3. Clone the repository: `git clone -b master` +4. Run `pnpm i` +5. Run `pnpm run build:etherpad` +6. Run `pnpm run prod` +7. Visit `http://localhost:9001` in your browser. ### Docker container @@ -130,9 +129,9 @@ Find [here](doc/docker.adoc) information on running Etherpad in a container. Etherpad is very customizable through plugins. - + - + ### Available Plugins @@ -148,7 +147,7 @@ Alternatively, you can install plugins from the command line: ```sh cd /path/to/etherpad-lite -pnpm run install-plugins ep_${plugin_name} +pnpm run plugins i ep_${plugin_name} ``` Also see [the plugin wiki @@ -160,7 +159,7 @@ Run the following command in your Etherpad folder to get all of the features visible in the above demo gif: ```sh -pnpm run install-plugins \ +pnpm run plugins i \ ep_align \ ep_comments_page \ ep_embedded_hyperlinks2 \ @@ -220,7 +219,7 @@ edit `settings.json` and restart Etherpad each time. Open http://127.0.0.1:9001/p/test#skinvariantsbuilder in your browser and start playing! - + ## Helpful resources diff --git a/admin/package.json b/admin/package.json index 9f829a531bc..527d725d5cf 100644 --- a/admin/package.json +++ b/admin/package.json @@ -1,39 +1,42 @@ { "name": "admin", "private": true, - "version": "2.0.1", + "version": "2.2.2", "type": "module", "scripts": { "dev": "vite", "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "build-copy": "tsc && vite build --outDir ../src/templates/admin --emptyOutDir", "preview": "vite preview" }, - "dependencies": {}, + "dependencies": { + "@radix-ui/react-switch": "^1.1.0" + }, "devDependencies": { - "@radix-ui/react-dialog": "^1.0.5", - "@radix-ui/react-toast": "^1.1.5", - "i18next": "^23.10.1", - "i18next-browser-languagedetector": "^7.2.0", - "lucide-react": "^0.356.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-hook-form": "^7.51.0", - "react-i18next": "^14.1.0", - "react-router-dom": "^6.22.3", - "zustand": "^4.5.2", - "@types/react": "^18.2.56", - "@types/react-dom": "^18.2.19", - "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.0.2", + "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-toast": "^1.2.1", + "@types/react": "^18.3.4", + "@types/react-dom": "^18.2.25", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "@vitejs/plugin-react-swc": "^3.5.0", - "eslint": "^8.56.0", + "eslint": "^9.9.0", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.5", - "socket.io-client": "^4.7.4", - "typescript": "^5.2.2", - "vite": "^5.1.4", - "vite-plugin-static-copy": "^1.0.1", - "vite-plugin-svgr": "^4.2.0" + "eslint-plugin-react-refresh": "^0.4.10", + "i18next": "^23.14.0", + "i18next-browser-languagedetector": "^8.0.0", + "lucide-react": "^0.429.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.52.2", + "react-i18next": "^15.0.1", + "react-router-dom": "^6.26.1", + "socket.io-client": "^4.7.5", + "typescript": "^5.5.4", + "vite": "^5.4.2", + "vite-plugin-static-copy": "^1.0.6", + "vite-plugin-svgr": "^4.2.0", + "zustand": "^4.5.5" } } diff --git a/admin/src/App.tsx b/admin/src/App.tsx index a1a1e4377af..b3238ef9a73 100644 --- a/admin/src/App.tsx +++ b/admin/src/App.tsx @@ -6,7 +6,7 @@ import {NavLink, Outlet, useNavigate} from "react-router-dom"; import {useStore} from "./store/store.ts"; import {LoadingScreen} from "./utils/LoadingScreen.tsx"; import {Trans, useTranslation} from "react-i18next"; -import {Cable, Construction, Crown, NotepadText, Wrench} from "lucide-react"; +import {Cable, Construction, Crown, NotepadText, Wrench, PhoneCall} from "lucide-react"; const WS_URL = import.meta.env.DEV? 'http://localhost:9001' : '' export const App = ()=> { @@ -96,8 +96,10 @@ export const App = ()=> { <ul> <li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li> <li><NavLink to={"/settings"}><Wrench/><Trans i18nKey="admin_settings"/></NavLink></li> - <li> <NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li> - <li><NavLink to={"/pads"}><NotepadText/><Trans i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></NavLink></li> + <li><NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li> + <li><NavLink to={"/pads"}><NotepadText/><Trans + i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></NavLink></li> + <li><NavLink to={"/shout"}><PhoneCall/>Communication</NavLink></li> </ul> </div> </div> diff --git a/admin/src/components/ShoutType.ts b/admin/src/components/ShoutType.ts new file mode 100644 index 00000000000..f7e8b1df3dc --- /dev/null +++ b/admin/src/components/ShoutType.ts @@ -0,0 +1,13 @@ +export type ShoutType = { + type: string, + data:{ + type: string, + payload: { + message: { + message: string, + sticky: boolean + }, + timestamp: number + } + } +} diff --git a/admin/src/index.css b/admin/src/index.css index 7a8e5df4821..99a406ee70d 100644 --- a/admin/src/index.css +++ b/admin/src/index.css @@ -250,11 +250,20 @@ td, th { color: #666; } + +.settings-page { + display: flex; + flex-direction: column; + gap: 20px; + height: 100%; +} + .settings { + flex-grow: max(1, 1); outline: none; width: 100%; - min-height: 80vh; resize: none; + font-family: monospace; } #response { @@ -596,6 +605,25 @@ pre { outline: none; } + +.send-message { + position: relative; +} + +.send-message input { + width: auto; +} + +.send-message { +} + +.send-message svg { + position: absolute; + right: 3px; + bottom: -3px; + left: auto !important; +} + .search-field svg { position: absolute; left: 3px; @@ -725,3 +753,61 @@ input, button, select, optgroup, textarea { right: 10px; color: #666; } + + +.SwitchRoot { + align-self: center; + width: 60px; + height: 30px; + background-color: black; + border-radius: 9999px; + position: relative; + box-shadow: 0 2px 10px var(--black-a7); + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +.SwitchRoot:focus { + box-shadow: 0 0 0 2px black; +} +.SwitchRoot[data-state='checked'] { + background-color: var(--etherpad-color); +} + +.SwitchThumb { + display: block; + width: 20px; + height: 20px; + background-color: white; + border-radius: 9999px; + box-shadow: 0 2px 2px var(--black-a7); + transition: transform 100ms; + transform: translateX(2px); + will-change: transform; +} +.SwitchThumb[data-state='checked'] { + transform: translateX(25px); +} + +.Label { + color: white; + font-size: 15px; + line-height: 1; +} + +.message { + position: relative; + padding: 10px; + border: 1px solid #e0e0e0; + margin: 10px 20px 10px 10px; + border-radius: 10px 0 10px 10px; + background-color: var(--etherpad-color); + color: white +} + +.search-pads{ + text-align: center; +} + +.search-pads-body tr td:last-child { + display: flex; + justify-content: center; +} diff --git a/admin/src/main.tsx b/admin/src/main.tsx index 03ec7310491..5efc26de6ba 100644 --- a/admin/src/main.tsx +++ b/admin/src/main.tsx @@ -12,6 +12,7 @@ import {I18nextProvider} from "react-i18next"; import i18n from "./localization/i18n.ts"; import {PadPage} from "./pages/PadPage.tsx"; import {ToastDialog} from "./utils/Toast.tsx"; +import {ShoutPage} from "./pages/ShoutPage.tsx"; const router = createBrowserRouter(createRoutesFromElements( <><Route element={<App/>}> @@ -20,6 +21,7 @@ const router = createBrowserRouter(createRoutesFromElements( <Route path="/settings" element={<SettingsPage/>}/> <Route path="/help" element={<HelpPage/>}/> <Route path="/pads" element={<PadPage/>}/> + <Route path="/shout" element={<ShoutPage/>}/> </Route><Route path="/login"> <Route index element={<LoginScreen/>}/> </Route></> diff --git a/admin/src/pages/HelpPage.tsx b/admin/src/pages/HelpPage.tsx index 6f06907e196..dd9695b0a4a 100644 --- a/admin/src/pages/HelpPage.tsx +++ b/admin/src/pages/HelpPage.tsx @@ -21,7 +21,7 @@ export const HelpPage = () => { return <div key={hookName+i}> <h3>{hookName}</h3> <ul> - {Object.keys(hooks[hookName]).map((hook, i) => <li>{hook} + {Object.keys(hooks[hookName]).map((hook, i) => <li key={hook+i}>{hook} <ul key={hookName+hook+i}> {Object.keys(hooks[hookName][hook]).map((subHook, i) => <li key={i}>{subHook}</li>)} </ul> @@ -46,12 +46,12 @@ export const HelpPage = () => { </div> <h2><Trans i18nKey="admin_plugins.installed"/></h2> <ul> - {helpData.installedPlugins.map((plugin, i) => <li key={i}>{plugin}</li>)} + {helpData.installedPlugins.map((plugin, i) => <li key={plugin+i}>{plugin}</li>)} </ul> <h2><Trans i18nKey="admin_plugins_info.parts"/></h2> <ul> - {helpData.installedParts.map((part, i) => <li key={i}>{part}</li>)} + {helpData.installedParts.map((part, i) => <li key={part+i}>{part}</li>)} </ul> <h2><Trans i18nKey="admin_plugins_info.hooks"/></h2> diff --git a/admin/src/pages/HomePage.tsx b/admin/src/pages/HomePage.tsx index 0ea1a26d451..c0d5913eefa 100644 --- a/admin/src/pages/HomePage.tsx +++ b/admin/src/pages/HomePage.tsx @@ -6,14 +6,51 @@ import {Trans, useTranslation} from "react-i18next"; import {SearchField} from "../components/SearchField.tsx"; import {Download, Trash} from "lucide-react"; import {IconButton} from "../components/IconButton.tsx"; +import {determineSorting} from "../utils/sorting.ts"; export const HomePage = () => { const pluginsSocket = useStore(state=>state.pluginsSocket) const [plugins,setPlugins] = useState<PluginDef[]>([]) const [installedPlugins, setInstalledPlugins] = useState<InstalledPlugin[]>([]) + const [searchParams, setSearchParams] = useState<SearchParams>({ + offset: 0, + limit: 99999, + sortBy: 'name', + sortDir: 'asc', + searchTerm: '' + }) + + const filteredInstallablePlugins = useMemo(()=>{ + return plugins.sort((a, b)=>{ + if(searchParams.sortBy === "version"){ + if(searchParams.sortDir === "asc"){ + return a.version.localeCompare(b.version) + } + return b.version.localeCompare(a.version) + } + + if(searchParams.sortBy === "last-updated"){ + if(searchParams.sortDir === "asc"){ + return a.time.localeCompare(b.time) + } + return b.time.localeCompare(a.time) + } + + + if (searchParams.sortBy === "name") { + if(searchParams.sortDir === "asc"){ + return a.name.localeCompare(b.name) + } + return b.name.localeCompare(a.name) + } + return 0 + }) + }, [plugins, searchParams]) + const sortedInstalledPlugins = useMemo(()=>{ return installedPlugins.sort((a, b)=>{ + if(a.name < b.name){ return -1 } @@ -23,14 +60,8 @@ export const HomePage = () => { return 0 }) - } ,[installedPlugins]) - const [searchParams, setSearchParams] = useState<SearchParams>({ - offset: 0, - limit: 99999, - sortBy: 'name', - sortDir: 'asc', - searchTerm: '' - }) + } ,[installedPlugins, searchParams]) + const [searchTerm, setSearchTerm] = useState<string>('') const {t} = useTranslation() @@ -93,17 +124,20 @@ export const HomePage = () => { if (!pluginsSocket) { return } - pluginsSocket?.emit('search', searchParams) - - pluginsSocket!.on('results:search', (data: { results: PluginDef[] }) => { setPlugins(data.results) }) - - + pluginsSocket!.on('results:searcherror', (data: {error: string}) => { + console.log(data.error) + useStore.getState().setToastState({ + open: true, + title: "Error retrieving plugins", + success: false + }) + }) }, [searchParams, pluginsSocket]); const uninstallPlugin = (pluginName: string)=>{ @@ -117,7 +151,6 @@ export const HomePage = () => { setPlugins(plugins.filter(plugin=>plugin.name !== pluginName)) } - useDebounce(()=>{ setSearchParams({ ...searchParams, @@ -142,7 +175,7 @@ export const HomePage = () => { <tbody style={{overflow: 'auto'}}> {sortedInstalledPlugins.map((plugin, index) => { return <tr key={index}> - <td>{plugin.name}</td> + <td><a rel="noopener noreferrer" href={`https://npmjs.com/${plugin.name}`} target="_blank">{plugin.name}</a></td> <td>{plugin.version}</td> <td> { @@ -153,35 +186,58 @@ export const HomePage = () => { </td> </tr> })} - </tbody> - </table> + </tbody> + </table> - <h2><Trans i18nKey="admin_plugins.available"/></h2> - <SearchField onChange={v=>{setSearchTerm(v.target.value)}} placeholder={t('admin_plugins.available_search.placeholder')} value={searchTerm}/> + <h2><Trans i18nKey="admin_plugins.available"/></h2> + <SearchField onChange={v=>{setSearchTerm(v.target.value)}} placeholder={t('admin_plugins.available_search.placeholder')} value={searchTerm}/> <table id="available-plugins"> <thead> <tr> - <th><Trans i18nKey="admin_plugins.name"/></th> + <th className={determineSorting(searchParams.sortBy, searchParams.sortDir == "asc", 'name')} onClick={()=>{ + setSearchParams({ + ...searchParams, + sortBy: 'name', + sortDir: searchParams.sortDir === "asc"? "desc": "asc" + }) + }}> + <Trans i18nKey="admin_plugins.name" /></th> <th style={{width: '30%'}}><Trans i18nKey="admin_plugins.description"/></th> - <th><Trans i18nKey="admin_plugins.version"/></th> - <th><Trans i18nKey="admin_plugins.last-update"/></th> + <th className={determineSorting(searchParams.sortBy, searchParams.sortDir == "asc", 'version')} onClick={()=>{ + setSearchParams({ + ...searchParams, + sortBy: 'version', + sortDir: searchParams.sortDir === "asc"? "desc": "asc" + }) + }}><Trans i18nKey="admin_plugins.version"/></th> + <th className={determineSorting(searchParams.sortBy, searchParams.sortDir == "asc", 'last-updated')} onClick={()=>{ + setSearchParams({ + ...searchParams, + sortBy: 'last-updated', + sortDir: searchParams.sortDir === "asc"? "desc": "asc" + }) + }}><Trans i18nKey="admin_plugins.last-update"/></th> <th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th> </tr> </thead> <tbody style={{overflow: 'auto'}}> - {plugins.map((plugin) => { - return <tr key={plugin.name}> - <td><a rel="noopener noreferrer" href={`https://npmjs.com/${plugin.name}`} target="_blank">{plugin.name}</a></td> - <td>{plugin.description}</td> - <td>{plugin.version}</td> - <td>{plugin.time}</td> - <td> - <IconButton icon={<Download/>} onClick={() => installPlugin(plugin.name)} title={<Trans i18nKey="admin_plugins.available_install.value"/>}/> - </td> - </tr> - })} + {(filteredInstallablePlugins.length > 0) ? + filteredInstallablePlugins.map((plugin) => { + return <tr key={plugin.name}> + <td><a rel="noopener noreferrer" href={`https://npmjs.com/${plugin.name}`} target="_blank">{plugin.name}</a></td> + <td>{plugin.description}</td> + <td>{plugin.version}</td> + <td>{plugin.time}</td> + <td> + <IconButton icon={<Download/>} onClick={() => installPlugin(plugin.name)} title={<Trans i18nKey="admin_plugins.available_install.value"/>}/> + </td> + </tr> + }) + : + <tr><td colSpan={5}>{searchTerm == '' ? <Trans i18nKey="pad.loading"/>: <Trans i18nKey="admin_plugins.available_not-found"/>}</td></tr> + } </tbody> </table> </div> diff --git a/admin/src/pages/LoginScreen.tsx b/admin/src/pages/LoginScreen.tsx index ade83cd4c35..61ac8993e7e 100644 --- a/admin/src/pages/LoginScreen.tsx +++ b/admin/src/pages/LoginScreen.tsx @@ -46,7 +46,7 @@ export const LoginScreen = ()=>{ <input {...register('username', { required: true })} className="login-textinput input-control" type="text" placeholder="Username"/> - <div>Passwort</div> + <div>Password</div> <span className="icon-input"> <input {...register('password', { required: true diff --git a/admin/src/pages/PadPage.tsx b/admin/src/pages/PadPage.tsx index b5f2128bf27..e663603cdd9 100644 --- a/admin/src/pages/PadPage.tsx +++ b/admin/src/pages/PadPage.tsx @@ -26,7 +26,7 @@ export const PadPage = ()=>{ const [padToDelete, setPadToDelete] = useState<string>('') const pages = useMemo(()=>{ if(!pads){ - return [0] + return 0; } return Math.ceil(pads!.total / searchParams.limit) @@ -104,7 +104,7 @@ export const PadPage = ()=>{ <SearchField value={searchTerm} onChange={v=>setSearchTerm(v.target.value)} placeholder={t('ep_admin_pads:ep_adminpads2_search-heading')}/> <table> <thead> - <tr> + <tr className="search-pads"> <th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'padName')} onClick={()=>{ setSearchParams({ ...searchParams, @@ -112,17 +112,17 @@ export const PadPage = ()=>{ ascending: !searchParams.ascending }) }}><Trans i18nKey="ep_admin_pads:ep_adminpads2_padname"/></th> - <th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'lastEdited')} onClick={()=>{ + <th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'userCount')} onClick={()=>{ setSearchParams({ ...searchParams, - sortBy: 'lastEdited', + sortBy: 'userCount', ascending: !searchParams.ascending }) }}><Trans i18nKey="ep_admin_pads:ep_adminpads2_pad-user-count"/></th> - <th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'userCount')} onClick={()=>{ + <th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'lastEdited')} onClick={()=>{ setSearchParams({ ...searchParams, - sortBy: 'userCount', + sortBy: 'lastEdited', ascending: !searchParams.ascending }) }}><Trans i18nKey="ep_admin_pads:ep_adminpads2_last-edited"/></th> @@ -136,7 +136,7 @@ export const PadPage = ()=>{ <th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th> </tr> </thead> - <tbody> + <tbody className="search-pads-body"> { pads?.results?.map((pad)=>{ return <tr key={pad.padName}> @@ -166,11 +166,12 @@ export const PadPage = ()=>{ offset: (Number(currentPage)-1)*searchParams.limit}) }}><ChevronLeft/><span>Previous Page</span></button> <span>{currentPage+1} out of {pages}</span> - <button disabled={pages == currentPage+1} onClick={()=>{ - setCurrentPage(currentPage+1) + <button disabled={pages == 0 || pages == currentPage+1} onClick={()=>{ + const newCurrentPage = currentPage+1 + setCurrentPage(newCurrentPage) setSearchParams({ ...searchParams, - offset: (Number(currentPage)-1)*searchParams.limit + offset: (Number(newCurrentPage))*searchParams.limit }) }}><span>Next Page</span><ChevronRight/></button> </div> diff --git a/admin/src/pages/Plugin.ts b/admin/src/pages/Plugin.ts index 3188c247f0a..f5563863b53 100644 --- a/admin/src/pages/Plugin.ts +++ b/admin/src/pages/Plugin.ts @@ -20,7 +20,7 @@ export type SearchParams = { searchTerm: string, offset: number, limit: number, - sortBy: 'name'|'version', + sortBy: 'name'|'version'|'last-updated', sortDir: 'asc'|'desc' } diff --git a/admin/src/pages/SettingsPage.tsx b/admin/src/pages/SettingsPage.tsx index 2706ffa3847..f781f67e1cd 100644 --- a/admin/src/pages/SettingsPage.tsx +++ b/admin/src/pages/SettingsPage.tsx @@ -1,14 +1,14 @@ import {useStore} from "../store/store.ts"; -import {isJSONClean} from "../utils/utils.ts"; +import {isJSONClean, cleanComments} from "../utils/utils.ts"; import {Trans} from "react-i18next"; import {IconButton} from "../components/IconButton.tsx"; import {RotateCw, Save} from "lucide-react"; export const SettingsPage = ()=>{ const settingsSocket = useStore(state=>state.settingsSocket) - const settings = useStore(state=>state.settings) + const settings = cleanComments(useStore(state=>state.settings)) - return <div> + return <div className="settings-page"> <h1><Trans i18nKey="admin_settings.current"/></h1> <textarea value={settings} className="settings" onChange={v => { useStore.getState().setSettings(v.target.value) diff --git a/admin/src/pages/ShoutPage.tsx b/admin/src/pages/ShoutPage.tsx new file mode 100644 index 00000000000..8348e18fa8c --- /dev/null +++ b/admin/src/pages/ShoutPage.tsx @@ -0,0 +1,76 @@ +import {useEffect, useState} from "react"; +import {SendHorizonal} from 'lucide-react' +import {useStore} from "../store/store.ts"; +import * as Switch from '@radix-ui/react-switch'; +import {ShoutType} from "../components/ShoutType.ts"; + +export const ShoutPage = ()=>{ + const [totalUsers, setTotalUsers] = useState(0); + const [message, setMessage] = useState<string>(""); + const [sticky, setSticky] = useState<boolean>(false); + const socket = useStore(state => state.settingsSocket); + const [shouts, setShouts] = useState<ShoutType[]>([]); + + useEffect(() => { + fetch('/stats') + .then(response => response.json()) + .then(data => setTotalUsers(data.totalUsers)); + }, []); + + + useEffect(() => { + if(socket) { + socket.on('shout', (shout) => { + setShouts([...shouts, shout]) + }) + } + }, [socket, shouts]) + + const sendMessage = () => { + socket?.emit('shout', { + message, + sticky + }); + setMessage('') + } + + return ( + <div> + <h1>Communication</h1> + {totalUsers > 0 && <p>There {totalUsers>1?"are":"is"} currently {totalUsers} user{totalUsers>1?"s":""} online</p>} + <div style={{height: '80vh', display: 'flex', flexDirection: 'column'}}> + <div style={{flexGrow: 1, backgroundColor: 'white', overflowY: "auto"}}> + { + shouts.map((shout) => { + return ( + <div key={shout.data.payload.timestamp} className="message"> + <div>{shout.data.payload.message.message}</div> + <div style={{display: 'flex'}}> + <div style={{flexGrow: 1}}></div> + <div + style={{color: "lightgray"}}>{new Date(shout.data.payload.timestamp).toLocaleTimeString() + + " " + new Date(shout.data.payload.timestamp).toLocaleDateString()}</div> + </div> + </div> + ) + }) + } + </div> + <form onSubmit={(e) => { + e.preventDefault() + sendMessage() + }} className="send-message search-field" style={{display: 'flex', gap: '10px'}}> + <Switch.Root title="Change sticky message" className="SwitchRoot" checked={sticky} + onCheckedChange={() => { + setSticky(!sticky); + }}> + <Switch.Thumb className="SwitchThumb"/> + </Switch.Root> + <input required value={message} onChange={v=>setMessage(v.target.value)} + style={{width: '100%', paddingRight: '55px', backgroundColor: '#e0e0e0', flexGrow: 1}}/> + <SendHorizonal style={{bottom: '5px', right: '9px', color: '#0f775b'}} onClick={()=>sendMessage()}/> + </form> + </div> + </div> + ) +} diff --git a/admin/src/utils/utils.ts b/admin/src/utils/utils.ts index 2e8f52a0521..960de455f90 100644 --- a/admin/src/utils/utils.ts +++ b/admin/src/utils/utils.ts @@ -1,5 +1,14 @@ -const minify = (json: string)=>{ +export const cleanComments = (json: string|undefined)=>{ + if (json !== undefined){ + json = json.replace(/\/\*.*?\*\//g, ""); // remove single line comments + json = json.replace(/ *\/\*.*(.|\n)*?\*\//g, ""); // remove multi line comments + json = json.replace(/[ \t]+$/gm, ""); // trim trailing spaces + json = json.replace(/^(\n)/gm, ""); // remove empty lines + } + return json; +} +export const minify = (json: string)=>{ let tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g, in_string = false, in_multiline_comment = false, @@ -49,9 +58,6 @@ const minify = (json: string)=>{ return new_str.join(""); } - - - export const isJSONClean = (data: string) => { let cleanSettings = minify(data); // this is a bit naive. In theory some key/value might contain the sequences ',]' or ',}' diff --git a/admin/vite.config.ts b/admin/vite.config.ts index ff329032fcf..23921ca8541 100644 --- a/admin/vite.config.ts +++ b/admin/vite.config.ts @@ -15,7 +15,8 @@ export default defineConfig({ })], base: '/admin', build:{ - outDir: '../src/templates/admin' + outDir: '../src/templates/admin', + emptyOutDir: true, }, server:{ proxy: { @@ -27,8 +28,11 @@ export default defineConfig({ '/admin-auth/': { target: 'http://localhost:9001', changeOrigin: true, - rewrite: (path) => path.replace(/^\/admin-prox/, '/admin/') + }, + '/stats': { + target: 'http://localhost:9001', + changeOrigin: true, + } } - } } }) diff --git a/bin/buildForWindows.sh b/bin/buildForWindows.sh index 67f4eeae1ae..99a9fcf2f48 100755 --- a/bin/buildForWindows.sh +++ b/bin/buildForWindows.sh @@ -50,21 +50,13 @@ rm -rf src/node_modules || true #$(try cd ./bin/installDeps.sh) # Install admin frontend -cd admin try pnpm install -try pnpm run build -cd .. - - - +try pnpm run build:etherpad # Nuke the admin folder as it is not needed anymore :D rm -rf admin - -export NODE_ENV=production -try pnpm install --production - - +rm -rf oidc +rm -rf src/node_modules log "copy the windows settings template..." try cp settings.json.template settings.json diff --git a/bin/checkAllPads.ts b/bin/checkAllPads.ts index a967413d19b..305afabab3d 100644 --- a/bin/checkAllPads.ts +++ b/bin/checkAllPads.ts @@ -25,4 +25,5 @@ if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js'); console.log(`Pad ${padId}: OK`); })); console.log('Finished.'); + process.exit(0) })(); diff --git a/bin/checkPad.ts b/bin/checkPad.ts index 09f3d30c5aa..a35e08ea791 100644 --- a/bin/checkPad.ts +++ b/bin/checkPad.ts @@ -11,12 +11,19 @@ process.on('unhandledRejection', (err) => { throw err; }); if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID'); // @ts-ignore const padId = process.argv[2]; -(async () => { + +const performCheck = async () => { const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); + console.log("Checking if " + padId + " exists") const padManager = require('ep_etherpad-lite/node/db/PadManager'); if (!await padManager.doesPadExists(padId)) throw new Error('Pad does not exist'); const pad = await padManager.getPad(padId); await pad.check(); - console.log('Finished.'); -})(); + console.log('Finished checking pad.'); + process.exit(0) +} + +performCheck() + .then(e=>console.log("Finished")) + .catch(e=>console.log("Finished with errors")) diff --git a/bin/commonPlugins.ts b/bin/commonPlugins.ts new file mode 100644 index 00000000000..7fc5fc1ad91 --- /dev/null +++ b/bin/commonPlugins.ts @@ -0,0 +1,17 @@ +import {PackageData} from "ep_etherpad-lite/node/types/PackageInfo"; +import {writeFileSync} from "fs"; +import {installedPluginsPath} from "ep_etherpad-lite/static/js/pluginfw/installer"; +const pluginsModule = require('ep_etherpad-lite/static/js/pluginfw/plugins'); + +export const persistInstalledPlugins = async () => { + const plugins:PackageData[] = [] + const installedPlugins = {plugins: plugins}; + for (const pkg of Object.values(await pluginsModule.getPackages()) as PackageData[]) { + installedPlugins.plugins.push({ + name: pkg.name, + version: pkg.version, + }); + } + installedPlugins.plugins = [...new Set(installedPlugins.plugins)]; + writeFileSync(installedPluginsPath, JSON.stringify(installedPlugins)); +}; diff --git a/bin/createUserSession.ts b/bin/createUserSession.ts index 1dbab0d692c..165cf287a39 100644 --- a/bin/createUserSession.ts +++ b/bin/createUserSession.ts @@ -14,6 +14,7 @@ import path from "node:path"; import querystring from "node:querystring"; import axios from 'axios' +import process from "node:process"; process.on('unhandledRejection', (err) => { throw err; }); @@ -53,4 +54,5 @@ const settings = require('ep_etherpad-lite/node/utils/Settings'); if (res.data.code === 1) throw new Error(`Error creating session: ${JSON.stringify(res.data)}`); console.log('Session made: ====> create a cookie named sessionID and set the value to', res.data.data.sessionID); + process.exit(0) })(); diff --git a/bin/deleteAllGroupSessions.ts b/bin/deleteAllGroupSessions.ts index 23d21c5942d..6064757ec31 100644 --- a/bin/deleteAllGroupSessions.ts +++ b/bin/deleteAllGroupSessions.ts @@ -49,4 +49,5 @@ const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSe } } console.log(`Deleted ${deleteCount} sessions`); + process.exit(0) })(); diff --git a/bin/deletePad.ts b/bin/deletePad.ts index 20037bb33c3..2f6009158a6 100644 --- a/bin/deletePad.ts +++ b/bin/deletePad.ts @@ -38,4 +38,5 @@ const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); const deleteAttempt = await axios.post(uri); if (deleteAttempt.data.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.data}`); console.log('Deleted pad', deleteAttempt.data); + process.exit(0) })(); diff --git a/bin/extractPadData.ts b/bin/extractPadData.ts index 3c10a3acc42..466b31b89a7 100644 --- a/bin/extractPadData.ts +++ b/bin/extractPadData.ts @@ -60,4 +60,5 @@ const padId = process.argv[2]; } console.log('finished'); + process.exit(0) })(); diff --git a/bin/generateReleaseNotes.ts b/bin/generateReleaseNotes.ts new file mode 100644 index 00000000000..0e4604b7c18 --- /dev/null +++ b/bin/generateReleaseNotes.ts @@ -0,0 +1,39 @@ +import {readFileSync} from "node:fs"; + +const changelog = readFileSync('../changelog.md') +const changelogText = changelog.toString() +const changelogLines = changelogText.split('\n') + + +let cliArgs = process.argv.slice(2) + +let tagVar = cliArgs[0] + +if (!tagVar) { + console.error("No tag provided") + process.exit(1) +} + +tagVar = tagVar.replace("refs/tags/v", "") + +let startNum = -1 +let endline = 0 + +let counter = 0 +for (const line of changelogLines) { + if (line.trim().startsWith("#") && (line.match(new RegExp("#", "g"))||[]).length === 1) { + if (startNum !== -1) { + endline = counter-1 + break + } + + const sanitizedLine = line.replace("#","").trim() + if(sanitizedLine.includes(tagVar)) { + startNum = counter + } + } + counter++ +} + +let currentReleaseNotes = changelogLines.slice(startNum, endline).join('\n') +console.log(currentReleaseNotes) diff --git a/bin/importSqlFile.ts b/bin/importSqlFile.ts index 23ad9b7d115..7660fa40738 100644 --- a/bin/importSqlFile.ts +++ b/bin/importSqlFile.ts @@ -6,7 +6,8 @@ import util from "node:util"; const fs = require('fs'); import log4js from 'log4js'; import readline from 'readline'; -import ueberDB from "ueberdb2"; +import {Database} from "ueberdb2"; +import process from "node:process"; const settings = require('ep_etherpad-lite/node/utils/Settings'); process.on('unhandledRejection', (err) => { throw err; }); @@ -56,7 +57,7 @@ const unescape = (val: string) => { writeInterval: 100, json: false, // data is already json encoded }; - const db = new ueberDB.Database( // eslint-disable-line new-cap + const db = new Database( // eslint-disable-line new-cap settings.dbType, settings.dbSettings, dbWrapperSettings, @@ -96,6 +97,8 @@ const unescape = (val: string) => { 'depended on dbms this may take some time..\n'); const closeDB = util.promisify(db.close.bind(db)); + // @ts-ignore await closeDB(null); log(`finished, imported ${keyNo} keys.`); + process.exit(0) })(); diff --git a/bin/installOnWindows.bat b/bin/installOnWindows.bat index 5b04919bdfc..97cddce24ff 100644 --- a/bin/installOnWindows.bat +++ b/bin/installOnWindows.bat @@ -19,6 +19,15 @@ IF EXIST admin ( cd /D .. ) +:: Install ui only if available +IF EXIST ui ( + cd /D .\ui + dir + cmd /C pnpm i || exit /B 1 + cmd /C pnpm run build || exit /B 1 + cd /D .. +) + cmd /C pnpm i || exit /B 1 diff --git a/bin/make_docs.ts b/bin/make_docs.ts new file mode 100644 index 00000000000..d414822dd6f --- /dev/null +++ b/bin/make_docs.ts @@ -0,0 +1,63 @@ +import {exec} from 'child_process' +import fs from 'fs' +import path from 'path' + +import pjson from '../src/package.json' + +const VERSION=pjson.version +console.log(`Building docs for version ${VERSION}`) + +const createDirIfNotExists = (dir: fs.PathLike) => { + if (!fs.existsSync(dir)){ + fs.mkdirSync(dir) + } +} + + +function copyFolderSync(from: fs.PathLike, to: fs.PathLike) { + if(fs.existsSync(to)){ + const stat = fs.lstatSync(to) + if (stat.isDirectory()){ + fs.rmSync(to, { recursive: true }) + } + else{ + fs.rmSync(to) + } + } + fs.mkdirSync(to); + fs.readdirSync(from).forEach(element => { + if (fs.lstatSync(path.join(<string>from, element)).isFile()) { + if (typeof from === "string") { + if (typeof to === "string") { + fs.copyFileSync(path.join(from, element), path.join(to, element)) + } + } + } else { + if (typeof from === "string") { + if (typeof to === "string") { + copyFolderSync(path.join(from, element), path.join(to, element)) + } + } + } + }); +} + +exec('asciidoctor -v', (err,stdout)=>{ + if (err){ + console.log('Please install asciidoctor') + console.log('https://asciidoctor.org/docs/install-toolchain/') + process.exit(1) + } +}); + + +createDirIfNotExists('../out') +createDirIfNotExists('../out/doc') +createDirIfNotExists('../out/doc/api') + + + +exec(`asciidoctor -D ../out/doc ../doc/index.adoc */**.adoc -a VERSION=${VERSION}`) +exec(`asciidoctor -D ../out/doc/api ../doc/api/*.adoc -a VERSION=${VERSION}`) + +copyFolderSync('../doc/public/', '../out/doc/') diff --git a/bin/migrateDirtyDBtoRealDB.ts b/bin/migrateDirtyDBtoRealDB.ts index 144f6e88cdd..3cd32a85a7d 100644 --- a/bin/migrateDirtyDBtoRealDB.ts +++ b/bin/migrateDirtyDBtoRealDB.ts @@ -1,7 +1,7 @@ 'use strict'; import process from 'node:process'; -import ueberDB from "ueberdb2"; +import {Database} from "ueberdb2"; import log4js from 'log4js'; import util from 'util'; const settings = require('ep_etherpad-lite/node/utils/Settings'); @@ -23,7 +23,7 @@ process.on('unhandledRejection', (err) => { throw err; }); cache: '0', // The cache slows things down when you're mostly writing. writeInterval: 0, // Write directly to the database, don't buffer }; - const db = new ueberDB.Database( // eslint-disable-line new-cap + const db = new Database( // eslint-disable-line new-cap settings.dbType, settings.dbSettings, dbWrapperSettings, @@ -31,7 +31,7 @@ process.on('unhandledRejection', (err) => { throw err; }); await db.init(); console.log('Waiting for dirtyDB to parse its file.'); - const dirty = await new ueberDB.Database('dirty',`${__dirname}/../var/dirty.db`); + const dirty = new Database('dirty', `${__dirname}/../var/dirty.db`); await dirty.init(); const keys = await dirty.findKeys('*', '') @@ -57,4 +57,5 @@ process.on('unhandledRejection', (err) => { throw err; }); await db.close(null); await dirty.close(null); console.log('Finished.'); + process.exit(0) })(); diff --git a/bin/package.json b/bin/package.json index a39797a8b03..4298f938796 100644 --- a/bin/package.json +++ b/bin/package.json @@ -1,25 +1,26 @@ { "name": "bin", - "version": "2.0.1", + "version": "2.2.2", "description": "", "main": "checkAllPads.js", "directories": { "doc": "doc" }, "dependencies": { - "axios": "^1.6.8", + "axios": "^1.7.5", "ep_etherpad-lite": "workspace:../src", "log4js": "^6.9.1", - "semver": "^7.6.0", - "tsx": "^4.7.1", - "ueberdb2": "^4.2.63" + "semver": "^7.6.3", + "tsx": "^4.17.0", + "ueberdb2": "^4.2.93" }, "devDependencies": { - "@types/node": "^20.11.27", + "@types/node": "^22.4.2", "@types/semver": "^7.5.8", - "typescript": "^5.4.2" + "typescript": "^5.5.4" }, "scripts": { + "makeDocs": "node --import tsx make_docs.ts", "checkPad": "node --import tsx checkPad.ts", "checkAllPads": "node --import tsx checkAllPads.ts", "createUserSession": "node --import tsx createUserSession.ts", @@ -32,7 +33,8 @@ "rebuildPad": "node --import tsx rebuildPad.ts", "stalePlugins": "node --import tsx ./plugins/stalePlugins.ts", "checkPlugin": "node --import tsx ./plugins/checkPlugin.ts", - "install-plugins": "node --import tsx ./installPlugins.ts" + "plugins": "node --import tsx ./plugins.ts", + "generateChangelog": "node --import tsx generateReleaseNotes.ts" }, "author": "", "license": "ISC" diff --git a/bin/plugins.ts b/bin/plugins.ts new file mode 100644 index 00000000000..17c756f601f --- /dev/null +++ b/bin/plugins.ts @@ -0,0 +1,117 @@ +'use strict'; + +import {linkInstaller, checkForMigration} from "ep_etherpad-lite/static/js/pluginfw/installer"; +import {persistInstalledPlugins} from "./commonPlugins"; +import fs from "node:fs"; +const settings = require('ep_etherpad-lite/node/utils/Settings'); + +if (process.argv.length === 2) { + console.error('Expected at least one argument!'); + process.exit(1); +} + +let args = process.argv.slice(2) + + +const possibleActions = [ + "i", + "install", + "rm", + "remove", + "ls", + "list" +] + +const install = ()=> { + const argsAsString: string = args.join(" "); + const regexRegistryPlugins = /(?<=i\s)(.*?)(?=--github|--path|$)/; + const regexLocalPlugins = /(?<=--path\s)(.*?)(?=--github|$)/; + const regexGithubPlugins = /(?<=--github\s)(.*?)(?=--path|$)/; + const registryPlugins = argsAsString.match(regexRegistryPlugins)?.[0]?.split(" ")?.filter(s => s) || []; + const localPlugins = argsAsString.match(regexLocalPlugins)?.[0]?.split(" ")?.filter(s => s) || []; + const githubPlugins = argsAsString.match(regexGithubPlugins)?.[0]?.split(" ")?.filter(s => s) || []; + + async function run() { + for (const plugin of registryPlugins) { + if (possibleActions.includes(plugin)){ + continue + } + console.log(`Installing plugin from registry: ${plugin}`) + if (plugin.includes('@')) { + const [name, version] = plugin.split('@'); + await linkInstaller.installPlugin(name, version); + continue; + } + await linkInstaller.installPlugin(plugin); + } + + for (const plugin of localPlugins) { + console.log(`Installing plugin from path: ${plugin}`); + await linkInstaller.installFromPath(plugin); + } + + for (const plugin of githubPlugins) { + console.log(`Installing plugin from github: ${plugin}`); + await linkInstaller.installFromGitHub(plugin); + } + } + + (async () => { + await checkForMigration(); + await run(); + await persistInstalledPlugins(); + })(); +} + +const list = ()=>{ + const walk = async () => { + const plugins = fs.readFileSync(settings.root+"/var/installed_plugins.json", "utf-8") + const pluginNames = JSON.parse(plugins).plugins.map((plugin: any) => plugin.name).join(", ") + + console.log("Installed plugins are:", pluginNames) + } + + (async () => { + await walk(); + })(); +} + +const remove = (plugins: string[])=>{ + const walk = async () => { + for (const plugin of plugins) { + console.log(`Uninstalling plugin: ${plugin}`) + await linkInstaller.uninstallPlugin(plugin); + } + await persistInstalledPlugins(); + } + + (async () => { + await checkForMigration(); + await walk(); + })(); +} + +let action = args[0]; + +switch (action) { + case "install": + install(); + break; + case "i": + install(); + break; + case "ls": + list(); + break; + case "list": + list(); + break; + case "rm": + remove(args.slice(1)); + break; + default: + console.error('Expected at least one argument!'); + process.exit(1); +} + + diff --git a/bin/plugins/checkPlugin.ts b/bin/plugins/checkPlugin.ts index cdd78d7914f..e2ca59d5ee0 100644 --- a/bin/plugins/checkPlugin.ts +++ b/bin/plugins/checkPlugin.ts @@ -474,6 +474,6 @@ log4js.configure({ logger.info('No changes.'); } } - logger.info('Finished'); + process.exit(0) })(); diff --git a/bin/plugins/lib/backend-tests.yml b/bin/plugins/lib/backend-tests.yml index 7b69a00cd39..79ffc6f888e 100644 --- a/bin/plugins/lib/backend-tests.yml +++ b/bin/plugins/lib/backend-tests.yml @@ -69,7 +69,7 @@ jobs: working-directory: ./etherpad-lite run: | pnpm link --global $PLUGIN_NAME - pnpm run install-plugins --path ../../plugin + pnpm run plugins i --path ../../plugin env: PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }} - name: Link ep_etherpad-lite diff --git a/bin/plugins/lib/frontend-tests.yml b/bin/plugins/lib/frontend-tests.yml index d1eaf870102..053ce180dcd 100644 --- a/bin/plugins/lib/frontend-tests.yml +++ b/bin/plugins/lib/frontend-tests.yml @@ -78,7 +78,7 @@ jobs: - name: Run the frontend tests shell: bash run: | - pnpm run dev & + pnpm run prod & connected=false can_connect() { curl -sSfo /dev/null http://localhost:9001/ || return 1 diff --git a/bin/plugins/stalePlugins.ts b/bin/plugins/stalePlugins.ts index ce2b876ed22..563bf51d126 100644 --- a/bin/plugins/stalePlugins.ts +++ b/bin/plugins/stalePlugins.ts @@ -3,6 +3,7 @@ // Returns a list of stale plugins and their authors email import axios from 'axios' +import process from "node:process"; const currentTime = new Date(); (async () => { @@ -19,4 +20,5 @@ const currentTime = new Date(); console.log(`${name}, ${res.data[plugin].data.maintainers[0].email}`); } } + process.exit(0) })(); diff --git a/bin/push-after-release.sh b/bin/push-after-release.sh index b426f630bb9..326f8a7f7c6 100644 --- a/bin/push-after-release.sh +++ b/bin/push-after-release.sh @@ -1,3 +1,4 @@ + #!/bin/bash # Specify the path to your package.json file @@ -13,4 +14,4 @@ fi VERSION=$(jq -r '.version' "$PACKAGE_JSON_PATH") git push origin master develop $VERSION git push --tags -(cd ../ether.github.com && git push) \ No newline at end of file +(cd ../ether.github.com && git push) diff --git a/bin/rebuildPad.ts b/bin/rebuildPad.ts index 0d77940c096..8bb63ed84bb 100644 --- a/bin/rebuildPad.ts +++ b/bin/rebuildPad.ts @@ -7,6 +7,8 @@ // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. +import process from "node:process"; + process.on('unhandledRejection', (err) => { throw err; }); if (process.argv.length !== 4 && process.argv.length !== 5) { @@ -82,4 +84,5 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`; await db.shutdown(); console.info('finished'); + process.exit(0) })(); diff --git a/bin/release.ts b/bin/release.ts index 24c5a7d8b21..5c2b1b4e9cf 100644 --- a/bin/release.ts +++ b/bin/release.ts @@ -197,7 +197,7 @@ try { try { console.log('Building documentation...'); - run('node ./make_docs.js'); + run('pnpm run makeDocs'); console.log('Updating ether.github.com master branch...'); run('git pull --ff-only', {cwd: '../ether.github.com/'}); console.log('Committing documentation...'); diff --git a/bin/repairPad.ts b/bin/repairPad.ts index 848b7205f55..f59cf347ebc 100644 --- a/bin/repairPad.ts +++ b/bin/repairPad.ts @@ -57,4 +57,5 @@ let valueCount = 0; } console.info(`Finished: Replaced ${valueCount} values in the database`); + process.exit(0) })(); diff --git a/bin/run.sh b/bin/run.sh index 654897fa436..3f6b119bc48 100755 --- a/bin/run.sh +++ b/bin/run.sh @@ -22,7 +22,7 @@ Please type 'Etherpad rocks my socks' (or restart with the '--root' argument) if you still want to start it as root: EOF printf "> " >&2 - read rocks + read -r rocks [ "$rocks" = "Etherpad rocks my socks" ] || fatal "Your input was incorrect" fi @@ -32,9 +32,11 @@ bin/installDeps.sh "$@" || exit 1 ## Create the admin ui if [ -z "$NODE_ENV" ] || [ "$NODE_ENV" = "development" ]; then - ADMIN_UI_PATH="$(dirname $0)/../admin" + ADMIN_UI_PATH="$(dirname "$0")/../admin" + UI_PATH="$(dirname "$0")/../ui" log "Creating the admin UI..." - (cd $ADMIN_UI_PATH && pnpm run build) + (cd "$ADMIN_UI_PATH" && pnpm run build) + (cd "$UI_PATH" && pnpm run build) else log "Cannot create the admin UI in production mode" fi @@ -43,4 +45,4 @@ fi log "Starting Etherpad..." # cd src -exec pnpm run dev "$@" +exec pnpm run prod "$@" diff --git a/bin/tsconfig.json b/bin/tsconfig.json index e075f973c4d..afa29e71260 100644 --- a/bin/tsconfig.json +++ b/bin/tsconfig.json @@ -39,7 +39,7 @@ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ + "resolveJsonModule": true, /* Enable importing .json files. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000000..4c6f9986d4e --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,2 @@ +.vitepress/cache +dist diff --git a/doc/.vitepress/config.mts b/doc/.vitepress/config.mts new file mode 100644 index 00000000000..170f2189832 --- /dev/null +++ b/doc/.vitepress/config.mts @@ -0,0 +1,78 @@ +import { defineConfig } from 'vitepress' +import {version} from '../../package.json' +// https://vitepress.dev/reference/site-config +const commitRef = process.env.COMMIT_REF?.slice(0, 8) || 'dev' + + +export default defineConfig({ + title: "Etherpad Documentation", + description: "Next Generation Collaborative Document Editing", + base: '/', + themeConfig: { + search: { + provider: 'local' + }, + // https://vitepress.dev/reference/default-theme-config + nav: [ + { text: 'Home', link: '/' }, + { text: 'Getting started', link: '/docker.md' } + ], + logo:'/favicon.ico', + + sidebar: { + '/': [ + { + link: '/', + text: 'About', + items: [ + { text: 'Docker', link: '/docker.md' }, + { text: 'Localization', link: '/localization.md' }, + { text: 'Cookies', link: '/cookies.md' }, + { text: 'Plugins', link: '/plugins.md' }, + { text: 'Stats', link: '/stats.md' }, + {text: 'Skins', link: '/skins.md' }, + {text: 'Demo', link: '/demo.md' }, + ] + }, + { + text: 'API', + link: '/api/', + items: [ + { text: 'Changeset', link: '/api/changeset_library.md' }, + {text: 'Editbar', link: '/api/editbar.md' }, + {text: 'EditorInfo', link: '/api/editorInfo.md' }, + {text: 'Embed Parameters', link: '/api/embed_parameters.md' }, + {text: 'Hooks Client Side', link: '/api/hooks_client-side.md' }, + {text: 'Hooks Server Side', link: '/api/hooks_server-side.md' }, + {text: 'Plugins', link: '/api/pluginfw.md' }, + {text: 'Toolbar', link: '/api/toolbar.md' }, + {text: 'HTTP API', link: '/api/http_api.md' }, + ] + }, + { + text: 'Old Docs', + items: [ + { text: 'Easysync description', link: '/etherpad-lite/easysync/easysync-full-description.pdf' }, + { text: 'Easysync notes', link: '/etherpad-lite/easysync/easysync-notes.pdf' } + ] + } + ], + '/stats': [ + { + text: 'Stats', + items:[ + { text: 'Stats', link: '/stats/' } + ] + } + ] + }, + footer: { + message: `Published under Apache License`, + copyright: `(${commitRef}) v${version} by Etherpad Foundation` + }, + + socialLinks: [ + { icon: 'github', link: 'https://github.com/ether/etherpad-lite' } + ] + } +}) diff --git a/doc/.vitepress/theme/components/SvgImage.vue b/doc/.vitepress/theme/components/SvgImage.vue new file mode 100644 index 00000000000..3440988030d --- /dev/null +++ b/doc/.vitepress/theme/components/SvgImage.vue @@ -0,0 +1,22 @@ +<script setup lang="ts"> +defineProps<{ svg: string }>() +</script> + +<template> + <figure class="svg-image-root" v-html="svg" /> +</template> + +<style> +.svg-image-root { + background-color: #eee; + border-radius: 8px; + padding: 1ch; + margin: 1ch 0; +} +html.dark .svg-image-root { + background-color: #313641; +} +.svg-image-root svg text { + font-family: var(--vp-font-family-base); +} +</style> diff --git a/doc/.vitepress/theme/index.ts b/doc/.vitepress/theme/index.ts new file mode 100644 index 00000000000..b7c35ce47ba --- /dev/null +++ b/doc/.vitepress/theme/index.ts @@ -0,0 +1,12 @@ +import { h } from 'vue' +import type { Theme } from 'vitepress' +import DefaultTheme from 'vitepress/theme' +import './styles/vars.css' +import SvgImage from './components/SvgImage.vue' + +export default { + extends: DefaultTheme, + enhanceApp({ app }) { + app.component('SvgImage', SvgImage) + }, +} satisfies Theme diff --git a/doc/.vitepress/theme/styles/vars.css b/doc/.vitepress/theme/styles/vars.css new file mode 100644 index 00000000000..cc59c1443af --- /dev/null +++ b/doc/.vitepress/theme/styles/vars.css @@ -0,0 +1,77 @@ +/** + * Colors + * -------------------------------------------------------------------------- */ + +:root { + --vp-c-brand: #646cff; + --vp-c-brand-light: #747bff; + --vp-c-brand-lighter: #9499ff; + --vp-c-brand-lightest: #bcc0ff; + --vp-c-brand-dark: #535bf2; + --vp-c-brand-darker: #454ce1; + --vp-c-brand-dimm: rgba(100, 108, 255, 0.08); +} + +/** + * Component: Button + * -------------------------------------------------------------------------- */ + +:root { + --vp-button-brand-border: var(--vp-c-brand-light); + --vp-button-brand-text: var(--vp-c-white); + --vp-button-brand-bg: var(--vp-c-brand); + --vp-button-brand-hover-border: var(--vp-c-brand-light); + --vp-button-brand-hover-text: var(--vp-c-white); + --vp-button-brand-hover-bg: var(--vp-c-brand-light); + --vp-button-brand-active-border: var(--vp-c-brand-light); + --vp-button-brand-active-text: var(--vp-c-white); + --vp-button-brand-active-bg: var(--vp-button-brand-bg); +} + +/** + * Component: Home + * -------------------------------------------------------------------------- */ + +:root { + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient( + 120deg, + #0f775b 30%, + #0f775b + ); + + --vp-home-hero-image-background-image: linear-gradient( + -45deg, + #0f775b 50%, + #0f775b 50% + ); + --vp-home-hero-image-filter: blur(40px); +} + +@media (min-width: 640px) { + :root { + --vp-home-hero-image-filter: blur(56px); + } +} + +@media (min-width: 960px) { + :root { + --vp-home-hero-image-filter: blur(72px); + } +} + +/** + * Component: Custom Block + * -------------------------------------------------------------------------- */ + +:root { + --vp-custom-block-tip-border: var(--vp-c-brand); + --vp-custom-block-tip-text: var(--vp-c-brand-darker); + --vp-custom-block-tip-bg: var(--vp-c-brand-dimm); +} + +.dark { + --vp-custom-block-tip-border: var(--vp-c-brand); + --vp-custom-block-tip-text: var(--vp-c-brand-lightest); + --vp-custom-block-tip-bg: var(--vp-c-brand-dimm); +} diff --git a/doc/api/editbar.adoc b/doc/api/editbar.adoc index 8dc865f861c..9135d8d4f5d 100644 --- a/doc/api/editbar.adoc +++ b/doc/api/editbar.adoc @@ -1,14 +1,22 @@ -== Editbar -src/static/js/pad_editbar.js +# Editbar + +Located in `src/static/js/pad_editbar.js` === isEnabled() -=== disable() +If the editorbar contains the class `enabledtoolbar`, it is enabled. + + +## disable() + +Disables the editorbar. This is done by adding the class `disabledtoolbar` and removing the enabledtoolbar + +## toggleDropDown(dropdown) -=== toggleDropDown(dropdown) Shows the dropdown `div.popup` whose `id` equals `dropdown`. -=== registerCommand(cmd, callback) +## registerCommand(cmd, callback) + Register a handler for a specific command. Commands are fired if the corresponding button is clicked or the corresponding select is changed. === registerAceCommand(cmd, callback) diff --git a/doc/api/editorInfo.md b/doc/api/editorInfo.md new file mode 100644 index 00000000000..7b3c27153f0 --- /dev/null +++ b/doc/api/editorInfo.md @@ -0,0 +1,208 @@ +# EditorInfo + +Location: `src/static/js/ace2_inner.js` + +## editorInfo.ace_replaceRange(start, end, text) +This function replaces a range (from `start` to `end`) with `text`. + +## editorInfo.ace_getRep() + +Returns the `rep` object. The rep object consists of the following properties: + +- `lines`: Implemented as a skip list +- `selStart`: The start of the selection +- `selEnd`: The end of the selection +- `selFocusAtStart`: Whether the selection is focused at the start +- `alltext`: The entire text of the document +- `alines`: The entire text of the document, split into lines +- `apool`: The pool of attributes + +## editorInfo.ace_getAuthor() + +Returns the authors of the pad. If the pad has no authors, it returns an empty object. + + +## editorInfo.ace_inCallStack() + +Returns true if the editor is in the call stack. + +## editorInfo.ace_inCallStackIfNecessary(?) + +Executes the function if the editor is in the call stack. + +## editorInfo.ace_focus(?) + +Focuses the editor. + +## editorInfo.ace_importText(?) + +Imports text into the editor. + +## editorInfo.ace_importAText(?) + +Imports text and attributes into the editor. + +## editorInfo.ace_exportText(?) + +Exports the text from the editor. + +## editorInfo.ace_editorChangedSize(?) + +Changes the size of the editor. + +## editorInfo.ace_setOnKeyPress(?) + +Sets the key press event. + +## editorInfo.ace_setOnKeyDown(?) + +Sets the key down event. + +## editorInfo.ace_setNotifyDirty(?) + +Sets the dirty notification. + +## editorInfo.ace_dispose(?) + +Disposes the editor. + +## editorInfo.ace_setEditable(bool) + +Sets the editor to be editable or not. + +## editorInfo.ace_execCommand(?) + +Executes a command. + +## editorInfo.ace_callWithAce(fn, callStack, normalize) + +Calls a function with the ace instance. + +## editorInfo.ace_setProperty(key, value) + +Sets a property. + +## editorInfo.ace_setBaseText(txt) + +Sets the base text. + +## editorInfo.ace_setBaseAttributedText(atxt, apoolJsonObj) + +Sets the base attributed text. + +## editorInfo.ace_applyChangesToBase(c, optAuthor, apoolJsonObj) + +Applies changes to the base. + +## editorInfo.ace_prepareUserChangeset() + +Prepares the user changeset. + +## editorInfo.ace_applyPreparedChangesetToBase() + +Applies the prepared changeset to the base. + +## editorInfo.ace_setUserChangeNotificationCallback(f) + +Sets the user change notification callback. + +## editorInfo.ace_setAuthorInfo(author, info) + +Sets the author info. + +## editorInfo.ace_fastIncorp(?) + +Incorporates changes quickly. + +## editorInfo.ace_isCaret(?) + +Returns true if the caret is at the specified position. + +## editorInfo.ace_getLineAndCharForPoint(?) + +Returns the line and character for a point. + +## editorInfo.ace_performDocumentApplyAttributesToCharRange(?) + +Applies attributes to a character range. + +## editorInfo.ace_setAttributeOnSelection(attribute, enabled) + +Sets an attribute on current range. +Example: `call.editorInfo.ace_setAttributeOnSelection("turkey::balls", true); // turkey is the attribute here, balls is the value +Notes: to remove the attribute pass enabled as false + +## editorInfo.ace_toggleAttributeOnSelection(?) + +Toggles an attribute on the current range. + +## editorInfo.ace_getAttributeOnSelection(attribute, prevChar) +Returns a boolean if an attribute exists on a selected range. +prevChar value should be true if you want to get the previous Character attribute instead of the current selection for example +if the caret is at position 0,1 (after first character) it's probable you want the attributes on the character at 0,0 +The attribute should be the string name of the attribute applied to the selection IE subscript +Example usage: Apply the activeButton Class to a button if an attribute is on a highlighted/selected caret position or range. +Example `var isItThere = documentAttributeManager.getAttributeOnSelection("turkey::balls", true);` + +See the ep_subscript plugin for an example of this function in action. +Notes: Does not work on first or last character of a line. Suffers from a race condition if called with aceEditEvent. + +## editorInfo.ace_performSelectionChange(?) + +Performs a selection change. + +## editorInfo.ace_doIndentOutdent(?) + +Indents or outdents the selection. + +## editorInfo.ace_doUndoRedo(?) + +Undoes or redoes the last action. + +## editorInfo.ace_doInsertUnorderedList(?) + +Inserts an unordered list. + +## editorInfo.ace_doInsertOrderedList(?) + +Inserts an ordered list. + +## editorInfo.ace_performDocumentApplyAttributesToRange() + +Applies attributes to a range. + +## editorInfo.ace_getAuthorInfos() +Returns an info object about the author. Object key = author_id and info includes author's bg color value. +Use to define your own authorship. + +## editorInfo.ace_performDocumentReplaceRange(start, end, newText) +This function replaces a range (from [x1,y1] to [x2,y2]) with `newText`. + +## editorInfo.ace_performDocumentReplaceCharRange(startChar, endChar, newText) +This function replaces a range (from y1 to y2) with `newText`. + +## editorInfo.ace_renumberList(lineNum) +If you delete a line, calling this method will fix the line numbering. + +## editorInfo.ace_doReturnKey() +Forces a return key at the current caret position. + +## editorInfo.ace_isBlockElement(element) +Returns true if your passed element is registered as a block element. + +## editorInfo.ace_getLineListType(lineNum) +Returns the line's html list type. + +## editorInfo.ace_caretLine() +Returns X position of the caret. + +## editorInfo.ace_caretColumn() +Returns Y position of the caret. + +## editorInfo.ace_caretDocChar() + +Returns the Y offset starting from [x=0,y=0] + +## editorInfo.ace_isWordChar(?) + +Returns true if the character is a word character. diff --git a/doc/api/embed_parameters.adoc b/doc/api/embed_parameters.adoc index 51130771f7c..2185e7ffab9 100644 --- a/doc/api/embed_parameters.adoc +++ b/doc/api/embed_parameters.adoc @@ -10,65 +10,65 @@ Cut and paste the following code into any webpage to embed a pad. The parameters <iframe src='http://pad.test.de/p/PAD_NAME#L4?showChat=false&showLineNumbers=false' width=600 height=400></iframe> ---- -=== showLineNumbers - * Boolean +## showLineNumbers +* Boolean Default: true -=== showControls - * Boolean +## showControls +* Boolean Default: true -=== showChat - * Boolean +## showChat +* Boolean Default: true -=== useMonospaceFont - * Boolean +## useMonospaceFont +* Boolean Default: false -=== userName - * String +## userName +* String Default: "unnamed" Example: `userName=Etherpad%20User` -=== userColor - * String (css hex color value) +## userColor +* String (css hex color value) Default: randomly chosen by pad server Example: `userColor=%23ff9900` -=== noColors - * Boolean +## noColors +* Boolean Default: false -=== alwaysShowChat - * Boolean +## alwaysShowChat +* Boolean Default: false -=== lang - * String +## lang +* String Default: en Example: `lang=ar` (translates the interface into Arabic) -=== rtl - * Boolean +## rtl +* Boolean Default: true Displays pad text from right to left. -=== #L - * Int +## #L +* Int Default: 0 Focuses pad at specific line number and places caret at beginning of this line diff --git a/doc/api/hooks_client-side.adoc b/doc/api/hooks_client-side.adoc index e88737cce5d..9b0e0d18c56 100644 --- a/doc/api/hooks_client-side.adoc +++ b/doc/api/hooks_client-side.adoc @@ -4,7 +4,7 @@ Most of these hooks are called during or in order to set up the formatting process. === documentReady -Called from: src/templates/pad.html +Called from: `src/templates/pad.html` Things in context: @@ -14,7 +14,7 @@ This hook proxies the functionality of jQuery's `$(document).ready` event. === aceDomLinePreProcessLineAttributes -Called from: src/static/js/domline.js +Called from: `src/static/js/domline.js` Things in context: @@ -36,7 +36,7 @@ more. === aceDomLineProcessLineAttributes -Called from: src/static/js/domline.js +Called from: `src/static/js/domline.js` Things in context: @@ -58,7 +58,7 @@ more. === aceCreateDomLine -Called from: src/static/js/domline.js +Called from: `src/static/js/domline.js` Things in context: @@ -78,7 +78,7 @@ question, and cls will be the new class of the element going forward. === acePostWriteDomLineHTML -Called from: src/static/js/domline.js +Called from: `src/static/js/domline.js` Things in context: @@ -89,7 +89,7 @@ page. === aceAttribsToClasses -Called from: src/static/js/linestylefilter.js +Called from: `src/static/js/linestylefilter.js` Things in context: @@ -107,7 +107,7 @@ be parsed into a valid class string. === aceAttribClasses -Called from: src/static/js/linestylefilter.js +Called from: `src/static/js/linestylefilter.js` Things in context: 1. Attributes - Object of Attributes @@ -127,7 +127,7 @@ exports.aceAttribClasses = function(hook_name, attr, cb){ === aceGetFilterStack -Called from: src/static/js/linestylefilter.js +Called from: `src/static/js/linestylefilter.js` Things in context: @@ -143,7 +143,7 @@ later used by the aceCreateDomLine hook (documented above). === aceEditorCSS -Called from: src/static/js/ace.js +Called from: `src/static/js/ace.js` Things in context: None @@ -152,7 +152,7 @@ should be an array of resource urls or paths relative to the plugins directory. === aceInitInnerdocbodyHead -Called from: src/static/js/ace.js +Called from: `src/static/js/ace.js` Things in context: @@ -165,7 +165,7 @@ editor HTML document. === aceEditEvent -Called from: src/static/js/ace2_inner.js +Called from: `src/static/js/ace2_inner.js` Things in context: @@ -182,7 +182,7 @@ your plugin) that use the information provided by the edit event. === aceRegisterNonScrollableEditEvents -Called from: src/static/js/ace2_inner.js +Called from: `src/static/js/ace2_inner.js` Things in context: None @@ -203,7 +203,7 @@ exports.aceRegisterNonScrollableEditEvents = function(){ === aceRegisterBlockElements -Called from: src/static/js/ace2_inner.js +Called from: `src/static/js/ace2_inner.js` Things in context: None @@ -213,7 +213,7 @@ call for those elements. === aceInitialized -Called from: src/static/js/ace2_inner.js +Called from: `src/static/js/ace2_inner.js` Things in context: @@ -228,7 +228,7 @@ use in formatting hooks. === postAceInit -Called from: src/static/js/pad.js +Called from: `src/static/js/pad.js` Things in context: @@ -240,7 +240,7 @@ Things in context: === postToolbarInit -Called from: src/static/js/pad_editbar.js +Called from: `src/static/js/pad_editbar.js` Things in context: @@ -255,14 +255,14 @@ Usage examples: === postTimesliderInit -Called from: src/static/js/timeslider.js +Called from: `src/static/js/timeslider.js` There doesn't appear to be any example available of this particular hook being used, but it gets fired after the timeslider is all set up. === goToRevisionEvent -Called from: src/static/js/broadcast.js +Called from: `src/static/js/broadcast.js` Things in context: @@ -274,7 +274,7 @@ be any example available of this particular hook being used. === userJoinOrUpdate -Called from: src/static/js/pad_userlist.js +Called from: `src/static/js/pad_userlist.js` Things in context: diff --git a/doc/api/hooks_server-side.adoc b/doc/api/hooks_server-side.adoc index d95d4ec6bda..25a696b432b 100644 --- a/doc/api/hooks_server-side.adoc +++ b/doc/api/hooks_server-side.adoc @@ -2,7 +2,7 @@ These hooks are called on server-side. === loadSettings -Called from: src/node/server.ts +Called from: `src/node/server.ts` Things in context: @@ -11,7 +11,7 @@ Things in context: Use this hook to receive the global settings in your plugin. === shutdown -Called from: src/node/server.ts +Called from: `src/node/server.ts` Things in context: None @@ -34,7 +34,7 @@ exports.shutdown = async (hookName, context) => { ---- === pluginUninstall -Called from: src/static/js/pluginfw/installer.js +Called from: `src/static/js/pluginfw/installer.js` Things in context: @@ -43,7 +43,7 @@ Things in context: If this hook returns an error, the callback to the uninstall function gets an error as well. This mostly seems useful for handling additional features added in based on the installation of other plugins, which is pretty cool! === pluginInstall -Called from: src/static/js/pluginfw/installer.js +Called from: `src/static/js/pluginfw/installer.js` Things in context: @@ -123,7 +123,7 @@ Context properties: === expressCloseServer -Called from: src/node/hooks/express.js +Called from: `src/node/hooks/express.js` Things in context: Nothing @@ -142,7 +142,7 @@ exports.expressCloseServer = async () => { ---- === eejsBlock_`<name>` -Called from: src/node/eejs/index.js +Called from: `src/node/eejs/index.js` Things in context: diff --git a/doc/api/http_api.adoc b/doc/api/http_api.adoc index c3ed708ddcd..54b4a4bd1c5 100644 --- a/doc/api/http_api.adoc +++ b/doc/api/http_api.adoc @@ -1,6 +1,7 @@ == HTTP API -=== What can I do with this API? +## What can I do with this API? + The API gives another web application control of the pads. The basic functions are * create/delete pads @@ -23,27 +24,60 @@ A portal (such as WordPress) wants to give a user access to a new pad. Let's ass Portal maps the internal userid to an etherpad author. -> Request: `http://pad.domain/api/1/createAuthorIfNotExistsFor?apikey=secret&name=Michael&authorMapper=7` -> -> Response: `{code: 0, message:"ok", data: {authorID: "a.s8oes9dhwrvt0zif"}}` -Portal maps the internal userid to an etherpad group: +#### Request -> Request: `http://pad.domain/api/1/createGroupIfNotExistsFor?apikey=secret&groupMapper=7` -> -> Response: `{code: 0, message:"ok", data: {groupID: "g.s8oes9dhwrvt0zif"}}` +```http +GET /api/1/createAuthorIfNotExistsFor?name=Michael&authorMapper=7 +``` -Portal creates a pad in the userGroup -> Request: `http://pad.domain/api/1/createGroupPad?apikey=secret&groupID=g.s8oes9dhwrvt0zif&padName=samplePad&text=This is the first sentence in the pad` -> -> Response: `{code: 0, message:"ok", data: null}` +### Response -Portal starts the session for the user on the group: +```json +{"code": 0, "message":"ok", "data": {"authorID": "a.s8oes9dhwrvt0zif"}} +``` -> Request: `http://pad.domain/api/1/createSession?apikey=secret&groupID=g.s8oes9dhwrvt0zif&authorID=a.s8oes9dhwrvt0zif&validUntil=1312201246` -> -> Response: `{"data":{"sessionID": "s.s8oes9dhwrvt0zif"}}` +#### Request +> Portal maps the internal userid to an etherpad group: + +```http +GET http://pad.domain/api/1/createGroupIfNotExistsFor?groupMapper=7 +``` + +### Response + +```json +{"code": 0, "message":"ok", "data": {"groupID": "g.s8oes9dhwrvt0zif"}} +``` + +> Portal creates a pad in the userGroup + +#### Request + +```http +GET http://pad.domain/api/1/createGroupPad?groupID=g.s8oes9dhwrvt0zif&padName=samplePad&text=This is the first sentence in the pad +``` + +#### Response + +```json +{"code": 0, "message":"ok", "data": null} +``` + +> Portal starts the session for the user on the group: + +#### Request + +```http +GET http://pad.domain/api/1/createSession?groupID=g.s8oes9dhwrvt0zif&authorID=a.s8oes9dhwrvt0zif&validUntil=1312201246 +``` + +### Response + +```json +{"code": 0, "message":"ok", "data": {"sessionID": "s.s8oes9dhwrvt0zif"}} +``` Portal places the cookie "sessionID" with the given value on the client and creates an iframe including the pad. @@ -53,14 +87,13 @@ A portal (such as WordPress) wants to transform the contents of a pad that multi Portal retrieves the contents of the pad for entry into the db as a blog post: -> Request: `http://pad.domain/api/1/getText?apikey=secret&padID=g.s8oes9dhwrvt0zif$123` +> Request: `http://pad.domain/api/1/getText?&padID=g.s8oes9dhwrvt0zif$123` > > Response: `{code: 0, message:"ok", data: {text:"Welcome Text"}}` Portal submits content into new blog post > Portal.AddNewBlog(content) -> === Usage @@ -75,25 +108,24 @@ The API is accessible via HTTP. Starting from **1.8**, API endpoints can be invo The URL of the HTTP request is of the form: `/api/$APIVERSION/$FUNCTIONNAME`. $APIVERSION depends on the endpoint you want to use. Depending on the verb you use (GET or POST) **parameters** can be passed differently. -When invoking via GET (mandatory until **1.7.5** included), parameters must be included in the query string (example: `/api/$APIVERSION/$FUNCTIONNAME?apikey=<APIKEY>¶m1=value1`). Please note that starting with nodejs 8.14+ the total size of HTTP request headers has been capped to 8192 bytes. This limits the quantity of data that can be sent in an API request. +When invoking via GET (mandatory until **1.7.5** included), parameters must be included in the query string (example: `/api/$APIVERSION/$FUNCTIONNAME?param1=value1`). Please note that starting with nodejs 8.14+ the total size of HTTP request headers has been capped to 8192 bytes. This limits the quantity of data that can be sent in an API request. Starting from Etherpad **1.8** it is also possible to invoke the HTTP API via POST. In this case, querystring parameters will still be accepted, but **any parameter with the same name sent via POST will take precedence**. If you need to send large chunks of text (for example, for `setText()`) it is advisable to invoke via POST. Example with cURL using GET (toy example, no encoding): - -[source,bash] ----- -curl "http://pad.domain/api/1/setText?apikey=secret&padID=padname&text=this_text_will_NOT_be_encoded_by_curl_use_next_example" ----- +``` +curl "http://pad.domain/api/1/setText?padID=padname&text=this_text_will_NOT_be_encoded_by_curl_use_next_example" +``` Example with cURL using GET (better example, encodes text): - -[source,bash] ----- -curl "http://pad.domain/api/1/setText?apikey=secret&padID=padname" --get --data-urlencode "text=Text sent via GET with proper encoding. For big documents, please use POST" ----- +``` +curl "http://pad.domain/api/1/setText?padID=padname" --get --data-urlencode "text=Text sent via GET with proper encoding. For big documents, please use POST" +``` Example with cURL using POST: +``` +curl "http://pad.domain/api/1/setText?padID=padname" --data-urlencode "text=Text sent via POST with proper encoding. For big texts (>8 KB), use this method" +``` [source,bash] ---- @@ -135,7 +167,49 @@ image::https://i.imgur.com/d0nWp.png[API Overview] ==== Authentication -Authentication works via a token that is sent with each request as a post parameter. There is a single token per Etherpad deployment. This token will be random string, generated by Etherpad at the first start. It will be saved in APIKEY.txt in the root folder of Etherpad. Only Etherpad and the requesting application knows this key. Token management will not be exposed through this API. +Authentication works via an OAuth token that is sent with each request as an Authorization header, i.e. `Authorization: Bearer YOUR_TOKEN`. You can add new clients that can sign in via the API by adding new entries to the sso section in the settings.json. + + +#### Example for browser login clients + +This example illustrates how to add a new client that can sign in via the API using the browser login method. This method is used for users trying to sign in to the API via the browser. You can log in with the users in the settings.json file. The redirect URI is the URL where the user is redirected after the login. This is normally your etherpad instance url. + +```json + { + "client_id": "admin_client", + "client_secret": "admin", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "redirect_uris": ["http://my-etherpad-instance.com"], + } +``` + + +#### Example for services + +This example illustrates how to add a new client that can sign in via the API using the client credentials method. This method is used for services trying to sign in to the API where there is no browser. +E.g. a service that creates a pad for a user or a service that inserts a text into a pad. Just make sure that the secret is complex enough as anybody who knows the secret can access the API. + +```json + { + "client_id": "client_credentials", + "redirect_uris": [], + "response_types": [], + "grant_types": ["code"], + "client_secret": "client_credentials", + "extraParams": [ + { + "name": "admin", + "value": "true" + } + ] +} +``` + +Obtain a Bearer token: + +`curl --request POST --url 'https://your.server.tld/oidc/token' --header 'content-type: application/x-www-form-urlencoded' --data grant_type=client_credentials --data client_id=client_credentials --data client_secret=client_credentials` + ==== Node Interoperability @@ -143,299 +217,274 @@ All functions will also be available through a node module accessible from other === API Methods -==== Groups -Pads can belong to a group. The padID of grouppads is starting with a groupID like g.asdfasdfasdfasdf$test +### Groups +Pads can belong to a group. The padID of grouppads is starting with a groupID like `g.asdfasdfasdfasdf$test` -===== createGroup() - * API >= 1 +#### createGroup() +* API >= 1 creates a new group -_Example returns:_ - +*Example returns:* * `{code: 0, message:"ok", data: {groupID: g.s8oes9dhwrvt0zif}}` -===== createGroupIfNotExistsFor(groupMapper) - * API >= 1 +#### createGroupIfNotExistsFor(groupMapper) +* API >= 1 this functions helps you to map your application group ids to Etherpad group ids -_Example returns:_ - - * `{code: 0, message:"ok", data: {groupID: g.s8oes9dhwrvt0zif}}` +*Example returns:* +* `{code: 0, message:"ok", data: {groupID: g.s8oes9dhwrvt0zif}}` -===== deleteGroup(groupID) - * API >= 1 +#### deleteGroup(groupID) +* API >= 1 deletes a group -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"groupID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"groupID does not exist", data: null}` -===== listPads(groupID) - * API >= 1 +#### listPads(groupID) +* API >= 1 returns all pads of this group -_Example returns:_ +*Example returns:* +* `{code: 0, message:"ok", data: {padIDs : ["g.s8oes9dhwrvt0zif$test", "g.s8oes9dhwrvt0zif$test2"]}` +* `{code: 1, message:"groupID does not exist", data: null}` - * `{code: 0, message:"ok", data: {padIDs : ["g.s8oes9dhwrvt0zif$test", "g.s8oes9dhwrvt0zif$test2"]}` - * `{code: 1, message:"groupID does not exist", data: null}` - -===== createGroupPad(groupID, padName, [text], [authorId]) - * API >= 1 - * `authorId` in API >= 1.3.0 +#### createGroupPad(groupID, padName, [text], [authorId]) +* API >= 1 +* `authorId` in API >= 1.3.0 creates a new pad in this group -_Example returns:_ - - * `{code: 0, message:"ok", data: {padID: "g.s8oes9dhwrvt0zif$test"}` - * `{code: 1, message:"padName does already exist", data: null}` - * `{code: 1, message:"groupID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {padID: "g.s8oes9dhwrvt0zif$test"}` +* `{code: 1, message:"padName does already exist", data: null}` +* `{code: 1, message:"groupID does not exist", data: null}` -===== listAllGroups() - * API >= 1.1 +#### listAllGroups() +* API >= 1.1 lists all existing groups -_Example returns:_ - - * `{code: 0, message:"ok", data: {groupIDs: ["g.mKjkmnAbSMtCt8eL", "g.3ADWx6sbGuAiUmCy"]}}` - * `{code: 0, message:"ok", data: {groupIDs: []}}` +*Example returns:* +* `{code: 0, message:"ok", data: {groupIDs: ["g.mKjkmnAbSMtCt8eL", "g.3ADWx6sbGuAiUmCy"]}}` +* `{code: 0, message:"ok", data: {groupIDs: []}}` ==== Author These authors are bound to the attributes the users choose (color and name). -===== createAuthor([name]) - * API >= 1 +#### createAuthor([name]) +* API >= 1 creates a new author -_Example returns:_ - - * `{code: 0, message:"ok", data: {authorID: "a.s8oes9dhwrvt0zif"}}` +*Example returns:* +* `{code: 0, message:"ok", data: {authorID: "a.s8oes9dhwrvt0zif"}}` -===== createAuthorIfNotExistsFor(authorMapper [, name]) - * API >= 1 +#### createAuthorIfNotExistsFor(authorMapper [, name]) +* API >= 1 this functions helps you to map your application author ids to Etherpad author ids -_Example returns:_ - - * `{code: 0, message:"ok", data: {authorID: "a.s8oes9dhwrvt0zif"}}` +*Example returns:* +* `{code: 0, message:"ok", data: {authorID: "a.s8oes9dhwrvt0zif"}}` -===== listPadsOfAuthor(authorID) - * API >= 1 +#### listPadsOfAuthor(authorID) +* API >= 1 returns an array of all pads this author contributed to -_Example returns:_ +*Example returns:* +* `{code: 0, message:"ok", data: {padIDs: ["g.s8oes9dhwrvt0zif$test", "g.s8oejklhwrvt0zif$foo"]}}` +* `{code: 1, message:"authorID does not exist", data: null}` - * `{code: 0, message:"ok", data: {padIDs: ["g.s8oes9dhwrvt0zif$test", "g.s8oejklhwrvt0zif$foo"]}}` - * `{code: 1, message:"authorID does not exist", data: null}` - -===== getAuthorName(authorID) - * API >= 1.1 +#### getAuthorName(authorID) +* API >= 1.1 Returns the Author Name of the author -_Example returns:_ - - * `{code: 0, message:"ok", data: {authorName: "John McLear"}}` +*Example returns:* +* `{code: 0, message:"ok", data: {authorName: "John McLear"}}` -> can't be deleted cause this would involve scanning all the pads where this author was ==== Session Sessions can be created between a group and an author. This allows an author to access more than one group. The sessionID will be set as a cookie to the client and is valid until a certain date. The session cookie can also contain multiple comma-separated sessionIDs, allowing a user to edit pads in different groups at the same time. Only users with a valid session for this group, can access group pads. You can create a session after you authenticated the user at your web application, to give them access to the pads. You should save the sessionID of this session and delete it after the user logged out. -===== createSession(groupID, authorID, validUntil) - * API >= 1 +#### createSession(groupID, authorID, validUntil) +* API >= 1 creates a new session. validUntil is an unix timestamp in seconds -_Example returns:_ +*Example returns:* +* `{code: 0, message:"ok", data: {sessionID: "s.s8oes9dhwrvt0zif"}}` +* `{code: 1, message:"groupID doesn't exist", data: null}` +* `{code: 1, message:"authorID doesn't exist", data: null}` +* `{code: 1, message:"validUntil is in the past", data: null}` - * `{code: 0, message:"ok", data: {sessionID: "s.s8oes9dhwrvt0zif"}}` - * `{code: 1, message:"groupID doesn't exist", data: null}` - * `{code: 1, message:"authorID doesn't exist", data: null}` - * `{code: 1, message:"validUntil is in the past", data: null}` -===== deleteSession(sessionID) - * API >= 1 +#### deleteSession(sessionID) +* API >= 1 deletes a session -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"sessionID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"sessionID does not exist", data: null}` -===== getSessionInfo(sessionID) - * API >= 1 +#### getSessionInfo(sessionID) +* API >= 1 returns information about a session -_Example returns:_ - - * `{code: 0, message:"ok", data: {authorID: "a.s8oes9dhwrvt0zif", groupID: g.s8oes9dhwrvt0zif, validUntil: 1312201246}}` - * `{code: 1, message:"sessionID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {authorID: "a.s8oes9dhwrvt0zif", groupID: g.s8oes9dhwrvt0zif, validUntil: 1312201246}}` +* `{code: 1, message:"sessionID does not exist", data: null}` -===== listSessionsOfGroup(groupID) - * API >= 1 +#### listSessionsOfGroup(groupID) +* API >= 1 returns all sessions of a group -_Example returns:_ +*Example returns:* +* `{"code":0,"message":"ok","data":{"s.oxf2ras6lvhv2132":{"groupID":"g.s8oes9dhwrvt0zif","authorID":"a.akf8finncvomlqva","validUntil":2312905480}}}` +* `{code: 1, message:"groupID does not exist", data: null}` - * `{"code":0,"message":"ok","data":{"s.oxf2ras6lvhv2132":{"groupID":"g.s8oes9dhwrvt0zif","authorID":"a.akf8finncvomlqva","validUntil":2312905480}}}` - * `{code: 1, message:"groupID does not exist", data: null}` - -===== listSessionsOfAuthor(authorID) - * API >= 1 +#### listSessionsOfAuthor(authorID) +* API >= 1 returns all sessions of an author -_Example returns:_ - - * `{"code":0,"message":"ok","data":{"s.oxf2ras6lvhv2132":{"groupID":"g.s8oes9dhwrvt0zif","authorID":"a.akf8finncvomlqva","validUntil":2312905480}}}` - * `{code: 1, message:"authorID does not exist", data: null}` +*Example returns:* +* `{"code":0,"message":"ok","data":{"s.oxf2ras6lvhv2132":{"groupID":"g.s8oes9dhwrvt0zif","authorID":"a.akf8finncvomlqva","validUntil":2312905480}}}` +* `{code: 1, message:"authorID does not exist", data: null}` ==== Pad Content Pad content can be updated and retrieved through the API -===== getText(padID, [rev]) - * API >= 1 +#### getText(padID, [rev]) +* API >= 1 returns the text of a pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {text:"Welcome Text"}}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {text:"Welcome Text"}}` +* `{code: 1, message:"padID does not exist", data: null}` -===== setText(padID, text, [authorId]) - * API >= 1 - * `authorId` in API >= 1.3.0 +#### setText(padID, text, [authorId]) +* API >= 1 +* `authorId` in API >= 1.3.0 Sets the text of a pad. If your text is long (>8 KB), please invoke via POST and include `text` parameter in the body of the request, not in the URL (since Etherpad **1.8**). -_Example returns:_ +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does not exist", data: null}` +* `{code: 1, message:"text too long", data: null}` - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does not exist", data: null}` - * `{code: 1, message:"text too long", data: null}` +#### appendText(padID, text, [authorId]) +* API >= 1.2.13 +* `authorId` in API >= 1.3.0 -===== appendText(padID, text, [authorId]) - * API >= 1.2.13 - * `authorId` in API >= 1.3.0 Appends text to a pad. If your text is long (>8 KB), please invoke via POST and include `text` parameter in the body of the request, not in the URL (since Etherpad **1.8**). -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does not exist", data: null}` - * `{code: 1, message:"text too long", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does not exist", data: null}` +* `{code: 1, message:"text too long", data: null}` -===== getHTML(padID, [rev]) - * API >= 1 +#### getHTML(padID, [rev]) +* API >= 1 returns the text of a pad formatted as HTML -_Example returns:_ - - * `{code: 0, message:"ok", data: {html:"Welcome Text<br>More Text"}}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {html:"Welcome Text<br>More Text"}}` +* `{code: 1, message:"padID does not exist", data: null}` -===== setHTML(padID, html, [authorId]) - * API >= 1 - * `authorId` in API >= 1.3.0 +#### setHTML(padID, html, [authorId]) +* API >= 1 +* `authorId` in API >= 1.3.0 sets the text of a pad based on HTML, HTML must be well-formed. Malformed HTML will send a warning to the API log. If `html` is long (>8 KB), please invoke via POST and include `html` parameter in the body of the request, not in the URL (since Etherpad **1.8**). -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does not exist", data: null}` -===== getAttributePool(padID) - * API >= 1.2.8 +#### getAttributePool(padID) +* API >= 1.2.8 returns the attribute pool of a pad -_Example returns:_ - - * `{ "code":0, - "message":"ok", - "data": { - "pool":{ - "numToAttrib":{ - "0":["author","a.X4m8bBWJBZJnWGSh"], - "1":["author","a.TotfBPzov54ihMdH"], - "2":["author","a.StiblqrzgeNTbK05"], - "3":["bold","true"] - }, - "attribToNum":{ - "author,a.X4m8bBWJBZJnWGSh":0, - "author,a.TotfBPzov54ihMdH":1, - "author,a.StiblqrzgeNTbK05":2, - "bold,true":3 - }, - "nextNum":4 - } - } - }` - * `{"code":1,"message":"padID does not exist","data":null}` - -===== getRevisionChangeset(padID, [rev]) - * API >= 1.2.8 +*Example returns:* +* `{ "code":0, + "message":"ok", + "data": { + "pool":{ + "numToAttrib":{ + "0":["author","a.X4m8bBWJBZJnWGSh"], + "1":["author","a.TotfBPzov54ihMdH"], + "2":["author","a.StiblqrzgeNTbK05"], + "3":["bold","true"] + }, + "attribToNum":{ + "author,a.X4m8bBWJBZJnWGSh":0, + "author,a.TotfBPzov54ihMdH":1, + "author,a.StiblqrzgeNTbK05":2, + "bold,true":3 + }, + "nextNum":4 + } + } + }` +* `{"code":1,"message":"padID does not exist","data":null}` + +#### getRevisionChangeset(padID, [rev]) +* API >= 1.2.8 get the changeset at a given revision, or last revision if 'rev' is not defined. -_Example returns:_ - - * `{ "code" : 0, - "message" : "ok", - "data" : "Z:1>6b|5+6b$Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nGet involved with Etherpad at https://etherpad.org\n" - }` - * `{"code":1,"message":"padID does not exist","data":null}` - * `{"code":1,"message":"rev is higher than the head revision of the pad","data":null}` +*Example returns:* +* `{ "code" : 0, + "message" : "ok", + "data" : "Z:1>6b|5+6b$Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nGet involved with Etherpad at https://etherpad.org\n" + }` +* `{"code":1,"message":"padID does not exist","data":null}` +* `{"code":1,"message":"rev is higher than the head revision of the pad","data":null}` -===== createDiffHTML(padID, startRev, endRev) - * API >= 1.2.7 +#### createDiffHTML(padID, startRev, endRev) +* API >= 1.2.7 returns an object of diffs from 2 points in a pad -_Example returns:_ - - * `{"code":0,"message":"ok","data":{"html":"<style>\n.authora_HKIv23mEbachFYfH {background-color: #a979d9}\n.authora_n4gEeMLsv1GivNeh {background-color: #a9b5d9}\n.removed {text-decoration: line-through; -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; filter: alpha(opacity=80); opacity: 0.8; }\n</style>Welcome to Etherpad!<br><br>This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!<br><br>Get involved with Etherpad at <a href=\"http://etherpad.org\">http://etherpad.org</a><br><span class=\"authora_HKIv23mEbachFYfH\">aw</span><br><br>","authors":["a.HKIv23mEbachFYfH",""]}}` - * `{"code":4,"message":"no or wrong API Key","data":null}` +*Example returns:* +* `{"code":0,"message":"ok","data":{"html":"<style>\n.authora_HKIv23mEbachFYfH {background-color: #a979d9}\n.authora_n4gEeMLsv1GivNeh {background-color: #a9b5d9}\n.removed {text-decoration: line-through; -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; filter: alpha(opacity=80); opacity: 0.8; }\n</style>Welcome to Etherpad!<br><br>This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!<br><br>Get involved with Etherpad at <a href=\"http://etherpad.org\">http://etherpad.org</a><br><span class=\"authora_HKIv23mEbachFYfH\">aw</span><br><br>","authors":["a.HKIv23mEbachFYfH",""]}}` +* `{"code":4,"message":"no or wrong API Key","data":null}` -===== restoreRevision(padId, rev, [authorId]) - * API >= 1.2.11 - * `authorId` in API >= 1.3.0 +#### restoreRevision(padId, rev, [authorId]) +* API >= 1.2.11 +* `authorId` in API >= 1.3.0 -Restores revision from past as new changeset - -_Example returns:_ - - * {code:0, message:"ok", data:null} - * {code: 1, message:"padID does not exist", data: null} - -==== Chat +* Example returns:* +* `{code:0, message:"ok", data:null}` +* `{code: 1, message:"padID does not exist", data: null}` -===== getChatHistory(padID, [start, end]) - * API >= 1.2.7 +### Chat +#### getChatHistory(padID, [start, end]) +* API >= 1.2.7 returns @@ -449,8 +498,8 @@ _Example returns:_ * `{code: 1, message:"start is higher or equal to the current chatHead", data: null}` * `{code: 1, message:"padID does not exist", data: null}` -===== getChatHead(padID) - * API >= 1.2.7 +#### getChatHead(padID) +* API >= 1.2.7 returns the chatHead (last number of the last chat-message) of the pad @@ -460,13 +509,12 @@ _Example returns:_ * `{code: 0, message:"ok", data: {chatHead: 42}}` * `{code: 1, message:"padID does not exist", data: null}` -===== appendChatMessage(padID, text, authorID [, time]) - * API >= 1.2.12 +#### appendChatMessage(padID, text, authorID [, time]) +* API >= 1.2.12 creates a chat message, saves it to the database and sends it to all connected clients of this pad - -_Example returns:_ +*Example returns:* * `{code: 0, message:"ok", data: null}` * `{code: 1, message:"text is no string", data: null}` @@ -474,101 +522,92 @@ _Example returns:_ === Pad Group pads are normal pads, but with the name schema GROUPID$PADNAME. A security manager controls access of them and it's forbidden for normal pads to include a $ in the name. -==== createPad(padID, [text], [authorId]) - * API >= 1 - * `authorId` in API >= 1.3.0 +#### createPad(padID, [text], [authorId]) +* API >= 1 +* `authorId` in API >= 1.3.0 creates a new (non-group) pad. Note that if you need to create a group Pad, you should call **createGroupPad**. You get an error message if you use one of the following characters in the padID: "/", "?", "&" or "#". -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does already exist", data: null}` - * `{code: 1, message:"malformed padID: Remove special characters", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does already exist", data: null}` +* `{code: 1, message:"malformed padID: Remove special characters", data: null}` -==== getRevisionsCount(padID) - * API >= 1 +#### getRevisionsCount(padID) +* API >= 1 returns the number of revisions of this pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {revisions: 56}}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {revisions: 56}}` +* `{code: 1, message:"padID does not exist", data: null}` -==== getSavedRevisionsCount(padID) - * API >= 1.2.11 +#### getSavedRevisionsCount(padID) +* API >= 1.2.11 returns the number of saved revisions of this pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {savedRevisions: 42}}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {savedRevisions: 42}}` +* `{code: 1, message:"padID does not exist", data: null}` -==== listSavedRevisions(padID) - * API >= 1.2.11 +#### listSavedRevisions(padID) +* API >= 1.2.11 returns the list of saved revisions of this pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}}` +* `{code: 1, message:"padID does not exist", data: null}` -==== saveRevision(padID [, rev]) - * API >= 1.2.11 +#### saveRevision(padID [, rev]) +* API >= 1.2.11 saves a revision -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does not exist", data: null}` -==== padUsersCount(padID) - * API >= 1 +#### padUsersCount(padID) +* API >= 1 returns the number of user that are currently editing this pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {padUsersCount: 5}}` +*Example returns:* +* `{code: 0, message:"ok", data: {padUsersCount: 5}}` -==== padUsers(padID) - * API >= 1.1 +#### padUsers(padID) +* API >= 1.1 returns the list of users that are currently editing this pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {padUsers: [{colorId:"#c1a9d9","name":"username1","timestamp":1345228793126,"id":"a.n4gEeMLsvg12452n"},{"colorId":"#d9a9cd","name":"Hmmm","timestamp":1345228796042,"id":"a.n4gEeMLsvg12452n"}]}}` - * `{code: 0, message:"ok", data: {padUsers: []}}` +*Example returns:* +* `{code: 0, message:"ok", data: {padUsers: [{colorId:"#c1a9d9","name":"username1","timestamp":1345228793126,"id":"a.n4gEeMLsvg12452n"},{"colorId":"#d9a9cd","name":"Hmmm","timestamp":1345228796042,"id":"a.n4gEeMLsvg12452n"}]}}` +* `{code: 0, message:"ok", data: {padUsers: []}}` -==== deletePad(padID) - * API >= 1 +#### deletePad(padID) +* API >= 1 deletes a pad -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does not exist", data: null}` -==== copyPad(sourceID, destinationID[, force=false]) - * API >= 1.2.8 +#### copyPad(sourceID, destinationID[, force=false]) +* API >= 1.2.8 copies a pad with full history and chat. If force is true and the destination pad exists, it will be overwritten. -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does not exist", data: null}` ==== copyPadWithoutHistory(sourceID, destinationID, [force=false], [authorId]) * API >= 1.2.15 - * `authorId` in API >= 1.3.0 +* `authorId` in API >= 1.3.0 copies a pad without copying the history and chat. If force is true and the destination pad exists, it will be overwritten. Note that all the revisions will be lost! In most of the cases one should use `copyPad` API instead. @@ -578,116 +617,119 @@ _Example returns:_ * `{code: 0, message:"ok", data: null}` * `{code: 1, message:"padID does not exist", data: null}` -==== movePad(sourceID, destinationID[, force=false]) - * API >= 1.2.8 +#### movePad(sourceID, destinationID[, force=false]) +* API >= 1.2.8 moves a pad. If force is true and the destination pad exists, it will be overwritten. -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does not exist", data: null}` -==== getReadOnlyID(padID) - * API >= 1 +#### getReadOnlyID(padID) +* API >= 1 returns the read only link of a pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {readOnlyID: "r.s8oes9dhwrvt0zif"}}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {readOnlyID: "r.s8oes9dhwrvt0zif"}}` +* `{code: 1, message:"padID does not exist", data: null}` -==== getPadID(readOnlyID) - * API >= 1.2.10 +#### getPadID(readOnlyID) +* API >= 1.2.10 returns the id of a pad which is assigned to the readOnlyID -_Example returns:_ - - * `{code: 0, message:"ok", data: {padID: "p.s8oes9dhwrvt0zif"}}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {padID: "p.s8oes9dhwrvt0zif"}}` +* `{code: 1, message:"padID does not exist", data: null}` -==== setPublicStatus(padID, publicStatus) - * API >= 1 +#### setPublicStatus(padID, publicStatus) +* API >= 1 sets a boolean for the public status of a group pad -_Example returns:_ - - * `{code: 0, message:"ok", data: null}` - * `{code: 1, message:"padID does not exist", data: null}` - * `{code: 1, message:"You can only get/set the publicStatus of pads that belong to a group", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: null}` +* `{code: 1, message:"padID does not exist", data: null}` +* `{code: 1, message:"You can only get/set the publicStatus of pads that belong to a group", data: null}` -==== getPublicStatus(padID) - * API >= 1 +#### getPublicStatus(padID) +* API >= 1 return true of false -_Example returns:_ - - * `{code: 0, message:"ok", data: {publicStatus: true}}` - * `{code: 1, message:"padID does not exist", data: null}` - * `{code: 1, message:"You can only get/set the publicStatus of pads that belong to a group", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {publicStatus: true}}` +* `{code: 1, message:"padID does not exist", data: null}` +* `{code: 1, message:"You can only get/set the publicStatus of pads that belong to a group", data: null}` -==== listAuthorsOfPad(padID) - * API >= 1 +#### listAuthorsOfPad(padID) +* API >= 1 returns an array of authors who contributed to this pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {authorIDs : ["a.s8oes9dhwrvt0zif", "a.akf8finncvomlqva"]}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {authorIDs : ["a.s8oes9dhwrvt0zif", "a.akf8finncvomlqva"]}` +* `{code: 1, message:"padID does not exist", data: null}` -==== getLastEdited(padID) - * API >= 1 +#### getLastEdited(padID) +* API >= 1 returns the timestamp of the last revision of the pad -_Example returns:_ - - * `{code: 0, message:"ok", data: {lastEdited: 1340815946602}}` - * `{code: 1, message:"padID does not exist", data: null}` +*Example returns:* +* `{code: 0, message:"ok", data: {lastEdited: 1340815946602}}` +* `{code: 1, message:"padID does not exist", data: null}` -==== sendClientsMessage(padID, msg) - * API >= 1.1 +#### sendClientsMessage(padID, msg) +* API >= 1.1 sends a custom message of type `msg` to the pad -_Example returns:_ +*Example returns:* +```json +{"code": 0, "message":"ok", "data": {}} +``` - * `{code: 0, message:"ok", data: {}}` - * `{code: 1, message:"padID does not exist", data: null}` +```json +{"code": 1, "message":"padID does not exist", "data": null} +``` -==== checkToken() - * API >= 1.2 +#### checkToken() +* API >= 1.2 returns ok when the current api token is valid -_Example returns:_ - - * `{"code":0,"message":"ok","data":null}` - * `{"code":4,"message":"no or wrong API Key","data":null}` +*Example returns:* +```json +{"code":0,"message":"ok","data":null} +``` +```json +{"code":4,"message":"no or wrong API Key","data":null} +``` === Pads -==== listAllPads() - * API >= 1.2.1 +#### listAllPads() +* API >= 1.2.1 lists all pads on this epl instance -_Example returns:_ - - * `{code: 0, message:"ok", data: {padIDs: ["testPad", "thePadsOfTheOthers"]}}` +*Example returns:* +```json +{"code": 0, "message":"ok", "data": {"padIDs": ["testPad", "thePadsOfTheOthers"]}} +``` ==== Global -===== getStats() - * API >= 1.2.14 +#### getStats() +* API >= 1.2.14 get stats of the etherpad instance -_Example returns_: +*Example returns* +```json +{"code":0,"message":"ok","data":{"totalPads":3,"totalSessions": 2,"totalActivePads": 1}} +``` - * `{"code":0,"message":"ok","data":{"totalPads":3,"totalSessions": 2,"totalActivePads": 1}}` diff --git a/doc/api/index.md b/doc/api/index.md new file mode 100644 index 00000000000..4b9ebcdea3b --- /dev/null +++ b/doc/api/index.md @@ -0,0 +1,3 @@ +# API documentation + +This part of the documentation is about the API of Etherpad. It is intended for developers who want to write applications that interact with Etherpad instances or plugins. diff --git a/doc/api/toolbar.adoc b/doc/api/toolbar.adoc index 3a1d1461a33..395d49e68a1 100644 --- a/doc/api/toolbar.adoc +++ b/doc/api/toolbar.adoc @@ -1,11 +1,11 @@ == Toolbar controller src/node/utils/toolbar.js -=== button(opts) - * {Object} `opts` - * `command` - this command fill be fired on the editbar on click - * `localizationId` - will be set as `data-l10-id` - * `class` - here you can add additional classes to the button +## button(opts) +* {Object} `opts` + * `command` - this command fill be fired on the editbar on click + * `localizationId` - will be set as `data-l10-id` + * `class` - here you can add additional classes to the button Returns: {Button} @@ -31,19 +31,19 @@ var myButton = toolbar.button({ }) ---- -=== selectButton(opts) - * {Object} `opts` - * `id` - id of the menu item - * `selectId` - id of the select element - * `command` - this command fill be fired on the editbar on change +## selectButton(opts) +* {Object} `opts` + * `id` - id of the menu item + * `selectId` - id of the select element + * `command` - this command fill be fired on the editbar on change Returns: {SelectButton} -=== SelectButton.addOption(value, text, attributes) - * {String} value - The value of this option - * {String} text - the label text used for this option - * {Object} attributes - any additional html attributes go here (e.g. `data-l10n-id`) +## SelectButton.addOption(value, text, attributes) +* {String} value - The value of this option +* {String} text - the label text used for this option +* {Object} attributes - any additional html attributes go here (e.g. `data-l10n-id`) -=== registerButton(name, item) - * {String} name - used to reference the item in the toolbar config in settings.json - * {Button|SelectButton} item - the button to add +## registerButton(name, item) +* {String} name - used to reference the item in the toolbar config in settings.json +* {Button|SelectButton} item - the button to add diff --git a/doc/cookies.md b/doc/cookies.md new file mode 100644 index 00000000000..171de8a4640 --- /dev/null +++ b/doc/cookies.md @@ -0,0 +1,18 @@ +# Cookies + +Cookies used by Etherpad. + +| Name | Sample value | Domain | Path | Expires/max-age | Http-only | Secure | Usage description | +|-------------------|----------------------------------|-------------|------|-----------------|-----------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| express_sid | s%3A7yCNjRmTW8ylGQ53I2IhOwYF9... | example.org | / | Session | true | true | Session ID of the [Express web framework](https://expressjs.com). When Etherpad is behind a reverse proxy, and an administrator wants to use session stickiness, he may use this cookie. If you are behind a reverse proxy, please remember to set `trustProxy: true` in `settings.json`. Set in [webaccess.js#L131](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/hooks/express/webaccess.js#L131). | +| language | en | example.org | / | Session | false | true | The language of the UI (e.g.: `en-GB`, `it`). Set in [pad_editor.js#L111](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_editor.js#L111). | +| prefs / prefsHttp | %7B%22epThemesExtTheme%22... | example.org | /p | year 3000 | false | true | Client-side preferences (e.g.: font family, chat always visible, show authorship colors, ...). Set in [pad_cookie.js#L49](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_cookie.js#L49). `prefs` is used if Etherpad is accessed over HTTPS, `prefsHttp` if accessed over HTTP. For more info see https://github.com/ether/etherpad-lite/issues/3179. | +| token | t.tFzkihhhBf4xKEpCK3PU | example.org | / | 60 days | false | true | A random token representing the author, of the form `t.randomstring_of_lenght_20`. The random string is generated by the client, at ([pad.js#L55-L66](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad.js#L55-L66)). This cookie is always set by the client (at [pad.js#L153-L158](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad.js#L153-L158)) without any solicitation from the server. It is used for all the pads accessed via the web UI (not used for the HTTP API). On the server side, its value is accessed at [SecurityManager.js#L33](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/db/SecurityManager.js#L33). | + +For more info, visit the related discussion at https://github.com/ether/etherpad-lite/issues/3563. + +Etherpad HTTP API clients may make use (if they choose so) to send another cookie: + +| Name | Sample value | Domain | Usage description | +|-----------|------------------------------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| sessionID | s.1c70968b333b25476a2c7bdd0e0bed17 | example.org | Sessions can be created between a group and an author. This allows an author to access more than one group. The sessionID will be set as a cookie to the client and is valid until a certain date. The session cookie can also contain multiple comma-separated sessionIDs, allowing a user to edit pads in different groups at the same time. More info - https://github.com/ether/etherpad-lite/blob/develop/doc/api/http_api.md#session | diff --git a/doc/demo.md b/doc/demo.md new file mode 100644 index 00000000000..1ee02d17d81 --- /dev/null +++ b/doc/demo.md @@ -0,0 +1,19 @@ +# Demo of Etherpad + +This is a demo of Etherpad. It shows how to use Etherpad and what it can do. + +## The toolbar + + + +## The pad + + + +## Etherpad with a virtual webcam + + + +## Etherpad skin variants + + diff --git a/doc/docker.adoc b/doc/docker.adoc index e263233377f..ca47555fbe2 100644 --- a/doc/docker.adoc +++ b/doc/docker.adoc @@ -510,7 +510,7 @@ For the editor container, you can also make it full width by adding `full-width- | `SOCKETIO_MAX_HTTP_BUFFER_SIZE` | The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks. -| `10000` +| `50000` | `LOAD_TEST` | Allow Load Testing tools to hit the Etherpad Instance. WARNING: this will disable security on the instance. diff --git a/doc/docker.md b/doc/docker.md new file mode 100644 index 00000000000..6826d239e1c --- /dev/null +++ b/doc/docker.md @@ -0,0 +1,331 @@ +# Docker + +The official Docker image is available on https://hub.docker.com/r/etherpad/etherpad. + +## Downloading from Docker Hub +If you are ok downloading a [prebuilt image from Docker Hub](https://hub.docker.com/r/etherpad/etherpad), these are the commands: +```bash +# gets the latest published version +docker pull etherpad/etherpad + +# gets a specific version +docker pull etherpad/etherpad:1.8.0 +``` + +## Build a personalized container + +If you want to use a personalized settings file, **you will have to rebuild your image**. +All of the following instructions are as a member of the `docker` group. +By default, the Etherpad Docker image is built and run in `production` mode: no development dependencies are installed, and asset bundling speeds up page load time. + +### Rebuilding with custom settings +Edit `<BASEDIR>/settings.json.docker` at your will. When rebuilding the image, this file will be copied inside your image and renamed to `settings.json`. + +**Each configuration parameter can also be set via an environment variable**, using the syntax `"${ENV_VAR}"` or `"${ENV_VAR:default_value}"`. For details, refer to `settings.json.template`. + +### Rebuilding including some plugins +If you want to install some plugins in your container, it is sufficient to list them in the ETHERPAD_PLUGINS build variable. +The variable value has to be a space separated, double quoted list of plugin names (see examples). + +Some plugins will need personalized settings. Just refer to the previous section, and include them in your custom `settings.json.docker`. + +### Rebuilding including export functionality for DOC/PDF/ODT + +If you want to be able to export your pads to DOC/PDF/ODT files, you can install +either Abiword or Libreoffice via setting a build variable. + +#### Via Abiword + +For installing Abiword, set the `INSTALL_ABIWORD` build variable to any value. + +Also, you will need to configure the path to the abiword executable +via setting the `abiword` property in `<BASEDIR>/settings.json.docker` to +`/usr/bin/abiword` or via setting the environment variable `ABIWORD` to +`/usr/bin/abiword`. + +#### Via Libreoffice + +For installing Libreoffice instead, set the `INSTALL_SOFFICE` build variable +to any value. + +Also, you will need to configure the path to the libreoffice executable +via setting the `soffice` property in `<BASEDIR>/settings.json.docker` to +`/usr/bin/soffice` or via setting the environment variable `SOFFICE` to +`/usr/bin/soffice`. + +### Examples + +Build a Docker image from the currently checked-out code: +```bash +docker build --tag <YOUR_USERNAME>/etherpad . +``` + +Include two plugins in the container: +```bash +docker build --build-arg ETHERPAD_PLUGINS="ep_comments_page ep_author_neat" --tag <YOUR_USERNAME>/etherpad . +``` + +## Running your instance: + +To run your instance: +```bash +docker run --detach --publish <DESIRED_PORT>:9001 <YOUR_USERNAME>/etherpad +``` + +And point your browser to `http://<YOUR_IP>:<DESIRED_PORT>` + +## Options available by default + +The `settings.json.docker` available by default allows to control almost every setting via environment variables. + +### General + +| Variable | Description | Default | +| ------------------ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TITLE` | The name of the instance | `Etherpad` | +| `FAVICON` | favicon default name, or a fully specified URL to your own favicon | `favicon.ico` | +| `DEFAULT_PAD_TEXT` | The default text of a pad | `Welcome to Etherpad! This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents! Get involved with Etherpad at https://etherpad.org` | +| `IP` | IP which etherpad should bind at. Change to `::` for IPv6 | `0.0.0.0` | +| `PORT` | port which etherpad should bind at | `9001` | +| `ADMIN_PASSWORD` | the password for the `admin` user (leave unspecified if you do not want to create it) | | +| `USER_PASSWORD` | the password for the first user `user` (leave unspecified if you do not want to create it) | | + + +### Database + +| Variable | Description | Default | +| ------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- | +| `DB_TYPE` | a database supported by https://www.npmjs.com/package/ueberdb2 | not set, thus will fall back to `DirtyDB` (please choose one instead) | +| `DB_HOST` | the host of the database | | +| `DB_PORT` | the port of the database | | +| `DB_NAME` | the database name | | +| `DB_USER` | a database user with sufficient permissions to create tables | | +| `DB_PASS` | the password for the database username | | +| `DB_CHARSET` | the character set for the tables (only required for MySQL) | | +| `DB_FILENAME` | in case `DB_TYPE` is `DirtyDB` or `sqlite`, the database file. | `var/dirty.db`, `var/etherpad.sq3` | + +If your database needs additional settings, you will have to use a personalized `settings.json.docker` and rebuild the container (or otherwise put the updated `settings.json` inside your image). + + +### Pad Options + +| Variable | Description | Default | +| -------------------------------- | ----------- | ------- | +| `PAD_OPTIONS_NO_COLORS` | | `false` | +| `PAD_OPTIONS_SHOW_CONTROLS` | | `true` | +| `PAD_OPTIONS_SHOW_CHAT` | | `true` | +| `PAD_OPTIONS_SHOW_LINE_NUMBERS` | | `true` | +| `PAD_OPTIONS_USE_MONOSPACE_FONT` | | `false` | +| `PAD_OPTIONS_USER_NAME` | | `null` | +| `PAD_OPTIONS_USER_COLOR` | | `null` | +| `PAD_OPTIONS_RTL` | | `false` | +| `PAD_OPTIONS_ALWAYS_SHOW_CHAT` | | `false` | +| `PAD_OPTIONS_CHAT_AND_USERS` | | `false` | +| `PAD_OPTIONS_LANG` | | `null` | + + +### Shortcuts + +| Variable | Description | Default | +| ----------------------------------- | ------------------------------------------------ | ------- | +| `PAD_SHORTCUTS_ENABLED_ALT_F9` | focus on the File Menu and/or editbar | `true` | +| `PAD_SHORTCUTS_ENABLED_ALT_C` | focus on the Chat window | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_S` | save a revision | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_Z` | undo/redo | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_Y` | redo | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_I` | italic | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_B` | bold | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_U` | underline | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_H` | backspace | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_5` | strike through | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_SHIFT_1` | ordered list | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_SHIFT_2` | shows a gritter popup showing a line author | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_SHIFT_L` | unordered list | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_SHIFT_N` | ordered list | `true` | +| `PAD_SHORTCUTS_ENABLED_CMD_SHIFT_C` | clear authorship | `true` | +| `PAD_SHORTCUTS_ENABLED_DELETE` | | `true` | +| `PAD_SHORTCUTS_ENABLED_RETURN` | | `true` | +| `PAD_SHORTCUTS_ENABLED_ESC` | in mozilla versions 14-19 avoid reconnecting pad | `true` | +| `PAD_SHORTCUTS_ENABLED_TAB` | indent | `true` | +| `PAD_SHORTCUTS_ENABLED_CTRL_HOME` | scroll to top of pad | `true` | +| `PAD_SHORTCUTS_ENABLED_PAGE_UP` | | `true` | +| `PAD_SHORTCUTS_ENABLED_PAGE_DOWN` | | `true` | + + +### Skins + +You can use the UI skin variants builder at `/p/test#skinvariantsbuilder` + +For the colibris skin only, you can choose how to render the three main containers: +* toolbar (top menu with icons) +* editor (containing the text of the pad) +* background (area outside of editor, mostly visible when using page style) + +For each of the 3 containers you can choose 4 color combinations: +* super-light +* light +* dark +* super-dark + +For the editor container, you can also make it full width by adding `full-width-editor` variant (by default editor is rendered as a page, with a max-width of 900px). + +| Variable | Description | Default | +| --------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------- | +| `SKIN_NAME` | either `no-skin`, `colibris` or an existing directory under `src/static/skins` | `colibris` | +| `SKIN_VARIANTS` | multiple skin variants separated by spaces | `super-light-toolbar super-light-editor light-background` | + + +### Logging + +| Variable | Description | Default | +| -------------------- | ---------------------------------------------------- | ------- | +| `LOGLEVEL` | valid values are `DEBUG`, `INFO`, `WARN` and `ERROR` | `INFO` | +| `DISABLE_IP_LOGGING` | Privacy: disable IP logging | `false` | + + +### Advanced + +| Variable | Description | Default | +|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------| +| `COOKIE_SAME_SITE` | Value of the SameSite cookie property. | `"Lax"` | +| `COOKIE_SESSION_LIFETIME` | How long (ms) a user can be away before they must log in again. | `864000000` (10 days) | +| `COOKIE_SESSION_REFRESH_INTERVAL` | How often (ms) to write the latest cookie expiration time. | `86400000` (1 day) | +| `SHOW_SETTINGS_IN_ADMIN_PAGE` | hide/show the settings.json in admin page | `true` | +| `TRUST_PROXY` | set to `true` if you are using a reverse proxy in front of Etherpad (for example: Traefik for SSL termination via Let's Encrypt). This will affect security and correctness of the logs if not done | `false` | +| `IMPORT_MAX_FILE_SIZE` | maximum allowed file size when importing a pad, in bytes. | `52428800` (50 MB) | +| `IMPORT_EXPORT_MAX_REQ_PER_IP` | maximum number of import/export calls per IP. | `10` | +| `IMPORT_EXPORT_RATE_LIMIT_WINDOW` | the call rate for import/export requests will be estimated in this time window (in milliseconds) | `90000` | +| `COMMIT_RATE_LIMIT_DURATION` | duration of the rate limit window for commits by individual users/IPs (in seconds) | `1` | +| `COMMIT_RATE_LIMIT_POINTS` | maximum number of changes per IP to allow during the rate limit window | `10` | +| `SUPPRESS_ERRORS_IN_PAD_TEXT` | Should we suppress errors from being visible in the default Pad Text? | `false` | +| `REQUIRE_SESSION` | If this option is enabled, a user must have a session to access pads. This effectively allows only group pads to be accessed. | `false` | +| `EDIT_ONLY` | Users may edit pads but not create new ones. Pad creation is only via the API. This applies both to group pads and regular pads. | `false` | +| `MINIFY` | If true, all css & js will be minified before sending to the client. This will improve the loading performance massively, but makes it difficult to debug the javascript/css | `true` | +| `MAX_AGE` | How long may clients use served javascript code (in seconds)? Not setting this may cause problems during deployment. Set to 0 to disable caching. | `21600` (6 hours) | +| `ABIWORD` | Absolute path to the Abiword executable. Abiword is needed to get advanced import/export features of pads. Setting it to null disables Abiword and will only allow plain text and HTML import/exports. | `null` | +| `SOFFICE` | This is the absolute path to the soffice executable. LibreOffice can be used in lieu of Abiword to export pads. Setting it to null disables LibreOffice exporting. | `null` | +| `ALLOW_UNKNOWN_FILE_ENDS` | Allow import of file types other than the supported ones: txt, doc, docx, rtf, odt, html & htm | `true` | +| `REQUIRE_AUTHENTICATION` | This setting is used if you require authentication of all users. Note: "/admin" always requires authentication. | `false` | +| `REQUIRE_AUTHORIZATION` | Require authorization by a module, or a user with is_admin set, see below. | `false` | +| `AUTOMATIC_RECONNECTION_TIMEOUT` | Time (in seconds) to automatically reconnect pad when a "Force reconnect" message is shown to user. Set to 0 to disable automatic reconnection. | `0` | +| `FOCUS_LINE_PERCENTAGE_ABOVE` | Percentage of viewport height to be additionally scrolled. e.g. 0.5, to place caret line in the middle of viewport, when user edits a line above of the viewport. Set to 0 to disable extra scrolling | `0` | +| `FOCUS_LINE_PERCENTAGE_BELOW` | Percentage of viewport height to be additionally scrolled. e.g. 0.5, to place caret line in the middle of viewport, when user edits a line below of the viewport. Set to 0 to disable extra scrolling | `0` | +| `FOCUS_LINE_PERCENTAGE_ARROW_UP` | Percentage of viewport height to be additionally scrolled when user presses arrow up in the line of the top of the viewport. Set to 0 to let the scroll to be handled as default by Etherpad | `0` | +| `FOCUS_LINE_DURATION` | Time (in milliseconds) used to animate the scroll transition. Set to 0 to disable animation | `0` | +| `FOCUS_LINE_CARET_SCROLL` | Flag to control if it should scroll when user places the caret in the last line of the viewport | `false` | +| `SOCKETIO_MAX_HTTP_BUFFER_SIZE` | The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks. | `50000` | +| `LOAD_TEST` | Allow Load Testing tools to hit the Etherpad Instance. WARNING: this will disable security on the instance. | `false` | +| `DUMP_ON_UNCLEAN_EXIT` | Enable dumping objects preventing a clean exit of Node.js. WARNING: this has a significant performance impact. | `false` | +| `EXPOSE_VERSION` | Expose Etherpad version in the web interface and in the Server http header. Do not enable on production machines. | `false` | + +### Add plugin configurations + +It is possible to add arbitrary configurations for plugins by setting the `EP__PLUGIN__<PLUGIN_NAME>__<CONFIG_NAME>` environment variable. It is important to separate paths with a double underscore `__`. + +For example, to configure the `ep_comments` plugin to use the `comments` database, you can set the following environment variables: + +The original config looks like this: +```json +"ep_comments_page": { + "highlightSelectedText": true +}, +``` +We have two paths ep_comments_page and highlightSelectedText, so we need to set the following environment variable: + + +```yaml +EP__ep_comments_page__highlightSelectedText=true +``` + +### Examples + +Use a Postgres database, no admin user enabled: + +```shell +docker run -d \ + --name etherpad \ + -p 9001:9001 \ + -e 'DB_TYPE=postgres' \ + -e 'DB_HOST=db.local' \ + -e 'DB_PORT=4321' \ + -e 'DB_NAME=etherpad' \ + -e 'DB_USER=dbusername' \ + -e 'DB_PASS=mypassword' \ + etherpad/etherpad +``` + +Run enabling the administrative user `admin`: + +```shell +docker run -d \ + --name etherpad \ + -p 9001:9001 \ + -e 'ADMIN_PASSWORD=supersecret' \ + etherpad/etherpad +``` + +Run a test instance running DirtyDB on a persistent volume: + +```shell +docker run -d \ + -v etherpad_data:/opt/etherpad-lite/var \ + -p 9001:9001 \ + etherpad/etherpad +``` + + + +## Ready to use Docker Compose + +```yaml +services: + app: + user: "0:0" + image: etherpad/etherpad:latest + tty: true + stdin_open: true + volumes: + - plugins:/opt/etherpad-lite/src/plugin_packages + - etherpad-var:/opt/etherpad-lite/var + depends_on: + - postgres + environment: + NODE_ENV: production + ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_ADMIN_PASSWORD:-admin} + DB_CHARSET: ${DOCKER_COMPOSE_APP_DB_CHARSET:-utf8mb4} + DB_HOST: postgres + DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad} + DB_PASS: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin} + DB_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432} + DB_TYPE: "postgres" + DB_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin} + # For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad + DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEFAULT_PAD_TEXT:- } + DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DISABLE_IP_LOGGING:-false} + SOFFICE: ${DOCKER_COMPOSE_APP_SOFFICE:-null} + TRUST_PROXY: ${DOCKER_COMPOSE_APP_TRUST_PROXY:-true} + restart: always + ports: + - "${DOCKER_COMPOSE_APP_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_PORT_TARGET:-9001}" + + postgres: + image: postgres:15-alpine + environment: + POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad} + POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin} + POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432} + POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin} + PGDATA: /var/lib/postgresql/data/pgdata + restart: always + # Exposing the port is not needed unless you want to access this database instance from the host. + # Be careful when other postgres docker container are running on the same port + # ports: + # - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data/pgdata + +volumes: + postgres_data: + plugins: + etherpad-var: +``` diff --git a/doc/documentation.adoc b/doc/documentation.adoc index c5751d4b6a1..5eb35c18c41 100644 --- a/doc/documentation.adoc +++ b/doc/documentation.adoc @@ -1,7 +1,7 @@ == About this Documentation The goal of this documentation is to comprehensively explain Etherpad, -both from a reference as well as a conceptual point of view. +both from a reference and a conceptual point of view. Where appropriate, property types, method arguments, and the arguments provided to event handlers are detailed in a list underneath the topic diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 00000000000..260ccc79660 --- /dev/null +++ b/doc/index.md @@ -0,0 +1,39 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "Etherpad" + text: "Next generation collaborative document editing" + tagline: Make your documents come alive + image: + src: /favicon.ico + alt: Etherpad hero image + actions: + - theme: brand + text: Install + link: /docker + - theme: alt + text: API documentation + link: /api/ +features: + - icon: ⚡️ + title: Real-time editing + details: Collaborate with others in real-time. See changes as they happen in an instant + - icon: 🛠️ + title: Extensible plugin framework + details: Add new features to Etherpad with plugins. Create your own or use existing ones + - icon: 💬 + title: Real-time chat + details: Communicate with others while editing. Discuss changes and share ideas + - icon: 📝 + title: Rich text editing + details: Format text, add images, and more. Create beautiful documents with ease + - icon: 🌐 + title: Multi-language support + details: Use Etherpad in your preferred language. Localize the interface and documents + - icon: 📦 + title: Easy to install + details: Get started quickly with Docker. Install Etherpad with a single command +--- + diff --git a/doc/package.json b/doc/package.json new file mode 100644 index 00000000000..48f1bfb89a0 --- /dev/null +++ b/doc/package.json @@ -0,0 +1,10 @@ +{ + "devDependencies": { + "vitepress": "^1.3.3" + }, + "scripts": { + "docs:dev": "vitepress dev", + "docs:build": "vitepress build", + "docs:preview": "vitepress preview" + } +} diff --git a/doc/easysync/README.md b/doc/public/easysync/README.md similarity index 100% rename from doc/easysync/README.md rename to doc/public/easysync/README.md diff --git a/doc/easysync/easysync-full-description.pdf b/doc/public/easysync/easysync-full-description.pdf similarity index 100% rename from doc/easysync/easysync-full-description.pdf rename to doc/public/easysync/easysync-full-description.pdf diff --git a/doc/easysync/easysync-full-description.tex b/doc/public/easysync/easysync-full-description.tex similarity index 100% rename from doc/easysync/easysync-full-description.tex rename to doc/public/easysync/easysync-full-description.tex diff --git a/doc/easysync/easysync-notes.pdf b/doc/public/easysync/easysync-notes.pdf similarity index 100% rename from doc/easysync/easysync-notes.pdf rename to doc/public/easysync/easysync-notes.pdf diff --git a/doc/easysync/easysync-notes.tex b/doc/public/easysync/easysync-notes.tex similarity index 100% rename from doc/easysync/easysync-notes.tex rename to doc/public/easysync/easysync-notes.tex diff --git a/doc/easysync/easysync-notes.txt b/doc/public/easysync/easysync-notes.txt similarity index 100% rename from doc/easysync/easysync-notes.txt rename to doc/public/easysync/easysync-notes.txt diff --git a/doc/images/etherpad_basic.png b/doc/public/etherpad_basic.png similarity index 100% rename from doc/images/etherpad_basic.png rename to doc/public/etherpad_basic.png diff --git a/doc/images/etherpad_demo.gif b/doc/public/etherpad_demo.gif similarity index 100% rename from doc/images/etherpad_demo.gif rename to doc/public/etherpad_demo.gif diff --git a/doc/images/etherpad_full_features.png b/doc/public/etherpad_full_features.png similarity index 100% rename from doc/images/etherpad_full_features.png rename to doc/public/etherpad_full_features.png diff --git a/doc/images/etherpad_skin_variants.gif b/doc/public/etherpad_skin_variants.gif similarity index 100% rename from doc/images/etherpad_skin_variants.gif rename to doc/public/etherpad_skin_variants.gif diff --git a/doc/public/favicon.ico b/doc/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1a4f8aac19634ea08a0fa67daa07636b310856a6 GIT binary patch literal 130045 zcmXV01yCGKus$RZToT+82(E$P8r%sG+&u(1?r@jj?iMs?2o8ZeG`LHEpm)H*;qESv z|Gl?WTf4nmGrc?0{Y}r;4FI43FaZA@D1bMB1P=hf_W3?2=>KGvmneXX=Vww<|1V1e z05pLp03M$IlYcS-0CHW=E#LgVOb7rRY@z@NpRfP*_XQmQNV`G-M14|Mz{94*e(oAi zNl{kozqkLLFaJ|*?DF$jr-!bR?8nbuiwEt#e?1Iz=&p^nyq95~d%)t>1B_64o;Rq( z;17x5Uh+?sy^)hb<%(IopDii4Ewr@Dzq7^EfBmJUO?r~dsw&5f|M>nSAgWM5zTg$5 z>YD=kf~6@!D$4EBo5Q06?>zoMF}wVR*+qwg_KCBOl{vhJgPP@|wj*KRng^J})1{%q zQNx6AD6@qR!ylT!n~sD-4*d8->*J+2aEwhm-btL7UbLN8yr8_GGXNJ42!B)*fbtIg z{cK>FRqDKO#_&+LE(V{}Z;9cUMiGO@DLbaAIl-=~MXuT;BI4D{#mH}?tMieNQU__S z=~+6Is@2PL#|x95DwvmIrw$;40xBKL{>JayM*`xrh>uG>h#5xdtMZ$}%c2RN1y}qP z#hUIE&YAE2+S6BUS9TuNbaWj-UEG++jnR8Q!aSVKRzOZLO;i-mM@xWpl;L#en}qu| zm*aGd%yDWR4-qI|h~+W94{=BkO;Lz`#-jOWP{wz=xKuk~UN~xqUb#`0y#op)d1*8W zaAk;r2E|8A10<wn34)GkQQNmhXU8K}?nzeMBbk+ar13f>JD$pHV}M|bK4?PF9BPHL zO=)Z0<-Xm=H~((OX<qU$JA~9BqU4(0xlltselNLI4ExF%%CmwYFYAqDh0>fbcbn|7 zC~VRH`nz3tDFRhttGHC)|A2L3*o6<oxTciSi@es3J<I?Eut(A@nmU<1?&$el5o>(% zk;d($x;Z#c@(kRzG52}#vc>}8&?N+!eT`FZ_|tG9U|k?@vYwiN!KG=Swj-7oXrLjx zss_e%m$UW+ZRyjiYRNQr@V51RK6xa86Km($i`_!Sg+G^b^IY=7fg=>?#xEvCBX^t% zW*#OUcF8)9;?94`W0hq9f1zA=U@j}`(LVLM>Q*3+Y&V@zKy;BFOTu+3mA3$=w_zH@ zY=rYW8Gmtp55|D+&^Tss{$+V^Q&EU<^7TvfA-v~?MSdypPp@wrj2P*C&-3;lp5a|c zogDC&Du|G}PBVa9Xm90v94Q2@-PE+(ha*bKgN}TzvafUtGI-ARy1c||FE86s?@=5` zGc3Dzidz|(AsWQo67Sx%c%9axP>nCBOh(HgUO{7FyW`xib|<*jY%cmg&sq35s`9K4 zT@>EJDa;vhJF4yJ4}VG)qO5$~7d|}8gy}_XbIN+DVBVih_+4D>BQeUTfB@5{!$41h zvr|0t+g5;N_OuuH&2O)+!S1{CcBeNtAu<~L%Jq9tE3p>7wcyvQ@=UqkZ8F+D;is(7 zfj#d)Z;alIIH~HsL#iZRpyZ5@R`-FN#-n=%4EJ~-a+~bqQugxc%+om$ywgnvw{zlb z0x7tBaeeT{@738ZCWRMkP}TRfDi_QWKKA12wgO2W1_FCB*Wc@>ovB_?U!hKTuM;^2 zmgqH9E<wJ_**)2B4ov@+l7^&m-^(R^`bQU19pOGNaFpA?v`b)SiUW)>p}>z|XpglD zdW!scr%8W1=6w50lq*2gUsPQ*w*|%P;lVi%<=O?~PjIj^npMotN<u8#SM<GcF(giB ztX9(@th3n@S_1UPp!hlgN&)n0gsw|i4-Uf%Oi0=mqY8o-2sOg3Z;JU?VbcrcFC}lK z{>=#2HS{Cs0V{GsBTidN(|1hs<FU?hWW8RI9|exEcWiw=A|KVzCI|4WW_Mk`4eE&P z<RfgxPD=L?ViYU;%u&Ovth{5GShH)vLq)&AkRE|#S?qG+v8G))A+O($^zG%>-6H>y z{6ht56I1@d%jZTmD{qOnzI{-<shg7_k6zeX5X%H|&c6{}?Z{z$SKKkc&&0@Grcu2Y z%wQ<rTyv)#JbAq65W7R#8JLh|DjQ0NaXcc_=f+0V>G|hwW%_+pUY%ie2WICJ_OY0u zBWpNVQ_<v;)@)ROkbB|Z1<3a2{ym)09$nX}HZQ&J((CgM^}q5e%#RB}qEkA>4xYnX zUQQZK6%JPLs;-9n$i>6G7V676C{0s&7;G=C;RVXmfTM)(nV%fpQ+drJGjoR#=vno% z)`5zd1fJ^|rBW(gw5%b`ch*6^DkHtOB0Hy?kh#L(EKm8r_OnGK>8YHDA9b-xqq-P7 zD1m0i63n#mT~bM2e8TcujN-74)C)Oi%pQ|?|KH5Qg1v2WM7k4~YiQrf#szH!!Axvi z^O!5fa;-7fpIFh`3;=B>)^rqo(2?Jq#<<9FOvb1r3W5Y60@Hs}`?V<D?}MrJM3<%w z&gSQ3^UL)tF^kuo2fT)h*{kv0yDo%;du^c^sKPhB{L{@5zV)=krti~!&FnR`&vrP1 zKSi(Ax0OSkmZvu^u|w}f`>(1ZAJ^@k7A0X`CIztHh?cka7wr9k0Aw2a?#l<sDO&4j zOl@Ap(USGwLQxQyhqs#4O+&bWsUia}RLATjNVI-53pYme;aueH?pc%r1@LPp=k8gX zhKPDsE>Yu?(t&*!=bh<LJ(QjLq^&IN{q__xG9)`oChzlmja84(<}jG?hQ|PVzy|g` zUFZFU(8om~)a&uPz|%Cq6K+WU7QfIbX|uws!?j*M%2dvSPmKJ&Y-Fz8G(+>RJ%^}N z;CN1+Wu|`7P@|&A(ALSGe(iqREBkHXl4Q0bxo#z>MiLBse)dN5%0%)mI`DFQn$SpX z<)%76C-`PB>t^NKw)gGir=MXNLobUTDfN_9E}%G4<8KB}8`t7VvkSfV2)~s&bisvs z9Wa7l+E<|CU^qIwG@8=%aw11>ua)=ayicUooC~?MX*oNu(un^iu<BvaqP5`>Vj3HZ z8MU2{cHXL8hL%zWpt<e?Jo3Mspn5&LIGjfMP;V_`?aCSEnB^zm$l%7p=^Sw9#f*$! zq;9nTLO|AT;8-b?$UH7QjY3cZ^N51!ZtM)UrtfN8M_lR-%ZhLxGxQkmW{~=`?~AP^ z+%}V3eFkFsTrPHuD@>rB{W;7ui;XH5M!i1teDW3PKlL7&*?mGqrWq^@s?1c+{MG3m zsf@Q>h|4S;&i&@A-ToUP5;YDE0cD&Tg$VMKkXdRJY#H9X8tJzOXMgnCs@!{=B()`I zTBdi52gKW7#Lj%ZU;wnE=B=_AEMl-ze^8=}@n+A0XLx)_g%xWXfg$E^*fPYU_Fvw` zb(;)N;CX+AZk&8@o78&jt!Ms6$4I$pSfgL1E9s(IVfR~i#Sx%ev2n$NoEa6fU;MN& zCF}2GXm$pFbS3wEl>uDE-%zVUDh&|H55(%#=#CuOry1m;wmm)yPUyxuV;s8z$OQ2h z_%o+f${PO#G=Ma+T61tAl_zdmBh(1D+CZ+Id)H`~EDq13sls*RVgs6-^W)k58Q^#W z5b#srT3dVl<eXUJJD%$7Tse|<GJQ5!9nJIirYG-yWq|7}uf~g3u{;c*?!a`PKVP%I z<pb{o$~|Ea_t5|-Ga3US*}iuI(;IaLT#v{@HTkeZS=D}^HY?LYjLfQYuHyUp7R@I+ zSQ7XT(?1TUSt+d?x<6s439%GcZ*-6oi1H7!TZ}Ibb=B$QK{bi-bfrsbsNBTUU}MnW z(rj#^j{8BgCd+`HK%Y4zq$u(0*^M*mu@k^nE6TLqQ*&RAD^*27*!lD6`DR6-rSf&5 zRcQ7duI$bC^mkNz4&KFJg>pBA@=dTIUd&8x3(i+?k8{No=57c__rcD01X(1=DA$Ji zWR@21P|-Qtg(}TOX-*Cv+_8{E@{wnNBFNcl(^>j$*s<AD{?n2;J4aw{aK=bZ)!R=8 zI{WoWM42B$UAOw!^Yay{jG5Lvc29mq6(*Cy@&Cnv8}1pmnPUXIb1jHY;S^0Ft^795 zY}^*Vw!7O!uikaxsUj7j$re<JO~M(g9c9^Lj~?f?Zv|1k9$Hu029{3o-n4!2MsJkt z4((e`D*o1cb7JRL$-C(LUjMfQny~F(!z~o<5)+*l@In~_M7rtUo*c2VgWi{()*XU< zcwZccbb23p*|h5JqAN3ge5*DBwMvHfq%(Yi8m!VThSufRqv);<GC_@+X?8@B0{L7J zs)2PC??%6#y`#W0+MIRDpc!m#i~f}`ueb~yk6Vj(a+Ul)JTSEJ6|XYY!YJ}xS@HTU z%nNgP;pE^E{5ZiqIq)VFu(2k9kf(PJ_vK0cCPvzN)bHf=$y;p1JOhL0OI3_8MJf6S zGP2(!8&Jg;K|4jwd8lhr_6G(v!J<)Ob3+AIi%Vx91*}|RC;~IExKD;{Fc5dNUhKg) z7o%QHb|gC2Rh4Uo47wImEjHYzV2n)Mum6nGPH*RQlQ0}20X1_L)*u-~y5#5XF(RNY zv<jDap2+K#_%PV2osk?Pp<7Lu3=PhvLQmrx5{v;i?mSS?Sd3X<rX50fOc(ePi_n2p zaz~ns;6yc?kO2oyIM<j8KQJGLa!Bh-ZqNv#@)D@IzF4{HG~(N^#Xw9U2_!N1^gOk# zkqvdC>&2^?ytqG@_E1(?MbwL5ld^XAeeJQGrb9E062<#K)x-+lAC5@U+lBrO`3I_i z7Y9)FNaM#U#%A*+pUOkezG#%sf~?;`dvnh@dv;xtMVwb76O6z{2rjVET70Y-mgm&n zXQ15!a{{H{d*nq1t?#vPQ!FpQc^xwC(tHiX$ft_V{=`+Gb-|V}@YvEPArY86g6Z`} zr@l7CF2$c(z%09z8uCe#2=Wl;tE$y>UP}U(w+uBP{zC!jwfm5GZrpPqN2u*)((fdn z-d!y`_04E_tX5-PQ9!NkVP?=Ns<qcC>JAHW+04k@aTj;*p_9T3An3T0;YLFmy~8nf zNNp>|<2xf~A|TPambYSAHxO==D%P!Ffo`Pm2|@~sL3_ioQsW!Lm%mUXUo(d234G-c z^~j+G*Xz@M!+<Wm_*M?RNVPP!VbDo)w8J_4456C!r6#g@cc_`>LYR0MSmNqZzP&0z z0>r!>Z|1aKHV3vs^{&$~R~~+3C=G?6Tw`86{ljH%`__47VQE?NGWNUmf<+5Wk=8p0 zck&F`1^JD)Ux0628Ph4vK|KRi_2t?8KGD1$ZuoD^j(;KXWjcQdZO!wIgI-+C&tDW= z@F-H8lJAM)TA0Mw=1|PbcMB*F>o`At94B&a(NfMbjfUGsz|vKiH!gp_MsyIcx2@5^ zpR{{A^11+o@!S9}JiY^wHDjK~95w?4JbI7Ou|~T@IwkA(+aEge)R2170+n~;RbCVj zp;DfNeh-lKC*ys7cv6Rgu<};n^_Nt7I8S)@?sqjNUq`2!m7|-z{&__DJz@P!!I=>8 zD%Wasby?AXQxpt&VGn8zZH|M<{>DM@Kj>o?lmgu7_yViAp*Ai*)2RQ<7p(KgO7nS| z%b05rC4r*`9bgQi@6@dng;AWXnAN8tiRojxX9Cwy8(pIsaz{2-Yg`qm{{km>*Px77 zMGK_@y}vJ%-b4WoJo&2)?G_A$J<ucPP(R4tgrMl3>^1^&9kdlylHXV&z^^qAL-miC z`)IwX<CY-54@^7C{h9l@5?Bzx#DV3;l(i=6D9~J)?^`CaD|=TMUywj9(_spTYD;&t zM$JwHa9tL7JuQmvSE19APhZ360va-ubNQe-a-n*WZUbK|%&2r6I(xqh4Q??tA5K-u zw*pR<^if?z^vT5Uf6w#x_TGjSju-~Y8V}(Oe~$0Hy|kVayJUhd<3a|Ux_6D9cIY0@ z@&5+)B5&8O!$zT^ICr1Fe{~m2mb<Z*QU2l0s^LI0uG2`!CuciSa4hn#bva&eU|hXf z^AI}!`Ly2DfQo4ZxdRGi5m0nVQk`4)<xt~K1U&Nl_msq?(vsOtG*KWc#73!mtD;Q~ z_xA^1O9PIMmIs{&qV>#Bj&HOw$NT#5oMujE%-Qa}l(#x%3I?odZq9_8y9!@GY7hc& ziBkCK-V1@iAjY=$hlVVbbrpTTnBaL_Di4}d4GBwIilMEeNkY3nO23@{1`nV$q`}tD zBLv+yZGlwqEsE=C!Bg0z_<0AWN96VfAa`tQ-GAmowco95@Ea6q!T{*^5k>d-cYjY$ z9gyNCF`^p@q>g@YPD#V?4m=V(ecl#XP(GB#^Ru2CX77L+KJZ}1TEaI*6=E`*P$}WQ z(h|i0K6S4W|G5#F^k&g?YeNaN1>sPESFbibyw8H!QFz_R9Qm5tb->NweWMc^lvJ0Z z1SH-hW!T9!lXqD~H4{E(Ja?)snlON$+U$Km^^#DMRYo*Tl>?S%xU-PW;<8oc>g<;5 zXnmf$r~~vYpx*yg*qu8(U8<w{fYu|9DkOkk0=7)7;Pz$A@j5Bk8L2pW09_G83b2nd zZ+I-vHLWm%vpc;Wcg@|ms5?yn;)29^DcwFV{%9`em`vz}sBdRkljP>bn;kjo#<CAm zfqQA}U_WKd@4W3zG^(#PJ386FlZ8cCmBnd?@vfeB42gS4)LeoT9?I*o9fl*lq2d9u zElsqw#Yc6O{NvvllqwToJ!?)JQ{D_fS1JgJUMU#16rLXdi-k?tm@bK9ZVLn*QI4i% zp|%*wmnb$7<i-vs7S|4~Fb-7X-hm^2gmj(!qcKb{EiM3WVf+d=VqYExbre{Mc1^mU zqBFvUSy^7O^hitodb>V3w(Vq+M*8gd>CjV@O{%s?h7K}n*mxl^Khb<5jO~^M)EW*u zO2~v^LtsSATLM9?L5EGknfXJbcKggxiaPYE`=P&m@Ey`@WhDRbX`}tFw_%w@Gzuk2 z8NVV5yWk)Ks`11v;Y+OmX2HJK)^<A+GB>VmVvZMezGw=w8P;29Z}UaGu8j|&UJusI z^zQcHtpl1e9k%YORb-GT5=&uD0<tkV4Vj~3jE7EZCcqY?ng5PENuZgEyN-9_pd>CW zz57x<Us6Q5Vznt+qDV$`>OwaZW@TEq^KfRF`{M<q-zo+Ro|Oy(SKOxNh2~L#@09k8 z@l+$ITX0(kR%<?)b>%>|0gkW>wD-T_-#XBqT(?}_Wd4wN58(%z30}Cz55W&bU1fl; z(6}>0PWt)?h~<^3zQ2oPVwYFvCW!R14{oUK4Q4L*l-JjRb!Z$bNkZN}<OZ)cwjeBB zyR5DF!gvuC(khq1NgV}t9#5Jq7hQFEKYY^uCkEC9(gJFuZsT@RDoD3s+_8f0yj_{Z z(2@z6tmwytHh^bRRd{u)ZJbCFcdRx3+9N>Bk4U4`s#SF}&x_+(P+7~nEcpRAlGZlV z9j+AUGn}$lD#8+dFhflNUF+)#^RPSMMo`$Xh*2g7AW{b+Kx~oCRp+xo!W$E1ukjCG zfsB@Ew0+25&>#Hxy6GXXQEWGg<tOpoM%T>sv!bcYS9(KQ?4e;<yegYtLk^wdZCD^w z)0*lJFV(*nM{wXBs+~KY7{ap=dh%F5DNDM@)i>x*Ez5=u($_Swfhqx&fufR_Ag-^y zuWPN_g6!;3Xz&-`^rx&^oY`tH!cA+7f!XLk%uQ=<s$2V({xT<Nf(6G5y1ol;cdh~L zTO(l8H}cd(7dC>k*48%X)Q#;QUP`Vs)2uqJ3ItD#Q9ui@F_mjd{64Y|>LnfwnPY0V zRSM7?eTW&|&0G~2nr76i*690#b(rGCP$Q}SZGq9dU`tUQYubMME^71gGyLE|g-j%9 zJdH!ow~F%EzVxMbRUQ+y2H-HIX=JT~qCAX2`}|54WNii4;DIc$Y`=z%bv8e<zf=T1 zaX%M-e(j2~pGNU$fofzBW+OT%yo`9%PPN9o^U<M_X3b-5+IIqo(rxF@o7tiE(VbMW zPyc#X-n9v;qG%Aq^DE%^r*2*EDnCW8`57nmY}zTeSBbsU3t?Gm_=WsE$FW(yQHG#B zdO->Q*q$Vz0u}84Jjlo9BL<wB<D?eGIxNG6P~8C!<yrfF#vuKw1`J)A{In`!k44H3 z+`aRX_osvf<}}c*5;e}v--S@FH*Pw2|D&Jo5|{HR#~tuCHjzwgUiul+{I#_W6a1~$ zQMS?T*BCSClfw-uqW}V!OtMo!;#qz`g&3FB_a=2l@pR4B=p@Nt$ZJcEF9og3yF4R; z<w_{5EEG1JO>l+7pwe}zQO7XS6!lw<ox)&3Xg-47n!=Z^Rp~uPVB=NBhWhXD@t_^Q z$rChFyP!cTv6~2SF-DOC&h6Gn7$PPfJ7&@Vb={>`UYZ216aOg}v%cj^g>lo|s=O;~ zTm5qEO^9np$><GjoWz70;xcEdt&7m-i(bEh9X=-|tLUepu(TAxcWFbOw!`6Bx8fUi zX#jJ|6PSvoFCK7+VL{%-4KuuaU^4q}96uWxk+AK@&6~9M8z1#}Mclx5ChsspCWt1; z@o(KhWw$7y^3y#|qEapcrs>qp_HY5}2@u*a=pR%(l?*;^EBUIK^Zg(`-7FxSMt{L7 zQk`2Q=W_wy$k)>sIlAQ5Ov6;RVa$6O{94INN~U&UYJ$N7+vUWE%KGGug5LTOpR*C9 z!n_GG<)%|U9#I>tp{m{lo*(h+lI&W3LFk*W6zKV`n*HFWjjK3(VhZ&r5OJAp%VYW? zREPq@o@~No=QH_Pe}@z^Ae-wE30Z0|)#VLQnCL4_8D(P~sisT)NDii8C%w#Gaed@t z)BMQ{>Y;cI%wz>$9c+mJqp~=BnUUis2HcXw`~%{b*+~|=<gt0@wfMj?DnAn$*WMWQ zQ`S_;AaRqCNU1ei-Xt@Lf9{=5BqWL^$~3A751<Mju;^b3$d_wN8#yi4{LP=?RV$95 zh|AFX;aVJ$<<vKPwU*1|mgBpe$nofMv;O#0G=E;;hixC!L)_Nnik?w=;FnS5OL=Nm z<k&Y_OwZv1E)VQ|-8sP+n5KTsxGEwN(odwpS+*KZ8sdG5?XXuXVMrAFJ0Y^ZYs;67 zvn!Xu$jSC0*(%x8k>$(g_m(8h`I4>Lx4<O9R-it@(o$omzHWUsXO(M*d)IMBFttJV z-^S_*PugwKZ*$?@pM?(kTsKe>1;^J(?rK>utrc3^4=&kad5(hz7Ds`d33V&8jK;t@ z(ACq}oC1wrY%z<*b|rHXV!(4EI^B1@F@`aOH*`brF$EjyW(wnqnOpOCGm8N?PZ~m8 zN<V(%Qh}l+INd3EJJx;C`Kka`UfrY;mM(PGHy&9RGG#|bcI9G~%O>13l6%JYPX_<I z`VBqU;a&3xz+|LML4iDn*{)`9>tB>~a<MYdDA3E&!te>>cL~#RIifZR*v-yfp9eG2 zHESs(sO`cHyNERoOO*bt3se_qPb|yqEM#a$(}y#Rpv2%pW|C{t{1E-dQMccW%WtYU zoON^Dlao!h6uG!FfbO)eu$Ta$#P7c(@ql-%lqse>eVKRfqT4eu$`3f~l&L-z8{dFx zWw+CkB1QD;T|TV6P!VPoi`h$w#X0>zq)3JSzVpK&v27&pqTw8PMBuANVVseaIsfVv zhH|{2Ywv*gY$Ac1zX_Gq5Rt$|qKM?KScb`3WPjBKgaX0|)CeSAv4!Iovdcb)>ki^$ z8>mSnl@sf4bc$YorXsd&SotB^9&Vla%}Le!f!3pf@M|ZY-q+Xds(lA48kDum<?%4* z=6&0C;a&z+2_g4i;CJ1eXASHcoAhLmxl-JeialLm*?qQ4Ypm#Q!WcN=LfOQ*`@kDD zKP>7{m*eNPM`h59ms#A2NgF9~n7n&AX&D%OghGqPV;plP-Q)3|>Y4E`o2DBPw0k6I z4Yei-eEupFJCxZZJg=I0LP(ay(8o)Z*V8-0*~@c6M3bYiS3U=di2d5TJ%>7-;ni@l z?@VnxuPo^>CEYk!SLTNc535EoT9rG@2;Vwl*`u+IY{j3>s`f1v7lj|Abu#NCC(FVz z8G-cHI2;_XyzjawK&RM{)^vSJ-XE^Kfm^q<d)Bl^lep_V^J(p8=VKyX&OK_BSD!S{ zfEru!>HRdvUph_t11RC)t4-ZB<d8&Y9Nabo_5vOavsJ}_=iO2Dwl{y0(Hfo^!Bc*( z?1SL1(!&$f#woEa^WUd1fe+NRy`bi<EOi918-zt#{=N8=mRG^QC)!gs_o1zQ4XtA; z1fBYyyB6G_ogXfd;^(im;ne_Dldugla)EVa?+Wt+8-!8M9QV@NgSWk7I}sZsF&OB% z{g>YZUunAqRsACr?$Oh-45bnVhKPp+0=H>Xhu9jadIkFqRDW<}O%>Fv(SS4y91<$= zYwI6gUkrN4L;GGU4aEpqVmQ2lEDVT|KsftLYswJmaIS-IA}Ek8E4ve~N(s`fU^?`w zEAa}d^05B=cZ_X7290Pc_wHPDLEOtJnN>OXsZY`SrPr?gM8KW{a_0;N&i<XxAk3kb zeO+KalW7dhl6AwmUwreV4A5nG%`-3pBxml^;%=Q}2w%n&W^y)fNoa_>fesX!CRJXV zVnB=jz5elgt?6I_JQrnyq03(SQ!+W$@=qtI7cP52>+9NP{z}2{mZ6)5GSxV*o3n-; zUe<Z=(um0OTWW$*$r)a=xE_hGvPO*70)0kG_zO(5U&X20BAT&7JLJxU?{IbQ8EcNx zSiRYzM3a%6vRDSzqV?}8K4x$!iNW)#+pVn=BSe*n7IIrIIxBw*uPL%>_((DH`rrdI zU{&EdrrDvQ&j0Z@E&J16WCeQC*IXwZJTO;VuKgsZL=#=`Mn_qVu{!%|iFz^k2T*8H zxXm%)+TPo<Bbf#<I<C|%<gVXFN~hwzqp(KmQdka6HtAWiQWwb>D0Dn2k{I9d{@9bm zvaRDpP?vo{+3yAvg*uZ%XvzwpCM<K^PT0`)WvoGRAYJ7d7Wb|}9?>djR6>L`{uaD2 zuUuZ;kSFCXm9K(O*VQ8Wi75FQWW*-e%Tb#mYzQy1WJJ65B&(g5o<=fHR8VTXhexDu zIZ6&)&ilPM>QW9DTi7$+9qpkxZ|qIo5sRj8+X<O24Q2?5&0)A~u<}r^oI`gtiGXvh z3S`Ov$v{1^N?{M7@H}4$>5A@Aanx?7oX<TN$`*b~^3RE%D)QGE3offw#&U3UctF5| zZ@1Hszbj~uFzHujUqLhonR-pM7T$6)-%Iow%2|{$-4A@HzS-1$AZx;m81yTy5}o<9 z)TVnL`Oyh5*0%12DR>*X#$tp^`&Q3~j^xEloy|Yq%01goG)E-vsbq_B<ogd7<epr@ zc!&B;9msU&-xuPse{DS4g9OvaCY3r4bhkx%awS`G&Z{s!TfEoqz+*d-8JZ&cc{$%? z=2o5wTNCWQe$Dum`aK-~jzOt`NI%x7>Ae<g6Fz&N$&9r(IvH<4A+SM1upS~!u#jwn zXRGk*Jw<e7o865YbNpf@(t~KHix<U{>+M6@o)3AZ*c$EL*i}x3TX~t<au1}rqx*v_ zAFYD^YWmDO+&o{YQdc_23YCP1lM6H$)=AjK0(AJC&af<yp9yQ`as?(pImsA+1ZzyL zz8s=+v+S6ToCbcmBt&izn;kF#X*T~DTd2bos_E%(yVd5c!)_;-T`gZW=C5NdC#8Pp zz9H-1!|h3#8kkp>jlCsB{(gQ>4PGmD644sQPaOEVF`ojgr8X`c*>*s(5?a|9*7ll- zOCq>r6>tp1S`z`|6;717q65(%6k!2;kwP4h-tDAoeM@zHTdEzGoW5oef08TX!gu55 z{zuB14NugX#%~6KJ!j)9ae6CvmVPt2X0h?W(Tovb$>n(q_pP#+*Nyh|%+sUU$q$wM z&aP8(#UB!mgC@u*^&7WeyrBA1-tK?z-3>J=T#Xd*q3t4u=5>LQ8>{a-V}TCwH_Q08 zqDBaO9r(-L@tl$n*mTz1^6dMp02So-4&>F7lc6W>YZAfYRAcXb9M!m+Ti*`cz&!)* z9ur@(oa0MfANuK%qP^=S4L`u2ljA#3tr%@PnOuKI9*07>w}{-0`@3IX+0rXqMWy}y z-03j$n`37tPYBfrgPwI)x%$pOe8VZIW?!SN(-@#x#o5p~ciI?>n8P@ia+E2wL9YqE z<=4EBFa7i-#`c=-=#l*z)9hY(f4qq@+m(H3)D3n?;de&n|H8x0=(;)K@ol_rOu)Ft z8rY^fIh|(W!~H5qmI{+kDdUEPW2X1OF?po(WM*$Z@SNVOZW!7wW@_$!!sgXC?|<u5 zz?Gm|GP;Uo5MmJr6VzlJuqPvFd>c1Vh)3FD;jdK#!uvyvovtY7NB>&I72>A4T4-oa zhZ8vDbcHa=dw9-+Jt^gFF5#MaPQ)C~cuElDQYb|+a|AdJm%e;>54P67mXUaaNoyb- zy@w>Iu)=|GZq8Un^-P#{eXWaa9*Zp+nFC!fuper*C-0b(pVbOeKJdp6fkzkYlm0ez z{aX;JMBv7H);E31F4gxJHjVq@VQX|vb>+vJ*WpVGnM#p77=xU+Kg)-3aqjf2SyKU` zzYpal!3+;?*wb9cF_G+T=^y#LG9nc8sj2nq&Z5CyCv5oKrd0-#WWM%=RCt%#R4F;Q zK^OC3sKjYL$3>~(FQwLx0^2>0Jp3+lCtCTpo~V`0Tvln@wlD1{JHY)3$phob>xxx5 z?yw<qz>P_dE|99NqR0yOM_lTb`RWYz^IV5M{}zu}=PL^Vi{uEsHT;8uHt0?!H9f$A zo{AG1@WWa>&i69KxL@V)y3nZNA>XQ;p*!ZtYxefhb!<;93iS<clwSWZI|Vj&{1`kh zGMfT3!KC|S=AnhF6akzmi7g1Sh3D2J8r~=B8FTQO6Q}fb6y^IqRtpqA4+)$272Kqj zD(2UE1A4r)?gpnfh;-(iO{jKK(9g|@g;}wy$F#ozjmDNwlQH_V)Nz>(+TU#9jD4i% zgflZw67qoEi2fpGc!PC`ntj*1a0;mZovKk0u!LEs#c2ySJWaHdj-yIDI8~1p)1aF& zm8myr3!VsTSaKR|f-FKg<2o&)9tOB98})a;kwV%TKJ{_d>7CE<GTn)YxSycM2m-Cz zYE?*3(>c>0bbY2uNNR%8GQCDvCREZjXceaFNHJ6>9yE4{Hq@X?PGhzHcg+_E?WAye zPJ(!l*GY80rA=*rF2xk_;bLM|!H7Bu4@LX9^$`K{KXNkD`*;I0JF%uf3dq!!sn0$_ z`Ooc9#FWf$lkav{PtAAofS$6`PFg`mO^HaY@7+#>9zfBECDCy^?x~mHvOV_x$-as` zxvtt(%B!FW92h%}&*3N&d5YuW^39Z1yXn}+qG>5Q%Gr@YUB-)?!{Cwp-m3HF4BEEy zp$;4saJx8zK)NqA__XZxGGo2q8IddsM)LE5XmNMxI^P1a%{lmhz?hXs9G|f3)6&>E zMHNPp!nJw5tdemLxh{URdUwtUl1$j?38(j(w>{M*tnHqBtO|RcP!<}Fgp*|jIztUK z`O9Boj{t^1);9>nO`7(1^tkq1;eVJxu)e?U&yWJE^?!8iaQs9BG2{Rp&ofq%s#Fmm zb4h?@?Sd|<9`+wi1JY74jh6DdO;pQK966_M;dc1dYgdvdZ!3XSFZ|TsVIVam&Q?yg zGH4B+#+-1@t!_{ia;(hFcnua>@up5*L)FgkTQ&aR8QFPMKsh^+4wynME+*br^gw2} znpLh`?w#zeJyxj>A;lj$b%+!mn<WFiN=a{3?6H%t!Jd9=8Ygw^3F^D4MmteT83b1+ zO@Hngj73`T3l1|6a`=#nKDp$BtI-_UDaPC$DcO6!p-W?S&oFh6c?7hBr$ZaXFwU?Q z$ZEckjWEGA9aNww*x(Teo&g-)Py^+gnsP{5V!G^#&EH)9&K)zVb&se9^6S583Mblh z_u^tM2lUTY?A+qzg(rDu7Z`LUdHG&Ee?TZx`_zkmx|8T8npJQi0W8nziwYQnlWN?g zLB(MU-G|f8J&q2fnN(4>sZcRf1cEOkM7X2h=?I_6fC31lO6h4JvF5K;r$JFB=e5{q z&zX9*{eG}%>E7*GOn#?E;PKV*78#wr&#R*<H`i%~If<D3hhsWKZvH(V&4lKM-m}lh zNY6QaUY<WxOx~jX1(!t?IzT;%mbZI3M+fFxmuD%US3yM%clySXpEM^*p$>~&aY{x3 zd8VH-mzBRtcBNMN7W-XNQVA2QdL#&NmQ0Zo5w0dkoN0==-ArHS=bcEm?NV@ehvo9k zXFFS|^SL#07r~Xw(laS=OjkkPu_Fp|CkhGvxPLB_t`H|Z6^O?@5u_RohWjF{m_Oe! z4c=i-b~`?os6cWiu>TIX+4XJg>d|Xp!UV4nj$Jqm2FdMNL%dYbtFiLy7a~|pi`*cN zAgi%#Y7YZCRM#lAb-QMCSaZ0KVC4J7xc%JvSAFC)@W*{z6vB~1BIjGdd)v#8mF0p0 z@+!OiTJneygk$9Eb?D@Yq_8{cY_1X?ZKe2Gy5&MAW#0|+o~5t(x00oE{@XNhj=qSb zhsoylw|*7q;2Mx{Xx5KLMrT0aXTFrtb9ee)4DC-qs#P0-B;_~hQz5NSVazJ-IzD8N zE{&V7f`GF&6FUm<>R)h#2@Ngswv2Y*6!BA$!9D(^ujg`nE^LBTqsBDqu$*m&-Qiw{ zsRu|9t9a`)u8zsFmwb`^xge0cku`{pjar6>a&ZXi=0<ZAxL@S8Qba%!7o&oq7xSV! zjl)T;I?;mRjs3M)Zrc7U%0;7;C2QtbJr9^NvVfL;^Yx2N0z=#lR1@`FxExxbzAGV} zp>Rh_Lf@*UGvMVvqD^1X`9;_Z5jGc5Dr*v;C(Lyp4yK)xytsxk)CY;~+-|RtIGPFJ z?4Ax53+X#pX{F{DZr!k;`^z4tM31i2Tk?by)l`_L+C$`rK{ooaJDjj%fme5ZG)E1F z^+&z-yc^_J+vTM!!K14_$KdFX7&ti|x7v#wYtY!PJuFpX!S6PA#m6858&q%P!}SG6 z>c_PucQmln@TWupj3mDyqPvSX>p!a7(ub=_Rj7^IrX;)Bt2xO>kS|Cv73P}{s9OxS zW@%ht+kK~?y?Y8rlO?m2O+K0NfFJZGX4Kpuw+qT9?i^S5Vo}YEnHiNrocQ88mVWC& zg5rq~g7lX?nr{J?1)DgG)tKiUO#fPaX#+4LMS{E{QKy_@i5DV1jbyVG9>9f!zIiUn zh5rDxGZNigsCAk2$)-m-LHWCSE$QMm!tJg~_NP#RzcC2qL`PZVb4K@^AbQnS?rnqK zvm^R6G|21Wi{&#W26L!M7Y*3=8LRd8=`IPL6tlYgQiGA~ck?0*nBU#mP@A9w5`zQQ zO+Zw;Sd2Xv-flu7Kt(<Nx#M1$e~7(xpHmr{%is5zz6r^=DC^yWK7U-uXQ^V7S=e3^ zOKp(VO!41jaj`4RCb3SAFNC=~gprj~#Dhg&SrS0*`@*BK1!K(EpgJ%t16f|KoGV-H zwk-)k6%0t!2a_IKF+*0)g!no!F*bGYOo{=_2y;{quAH&W#ndZVKFC%pY`)`w1E20s zp}*1FmuCBPmd|nSbBx&sYX9f4l$Q2BN<ZN&mazE}8!#Y2W9}_49rl=$p#&$MMk!QJ zl$#M5c82=%>GsH&82Dny7zNx0x3~03)n*#ENA+dXAEIc}K>lFKJ&xBBeT*~$NqPG= zW-^`HR_v+E7KzetDxTgd6U%)A07i=XZb@8c`F<%{`hNvK5H3~r%r5ZliITlw_z(tz zk&Rn|4*)SCGIWrnUUtOxsr%@}{-y>}2-^Ny*pUp<$H&!lK_qp#_?b^w$KR-akjdD} zgQKEOJ{2QCa?a8q_a2_gH#G6+G0RQgL&``JU-g8OaS%Sf>vp}UoAk5S^}?Ol`ZSdM z!GMJT(&<d{PbqmT5B%DmwDS9+GkAj<>n!_I76oF4xH|ctXm`H{t(hob%=X#Af_bgA zK~zA`vxUW%=NNCC%MQ@`*KQ@!6$YLcoeZ$T>1#+?tdc*!*8!F=Zy^ecx@g|At5Gy_ zh{F0Rd&5ouohJ5UhzMVZZ1dDqyUg9<`(gBjM2qu|-5k!jjgTO?GIl1)0XFe62ApE^ zyy86v1(456vojZ$Psw2Fi-mp-tGpnELEN%|&A)o;!dJawSz_ahlH_fMe-2x-$7Z?j z0j0=sXuBz4j!E0MjY)dK!a6>_cy5zEzatz<kf?tlk19=y)nl_{XQ}>)+c1USk4lSh z%f<OBrCGtR?ZP3JuRdXwjYX7VM<MJSIOg1|Nj5)OJ7M#n2$EEX@eV^>Pja|*@y<fu zYb={m<r|7oQ`1n;PGW6uo>@??)y%KMO*zwrT6~uLX9M|wHG(ouW<oK~@!i|awrV>t zD0Joob;)lw6!xg|&;1p!?xjAiU^L0lF<5_A=Kg@USxXCw5#)6c_TWVC?zgeZu(f-m z&Y@u%8#IO`?1!U!-wU-7e%f=h8M}e<GhxS)+PQBYRP7imQ)v2_am&B)ZE6Tx=xV!a zoAl$}x;)(`ADFwW^S_MMCtxiRfo$1%yoWW%Z>(y5GA(=FR`gxSIGRP8#*ifp)tky2 z3q$x1y_YGH-jrE-;@i`qo|34nNqWb~B<T=wUw!FdC9g7F=HeTmoeh*DH`+#zlkyA@ z8+6HfwEmiN_z!GEZMthtR}=*r&^kpCHP(zlq4b1bez8{;O}rck1bm;Ubw9+_C9C<_ zCnXNZ39vw1-@dhl!M2;W)Qg$6MmXV2ZZM-~@MsOdQtUA#tnq52;;C%?0te^U?mXv9 zY~VD!w_6FMOeYj?Tx+7`yy<>Rc7|_j?H`RhnE_w+=eYbWds--;;i~-yQph~pGsRJW z8~U!NT9zTL;KjG)FU;&Cwd*+WTGXlhBSn7ji3(X;o)y)_q&tO5DrFxzTI|7T`{Ve9 z6~usNE>Xxm%-xhL2DiA@@dN${$awzy{<>81k)GcXodZsHhLZAbF2D)l5nhktKLUkC z-`d!#^nR~K&NSf5O#7=*R1YqpiG^cGYocZw&+*RrP5dIMj$n-u(1#1-j|W%@{@G)^ zYbT@~8zK6B&z?WK?WY?!xR?!@tWfbv?M97re{%xjmLn4Ptd9tF^My?8qo_U`loJhB zWNk<9LEj?^X)EmEYuiRsKcTB{?|&hepEK3K96kWE!RhD}6uj~HJg}~l{eJVrqu;BF z97Ok#EMgn$7kG4gjeZkr*Vx%ZRT(qxCv6jkY*<6a#OVwtplspjaf&Fni3Htr6Zi)# z$XC0DYAO)>+ckpa*PgA_*&qe0&JD$9Dg+DVIh4JH`A&oA%p?aHE|uW3&@H=(*j><u z(%V?%9+Le8$OH*KSC?WIJ792OJ}1<Ta*KTH%q>H$gQf*F!%px@&1y^6m8P}4iKhA{ z4nT4;+(GO64%qTey`6t|$t@4N{}w1XcU$`A`%+cusk76*pEJG(@k;Hqdzbjo0$bpp z+Gg}b(r2|rOV!Ql9`o1Mn{H_+ZlP7KcF&*nFEqREaq4L8m@wPLx3~Q<>*%2JNZl)n zpXu1guD}n!^aE3v0ItF!NQE5NyyFs7x@xIWB&a&mVLj#WqP!q|XD1kwh<D|sVI{iJ zv6z;+J!>w#Kbtq$1|N%JmlD{hP(?7%{~+QRukKAnL;?4R0EUVl5>%;#Wuqh}h;Goo zdHKF^e1Li04^F8~jbUVx@b+;2DxEGMFfv$puhu`n`Jrx{<PKWZHiJF#z%UAd^bL6c z?OK0s@r_=3)V8!G_VrF4gBrVczXUIg>;7>zOHkq$#c8g(SQ25{^61H;2mKQK-je08 zQ%$SwbKUtQ-^@WKhM7d8qdi!)oHiHR>U{Up923H?Yb@zAFQ%XExNj-<%TJwcBo)-O zt>P-ZUH*5uHYMn>kKS;ur$_UfAEV!7<H9WIW~-=BM~B&#M}jlhE4B*xqQ&VX>Q){I z!}E|0y*b1u)w}9y8+Ly~=5~wHG%qO&X(w?;<sbJVR=~D$;le2ycJhif?+n|c3aYoZ zNhTsR$bA26HZHlJfwpK>9nrlm<5)`xz8iin95UYK(@?O>m3-kj<(+Jcxqj_S0jW@Y zG4*?>%Y@j$ol}^XTJFKh><+qQOZ4oN%ePB_5t}Sn`5&(^WC+9lzGGh|8qgO5)YOXi zeWG)qqQ2S}9`o=FWI{Oc7*sC6)-87E+>HWof_-8dsyk<-Hz=m3r8va*Mu2cZyP){1 zw92rUDZ}B<1NoMUGICF<WnFk<5~<Y{nEgF-uag5d*eL#!C@^NpCDfLtY5Z_?`*eow z@N<B-7kcG5fJP1ZTy+ZjZsW`l?FKM(#$MaNUYf<_9I{+Vg}F|s{Je_6N{WSs2#IK7 z#pqbo|7CG@bUL=nWM5^~Q<t5Lxx4>N<S85n9SCbtnjodvPa2Pz@aIR2(@0kz4SA^z z(z%3wJ3SWWeXjNp%(wDx`{|mE!l9>6y-Bd`*zL3{GV0k>t4i=vZc>LgFNQIF0Ozlm z0ASgRLyf`+x>RDeuG!J0u#{XrO4LAN|4Mppa5Vs{6cGjQlZ&B1@m7YWwVg083Upur zoh@8V2sHW+mVe;2)yK4UAtNT>LbugJCqk$Z3|zngU-|jg1>Cdt+<>#GNWtzgb5E+H zQh444<ej_Bs!M2w%ELhrDe!ElS-gh7F!dLXlw~!oA-l!DZthl$o#N#>Ir&UIp?#`2 z*t9GAp(DgYZG~i1TUhJgkAEqzP(mkX&~cY8Nq_Fu8glJrH52sdvAZ=?=fXaAEG%C} zwssSl*8Nvynq;=j>hOgPm8E3xTdV>FneYv?QjwrQL`459)T)LA!TI}r?^vlLLn*8G zeHR}TOqa*`3NJ4TY9I3W34&ha<A7l~yj!T)Q009|dH7NiP?<i`NR-*_bS?H{VRVIn zaNEzljT}8U6^3^BGw|+YUJg+;yGN?TRX%&Ft~f4RIzyFqChWz$y1ACmugd7{G@I{O zm$JI7pK<W8x~%@SUy^ZnimW~Zi0*_|+rj*&cCy<R&Q#F$)2`LBPIhO;k9IS~MO44^ z_c!dHOAb_VKl{HjacGd$HJWBeS|OPf*p2yvIRey!e35R%aLyZnuBx-@qZ+|<yMrjJ zHYe!PHUmqGA&0>F#3r0;Kpt3#HCLMKl(X-?4H_6X5W9m<g99AMd>c638U@kCEp|Te zEDd_d)jQ)KGS<}o5$oS5^1r`wNLX!%m_w)8k*$BYZ=rS%1v+#Ds6yUWrL&i9;z8Bk z;YyWr?m1K-H*8}#`n-)Pw%}+P{$CZP-N(Jt(yhHt`w2eq{zn}K?!`Q^<pr(+#r&_t zo>|oS!_5QTdtD!`5)!_6Jse{*>Q%l7y%W0(+O8V^{dVYlpar>9XPO6X$^Grif)&GQ zr?K0u^VNmORCgMF?)0z|ktA4;Q+o??eFnQ?2AglxI7rjq$;%J!N|5td$O3n;2vK{{ zsGbt?bU(}0@wiO3>g+#h*4JER0$0wbd#3-!9dSe(IN^1!q!X<mK~wLm48)w|rRn`L zvqa?HNKs;&4Q-?B%e{?5paV|l$CL(R&J<=Y(LPe3X9LBT+29Np1#`!+e!l~&n$WWF z)&|Ho^;4T=-M+yVEq|jW+@~|wC(4cuVKZ-sW62j&2Qa&1rz?Mwj*E_(UR}s#ax#Gv z>s8`15uhcWNiMM>ZMj=C4OUptP+U`$xR_mQnEM5Hb0!1qdE=08zx|Rx5nVk!DmMBS zo-71e8av^f2oUbL%p<e(Yzi~lDNnlGgk5J(c)d5h8_lqc`Ax)vx0%YZ6;1p7mLM#B zR&`?uuv%B<0(;JQWm{>OCX>-PST&!8#?}OUCk^l2SoeL3wPETW=|C*}Z!eoBFo|uW zoipZx@uuTNJAdJh^LRVx+)OJWanc>iKA4MA?CYHQZ{I{!ir^o;2P{LpRZ#CVDq7Zx zQU>2xP)%QQ4r;7L)N=Z8ybdXh2*oR0X_y?suxNtgfa-T3&fV(-^F63+PLrsaq7k(_ z5j^xKvi)ZtPmYf-n~s)w=GUx8n$Mp>6hwws{70`+h%047K-AQ?^CN&7o|G}G9poMi zZUci=eG~{%s8dX&i#<u>`lSq#R!dNIUA?!HXpIwN{6=GSR-`sBV5)iGP5bK#jll6& z(rrdNLn$4*)!h9ity?5YdTqR>p9#<bJq47Kri=LqH?W^6y)UFF79;j8c^=Xk;o<XP zUQqYL^Hz!0U#!&AT<ggeNdz&KCPyhd5sK#~wbDk?#-+t$+_Fjmqu?w`2WzQmBWd^d zx-$0h=m2j2#fH=P=CnMQC@HTY8ne_~Zq7*WR^piv^mT;IoBLs#+H5Yls_AAYPr`rK z2hP0F{l$V@jUWF)OX}*wzf_abPWgZ0w}7ZkLgX#`Vh2vT&|9yPPWD|r*K}Lb0EmUH zBe$ddk_e-=q+j`b<SQKSgsUXts;~$26=Oq4fhm-NNV1xC%;0@K8Z8O;2XgkfArxOp z=rp+_c}!E>*+&r6D)H3E)m_*%p;!zjuRpde?`cUHLJW9z2@D6lrHPEBHi{k`^mWIy zh#J)>&lN0nf*4R$l)RY{vgxwPD$lWnUN%9#2UlyiU$F!lqX$>s&z7*>()1Lu59t>Z z!y!D1A0*XS`qD4+GY#@+QbQN}w4w}2rY>eU>-+lJ5ilC*47SPf5CiL@6Ux@O8c~Ix z^ZA^L9mkdRA<4ceL)h;tE;zAoGXHU-gL@kum@(c*5x!eldY4eIwN#RDU{?O4BQ1}9 zr|yA$NTKS9?1w19(;m##hlHTE$jOgr@Ryl6`#WKtmJ3&APB?9X%Uj+BYHn2Wtqomy zUH~#xVZ>vg#JGUE{~hJ=r6<bz2<iYX2e!QAb{D|CFVf06E{lS|<njD--ZNa3?fhU* zs1Bsq&Zzl1Qk+9%0NmXHaO>^T`f^TI_Q=BppgiQyd#=&y!wUOReQYNrF$r7-2ZeC9 zMjR{Ot+35<QfOQ%E_#v<mQ1Q-baEwrsYzW`==^I%StHF2YK(YD2>5r5!=k2<reHmy zNvx3Be*ICIeYXMM@&y4ua0_YO+h1YrwJ(zS;S&RJS6QRY=Zdp0bKTyG*xaop&+X|m z=?BtqdMWS7!UU~HmdEKi?$tj5yWGGkbg{5kXkJ?kf#DOhrmoZ-0%!sDJ$bJsVx&ik z!tD;GKYQ^2oZy$3j?TUDDi`0&yJCY3c1UPGq8B(?;|ZbzDFso}J}ySLJ4gYAiW$R& z;x8Y}y<<K7?nAEkKi=PNK7)j)cuOF~k=M0G-LBxucG&8B57nXS{)C(R#LTQY?wc>F zOO6=dd!4?ub`Ex)dX@8RL3-q_CG%rggVT}N6VE0uhZQUTr{b%hh_r#4GvgFj(|k+6 zWVn235#096;=t&C4BzS&EtC#(q=`)6c`_t-c27_q^Cj;lt`T;07%Go(6`q#jQWGie zYsYr(er@<YR9D&VFxM{w`p6~(wClHk7}8jBWkPZ-XUxuacaqtTxj>TX1y{OP$tn>t zSFwYbqn_Ra@6Es#yY65LOL#r93KQe_GUDm=pOd7kwZ#()Bkm>T{Ii|m6O@3PuBUIF z5~p@P(Lx-=31t|V8^oYNL}}m05Gy5Qp^;>d8R@fk5H{<_&Nc9(nxHt1g|IfE2wSLX zy{nl!IW`2j_t?}*?-jv4a?xJD)qJYJU-a?jNYD=~OHO?3`KEVW^5<ISeq&|=;cj*> ziCA{kpvrsoCr#qd)H9o(U=o1)=aeik@+#o=Eyu0+6Cmgu|0!4@&&sguyi)Bc!?z6B zsBh`*CH6n;T?sr@-P)%@i9{qa6rmDDB2yX+A#)iLl0?aH$UGL2%oHM&AsIr%Ip%pT zm06B?CNgA>^X-kzc2aM>_r3SN-@Uhf>$isIdH(B}*4k_Dea_KqivJZ=v-8;^9-SEx zdHSmo_w$l;FL3w<PsC|0q#B&s%Q?ugZF8v0)Um8H>V<+8!HK2`N=(BoO39)J<0S3N zor_M>pL<FA=8fX`LJxtP*0=mpAtxcr&ssAXI(Ae!-JaHCpSR1D0?&#+zpLTLHH><n z`lg#=$8ND9^G)iOMD%vwx@KB(zu_d%(!1B3y_z-s?f||!iqf!QE=%{*sJ-PIoJ7Uy z>FeU2ra|vdb8b4l9oaCn+l+>t#M9wT!u~<JDctjD+ULRVi*H+U9n@0U9VkZiHa(lL zG*Yfn;2p6%p7=9ev1e>VB!`Gf%ujwQJwRY?$Use9n3*rnampHafjUl!!Rt#iLo_^T zPU2}Un7ZxhnQ^UMX*(gU-F3St=J%{xiO=^%97q%?A-73QYdwNyRqxPE&ztNzCjex# zk{3x*!5o{Q-pI6x-SC_kOD_(~{VfqGel_jjP1m`r#abf|nzO~85^MBp6Gs#c*DBZW zx;YkFOgu+a)0&>faQHsTuTSit*7~qH-b1{_G0(>RNQuG4=PohIxd-!gWrLpXf6#s^ z{&?IA&W!VpVL0lfFIA_Tdb(_{WLT(~Uo1Q6!t{d5n3>`Jfv9xgnY%h014+3AW7V#G z1t%ur^4ShvH4lstx?oRkG{0~&$?yUHt`8(%xf~|7`jhFX2U$oq51k2m@wkvQ4|sf2 z*`;-fz03ETt!Uuz?aT~z#NM0dykwat=KR{40vY!(CYLnpzdP0(q&M<lE~ZGI@#yEP znpd0;ou(ew5;Yqt4>;nq<Cybl$0$-wSLC$i!c6B}OpK=5*&WQ<+9E2{N&Mnrk9@lK z*m(>~+>%n2KDQXHgA5re+^l+ij%|#GLBq@+KkEcq-bL=3YjaL~LAzO{;%lDvDI$kF zX<93-)3(prrc(>p2p&5+ygk8j(kp?s+f7TFe^>JC)b0$=WTF!W(Ry0V4s{=|E!;GA zQ*-L7zVX2Z|E*5JgAy&{{=#?`dA0b`12_>C9SM0hHsR&wdx}4JzvE?Lc!=-s8-X7f zIKA~fA3NFXjwX{dW0_0c54Kr(H>Fu?zjD|8;;;5vcUzMxBH&ZY;Rx189>*jFMNpDA z^)2S|+gxmk#%>*P<tMfLJa;ARvj+bsp)J!IcxLs_T67K5GG9Ht9@+ot>q9DKIy#az z%A^_52oCDkz>iN@NnAf?R_dcOexS7?@J&x&p@<0)|1sHzER6OtXANhI+R(vS<QYV? z=c)<iDW2)b9#DU%=x{+e;PE9BmKH^Rl&Id2fc;2&+LW4qT!*4e7CuhTIrXk*4?RQ| zE7D#kDu*R>*0UN!emW+6WvX@NQ+4o57EfcM8QjKki@PG@i$)d$<YD@TT4{ps&zWuE z+{E%Grv!N6oTe`{(tN798S$dob39~9n#zy+T^-HwmwG#Ia9oU+=Q=%pEG^6}T0pp8 zIg8`cLBb1Eug@_7k8|ErGS+=u2pp;y+MeLTpU&TxeGHGR&PXSk6%})@dD!~(*~F}Q za&)(bf~Z4@olA_^DO%O9_wEe<j|>~annf9ruE&~NHf11uEDs}@M(T|37ccq{W~zyO z?0S)K4w*8XFz@8LKro<((kGk8`R3e|j<2*O!-K0r(T%C|=~J=sq{C&!xDvgT=A#s& zR-zJ*51o9L|HbDv>-}VOg-58H9;wVRgUlS#4v%A%;{4HDNxItKe>%W;sH*(4hzjsD zE2&Y)H~JwO-=WFI`N^z9|3!n!Z<UUWM@YPaD&LwR>llm3%(r>%4UsAmMB+(C-jaTs zej>oQ+!F0mR7g?y=;}-gZ#l1RY_9Od)0d@uSOd*_QwQ3(>r`|qln?~RsT;J~aL}Bc z97$&_GwNUC`m!2R-#=~k$lpcmWRBTRe>aq%P)d!^{RZz%29;-0Q5kJc`=ef^EV>v; zINBK#8og(;pv+6UNTrmR&2@=QZhR!Pm_AJPa^H(XyYslL+=U%wFFW)(ceHkT@W^yo zm7+dIoWgs_CLfAFVyy1Q@2z>3_x(_1z7XE@_~=%bfJB}5-x`3Ir=dEnLe*Nggb#Qn zwOXPYg^aW<<jx~csJ_Rr&K?=LZF>A=oU?U;lPE<)+lfbQw9lNgTJPXQi0#?U%Pfk! zx2yG-;Z2-0r|Wa>MYLVHJ}5oQA(SNN`E6wlj>2{>Xu1*L;j>skV!E3F6S|fZc(`#X z+nyEqgsK#H!E-53^Ywdk^IDc7NuPU)mhmP^q7K&XI9A$X5{ZXT>%a7@;H#Gq-Bce- z@q&~PdFf$>XWoI5KxVaRBWv1y9K^*oO+2*b=WcbBIjDw~4NfHbR48y9WMJs&AuLsz zetmg{b%Y6>5;8nlCFDB41$agt06cE`lGti#wf`(~NJc0K#inie0GKecS~6shbmSrW zt-F;`WHMcEPQQu1f!E}BXz+T}=S?HN4667KY`mlH1!o(x6la%D)(em!d5$rnQ|By( z?;dC5BAo`dO}E0Clr~ai<}0eS_hGnvw+aQ>o9EH^nwASYD;FY8i`vBo1COI`@YHBI zTc6y&7@7Q;)Lh^aPW35=`ZEUk*Jrua!)238r_+P^^d#GuwrZAcYto&5kRZMF)W~l4 z;7$V{xkB2<`YNP55DlY7BB!^Tl?>|2sSlc?q-ZsVzNuqca~19tXN5Y~a#wF{d25!| z@P$sdNG+DX`+U^?!hpNAMQ5^|^2Kk)2sUe9od_OUP(ANVkKEtcUoH_>YMg#$;iRAB zY2`DCYWJlljy3ZY3~S?|sm$UkccT^0vl<}Y1TuV<d(7l{p62yE6E#s9AJZ4tM%+$W za`j6e@3qbhG$hz=zt`CMbz7U=oeYK=mBirT2_0iW>#D+=$dV%k-wqob3ga-h&b>oY z<^1)?coT8&6Pj2HYM-MaELSaB5GNE<Dvp&J1!)<vUf~o-ox5w0E5L%#dAQw(l{T%N zy*vti&~jnFARRwvx6iwnt%(in@As*yiK4R8qH4d8kMnjqWdSc@`H-3HN?%dK=-wVe z;O#cU-aK&~>7XwoSJRLReQ6KR-#e85h$E(ASFcv-apykvCpI1G#nOSHf@+lPy?gHZ zwll=<r?h#d=_W{+NQj~ceR=NoTOleWZ~m4B-8!SqNnhSyYK&q@yC;a2Gv+NgWe_ZI z!ir{W_p7W+B69ENn5ge25F;~(snV*3@4l=gcJJ%Ij3REI8JwI<!MrTn<!LbV?a3td zdE#lKP-9DVwO#3g^sQ0`^aEsWbe5_I<17}sKhAh??sP6aE+`-Qjcj<-_PBeSxnvK) z?6K(}!Q#<_GMzLP$8TC@r(T>!)1p<Loa%acF7V8W9aX7S`Z~>X^_%kwX$Mr*NX}~_ z;&R4~+>8k`yU)kU3iHi&l~W^KERAk&a!k~@tJl6~l6AHexDc_T$LB4so<*kI5819$ z(qQXq^kjSB<3$>S#2sFUJ7cjKcmwPW4^%pVNBtAPI}+eK9uXgWlspsS&}KxFHbg?+ z#5C8U$imciXp(uO+1z~vVhTIy57K3|z7f8D(L88NOwD}tyaVE-1e%xKL)Euoerr^1 z2y5a)>tQ@WG?OjKi88Ucmi&58{-Ol@lssaOX1DqBx>U~#R|=W&a0HR{BD(dOL!a3d z-FoJ_=>5MEeXwyJp0Iv4d%O0@)^t<08kFswS!&=NVAJP=HBnnLdUebil$~8H%Zfwl z*wfi)by$(SeN)HqUaF`eS)Ss5k|ns+a<5Pt@4}X=*{^TkX^$zB2uRtUsJ<IDL8k<K z&3qnD@jic0A-5>G2wJgCH%{wz;;irOLl4i7Ur=L1Eu=D?%;XdE^j2`Gjk4-h|2hMF zznS0sY2jGv!eYW{!h>5=hIxB+rh$`D7#}iVUhHIsjo{NhJ%j0wv$vgxx9c3YrDXKl zMB?CwuvVw}c7OIo{1sEWdCLRG?u(el9*CIvys3Ha&J)Fm@Z@9Zk*tSzd!ldebfx6p zZKN|Cb=uA;#$oS=kB?k<=!qf%#s<fMZxL20gyn|oQG{$oZ*y#;9~x?@r{PD2X^3rd zn{+#wF?)N#GZ){0;C=l~Gz0xn|C1Kq$Sh8rGJQE;G-w=hvoFo0|3=8eXPmT;7@qrc z@ZN1UNo3%rXtYxG)pviabN7IEbwruB#Qm$d)Na;?Z;;67&2tyqQfHXG7BeZrN6&5N zoEyABqd9%+4g1Gx`l%6#YSV^X6&7mY=x{@PPj<D!&N)pBm-_9Iz5J?yVQuF;80nM@ z$dP?FTPd_JJmJk^rnN?Y^laVS;@=sA2vs(`ruO_5N-uB-xPaK4Q$BDOuW1k8S>j+b zwDxCH$IgM57yYh8H(qS>XEZgCv<*Ob(WqSE&+E`R8q8}MWvk{g-2DFeP&5NeF>Ru% zRM!+*1OMj0CPP!p_{MmubbAkv>Bsvk2U4P{Iy+B<p?D2c#}A;oIDy}oksYYJ&3fW; z;Df?JGPAucmNSwEJWrm@rxHpvx^7}1i54FjAGM&JQxeFL8hVV$-s^0wVDP@F*)*E+ zMliXymsQJw%Z$T9A)gZ|V?-lu<Q9pnMG&M0YC_(AuaO_{Wop$=EZoee?cPfuSw7S} zrINMDyiw+?D8fZ6tSx=FjJb_p)cmKyVXY)RlXDvWMg&&uGHeaTN^VR$PFdptA6P`% zCFbHd@D)_iQr1r|Ags`yDXH$|laCjfbg!AJE{fwGa(<if^rFD@QC%HsIcvAe$!F5V zNd4dDOf-=*i-pFsS~0xslpYs*Ts}e}R^U)sNZhsGwA9D?7K((3`#ST!0=9Y4e$gR5 zQpxrz0Sjl{+SF1~x4pOqHG^;W5qe(N6J;);i@xsPo}7KLM6J_|Q}^_XA>dR7d^WUX z?C}tTef^^&q@-zv{L!tvqr!+_S_3!Ad)<rdciu_6B+DCUbR}^O&Q=RF`Z?Kss#3}7 zzxlq%w(kq@Fi$6`<(*GdMNy??skZuo&*>STWkX}%sEu58baJOS4%`EF9w$&5?F%^N zDwI8gIHD9cN$2BEb&veaR(>lQdAd@yc(%#Zn@05qd|y5_-Y;g1*1|jqDwbqdPAHMe z8#cSgRtkJ-RS9utfI7Up*fnqNTtsu<``8QRWkXy0wxATYNDymP?+azwIh8ZzUA|W& zS?kit#{wD^y_Q09iq|!d-Kgz&q7&w<Bs5Vx6J)LKFx*6Q+(P4`?JcH~x%y6DrJ3$Y z$(dKVHbSin1G#xo5evFAJ+1d}vw7S|Ey?wjx5(9~R8m+*T(P22df|QKUhu&q??wGT zN}92a$?i#~Nql+bykF-5q!+t-@y9NuTJCmZ`$^dwv}R#;&2z3oN(};?Os{f{F6@xI zM}Cmu=yB#!T@tI7@S~^b$?-UN-forEO?~Jn-1x<Ecl6UYh(y%<tVN5hhO179HMK-s zvP`P5a_+YD2&z!ik()DOM`tc)wNF`(P1ujQSX{?+dh0Am(h*;uP&cg=t}f@>B*p1` zd`7D7$;+=S9-R1I{Vf-wl||jvNQ?uTYfFZ^;^u*UqhhpHvwI*!y0GegN?>K*0+WIE zg8P~Nc_unP+g4|e@~Xf`Ws{i?lFdm|UdKzb>c8U(w11t{VSX`avuXb*g?X#XmpN7q z$7baFcE!WdyrvdCly=*y-kOgdw_hZ%=$<Ry9~pUO+PGz(#(p}5_9)4>I|x*tUpQlw zR;3xue8k;}Jtj`p4{<y%+Bo=b@#6j+r%Vo6bfajt-41CfoEq-y2;P>>ysJvQr5KO& z5|5Yo9?P!4&vSGS_z}lsZ9R@uHR&K-Ol1AvTIU4M+Yq(SB5n8QOh_)yy~-Eb|L$dB z<(DiHdyJdL-6uA$96w)o7n`)?p2zW?*7CD;ametubM02~e85?uTy7<AijTA<Z}#i! zc!g*#y9|8h!sZ*NVyWAJZ3=J+jaF06HM+isgaXYZ=(0K6^a8VBbU2_1`N6s;dnisJ zc2r5wZI8F2pu{oD;8i#MvK{fhRo{=omK9gSvcY65${d}!81dyIg=xB4pQ+iojE^)v zo0*E#9&$;OOb_;-v@b%6Xl(&MU6wNZDu$`BHv`2ZcbAK7A@DPqt|^U#hg0Wux$q2E z=QE+%wTc(t4x9}e*gnnPt~X-ap<wsiu;xwOyHP2PubiK&iqw2-%LmNA;IR!Hq$!uJ zjeD6#s_4v87BKi_$e6dfk~O!eAYTg4PqAalp}AXyj*dX)e2FVxK`SNNbC&n0FKwwy z4n@<0hIaPBD^tiiWG&M){<Ep6eJ5Iy>d)iu5__UVC|iSDBAMIX)Hrej^_W{`Iy8Ip zbZGjykeV7%78+^$y+?ORu}RX%%}QQ*_yR}%!P7WvlHIQvT`Ic-inJ=rCQCc@-rDSM zMuwHv-%mL+%IQgB>i14h<HH@2<F1@dmtCR_5A74d8MKR4ZPq<|<r)=HJv#D!)rA-$ zfiG#9(TC;~E2@Dv1}Kv+XVXeLtxR^z#gg6iDGNYE<tlc`y;eWoca%xhkl4Hq|0Fu} zL{6L#+y2`j{EomU3P~~-B=n<wt324d9vtZ>=}lW4wjE8{dXP;*@wIZ^b{(7;#Ln$J zotG1QChLwi^17%FT)zptQAtTz9O8Tt#@Vuq>Wfb%O`DPXY%<=oFkfLr{4K;qH*4w8 ztYbZv3<TGWDAR}giInXVkJ=_N>Ls2e18#{z@_~=9k`UO})1M8XGKkPQ>lqwkqe4p| z7(Fz?BKP)<?9eWsONUjvPBNgcBDxhT<kN2AZ#}Ea-SgCxxwfH+p;|m05hFz%DETH( z^_FfA_q&FpBQHg$CPMT-pBmnjq0)uKlfgBQ9E#B*J0C_MX3)VvI$lGWar;U(u~heg zeqg5!rZ8un9RQxw>Xo_-K5g)#p~=43c}Co9PBN!f`w0Es`Q3|kD5_LLMuT18C#A0* z^JG`Yd)t@ulJWNN@YU%m{txeTq#9pzdVbQ^_Wm%NG(@r(_=dADt>AL;>}GO1M(3eI z<{dTL7UDbax`h>K<n|@DD}EAC6Ug!un?5?NsV;-hIp1F+$DYi{d_!2?+C$34q}?kV z*SFweB`MXE|FLpC?%J>d{i&+zxkEWkQ@j1=U6iNj_di#0nSG-6_WekB@VhH&^+ZpK zdOO%3r{2uk^-NWc^WeZ2OYx)ti8J?ek-_Nkn4D?BIk|5OM=kMy?<W9BDM_NSKtz`_ zZc+bnuvZepUvqIlYn(FTp?yg*^~p}lVTD3917`_U;6&Hfj5l#BPf|_k_PjVk=8%R> zuyvx&)RFBD33rAR*-jCee<`Wc=5BDA4;oNp>DkWGMVmKyG%Uw%TP|jl;B}BybC2RC z9Gb-4GafFBdv_?6c3cav6|lP!UpG;aC1cs2NG;}@#UN9pcF2p7bY!cBnTHrU*_H`? z8^PFaCibjpj@Gdy9(WITp*h7E4{c<`Gr-ren{f0_Q`DI_;A4WZEk$B6rCFcazv=r~ zjPAD#FYMWb`J!Qm<8@{J^9{!KL!qsct^}jcONA2p6df)zDel#d<k)x4|F)AyJc75g zY;WNXK3uW6>!az85`5&iP9n3r4dP@6xDb7@<jCS76oGDx`y+Egt>nFDCO6mUe$w0e zl%7-U#4eh{(+wJ!Z=Eb-HS`wzbh)Yf8lU>dQ2>((mA<!|!4&%T_}!{Ck(<xyILc{D z?Bl`pIvJNBpQ0o7^HT-l!!C?lPZp3JD$HjSpi^z);ydh?dAFIgceep6nxQE*$tjzb zXPh(KfS)-cqM`MxWa;e={CZ*KeGL`L6)MA1l+Gnn`%m9$w-#C;{q~i>j)Xq@>%c%} zQs}jtuP=8_(=YC`>vjB+7twGue+M;=<pZ{C&*=e9L<{hxVf<uCB_{NpL*Wmyf=p{T z3{HFalNoG96Qk2M7aC`k>ECS@rLx?58lNW<HMv!Huix!OX^V5V3b|Vk7UqAB5pSA1 zdywNIp9TIdd*Ur*TO0hZzHC?X5^!)~7ksEBG|yyFm!j^}GRxho=HmI(93ynU`XLpo zx+b7imlA!cp-p!#_@;#sWt^2bU2EQH?myV~T1zCikvIJ0bsFz8B2y<bLlnrXPb!RY z5YybX5p!Vs+@%;O?8AV(pYt$K61{tlPU2C{EK3$IF=_B-N$!@ter7i5D34Gne5;mb zJ9m>iq1Tk3(%>3zDR{WI;26;-YC2uzNij`)!!}+wO2Gvci>mwlh~_G)+iosu7j-G7 zn`06xLPe9A<Ljfhj7cOduv&})G0lH3;Et!bCr3_f%N;fQE!8{|C8wg5PpXnLF{*Zy zwI>|ft<?2^ysNy7?<0BZ7V~{9^A%0vMmn4?+RX-?6;!-p_?U-d8WyU&5Yp~vC2zYl z8WY}qTXB}O00$=m_xw@yV<Z>c*-WUJBX-FkNXPafTz04BZ_)|f#~{x-@;tHB$ajwZ zPGpsr(@QFcv$2z=Tx1f%v<VbjTW{mg06)E%%96!(RgWg7gZ&z?mCTKA;dz5}$W-w1 zIQ$H6JCk_j_`zQ18`Y6Q7Uvf*3y7_3rF%>k2Cl~!e(J#U^<*x*xs9@_N!&j_MCz%4 zT)bCu_BoYt=1A=a<B|DiKeh)r3M?F(jwY6FZexB_^{GtyNl=HZsMH<rxf&{)CL9qJ z`zHyGIX64Fo7>owt{Kdun9K;KjUqW)FKGCTv$ax>n1)&=5UKD}43(=MU=beIWxf^O zIQLll5x)g@7_QQN+4DOhTofX`7DTFYl@GD-ov~F*U>tXPB&zJxjPmQQn-@C2K<pqv zKk#Vb8X|J>OkUhp>wGfu*$&O|W8}>9<5s3hHO^==ZE_SX@L`3yl&Q1}1k~f)9^z_= zcIcRwTwgAKMJD1*2KD9hdyEm@%@=-l>bAJ5YIDvY_0352k?Y&tEzu_yO@mN9Zi+?0 zzWV)%MtB{$j|>yakFu4%`+Bmo_*GPzn4=R-Rx%w+*oV}4(htFT!xpyrAANM&W)Ldu zZ?cOW>WyY?UvuL$*?5XjWL&js+^6`cs;}H=f;6c$pf*gL)WLPjt&cj5hoVFGST)%S z5Nc#U|14K^EYcYxyye)1ZIRDEGkII^>2KZEgCkB<`;eGJ$67+L>&U$Lc;sV7x)*_N zeCMlcDu||ud(t>;ZV_r~4*<VQsZg_rWe`F1ksPh4F>(GS#V-3!305xBdo)yq+nzt; zLfw3+wV*n8;p^z@%`sdRPL>qcs|thVgmYMZ`+PFkH}|r;^Mq9*bQWu~q;CljYPHv~ z;b$L+pLQLkG3lNi$Bm{VS7@JD9OdmKKL13l(lrU$Q9p?yH0(8VZ?5s+rapm2(B>oz zp|pdVKO9PC?y~Odc!3_8Eo7ii?%GP9LwvN=HI$nuiiE*i?wO&^W<%McSC6Aj4p`ci zDHA4ln>QWNH@-P!iWhrOW%B+43a{wme)n94g*_@<Y5Oo%uRp8YymC$ZQRivH!xUXp zkB<{&b?j2+325GF8&dh;oQYermm=Xfb4R^WU;Xj2=@PNUBEjHSF0N+|?_NBQj-X?D zk$=j{hGOhs<(@mOFAmit5chOP4VXS2l{@|Tr2U+TeAD*SiKCrFeIaK$y2dxnU(~oM z`<UhC)4JovMRwc7WELvaFAXZt1HUBLm4Ik2)Z_4Hh~gmhr$+Xkp!2h&??xT(t0Bp= ztT@QC{e*p&pykl>u?Tr(<>M7uwaQ@&js5M~QIDUISGd>QjeHa|sVEUJbnH#*U6B^l z&WPN+PAb}62~oJG#~kD{uItyhc_Ab$ugyuRvDAyq=|_s=9jJIga_@o7IOD0P*@yg% z_~Ck<%Ew7xwUo!G`v&A(-b}_9-;ogcdDkG%-DWam{S$_JVb{YBIfgGtz57_cmo#+0 znvl#hS`P0QID;ZipU>Yv9B4aM|5m@n@HKDH9y&!M1C0;fxP@Jw+p1@tXqU!Pzh(3{ z_>9tQeTv_C@aWteqTcNQm3WoI2h_)|`s1IJ8-&#yR6D6X9xT3y(AAnNw7Xj`%_4lc zi+h%u*Eew>9+R}W2BRM473?us%6wVP?SlsKTW&O6FSCITO26qHqQSy9^_{zT-J(1F zfXC?@yByJi{v=0ejjcitBSXn@yY?#xUZF0N&+;qfI(bNmJtnm??GT|KBh_wmW|lm* z?9<z(pYgc94j&S@O^KQ)DR=x3bvt0!!a?-xVOPpI3G_h!E5*zO*^V+UgXW#hwW{rA z#K?vzzxJwmEfa(HDqOlmnfffAyCy!I;d@@ncbo0)HmL^@injbN?F(PjtZd0GANuAd zX6w?Q81j@P)FiUDddWzcm`QWVTN6>q>BMo}C4;$OkkBATh|DoAR!`@Z)Q+nm)r?54 z3%n(Vl;8W1=S1_Sjd4<Skeg>E$_4O}iR^vIcxHybzH84xg2J1|&3e|PJOaSyCG%BX zPV2@CWSI~h-M{rLo`U6wrbz#_uuN&W=s@~UVyS^|({rwLUrL$IBH5+kOmS3v;84Ll zzs6^ue3^vPadF3}_c5}DG~*zv>D@=RnBA;Rp`);kHnJ}7zcO?o?V*~Tg*Y)o^^Uya z;epn%SmT}fG+J7PBhP4pHF~Fv<}W{r>Ex!7L;0BSQ)<=k=kA^wJ>qcN?vvJtbK#Lw z6#l)Vtp!hRiE9#<S`qE=Ow)S0v%K&&#j%~ztqMphiXH3{0;JQUdB~#A?Q{j@IOcN* zr1<vF$va{akKCcqzCz1~(wj2_w&dP+-C5H6h4|JP+@~&ms?vA0K8$P`$jR3?4Mhl3 zM{hz#aPU5EK19rqD{1?T+K`p5;`J#OfAwLPv+-BvovDL`c<j={8Jcz*#>!ZpQafL# zP+fW*O&r*gp0x`XRd!MMxtC9X?BvNkstB>U`tj`Y#Eu68syv-{vST&+q`J(mMh5X- z<F$267i`6UWRg{I<@o@cps1o*VVk%}dZ-8&=c{nN<d(T3BhGi^(c9)-5!REtN4Yzu zy2Eyh-EL;3xe0vwcy3#_*`*t<jNu9ATW=z)12>Ti?I(Al<G!7mTvAeQz3ZHSvJ#)W zP24f}=fSE$Psgj;4(`}NV1k#aYN+7ycq{`Yg|a3?$Ly#JJ8Sl?SwzEv-AdUD_&y2a z4tz%b-l>dG7Chu;G-<9LdIw@JkqK9%u0yH?g}&vuNO1B!>MX_0BfE!GynQj3kH|<_ zJ*jSe*DY0??P~A|_%7BHMC9%i%E3lqu|@Yj;0y8Lt$JvKn#~bojyG<ch)jQ5YbLIj z(e%u#NB^=}>^CVyrEze3NOh}Ue{rELrzNA<Wh29a;>%XCM*K%>aPKG|)%D-uR-jGA z^~jGYobv{+<-H<4{MbjYDvuta2;FIZNbdQSSKr9d3Zv3Jz>li(=4Hnu@Z>L~R}9C$ z)g&18>(%}iRjSfrz2^i)i1Tw>!p_z$6Yq+L_qSeX;9(Wy<vpv!zf<j02=l|{-NfzR z1oE%7I`7wLrY4IprQY4QefNaHv60<xYm8Vv^AMU>`Bc-E9y$=M5K>=@pfs^hFbXzG z-`pkPR<N+J?_=zjUgFW$MjVEhh$+yo+`6M|sLgo=wolbxyyM-)uJz)Gj1zyuiOgA> z3znh&<XYjqz(uRTAhf2$)>k09c-WsqrIx8nLM-o6aCv5m@JNftK!w32^`0G<8(;QM z4ZbT<^XnUs_jl!IE2Z7*msX;D#7Dr3(NwVX=H-iIT8ZU)z;CN$Mdv@>jpl3{@Dk7A z)ZjQ}!1^ZSioArl<y1yMn;z<&&XgEA#p65ep*J)Zne~r!Ou3(-Fv&>UKeAg*3>A?p zX?eV9{D#9Jp}J#l+NVa}7<B{R6ZOq4Mo1M4Zqb!iQ%EQNz|t#hk%ag4CX?jD-7*^T z?H(oi{JmU=wj*j`<kR96f^w+re4`YEVTBLbh=O{Dg^~0#lH(ROY$~x)NX{tP^2<lJ zx2B}|lsdmL3h&T+GDMU>=643cb)7>blsEY)o8}Wk_wM>iRAt#W(t%{BOr~_y<^`|4 z=d+w&$yn34G9ZK4nZ$1MNAG#-!x(Dqju7JuDi|IN5=r`C$(Sy6-%gxi>(>t6%_PUW z_#9G_o<C6Xo|t;1Xxt%iYrtQ#jt5`h($K^sDXuh4zq)4QNFO>i3JZ?T5YOf;!oe*l zHnYtB>l%sN{b3He{E6>)7DKwYTRu4O+Y|fmC8PYL-}a&sH{hHB0SfpKHQ7z?LmHiC zjbF}OzgV(oV0#0Vg~g6hCI*`zs_CBgaFL*P>D!oM@*4|?3JagZ_OB<dZs8>Ay0qZ! zB=@F)^r^U#m|4y-1%pf~;T`HHP;<%J=tsx^rmFL8kJF+vdM2ju&7H~z(Oph%dr3#e zuEY^7h(2@9*`4A4r9sltU<=YjrFHv-6C-3u)%q>t{M)n*_&?@F<K}R&6hsG(k>X5G zirjgAfueiU<<g6?v_}LjcRa1#UqePPD}ss~>N}O27k!EUDO2@$xc<Z}x#*+Ub2r%b zAuUTXr!(j#&+_@=PyxS(YAdh1DT-2x+$huaEk{qomK2o!6H@bpA%RbRwkLD;ofD;$ zcOVf$6dCXDoq84)o?vUxx7nYwi|=u>%DHzZ*fM;YKkc-0x+`O<+A<Y!RlwnNz-_zR z2N?~e%50L~MMVeQ*>7KXJ*bOFXqy;QmAj1`@pRx(O|yic{%{KOZ7Gj9D3C6KV%=S7 zJ<)@oI_WC*0KfIZBo<^u5Y653pl9+0@O_e0qVnOIf?-X=Vz#S7NynS*-dJ~h!*khZ zMdF}WPFOK}e^|EaYP^@_c}}{9=Z2%)es`VZW}@~M)0{Y5_nf0L|52CU{=n$TMYbbl zxv}9I8OLa+`w>J2<3@cm-2$~9JXUI6d;u0FUX-2OC;E+Za!iB;%HGXi9UY_Qu|dnl zA|gwMw*tRE8IiiHWTv}7Jbl=!G_d(f=Txd^zu?iQx$Q~TU2*F7cUT<7RXXo*nVu@1 zT2n!I3qQ$6V@b|?Bln}Jm3oMjYt>Jl`X;)wkU#p~=SO}W^-L3Zujn|eQ6Wi}2l#m2 zA4a=fzmPY40iSDRljJpYx}|?8Yk^uRO;^q=`V^JHtD!?^=3@P7bk3#^)^o2(Zinz< zG<VuoQ1;H-#i7ww+xERu>%V+tud+n0GQ$-vT$8geE=+d!hB&KMR4ZR|?ChHQsx(fX zb|Er~)SP=;S&~!YlUw~Qdys~WcZS@8NRC;fB<FI*&{K%ElLn=zZNgt;CYr73@#o$* zFP0|GiV@&o<m0FED$BIE%oS!OQWRr$o)oJZ_m*UH{X~z?7w(2YYI+)}UOBguTHVbk zb1UmKL8<Gd?IUFh;wk+D)lOe<IZB%(q`VZq(hl$yc*xSVa2a@=7zBRLg7NJsWF=}g zVDU@hJc~=-jIG|zkE*y=E@>u7CgU_FT^J%Q@~3pt+b5DBCT_qQP&_Gx5VN@N9)p^< zvV9xLr$;?P=DDkreK3fn-9PYEL(3NP;x<Kup27Ux=|e`=W#-iAdRyB~1{!#y^<TwU zDg?K*)5h-akPeJIZ4%-bvHjw`BczVBq)~aSqq?_}74utsV^M|o@GLv6xYLOq(0lgD z3r=chUfD8B8HaKNE}K*a!uRh#c)&<uFmcFOwZ5-2v|u=oR9uY*5!{B`{`_mhRh%1v z8qDqbI6Fj{EFyOjzS)m&X<1{+O=u>6%i-2m*Tc%EjUPQTQtcrus5fFArZ7@17Ngy% zS!E=7p$f+>Ezswj?*0Hh6l3D0%L2M3A&2t6P#Q5e7pu`mdY~8$Sk)fdh!{k8G*bZ| zSv!H=YclU}+Avs4&w`-rDvn*p-W+pR&k=&rozn&1hMlBKNuEo7kt@qND8I+!uAc2d z#-4J)q|paV#9ne{ZHU)9*ka;!v_(GA@bsX>vlud*tVh*%h!Yv4OVAFg-Y?ia@qmrW z1@S%|#f;F2^i{9EAlJR)kXTzRouy&rHS;?yPO8N6j%vmSg2T6Fv7!BujIromZr~9S zlff?OxB3J;4lW-)F6539w`E??u9G~Yk+I-AO*+b2%TwUydvM5U>>$6dRdP4Oor8XC zT^2rx&9~%EE0^}yD;7vyf1q!~qLLFx!?SJACG_~y*ZuW~*!fDuwoOPEmEo)*uhwe$ z7de+JM%a4i6s=EfLi;3~LlYr|9#&F?Ch&G-Z$s}mCV4_p$Bm^U&1+ss9h_OE=&<cj zv)#9xo?Ek}SDz|mcRGhXE)Kky@TLQ;etfTgsiXkA;N!6BkI6j6uX@q(J0#M~xlotH zc=ZXjrHkMBnsP0^8@7mIH#JEuJ!Ok7(UU~uD;;P6ZqT&Li-*hg^JzLfD<~7BO4v+W zJ?97Ght$U<6K5A)y30SOwokK<$vL!gp1!fa(Ru%k9Dn`U$C}AJ(L;NA?7TcKQq4vP zB=n`tIcJJqa&EM>WbcqZLELAe)_e$u(#X$6)y%knC0v^uSfcd`2XD?g9=iY0<ORWV zz5CfhH|X0Q?R+QKId$jNSIUkXI)qnI5%_WiRP9yw@V)pIP;5q(<5Wn$su%6}<OY>P zq6WtuknyEyz^AMN8W!rk?167vV78W+Bqy+`l;klGw?)T2djCk1uPjc+_k^K*ag9{| zaJ%>W1({-xBN$<{+*@{h0zA$mBj-ia?Xz|(eZBHAb&<%5Hz=7}ukSJclaqpIJfh%s zx(NalE2ov9-J5gMw7K6r+AuCt$@ALJ44j43*~Z+67IAS@6$jEd&MAkoeSUT<%$Yjx zJg2Zi#FunpjZ_NF4}pc<+ONqAra0t6Jyn(Mn#a1!K4e5($0K;U-#`~F;mmYIE9>}e z5C4eD2T4(hwT&{$B45&3WI`Wlo>RSfnl>chh3pBn==b$;-|~xsoe!SUwZ6-E0!=qX zhD<r+5TO${u?gwrI_lOxS}Lc)lRbj8{~X%$&_2W=AmD0$-&@+kc3-uCX};3S;%${= zx9?vDewdsXNxmm0TivecRbo$|Li4M)TanR;{(9=P>?3EKkMa{g3VM59CvBE9D6sy; zm4*r<e3a_-FlGZ*OeE^=IEr&&EL5doVOV3)nckvEPj%*I(o)mufgO1sBEkFXE^pUv zNtTLWIjvVi6m`#ZjtNkIv}QVCn95rFj2V5zNJjb+`l+TgilB@FJw@t&QETD(Q&BYV z-POrDgcoLz=83blyXhz1P9$eTk4I7VHsJGl+t8<&!|6Tng&f9_xUQqY`@U^*47ho< zpNgJD%re4NN|~<B>1-L#{G+BB=Di%}U5w0aLS@*;yZtY9Jx1ZqbZBUvwwMt_JF2%^ z*8$hTjx?0O=HQ;=7Hl_%YY?}`sNToSpY56=sxF}!AUIpLOY3W6r&cbB^5b_N1>y)w z4kc8`coE9aodFpj4E*(<7Q=Awhxk_)gRI%*U3hS39GFyd<|+qYmUn8TRLBxCy=n}1 zzt@m*m(q>XM<7w11Sw$fkUuAl7x;z0k8Z4y(R#&7g|s3zCp;|_9#n7VVfd`l%*vO7 ziZ<pCno~9)>7fRGOrL+hxc<&rq0_XjT=wrM)sH+rLhYdO%&^oZXTpwQPLtvCd2OU! z^-KI#vumP_F&&rAOx^7AFRjUwI%~#tP}RuVR5goix}*kQY>K&2QuyfDaAae)k$r3e zBU+I@7VXi?{qf!dJBw3omYAa-kV>W>fCs%uyRY^H6MJz!R}k_jEjr;b@=u@CT;NH* zLTo-%P_ye{rjhL~A|AqNa?cCJqr7j2H}7oy8rL@Ch*6bXOkmoD^A=@&{H`5A-r<n# z9SF8_A>UG)h1fV5+|-M+n|hehZ0dT9Xc0A;!r{u7*01*PShK{n-$D&{qF<m`>K3|t zrykyI8$el`=<x>=<2ZOJxY%#|u%I=8cRpqh^%qMQO$y2?s<^Ut=3<@BzzJ8{X^iD~ zZ3yM~mbpE*P%N$Z2;jC=8WppXT^0Q}EspPU63f_?n~BjZB-1D|#dw)8LR4eqsHmDy zdipz@mR!1sq4x`^k>!4Gb0<_Wy_HYK=y7ajYm-?dx{RYwzcPq4Yz&w3GY>eGr&cg| zy*MOEB%_K)TVM>OdeXRNRQi0h<NSi~j7#pE2c~mDAvTl@_kgnf*}U9MUwQBf=dZ=! z5{E`6krI!Y^zLw~_pc><?C*TPr`H$H%K$Ap%hGl9s`*fRa_A)rc`=qvs-;&RN>{t) z(4XwA#%X9j#qpI8sg}hYTomdGe9wnh@O)k_L%R&OepB~XbGwPQEZ@6fxE+=%%7tb~ zzi1!ApqC5&ZWqh*MrVgCk`9mHQ@ukSpWS6tIe4<ai0p+TdMaaRk`_4l4P-dS)YFH? zvurlty5eP&Np;aGDPknku4k`(dr0FxDNh{vxjMr+$}a~Bga}6MkmB{0!mSs-(2Tve z7XDgwyk3wCQ5DkRZ&5_jY}2|=zgVjC+cqQCXws`Xi9!;n;~_!g(NSNv`W5Rp*TxR9 zY(={1E1)@Ul<<AV^aby*Yu^I=mB0t1nSj&!`$MkAlbviZYAO*ecd}GR1*mO*6nsD9 zvFCO~a3}k162BDXGA1<enZl<*-P+7u2IVYc)M#}Lu7OVnd5c<J4pAG_ag)4vMrmmS zXNQ-@*2T}v$vWuCMl*>|xC~tM7Y0%p^KQbjT<END;*An^6*rboVV7v`E2*QDwj$A~ zTt4;|8IOi7F+*umsHo3mBL%4*M$tm$F^ngBkl7B+g$5OM;e*$#fxju!?Z*CzFFdJD z&u|;kCzDi6LLrqJnVmytLhsOT{<1LlTb<!dK7moeo$#&uqMB|^wLk4`+x{W=fo{Hd z$4;@2W_3DmBb44$kZ(USV75)`?a@m2lFI@%2gnC<Q1|anE{>cEKG$kg;xlzE`s|s0 zfyIi1=>XK-*CAv)s^~pqu`fU1T)OMnC?6bNPBwC;VH3YMiCsE@kb-~Lz}CwRO<#*1 z_U}D7okMbK^aYVG<0hWQk10J*Jm`2+8;pvKo7?G5W*$Ozd_94~eB`u64N=RqSzOhT z0z-dkrcK@hU%u6Q<rt)1Uf6jz$2~u#gUUjyF1(w%(6{0BWx}2y0*Bk=4&OSS?M`Pm zc;9G&+HUpQLeOwXh~aX8K0;?^j`R8PFbDo>?Z8C2Hu=7YQ=1aFhORy8AF#49zxzHS z1~}aD+h%X++FbjQ1T)C9*nSjQSG_dxO+D+rhKz*y(tY1(?t0=Lj8hCsvm%sjt@J<8 zfkV?2lo#pwDZ)cWOGV&`i)ptGil9kbR9P>v=Vn$bwc^b$*}iJ|!(u!+3DKQq9F_|m z-D5_T^lnXc=_(37G_P%_=wpCih?P72Hi8<-Ho|PM%adw`cd(z{Q2)y}YH8jOE8tIA zwv`BZ6*tqRhgeQZ#Zh4P-Bl+XyRpCJqP}UwfHzymiF>q`T2GY2DXSCS_u-#e?6K^R zL1|}?ZRh@GP|@R6{kf~)Sc5?Hr7K6`JFA-Ie3cC{i+Yjeh1U~jZ4Q^S4rSma*552D z<`kRLE8X&qly46FLv^nj&?c{^uD)j;aNdLSWO2$6(<u0+8-v$wh&Os$RYy8rqTj8t z!r&9C>|`yz@b*J5+S#QMS5#VsTZI(pb`Fuf!f)?J;mS0e2r0%-M7NTS)L-l@BWHPh zU4&-9&5r1qMmJUVK*RQ<6DK|H6cXd$WC6zj8^hEB$vtrmj<b{Qi;uews+R1QImRBF zq{8VLt@CQLA7O*!M+r~<$FDUrp1zSM{c_Q<`k~juajvQ7R~QXs+L+WD<{riw>p!ZY z@0p)|z+74H8{LK9xyXe|B+EeV>}P9nUz`cZ?U<iNIMzsU;2g4TQ?-A3>TZw8Zuc)Y z+EsDT1WA@zjdFK7hEW{bIzPNu<KQIg`F1-iQZI#0s8H1^&Jb6lhCDAlF|u2G55ue{ zI{wuubs-}5EvS5MK67t;l&J=CKp?17CHwh{p+)2JJwf-vUwAhY?6Z21m&?gA7h}ya z-!GFjHLvQ3-W45u3rF6ntGP|u{tc$|s47S9ruK$oqp4R<QIhz}mp&@gKz30r=EV~! z^BBIqfXl<>+}Op{ZYIBHk()ZY-k76@HE#R;9M(=xT>qgb53L!*9-=xbG9>c&w|?bj z$iPHdAGK`Tt1%fH%st8|h_3yrpSxgR<*6h_xwXZX^@zsyJ8czj&374v-c7;ryIevm zOL^7WlH~%)SKG`WmXB_?B=B4Ipm$3$Gw!p_Dzo6EQTVh+)U5cBNV%4wgSBH$IZD1Z z#bxh>XL>|e_J_;h&P46_s)$ebX|Ok-2RUb82K=E;2dTC(exY=eg_-vITNVi&)OU<d z;0jRYsS!PG+G;52(vWp6|KOtv<dL)Y8&na7#_#GmI4&V{@IIN=yEk8n8<;CXZPx1X zXSs&cfNK-v;Duv>lV`o>YwK~<O!_&m#6=w!zsGit)qWK?kzY%YxwQ0rGow`<gQL<n za`w_1Y#zOEOuelADD#KoI8RHhIL3hQD(=auk@;}3s~}x_K)F`=!z_~*V}&mt)3=9A zCT?<v>n;_v@DU#~OmMyP<dt9<-}VgMYRd>*qxmzPreB{9WM0%@pWX@nvDEo43J0Mb zA2b~L&+W6?la(QSA?(wD8J%s%$o3s=GqFk?<^GHI3MTuyU0g$yfj=~hBQ7E>oN)H) zy@Np80UQAS3N83&YM1|kf0FjE{}-5{yvZw7h6yO@K=JiTl|})j8E6jyWd>+t;KlRJ zkNY4En+}&<*WLgJTNd8`S7O8GU8ghXi(l6p@SV(%=aMg%d<yt>$;V8f&;R;J{yJiA zt^<yDn(RS9A1k0#12#@Ub^`jYi#Ig>yEQfrcK>(r;QZKF*nK$f@3yhAu=~G@2j^eG z0(}kmT*eBF0T_#a)jwa9{3c+|<G|c6039ZP`X{l*reXL0C{KRIk4?kw|JPuHF?ivf zDtjD^%|9y-VlkI+ypghdfw_W!GQB?Tpzk67Lv5%Jj{`)5k72j5dEht@4L%0RLo}$r zQH{+5$AM_@F$e>qLH&(tY#ulcM1zk(7!VEWW7iwe;W!X|DJDVQNSVE0tp4hMUTHFl zK#cpA<GecOO57oRpmE3scpqwiUoY=z&^RaywKuB&zhGnAfvxvOw*6>qBP@s)3pY)s z&l`x<Ulo5Zl*~3D_TZUr@yBsl(Ju|sX`>ou!*_fz+S1tpF8?doEBqkt{~K(W%^T{y zfe(JVF4plcf-&<($!_~c^1v%ib{Bw?1jKzg_K==%A7i)S{Xfzd&I`7o0?A?1p>eQ= z)8OM!8&2N{7n=^r{pVnVIgsRql->1ba$qI)S*tO@_7~iz@HXT>Xl$bz@->|HqcMnQ zBRWKb*BkL+kNr^}jt6gJ%YiiPu|Mj=@!)N2ISBh_JfIAmzHu9f;qT>uH%boN^OJr7 z`&Z`wYk7Xn=07**zYF^xo%d(*%e185xfTHD2VWre;QsOF{P;h)znk9{fqQn}Ke`UA z+~X+%@&8}!v2qyj-#;y;|03)~;Qn3lXZMCaNV&bhwW4o%-Glc2vCY4(I~?n;#)kGO z2p2vVL|b3~y55l7uVI6@e@|=~Krfj-AK)3zM*Dzo#zpYCUeHE+{<1xgPqF=gJr4N` zoDbq%8(WW$?JJ0X8FS5#(D)k8#@Nep;2a=*O&*kkFqZ2z8Z`b_V?(%L{jb1=>|H+} zd<`%81SuQjoALc~LT{wpaezL(!t<TKmTds$KtlLy_AQS=<8a@@cvj-E#=A!QF*bm; zG~cgbW5;;S_Koz0u-C_CIWN|9`>{V@JgYiFV^F?9*vql`GuV)C)@<7tdyT#}*P8Dj zy_V`}FxK!rVCh{BQuYOKuejuINDi#wWoeF;F_0JRL%dKMyT7Uz3=7T+;V<$102|`@ zBW!3cIRB~)3>%_j>j9UC^nrW=r$O@n3T$XDxUTRvqywC9DK?P3EBhWqU#VZf-x&ed z{e!^x#74MqEC>S{Tjv{ie1SGZgUdi|uwL@#5BMN_h;N<T4`kNyfHwbt53bjD_WnTT zpMm`|{nqKYq7(MmI-LNXb^R6l*Nv^WZ32urv<CqDffby0qc(j0jp*>PjckL{vD^Q{ zULQZuKmRPBz+z(s#y_+`c@q%-YV4T&um62a{=<Ay9h{@F$p<CR1lTq8A7<OS{qUb< z+wZK=-__w?fIS7q-UlV84(hgkJ~=G^VfW!{<Bw_oI&7F;C{V9I)@|dsEX%Fe`>(}b zn-@#RKdtvh_HKl|&JJ}T{=RFv0JJ}xAL4=GEctn5E>IpSSm%;IkG(P%guA38eEyAe z!lHw^d?yF<&)*dr(rcYBH_&?>9kLz9w-FtTJxUI>78j5oDA1hn8uA}F24O*TXq~P0 zSLR>suVDUZY?w|P&Ap@(j0c(*yZ%mgB_|-h@92M^HzWtv-^u<28(J?*a=)lI_FPc^ zyLmxfeu4?|L$b@UZ(i-Mr@{G_?OT&uPy3bee=7Db+xsW<{(p$QloLPDdx^H117Pfj zR^$IGar__8@8{6p*JohtXZ{!eAFP$XZ`}T>9Q<GG|MO$~SLMq8aQ@qJCH?pJMW0`l z`Bgrc&;J(KFnxaYy7|j`{|W40)!|2cpl%!K=aVk~{dxyuh84GUd|332aF_J>MeKDt ze>d+Ba`P9k*Xae|V%xBez7i9VXB{6DtDo77g$?5VHa0X5P_L8SVBU4~U(p-n0sRGG zfi*NAY<$V?CEb^O0QFbpAQ_+rpP6}~mJ5glwU=s;wnSeZhvoulP#Z1-wISLcVlU0N z9vhT}Vh8CB^+Ekt+W;SA55x!cm*!vc$Fi^BGEf_mf%^Y?Z22WyR&|DAiKX`v9m^+> z&hUB}XBl(ZHqiFvK1f>{Tk<y;YcTF$k;Qld?;gS8l_s~^hQ>TM99xrF&$p30h`SLs z$h)3@Ssz#~fc{w3e=Uy7b}q*rvLE7u`fK)oj{)(*>s9|O&AFCi5Dpv{8e7&wekF#` znp~^le9P_iIzx1@h8Iw;pJ7A1kp58rkLu;M3Ho<A_F(KhQi1XYEHeMz0_Xwd5|{_j z{_%%4ZQ#2<l7Z8I<iq6{td9#^?pNAB$6lXfzk>IV@<8$T1pK=kcNvU(iVVgrSsL>Y zC~mKT`VR`ogUz?m7#!!1=u2}aFY5y81?~$_4!~{xkw4))f42Rfg8d^q!11N{1F`o6 ze7qF*l~}tbNn>0SrT#r737B`OEax7m7nlQ3{K0+j&&KrsliZKu_S-gO0DBf#zS|d< z_f*iYpnt(N560aec@+~|Y>!DObH*gD6tMkg3Ml_0xu0Rjm)^sKX6s`f0{Vda2ABij zwE(<MfjPTm%Ma|rwsXV&2L4#b_g7=X=lf@1Z-fb-6O26;54es%e}a1}=-<aEbxci1 zHfDZd4zmdS|9AgeSp0_R9r=U_&o=>b6SfCnuYbs{Kd|?^H2}#$>jJwE_u0P+8`1$j zAG{6M30rTt>{44{E#}|R!1JX1O8jLpzR1g%uFtjq?vQUd|K!XlkOPQy*MQYr-oUPZ z5AEl^{>OCs6>MxCun!-Dx0m;VwfM_na_d4i^!?vuux;=?aL>4$uS?g()wn};VcQGa z@4n*&X+PqJ@E{(jhL8UmHmIjJ3gX3{55oJ=I3x$n5B0J6Av!dUjSG!q%PTCce{b*_ z09^ljMjHPvq25rBnfXb~(>xtuzgoTzUS3ad+n_dNKek^s>VNN(wRH;N{YVZ@|DNZ5 z+y#tPh$zM~SQK+FT5|cB#kxF%^n`GJ)c+oPz3m&x!7;JhYj{ifxpWPX$Ba)8ZRq#E z%i!YT0w%K9WF=?UY*<^z*mnL-|F6Y!iI>8d`U|g=b4#^-g!oDh{El8f;{|p8$MuGC zp!ZAD-zC%=>M;wf|Ih+m*gE=|Z~nbB+h^xt@rUw27<2E1B<#BL@6~Z_-7KF23mTto z==Z<NV8`bVfbTADyoNIWiN5?3*mlpwH;g~HC)|&{u$BXV0(&iP8`zAE2llZGc<zD5 zR?a})z&;X?qlW4E((rc)_c}eMX2&qmWu_QV&+qKVwt4kj_r0B~c0=}WRAbA-`|vg- z3ynd2pmt4?!I%YzY?uRPfg-?jRoUejK(g31hy@>mVXxW%(&6I}|H_yzM8g_ilEIb* zd4aLuyla@o-U1AG-~D&^2VV0B##%5j<rZuHfb4?mjcoY=?YDfgk>4O(u*RAf8~c77 za0Uoi+xyqf1HwyZLhu~BE`AX92J^1NhWMZwOAiopm4Af?>|>8%&%H_q@Ini8Fv;(o zF=^Ev>&wRt`tULAHoT9Gh26*Eee>ZV=0&Lqp!da%{j_4!s@^bLA=}_IczYusK{8Mc z@?*<DG`Rdme2|>u%L^Os0bnjTL`r;@Ll8bB|1)fe7pg%&L2}R-Sa0ONjd0h?thaq5 zj6Z|D(Y*g^>}5ZFpHtYn!F`EM`?2j{3}D#6TC$Ij_(2XVUk9+Um-{e`)x5^SUL9YK z6{IhOx7J_I3uqkD9r6P-4%Zu^K{>I8g*_L9y<D%yUgP~a_Oh*OdVq4vwl0tT411Yx zP0#gygXaBrVuSm<Ww02K1Mu?z*cQQ9hn)w`G|Tp`*|66CJ-s2HZB)a3y5^%bKSMEq z{0a6q!r5r-|10)-Th{!)HV)5c&>tSDav0}VQWzJYcmVHA;B>ezvD*({%dVUUm*c*c z3nBoP^l$ijO*d$cjdXy=ZhadTtE}&_AWW!U#fI^&$6Lc#<puKw#t&g(*Q@+6UN|<K zf0g$yz+Q(1;H~S!bb-$gw%y;z0eiVP#xDFk#y(sexPFTRW8gV{ZLO@s0(FHIhe*kl zJpi5qz#b5Ke(4+l@?y*XDmHwc)i!7gWDiz*)z8p4#0S%NNfx5R$H6vy94-sA{}OCy zzCXf-_~1398(bEm!^g1N-{}p>tkjM%7nb4==D<=}$^nU`cc)7}$HIfh0mSx1%3~~o zM1gyP_3;P$;Qit{pJL6u8b@#(ng^_bvZOcG*s4A&JnQ-p21LWI;dDq3Y#w+Y#=oiy zNQaHD+J-InD?BSQKgGtz0sGiv@VT+te~1lWIlh!!jXSvhp**+?Jk#?|2liDg%+)?j zkEQzoF#b>h&jdi*_L=y)y<#KT)iG$E-?Z^Nc?btqgMI+_p#RRYvbJFUfcxx0qV#Ia zq1Z#UGw{6sSMm+q1I(d40LlTNHVYI5_7V9X=g<#yg6sE>w6Wt3o=JaNfP4pY{yUzH z#{LKkoBq$n#-0!8!=L5+$d90pzPkoM*8-q+2hN5+l7){uL;>djSnQ!T_`C?t_m8(P z0eE2fd0vBbXuU7R6Y#$mSb+N$&)+FPZiD%~RDMV9S9l>@u>KYPU*rL81Z{<K8S=xA zd<Oale3uKYdw31*?Z2`IxTVN#7=P<9;2QA5Sp7;Iv3cO}huYusKx^L{c((%j0*s#r zuor>%I$%z(E5LadJO{6iVYjjS-^s&dzoTJc!|5RI&#<xSa9OYom&Kxiwt_aV<p6Z1 zf_w(9pS3o)PlM0y;IUt717qMk%Q1xb;r?D81O06k0=yp#SUC$W)uKz+05HEcUbElh ztj`trd=M{u488}fABS=tv>)`tQfvV`+)`vQ53vjI-U57gv95qT&=|xI^}!lWhx!m7 zyoSp_ZMZDN3(+7f>^{T?ui-o^a-e-n1<YyiJ16UM06NcY>`RxWvjCU_@H~*fIK_f{ zI3%;;^K8i9YcaSR4Llddia*$Q1)dYE<r*9Z!oV6^hYiqH`!N3R##ZuqX?!`?!L<+W zt6=OwpLo2vhzZENjtS1b^&ebrVgfR*1GXwG+YH$5g<3w-fNS)-bqw<(xK15_bHDx4 zJ^<x`#L~X7wuYe?!2P{E_8>`iCH~+Z02JW-2iA@;EBOPPYh6C98;9u#j>Bp2KGcS4 zFh5t@EAfZ+{Zs`^Qso2SGq+8c+4(8p72v=BGr<46_mGQAHiDmfdsX2CT<_Md1FP`| z{j|<sYrb<y0PhEY*n>F$E8u?T2l;m+c>vEl{o>MozZ!cm{y?z<@K)#h!Mtm+!_o~t zCw3c-1*a{Y{g$u$;B~N|G4gNo?)RS;_wSLx_uaX5p-bl;@C>wcCIZi5fUnlY66QPb zdI66+)OL=SUW*~fzme~OG0Ugtmvdkx_F(+2LcufOT8^#88aD5`F&OVgdSZ`3eXw3? z12%dCdmU(VY`HDw8}PaR|JHx=3$vIPC6>$g11tMK<gax;hhhQv9(?u##T}~ef%gFb z4K53BLs+)Yf%k&2*n@5JAhD&~!{+~4ADaf@gX2G<K|J6!4$O7%`rpu7_}?1(Rl94u zvzMM}tlR&gc(3>q?pN@h9f~_tJ4C_u@C{@k-#fej&VaD^gU^7#;ssnAARegxqc}o3 z{frOdfiRb9F#g~k0L1^xWbdjU{#zSE<Lyhn0DXeJhCzS9{R_4qB+7gje=rX~e{SgS zm3#nc;8_nIf2i#VoI$bYfyeAexXa^;>v9J^hAjv7L0r&QFyBWf`~F+QzH0a9iH>#G ze#q~T?;xK+x!?-iOG4`&UW4}$kPO6&E$b93y%c|F-Geol1K>GqS$3mXF6SUN7Mvey z`{gJCILjDdz1&}p`BEF8uf`w9f#!j-RX_Z<HX8a0me&K|k0n2@_y}q*`4Lz%P#=8m zu^M+^--p`J8V326;t$4R1q<rm2hNFLyqDttL=33GA`0At1My#-cR7Yo93fc<7wSXx zl3t*GEAy?^*kdpoKv}RPRykr87Uut3Ll2yN=P~hb?UsD8<WFerz<jyt=asRg^$*4! ztAOX}Ro{cW-}zq=cvd8hF$3ei68oiG0NNgDE3()!8{zzDY(>9yI{ah|vbVB55d(ha z_P^~PXmeR>?Am_$6JNo7x^(@=ianSM;5`+bZ=*JN{a=kglm|fViTY#Njqv`_F)#-n z<tSsa>isbzlik2~z-RyC(*U$__-hv?z0MDam(m}`4BN*}z<XcpwGTcQ#NH>c<-k68 z{@*D6OJ~Pl%)gCr|B5mHJXK6oxiKd8trbwLmrC494fWwQHVwOv&5KRP?ql=+tp76_ zi2Gw~D@;@w0t4FW`zQPj#`rGyE)Z+|-vd4;^eZv24VU;X{uaUKm+w3Ngg(DA=l^(C z&cT0yf59^zcI?5vOM=WF#vjZ#OW++K_WHMe20S}}&gy@Gj{l4IUl(s|Upj;L{#a`t z>_g{wY<_qj+~cuh5BBXNe{Ju8WBs(9`<wRv4*&1)g1P*={J)d`zlxpv``7r5^!CoY zgmJ*$`+@5|IOYRB`~3smU0=&>82|h6KiE5dMQ@M?^uua_^&x#$X&~PUeU%3D%|^7J z$$}U^qeJ>H;chVR&t!iW8?t9f4(5aZYwyaUtf<a4VAf(?ACkMwV;<te%}SO}cdfkS zEqS<6iIHX!QH11Xy2(v)*S)Sp0i!gN%tJTGEP?|ln23VVOxl7Bief?#K}AW7Xe3cE z3W$-q-?yv2s=xNxbxxfgpc}f^`upGepY}iOs#B+G?>cS_Z|pYOp3GyP_x@KV-~EWu zpM<six!ULA%8r5Y598V&_xjyAfH;Ba>*MeKx!RQSTlr^vidk*p8Zv3VLi*ITcGMOw zYulIe$Fk#;u?_ju?~rZT@W&C|*RkV=ZO`18Jo|U`Pv!vX&+C47-zS$RYcC#?pYMFm z=hyyr>=AOG{b{}Bvu}faOS5eom)f$Uen{T(e7@h8tgz)5wJB?PAzjfdwmL=a`fSh3 z)kkBMv(0n+I6U8T$9=B;*F_U?KhJ%KOznqlPsjZ|9q$(3`M`DN7^~FH{I)#nx9DM9 zsUMKTvBYcBiv{9yL-`zK=eWw;@YtMy^<D9KpT~U${M_erJLy-;b>qt9+;7hNoClM! zCP@0#aFK1&i+HkE$oFfGSex+uwmSpsW#_SS;N!xac*il<Y~^Zh>OTeR_I$hS`?fhp zMseMTS9{WUO(`8;uZ(S9hBm6%vAi@-tUJze^d*JOXai;^z_!5BX5%{zf!}ScJY{4{ z@+r;Ob+it!t+5yfLGvKHPN+P#%jA6mpY@~W{G4}d|C0JoT!ZyKIDTwCL$x4n0`K{I zwP%0qL-^iTP_MFeoY$I<%j&r{_?DLW7#}B1sQnz|i#42udCF^<nFL(#bSr?*yob&h zcppAD2asdMU6d1>N6JWk67K8Ry0guDeVlK8?P$Y+si{5oxz2~c+Vi?^2G(6_Ea_?7 zGPXnGr8IwD$hU;<j{W4gr;o1Ycm_2C&!KrQgV!s>f?OXm><gTX(X;fl?eS%om@;X< zPTT3&v=88%<vifI$jt#9qr3)HUUK>Ma^iBH(A4H}5nubopZjd{oFzN5ODvjnNgK=i z{i?aCzpneFm)bFNFQpmVJ}v7HOJmGv$$U)oMUVZ&@y}x)^O*UXf!fdC`U9+m!+rlf zjTt;Q4r>HG*n>Vnbjqu3vE*{H)J^PLS+$*xu~*XeBamCM<5zg6<&AC*a85Hep5OEx zMDYvTI?Zbh<&U&sm`6jSY50y#pgqaXMP6mg$Fx0d9ryiRn?QW*$LD}+ovqYPQuEkG z>_?I9xON&h_AeV-eIHAnc$k}~JpOrnFi-bv{eHCX=!WRb*^j#Qb6`HpfB&On(Yk#z zF{k6}kN7$uJ~rcg$N9~27Uv}ED|gD7?T6a3XL&cexi{wCe(rp)^S<VSyWh*t2sh_K z`mz<`S--4}B3a*7oQq~W_hkL&ZM!9U^Y}{_)iYntgUtu#N3(hS<Je`a*|A=kkL<Wp z4nuhj(R^ERpX1$yd-^)oRd@FJtdDauU;Fa5V?D~}b>j1d^{2SaT*UPs8a?&O(u=D0 z3g+WaPDD$#|0Fs8qyApLviuk4sVy?h?f6IfoAbWT|1qAproq&{W5+J*ud6@&<Y!q| zefOj4*dDy;z+Cqojq#qZ`cHavKw<5Fb3g9&;oMNRPVJU^COp5Gi})Ncw9CK$xjpsY zb!3$}_b0h1$Vus3SJ9ugZZN*r<&Srr`$ywifZrRcj#I^Up&oTYdLbIGHxjze0q>rC ztvxC3(z;t;S?K0DV|8(daza{&u3Y)NkbFYpJzmw@Va^4>s3rZPsZR{b*WS0|_jPSp zi!OdGY~z8fI|txAu<OXmE?vX6I(O#NkD?WOewjb-`#G{X@$>D=R_iBrT>X=ptNy&+ zoBY_oxc1d-6!EN&lF89-%I3ECB{Ao~l4pMyz4zf8ZB28R)Z=d$?RsMsYH!Z@C3BLV zC)yxC9ryD_EyZ^}%>iIu54dx88|<~smumBYJdckY-M9O{qa(-nqL$r%-#33RVEbz; zqI;gX+2q}eIaiB7PSPBO^n6;r&p9ymq3hlEzbTI==lqKC_?eT}i)$UCC7(|@8%6q} z(~bqvW$Py7$BxDR&C!F;|6jD}U~{zX@S^A`1MG)kVJaKuhxIze_MwH*x<5{J=d;S= z#^xd)cP3kz6U)o;S((d>XKiP>Cg0C@rEM#3+t&A5V_R9jEnT0U@5g@TY5Dj<JZsyw z^Rhj*^ZBTq*N{B(o8_1}4dwNo?IGPtS~a<lT{o96V%y9|QBI0-T{b73WxL2<%`eOM zeGt#5r!iHjwbfBu_ELS$&p=L{To<%$eYNdSE=9B0>Znbue1zK47f*d3%bvC6bZ9@x zv6Ue$1%BQqGFIPj+kP$EDZhqo+7zVz8Q*d=Yl||rk9I1Xv}Kn#<+a1M?9rBO`?l40 za*_r1`_@KLTeegy^?h5e&wQBJC9B%1Uj`TH_<j`0RU4y7rz30^joV(^MLu&pYrC49 z_07uJwl8COwr%A)vmF{UL@V~;=JeR}WkPmBG+!_4Ul`lj`1$inh+d6WU#!_!)ohB_ znYJ}X53a8!=RaTe_+VRowbi$ASX!tZ(iM$%Y}?xP+x6O(U90EY42>`OkbX5@du(eg z>yO5=b}h~7NKe@ELTzhD_9Z85ZG~itXwcsw8^l%Q()ne_s-aWbw#K!#tI<L}hjguf zA%1`s^f@3`cRpYvXgd^(^}B$d+Dd$5-v=G9qPzMg_ELRo-{*CZZ97)l?o8Vz4#h59 zkrPi=eB#tsuPvTz3QO1OSy}N#6E2dMUCUS7@@!jU_%z#R+m7w?iuxDYc4+(}UoAbP z=f@wCu{Mg@z8%W>w9d3$<X0KG?`IjEvNAok!{_6&`RkFZ%wJDVY);NpeBSru$L8l^ zZlm_YkJqmM@J`G@kL}v|Pp%s~`nY`d=Hv699sXK=eAc6=CLvytEPeKQe&5E`V>@4m zQ2vT&%A=oOzc0Di4r>o+vE!m2sGHS8n=(#Lbki3v$B*UOY+LibqitIsyQ*#Zr5q54 z{I>k0T{GXPJ!G0=dr1v79x(Nt4UK1bngc%H_1$rA?6L3Zrh2|@#Rqy$Mtv8X;dNEp z#;-UhJO+&4UOt!~Ki)KFNlwU5ZAqWw7=EfFeu7im`TRD|-#L6_0WV)O=%wQu+p#}! zeSLmvm-6{>OWAg@Bst63f<3>l{8i8cey1|$**3?`#!imogqG5os|0+S?Xz9j_i}CJ zR0pSz+Ar97%U94or7?wbI>sNTmyE4;kxs}4WknYjP4+CV$F}rj&+3Y2vE{4n>xwQt zpD((Pt)AMxuISSB`J(&S>e)8s)EAa6`<9RPsrjh=z0dtuAGG&NPT1EMomjR)ZOgMd zwq4D(FQ<MzI<<W8ZH4+8*WxmAAsNzS$F@Zy4(W+5xgr_MSKDHL%%s1cweN*L%!4tM z_vNe&zwP^C`5|6s+O}iYY8Uw-pNsgl?2=!kE8Esak*t-m?IQWM+rDKCYtKK_|7#fQ zA9lR5?IOR*>N~le;oNmQ+lYhlbco*tb~?oNPShGTkE`FS*V9oW=fKLG?If0pIu_C4 zuWDJLBc8Bq2pf9N2hoLndC@JFjIem<x6Ag<TOwR5hhuDm?VK;wY{UPYtm3JHbMhhE zJBP1i&Fvqq1HX<ra5}A>(@SEuakHK4Lq@(xhkdmr&%Q4wo|8}PhHN{zL_f7_e3R`E z&)W9KPk9%cZRVyhrqisotv%Zd_KfTUoyEbrrFpe=O~z1dQW?@!OUd}y=lgxh`{PN@ zr(1c+Bs5#&)JIRo(M79eTVnxtDci=cG)~eKSIS?!VAn`@-RIANg}X+43H5&`n**ti zIi5jtaToA%W4A%3yV`DV+=co@^RW%NMQw5Hf61@A`m3YWy}%!e`k$*g2MYadlVADy z)nmIe@^Ox}WsPh7&EItk?hE$Jfs5!EI5&USh~`>#7k}ZN@BItp*Iq>NcgFG5eQctZ ztf}f-1~1q(;)@G*-Le(uf_$9S`sDfiqH}pj&ZpTv+qPe17j*cY_nESm=G(HoDt381 zoOj#SpSJnEb;0ftUo2B|UxstQod<T|v%uy^eu(Dxm2aP}zG70JxQJFuM!J&o?Wj*I z8DB?zU&hLZWJ0z@v${pJYBEJSq)SgSmL@-4%|@(c4gI>-(_i%5_x=SonlUCnLookG zWaDEjzn}VMdB#60AGc+%{kAK|Zl~=e&(>#-(II}9v~6R`#x`>xvE%X1!N=x>s9im^ zt|kk6Ls9>Cym6Abz&yvfW^M$r?|Gi&VxHnqos?VAiSxXww#_`t?C13}dKufX9~{%2 zpG>Z^*o;h$pC7l<Z=+Ym@4Q~D6Us>z*NAs;z8_kxmTlqjd&9!rBfo^P7Xwanyv#Sn zYL8(%4@9S22AMwN5<SZc`59d{V%bbr^h{PfUtc*AKdYf^bdK#fhRn8N^8J%vK3#r_ zufFWC58i3OV%Dh4cf7XD0qQK+^Zh=UUth;qD>42*=#%_lySSglFBvOu+p;Gtp7cl; zO)@^s@@-pk(i4x^r>QR;$w<eit1mstNl$#+_i57c>FN`!oyf8VD_u=co3_@qUOhfP zz<tE8Vcanoe?Q<<;!j!|_B~(I`C(<POd!6Ty&}CbV_ihHnd|*=?ni#jf0k2kE!!)< z{KcR3S-5B9S8(nbi8;{>-&)|?Yk=c`Gp@e7vtt&txTeHvn`7G1K(EmAWYT)dhP7wg zo?bdvq-Xg_+l|e(WUM{gwz`R|?{{jetZmsu?%*qP$XvF#+#<)!HFMtHx=}x}&jEYm z6@LG;R=*iVCv5)<QPgVtmsR#H8|KgD`(tzFd-eL}nLPi?e*b8m-`DTA=J}WT{l+}M zcddS2yr<Wbfu0QXWS}Pl7fS{j^MTa-JZR0!SA4!4&GY;EegE^!@B5#J{^w<sw_o{r z+iC^ydF+2)=LW#%x%YX`MPT0oGy;0}x)T5K_bvCo^uMMJ11_KaWW$izPxfC1*bg`f z_zVzrQJ`=1k-oy`h9ONG23}q|;6!lF#sPhydn;@l19U*F4Fxue==F?Q%SHoYXt=d$ zV?*CEd6>PS;j56}2Y}CIfWC(0is&KP&ZMP&>|<=zasd7}0LKB51IFdIMN`ZnzR#nK z)l)x26Hj(+Uv{M@nNVM}JZ5a)2<2eTh5=s%FN<BaFFt&nvDzFowG8Y#d&2<EkF4Go zv^}5`{cyJTHMI;#&j&c)-HMzfV-kmexe3ujw2*8iEgqBl884%S?B-=Ne=_}yR-_-( z88hbqYJWM}#{iLq+2|82O>NPINmsiFOINa#qpy~3+p;71GFURscH+D22ulX|SRDJn zA*5ZRfY=8vyF_DW&6mw-8Mr@=e<1pVWG=L*ozU6$Y1L#M&)P6^%-QknT3NNB<Byr^ z)25M4+r~Ei$mtk)#@;gUq~lZO(gVi+*(DgaYJDlk?sCwy3G<%7xxZ<UX&ZV+9~T-E zxQS!BvCVk$Lvr9dIrW{Ml~3A+?_y%#=~~%*jI=9Y{P8#mhVE>fQ)c0Fde)Qe2lu(` zV$H_X*cSVKSYD`Ik9<9{v*GJZ#6A6qhG^R3{iA7X+X2v@v97`8GOkJW9E9RLFSN~> zU#|A+2S;1}uqb-@t=8z3zif};tpwY?PqRF<|8R73wCdS0(aiO@?sIc88>duU#;>5f z_`+shSIAo#wjB&kaNOrSVf-(?@dWNIb<7`se|Occ(Q*9gxEjBmSn<|vmvM&x@wW_$ z_<OM(`M2-Qb?!VH9y8=eZQ8621EVqbULB2IdUZ4d^Q)X~=DWIW&Vj$4JkpWjN81lC zPv%+mv1|Ee`ICRwKl=6j|8OvB@zprT;PawJY!&=1i9f^w*jWAi7|ekWIwS{w{`()I z`P+tP_BD>irq7mU+m@$xf;mrFd*$Fa_gx#uFMY~MN3=TdJ==yw5A6D%=)qn8+h(|L z$IpVXHJ9|GmfTF#{PsoPPIEAE^>?FMI?Q$RmLa7eldZ1Pl-l#<{QeM^4~}1t?K!9; zk8xt(_o-_e5I=M9?dbM}-^TfSkjp_VS1Ir7cZdFjN3M&;-rp~pu<ANDr+uHtKJcA1 z2jd^+9H>#>t>D)H*0dhrnDxk4lg;@CyW<h}*a!MKnDprN6~52hFetih0p<YU_C;66 zIjRv~ZVV$E`!(3jwb`2nM|r^KBd-x!NM}2Q6CEpqb5&Vc&T)5a7@uY#zQzZ?ljdON zlLISqaOeG)14$01K0cs=pO5#3gimbS@o`CCd2X4peqb~m$KCXG39zkx2wOgNLNXy+ zlEru&m*eMia09Nz2c+>&d<1hm^*f%+lOMx*z{P)cG;WpUSH>9YCiHFM$9b5uc?f(M z6s_4aE_(j;$8jyV9`K|GKF#k-UUXs7ZCi3Ne*X2f(ds>oZhZO?*DUt03G-o`I}R`p zFc;<^M`;|0fphZL3-NgfxMNwrjK@4NM#ZUi2t$_RZhLtNKKn1s@1OGbYPTF%<os~) z89%3>#*CdXeI1Sg!()5Yl51T2w=cdXj^E?^@^b_HIXK^(JoR3OT>G@u$Oj+46VVU( zD?evEIVg>P3O@J57rtY8zjO}pJSMu2Y0n4AaGrg5`gmJi?-0Ecr{0a|hvJs6voOb1 ze_sEJPv6<*Iq){jf5PldvA%RA>-tX(jph~rZEb&b1>(3c^Pl5Bb9kAno$uqUjr=`b z%zpsScRv3&D-i#pYaH-6_WAYnpWHt^+W(hL(aXoSoj2fk>-SG9^m`hPdBs2Ni2)UI zV{qP6{BBMZ)VQ8JbS2qIkM>XF=doaYYu+|A;(3qpGY^(m*nZkO4*Y(#{MC~6`F`Kp z_IZ9EvOJ#0uEcd9=KeTb<K|+5{0#iov}!;0A;b4=vH$dq?F8S3-*1n7-xm6af5MvU z<M@$-sZR{7@PESU{?To$Kj7});n)bq=-V#v6XP`#a4zJ$ilL|-;#bp^JTS&^bqxBN z`Nngdt36^L&uc&G86EmJn#Vq3=P@vQGd>Rtzo<Pgkr8%su-ycEJb!Y|&FUne?KxZG zoX$<=WX=!c|M*AZ+Bf3*kN!wbc=Fl-Y3=WN@Vb=GcqAk2Xst1Q9<c%PnA_Qmz2(Me z@zXzzmOTBl(y+J=*+ozNB$~A`$pOZ+e)#Q~_}rWH+y2qamN<6HZ`z95-`mgCowetA zAdS()MxBg~-;8ec0er}?_A7Uf>hx<sUgy4Z;<adT>yKT|<Cy4^t%>!KblV^1;%Qv@ zUBrH^1D^L{8OaJuPqx+1W5&<=zkBla4!MpiV`TqZn=xi}zx=q>pZI7)8vCfF{oHXc zURyEe$f>g@%edYlGDrXRQZg^|wW#I)j15Dg+n2<3C!YGmpt$z@J#=rLRP^0%rb8}( z=R$Y?+nYNiwlYSx9a@<A9q=dQBWW{n?r`xVc7EO!$yKNG_k%0*uweU$Xy=iKq9ezj zkKXv(3!MV5y|*XY{K`D{`^E8ZAy@hl`xf#srp-VOI1jQIie)nyjzhUi7|d@Nr@tSa z{?22Z7yj;}<?;7rUCt6+{VrrG!+a*Z6bF8rx<U^+8cTW=GSzG+`c}vKDZe#_<T?X$ ztj@6Oe6PcHXZq1u@nyeN+Zj2;`}uSEWB(Ec{-(gC4WC`Y@gw%(CokdnPvV`Y``zzZ z;r*l;#xcQu2>81!mTrENBTrA{B%k6Ewrk0fFR*@Fzx;M;Q+_!=`1=sc<Tu~{YTN4j zm@=Z-wrD<<O=0qV8TG9_+ZJC~wk#%35Yn}@q`eICL!?s;OShVgc=gCqryluwWJ}l{ zf_KbZ4!L9U{Sv~*7(4fX;2lu6#_v<cyKMN*ru5y{_8q;pEjh`OC%%uZtS?`WthFOO z*%ME6V%ru!hu!`c#*g<y_eI?M;@ERCNj@$*+CIE9H{Jil9N>K%Uq$@KFM%8!XRM6b z{{#ISI40s8@SSIRf0;Wb3g&>7Q@coBeBm<oWy|V_X0iChYFj?rj14jRaofI|2fIe> zb2enFD(3rak}*qRD{tE&8`b#Y35WD^w0%W&xAC)$_x<!mo^FNwF+lvj9`lYHzV}A& zzERuutxkv^qDe<~Y+tkx7R}15onXdrD`WEGtkqwzd*tPKKhF^8F9YyhSSJCW0Sez; zb-s8uZZjr*qmT4;8GT3GRrkLq?<E+UyuaYf$!i9_a5PR%-_{sk))-^GSD|;@?fKtT zGSC=1lGdj+?q7B^#(h;+)me4tdm{cF@O?n~o``=#r^a9N#(}-Dr~N9x6ucv0FM#h| zIM*@nOQ`4l2X0J`O&{P(!&P|aL)G~?XG6mkkXs5k3rOBypfPP<ZHv?Q2|&-bDO1Kq zO&R*M6nmnt2$cZzD**>HzpZaI+T3TYcx_uWABSW@G+&Q>`Uqcb4A@(GMM6*Zd^@?W z+&)O38AEf+px)S<&boAEe=B^W&(8Oa1Fk|WXZ^k5+86HAxer)0VbOf7eeAO3_<UDD zvOn0@w{+Q(jCi60p9RqOsibvNn1X%Ed%LOc%=upI+3U{xr0M%P?mO(J(4Box_TXZ_ z8TJWi|7qMd`)j$!whnOTi7NXj?Km8(a_mZ4k&N_x%(*mkeM2;J-JodJx*I&edZ`MU zWbmvSdukbP$WMQq4(V%OBy*9pxxaJ6zS+@7r{9mxetNnzeDdk3=*{<Diso&)IoZpY z*_3VPv&O6k8~^e)l09tP6OsV;y?u1%{W41{f-;-`cu(SgW>dDy;3-&l&AmvokjFCe zzRVot<z1|MYTMtr=&=_kv}s)Vap&@D-F~?{?!5-bC~H_H4+Z=5xUcV}Kdp-nzWsD* zc=_#Z(dGkl-QFzjZD}iTADs65HDWDnH9M92zy`a$ac*x7_Xjv2O>JV^w{6M9HZr*) znRqV|k9E#jI|lc0xIKH=ufx4XnJtas`(f$lw>^Gkyzh#8p==CyalaMz9NxJCdu?+0 zGya)9Tz3DJs~P4U_j(C957ib9(ZmzBGU;5VKgrmWxNaDG=f+{KP)GC`*h8lMVs6i5 zfv=Jm=Dw12e;8~qFC7N3$Gun9PmZVkf1Ee+gZPeRIF>Po?#9|sOHbQw{c^JRiEZp} zqP@jfd-~Fw8>7R=c2~fw0UG5*7Y^x?w)5~ReC`f&<HYNyZ*Y5+xNn7V*gae0AHLr0 z*J*qR^u)g$_Wehn`-0wn|4`dbpCz^SyuRA)zmfmU8P7G0f$O40cYE%z*M|E}M6=lL zz0v*~?&~>q_QR5%v`y^ryE{{VH10U=9f1GbCnq1dj-Gpk?zj*8C6L<+z*^YcJ_Gcr zyYux2qtDL$vu)1R(E8-lkE7qdI5syf$81dYKDd2NiC<G6hyU2`!F^x8pSAiccK;%J zWbf!0UI<{H7Lt{WczZ`hOLqLI!WXV5=e`Zb!1*uV?*3ha^Z%{~u1jg+btTNbcJzND z_8wcmxb~X)=iUoTPupe<Ki3s!!2Jtt1lC@AT2dR<cD~L10&eag_gqIWAKadad#`o- z2Bxncl+wkohPg((iF-w{f1$lVJG56c-uTt=-UrSVD>H?2AM5=`BZeaV{95{WjX7mG zALnkt^NejjYBw}(#`gi_fi($@$8!vQ=eXi?z55uPbGTn&64tPbUWu_*H@B7>>+`Wc z;h94#BEDCqBmO?Ycc68@pPu;Vj~D9S7>$3pzgu_DeP;A0?_)?OqcQf5KK~y(amdY$ zGJZ^c4DZa~ehI7_pN{t&Rn-Zs4d?p!w>~(~5vfmY{PFEAu^)l)^6R#T;aI-j`QElQ z?_9$?ELytr$5`L~VD!SD9&0zWzP2n{xMO6po*Z>{F~sAzb;!AxZo)jf5&L*<FmjTm zuHR<A`$B%rZD*|O?d~?Q9xt~pxh?TjvC-Z&zFZ?+WTP6->Qs}f#*1y7D;(dB(KXxj zy*sb7<vQ8DTzl9ZfxZK#v}bK&z3VPRW0kQ{ATtGPv95y6vmv`(oOTv#5wF5pl-`J8 zX*XP7w-jq>+<F51M)Vc%|6n(Y;UIms&)^Zul~}KKFt=tcUK^LK_Y19KvodP?a-@qV zY-L0fwmh{n9(){erS)BH#Bc?8O95xO4yqEk?kz+=FSN7pk8>uMclj=i-)-*=nX3R( z0DA#kx5eN0J>P4~I3~xY5A@}#1@KLN3cK|~U+C5AC<$0e*Gha@{QELkIMnmk&I~kC zA9-vgMqZB+BhSh^*J)h|_!b~tr}eMs6#U_teIKm#8HP1UTLCAswMklk)NV|hu-z~W zzb%vd%|fh^`8=Q{U27CtAJmaFj)gIq3jC%)A2;^-U#HW8-_+=Xybmk;8x$Qa$FTuh z0T*hW%vL-TKXC!qSX{`l<FzGt#>eM;F<^ZI)(F_OP2qO3E+x@xO7*k#5}D16=F3rr zGy&_daLW&)wa-tf3J-4o6?}}}u`b;<WB06|&ynFLo^_u1_}%p0PEmvJ|9EKUubfW$ z+z!v@(zOJxo!CTtlh%B<;CXfBGi<(dx7Od8v;T<h+ws%t&%YBN_^hAr*SrhQI`h8L zzro4#TZ?aq7C-Z!(UNC=Pzd*IyD9hVI(`Nm+t4#@S6lZydT_*b2YlD>WIXGOpI>LP z?)j^Ieu?K*r180x#d=O#L>J#ZKZGuynci*QH+mP=J&eXX26J+D+@u{pA06c0Wy$wh z)&sNf{#q9wZ1Ejue0IxcqV$P<KJRqTqV2eNKBQ+_jBVS2C3r@7I6C&ht98SXclY9* zG(Sq8Q}UUmd%lV>`0S3)%lIBLjaPwjKmQD#VYTP4^-%KuJB-P(C-A+-NsObHUK>ro z`;gezv%U0r3!ftZw!gYO`s;^BqW3<0qi*=q-+v!1+xgS<c@D>)h-c^;!;N3`yeAo7 zvhBsZx6XGyO~JcQX^YQF_{?Jp-uINVoqM)~=Rc6;^9J`^#A}NuEE#9dvojgby*PgS zeB(y=G0=_go<SN}zFSJbXEby0{;hdiM@IK-{az`ctj4f*_|B&p8*q<48S9Sa---CF zP%<et&q#QG`OV`m)jj<Vy!}+wX9yG5;9Wy_X5ijIWyW*Q56nGoJ|Fn-bp7uSedpxi z!e;<;@Qy2v&1V<>^MLGrdv>3E#dE)mHXUq^Hoa034mL&0cK@{My>|LgbT7W1d$=9( zZjMSDya%lNz-n8}`^xGUwJl#V!sJzHU-<j4CobH5;^8N7FK%n-eq6zQy>!2NNXODj z>TkvUmtj_~YMXn6xo2Kj`{(_BExK&%96k(tiTl8ZmJ4u?tOa{|-S<%3!}vVzZ?)v^ zaq0e2=U|LA!hQeL*KzE->jOWA0k#5806O}<4sFu*Fpi~hTXFZG(cMW9<@{id*^04w zpMtj~t^}m_DL#je_lNK3pTqruiGbsE+$W&yM5nu90CB~BmvXcL6Y(AScssr)AGhCe z`Q1t2evb~{yDUG{cHfWiU1r}K>!U+|c^0t4;RAnu3fBoYo9{mSPJ{L3xOQ&hcRTm} zD!wk}wegNu?~h(Qx;BO9-&hm*-zWIJgzt!+@GyQeAz5#$b%A_$I<L_~;NGi_YiRzK z25s=2(&>6cUW4KqjMvp4p8i`9PJH~=X!)~0$J&B;UF4)S{axE#ui{#a*I9hW@l%Hu z2Vv_g3!?e0!()A1_j7%Sd#5FJlk1>n{5HwFZ8zs&F8ZWP#;uou4&MjruJL${hHDdE zpA@YhfB)mRbL%0dtR0AJO<Z&6dM9}nzv8)HM5|u7tpb+s`FZXdfn(WghIW5OJv;n3 qKNH#r+~Ys79p~tYIPZK8=huli?{QCk+57E~bvl7Ff_H+b(fxm4sn%!! literal 0 HcmV?d00001 diff --git a/doc/stats.adoc b/doc/stats.adoc index e1665a8535b..96cdee22d36 100644 --- a/doc/stats.adoc +++ b/doc/stats.adoc @@ -4,15 +4,15 @@ Etherpad keeps track of the goings-on inside the edit machinery. If you'd like t We currently measure: - - totalUsers (counter) - - connects (meter) - - disconnects (meter) - - pendingEdits (counter) - - edits (timer) - - failedChangesets (meter) - - httpRequests (timer) - - http500 (meter) - - memoryUsage (gauge) +- totalUsers (counter) +- connects (meter) +- disconnects (meter) +- pendingEdits (counter) +- edits (timer) +- failedChangesets (meter) +- httpRequests (timer) +- http500 (meter) +- memoryUsage (gauge) Under the hood, we are happy to rely on https://github.com/felixge/node-measured[measured] for all our metrics needs. diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml deleted file mode 100644 index d7dc806ae3c..00000000000 --- a/docker-compose-prod.yml +++ /dev/null @@ -1,64 +0,0 @@ -version: "3.8" - -# Add this file to extend the docker-compose setup, e.g.: -# docker-compose -f docker-compose-prod.yml -f docker-compose-prod.override.yml --env-file .env.prod build --no-cache -# docker-compose -f docker-compose-prod.yml -f docker-compose-prod.override.yml --env-file .env.prod up -d --build --force-recreate - -services: - app_prod: - build: - context: . - args: - ETHERPAD_PLUGINS: >- - kitsteam/ep_comments_page - ep_image_upload - kitsteam/ep_push2delete - ep_embedded_hyperlinks2 - ep_headings2 - ep_align - ep_font_color - kitsteam/ep_delete_empty_pads - kitsteam/ep_delete_after_delay - ep_helmet - ep_font_size - ep_disable_imports - target: production - depends_on: - - postgres_prod - environment: - DB_CHARSET: ${DOCKER_COMPOSE_APP_PROD_ENV_DB_CHARSET:-utf8mb4} - DB_HOST: postgres_prod - DB_NAME: ${DOCKER_COMPOSE_POSTGRES_PROD_ENV_POSTGRES_DATABASE:?} - DB_PASS: ${DOCKER_COMPOSE_POSTGRES_PROD_ENV_POSTGRES_PASSWORD:?} - DB_PORT: ${DOCKER_COMPOSE_POSTGRES_PROD_ENV_POSTGRES_PORT:-5432} - DB_TYPE: "postgres" - DB_USER: ${DOCKER_COMPOSE_POSTGRES_PROD_ENV_POSTGRES_USER:?} - # For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad - DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_PROD_ENV_DEFAULT_PAD_TEXT:- } - DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_PROD_ENV_DISABLE_IP_LOGGING:-true} - SOFFICE: ${DOCKER_COMPOSE_APP_PROD_ENV_SOFFICE:-null} - TRUST_PROXY: ${DOCKER_COMPOSE_APP_PROD_ENV_TRUST_PROXY:-true} - restart: always - ports: - - "${DOCKER_COMPOSE_APP_PROD_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_PROD_PORT_TARGET:-9001}" - - postgres_prod: - image: postgres:12-alpine - # Pass config parameters to the mysql server. - # Find more information below when you need to generate the ssl-relevant file your self - environment: - POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_PROD_ENV_POSTGRES_DATABASE:?} - POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_PROD_ENV_POSTGRES_PASSWORD:?} - POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_PROD_ENV_POSTGRES_PORT:-5432} - POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_PROD_ENV_POSTGRES_USER:?} - PGDATA: /var/lib/postgresql/data/pgdata - restart: always - # Exposing the port is not needed unless you want to access this database instance from the host. - # Be careful when other postgres docker container are running on the same port - # ports: - # - "5432:5432" - volumes: - - postgres_data:/var/lib/postgresql/data/pgdata - -volumes: - postgres_data: \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 00000000000..ba500a64687 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,74 @@ +version: "3.8" + +# Add this file to extend the docker-compose setup, e.g.: +# docker-compose build --no-cache +# docker-compose up -d --build --force-recreate + +services: + app: + build: + context: . + args: + ETHERPAD_PLUGINS: >- + ep_image_upload + ep_headings2 + ep_align + ep_font_color + ep_embedded_hyperlinks2 + ep_helmet + ep_font_size + ep_disable_imports + ETHERPAD_GITHUB_PLUGINS: >- + kitsteam/ep_push2delete + kitsteam/ep_delete_empty_pads + kitsteam/ep_comments_page#merge-upstream-1.0.36 + kitsteam/ep_delete_after_delay + target: development + tty: true + stdin_open: true + volumes: + # no volume mapping of node_modules as otherwise the build-time installed plugins will be overwritten with the mount + # the same applies to package.json and pnpm-lock.yaml in root dir as these would also get overwritten and build time installed plugins will be removed + - ./src:/opt/etherpad-lite/src + depends_on: + - postgres + environment: + # change from development to production if needed + NODE_ENV: development + ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_DEV_ADMIN_PASSWORD} + DB_CHARSET: ${DOCKER_COMPOSE_APP_DEV_ENV_DB_CHARSET:-utf8mb4} + DB_HOST: postgres + DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?} + DB_PASS: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?} + DB_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432} + DB_TYPE: "postgres" + DB_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?} + # For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad + DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEV_ENV_DEFAULT_PAD_TEXT:- } + DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DEV_ENV_DISABLE_IP_LOGGING:-true} + SOFFICE: ${DOCKER_COMPOSE_APP_DEV_ENV_SOFFICE:-null} + TRUST_PROXY: ${DOCKER_COMPOSE_APP_DEV_ENV_TRUST_PROXY:-true} + restart: always + ports: + - "${DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_DEV_PORT_TARGET:-9001}" + + postgres: + image: postgres:15-alpine + # Pass config parameters to the mysql server. + # Find more information below when you need to generate the ssl-relevant file your self + environment: + POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?} + POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?} + POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432} + POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?} + PGDATA: /var/lib/postgresql/data/pgdata + restart: always + # Exposing the port is not needed unless you want to access this database instance from the host. + # Be careful when other postgres docker container are running on the same port + # ports: + # - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data/pgdata + +volumes: + postgres_data: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 3c4042cad06..24a726164b0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,75 +1,50 @@ -version: "3.8" - -# Add this file to extend the docker-compose setup, e.g.: -# docker-compose build --no-cache -# docker-compose up -d --build --force-recreate - services: app: - build: - context: . - args: - ETHERPAD_PLUGINS: >- - kitsteam/ep_push2delete - ep_image_upload - ep_helmet - kitsteam/ep_comments_page - ep_embedded_hyperlinks2 - ep_headings2 - ep_align - ep_font_color - kitsteam/ep_delete_empty_pads - kitsteam/ep_delete_after_delay - ep_font_size - ep_disable_imports - # change from development to production if needed - target: development + user: "0:0" + image: etherpad/etherpad:latest tty: true stdin_open: true volumes: - # no volume mapping of node_modules as otherwise the build-time installed plugins will be overwritten with the mount - # the same applies to package.json and pnpm-lock.yaml in root dir as these would also get overwritten and build time installed plugins will be removed - - ./src:/opt/etherpad-lite/src - - ./bin:/opt/etherpad-lite/bin + - plugins:/opt/etherpad-lite/src/plugin_packages + - etherpad-var:/opt/etherpad-lite/var depends_on: - postgres environment: - # change from development to production if needed - NODE_ENV: development - ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_DEV_ENV_ADMIN_PASSWORD} - DB_CHARSET: ${DOCKER_COMPOSE_APP_DEV_ENV_DB_CHARSET:-utf8mb4} + NODE_ENV: production + ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_ADMIN_PASSWORD:-admin} + DB_CHARSET: ${DOCKER_COMPOSE_APP_DB_CHARSET:-utf8mb4} DB_HOST: postgres - DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?} - DB_PASS: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?} - DB_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432} + DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad} + DB_PASS: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin} + DB_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432} DB_TYPE: "postgres" - DB_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?} + DB_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin} # For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad - DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEV_ENV_DEFAULT_PAD_TEXT:- } - DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DEV_ENV_DISABLE_IP_LOGGING:-true} - SOFFICE: ${DOCKER_COMPOSE_APP_DEV_ENV_SOFFICE:-null} - TRUST_PROXY: ${DOCKER_COMPOSE_APP_DEV_ENV_TRUST_PROXY:-true} + DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEFAULT_PAD_TEXT:- } + DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DISABLE_IP_LOGGING:-false} + SOFFICE: ${DOCKER_COMPOSE_APP_SOFFICE:-null} + TRUST_PROXY: ${DOCKER_COMPOSE_APP_TRUST_PROXY:-true} restart: always ports: - - "${DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_DEV_PORT_TARGET:-9001}" + - "${DOCKER_COMPOSE_APP_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_PORT_TARGET:-9001}" postgres: image: postgres:15-alpine - # Pass config parameters to the mysql server. - # Find more information below when you need to generate the ssl-relevant file your self environment: - POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?} - POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?} - POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432} - POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?} + POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad} + POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin} + POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432} + POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin} PGDATA: /var/lib/postgresql/data/pgdata restart: always # Exposing the port is not needed unless you want to access this database instance from the host. # Be careful when other postgres docker container are running on the same port # ports: - # - "5432:5432" + # - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data/pgdata volumes: - postgres_data: \ No newline at end of file + postgres_data: + plugins: + etherpad-var: diff --git a/make_docs.js b/make_docs.js deleted file mode 100644 index 657afa9a2f8..00000000000 --- a/make_docs.js +++ /dev/null @@ -1,58 +0,0 @@ -import {exec} from 'child_process' -import fs from 'fs' -import path from 'path' - -import pjson from './src/package.json' assert {type: "json"} - -const VERSION=pjson.version -console.log(`Building docs for version ${VERSION}`) - -const createDirIfNotExists = (dir) => { - if (!fs.existsSync(dir)){ - fs.mkdirSync(dir) - } -} - - -function copyFolderSync(from, to) { - if(fs.existsSync(to)){ - const stat = fs.lstatSync(to) - if (stat.isDirectory()){ - fs.rmSync(to, { recursive: true }) - } - else{ - fs.rmSync(to) - } - } - fs.mkdirSync(to); - fs.readdirSync(from).forEach(element => { - if (fs.lstatSync(path.join(from, element)).isFile()) { - fs.copyFileSync(path.join(from, element), path.join(to, element)) - } else { - copyFolderSync(path.join(from, element), path.join(to, element)) - } - }); -} - -exec('asciidoctor -v', (err,stdout)=>{ - if (err){ - console.log('Please install asciidoctor') - console.log('https://asciidoctor.org/docs/install-toolchain/') - process.exit(1) - } -}); - - -createDirIfNotExists('./out') -createDirIfNotExists('./out/doc') -createDirIfNotExists('./out/doc/api') - - - -exec(`asciidoctor -D out/doc doc/index.adoc */**.adoc -a VERSION=${VERSION}`) -exec(`asciidoctor -D out/doc/api ./doc/api/*.adoc -a VERSION=${VERSION}`) - -copyFolderSync('./doc/easysync', './out/doc/easysync') -copyFolderSync('./doc/assets', './out/doc/assets') -copyFolderSync('./doc/easysync', './out/doc/easysync') -copyFolderSync('./doc/images', './out/doc/images') diff --git a/package.json b/package.json index 022eefbfb67..149d3e60b57 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "scripts": { "lint": "pnpm --filter ep_etherpad-lite run lint", "test": "pnpm --filter ep_etherpad-lite run test", + "test-utils": "pnpm --filter ep_etherpad-lite run test-utils", "test-container": "pnpm --filter ep_etherpad-lite run test-container", "dev": "pnpm --filter ep_etherpad-lite run dev", "prod": "pnpm --filter ep_etherpad-lite run prod", @@ -24,13 +25,20 @@ "test-ui:ui": "pnpm --filter ep_etherpad-lite run test-ui:ui", "test-admin": "pnpm --filter ep_etherpad-lite run test-admin", "test-admin:ui": "pnpm --filter ep_etherpad-lite run test-admin:ui", - "install-plugins": "pnpm --filter bin run install-plugins" + "plugins": "pnpm --filter bin run plugins", + "install-plugins": "pnpm --filter bin run plugins i", + "remove-plugins": "pnpm --filter bin run remove-plugins", + "list-plugins": "pnpm --filter bin run list-plugins", + "build:etherpad": "pnpm --filter admin run build-copy && pnpm --filter ui run build-copy", + "build:ui": "pnpm --filter ui run build-copy && pnpm --filter admin run build-copy" }, "dependencies": { "ep_etherpad-lite": "workspace:./src" }, "devDependencies": { - "admin": "workspace:./admin" + "admin": "workspace:./admin", + "docs": "workspace:./doc", + "ui": "workspace:./ui" }, "engines": { "node": ">=18.18.2", @@ -41,6 +49,6 @@ "type": "git", "url": "https://github.com/ether/etherpad-lite.git" }, - "version": "2.0.1", + "version": "2.2.2", "license": "Apache-2.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9b1bede2b3..b7d1f142be1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: false @@ -15,87 +15,97 @@ importers: admin: specifier: workspace:./admin version: link:admin + docs: + specifier: workspace:./doc + version: link:doc + ui: + specifier: workspace:./ui + version: link:ui admin: + dependencies: + '@radix-ui/react-switch': + specifier: ^1.1.0 + version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@radix-ui/react-dialog': - specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-toast': - specifier: ^1.1.5 - version: 1.1.5(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) + specifier: ^1.2.1 + version: 1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/react': - specifier: ^18.2.56 - version: 18.2.65 + specifier: ^18.3.4 + version: 18.3.4 '@types/react-dom': - specifier: ^18.2.19 - version: 18.2.21 + specifier: ^18.2.25 + version: 18.3.0 '@typescript-eslint/eslint-plugin': - specifier: ^7.0.2 - version: 7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.0.2 - version: 7.2.0(eslint@8.57.0)(typescript@5.4.2) + specifier: ^8.2.0 + version: 8.2.0(eslint@9.9.0)(typescript@5.5.4) '@vitejs/plugin-react-swc': specifier: ^3.5.0 - version: 3.6.0(vite@5.1.6) + version: 3.7.0(vite@5.4.2(@types/node@22.4.2)) eslint: - specifier: ^8.56.0 - version: 8.57.0 + specifier: ^9.9.0 + version: 9.9.0 eslint-plugin-react-hooks: specifier: ^4.6.0 - version: 4.6.0(eslint@8.57.0) + version: 4.6.2(eslint@9.9.0) eslint-plugin-react-refresh: - specifier: ^0.4.5 - version: 0.4.6(eslint@8.57.0) + specifier: ^0.4.10 + version: 0.4.10(eslint@9.9.0) i18next: - specifier: ^23.10.1 - version: 23.10.1 + specifier: ^23.14.0 + version: 23.14.0 i18next-browser-languagedetector: - specifier: ^7.2.0 - version: 7.2.0 + specifier: ^8.0.0 + version: 8.0.0 lucide-react: - specifier: ^0.356.0 - version: 0.356.0(react@18.2.0) + specifier: ^0.429.0 + version: 0.429.0(react@18.3.1) react: specifier: ^18.2.0 - version: 18.2.0 + version: 18.3.1 react-dom: specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) + version: 18.3.1(react@18.3.1) react-hook-form: - specifier: ^7.51.0 - version: 7.51.0(react@18.2.0) + specifier: ^7.52.2 + version: 7.52.2(react@18.3.1) react-i18next: - specifier: ^14.1.0 - version: 14.1.0(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0) + specifier: ^15.0.1 + version: 15.0.1(i18next@23.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-router-dom: - specifier: ^6.22.3 - version: 6.22.3(react-dom@18.2.0)(react@18.2.0) + specifier: ^6.26.1 + version: 6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) socket.io-client: - specifier: ^4.7.4 - version: 4.7.4 + specifier: ^4.7.5 + version: 4.7.5 typescript: - specifier: ^5.2.2 - version: 5.4.2 + specifier: ^5.5.4 + version: 5.5.4 vite: - specifier: ^5.1.4 - version: 5.1.6 + specifier: ^5.4.2 + version: 5.4.2(@types/node@22.4.2) vite-plugin-static-copy: - specifier: ^1.0.1 - version: 1.0.1(vite@5.1.6) + specifier: ^1.0.6 + version: 1.0.6(vite@5.4.2(@types/node@22.4.2)) vite-plugin-svgr: specifier: ^4.2.0 - version: 4.2.0(typescript@5.4.2)(vite@5.1.6) + version: 4.2.0(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.4.2)) zustand: - specifier: ^4.5.2 - version: 4.5.2(@types/react@18.2.65)(react@18.2.0) + specifier: ^4.5.5 + version: 4.5.5(@types/react@18.3.4)(react@18.3.1) bin: dependencies: axios: - specifier: ^1.6.8 - version: 1.6.8 + specifier: ^1.7.5 + version: 1.7.5 ep_etherpad-lite: specifier: workspace:../src version: link:../src @@ -103,60 +113,63 @@ importers: specifier: ^6.9.1 version: 6.9.1 semver: - specifier: ^7.6.0 - version: 7.6.0 + specifier: ^7.6.3 + version: 7.6.3 tsx: - specifier: ^4.7.1 - version: 4.7.1 + specifier: ^4.17.0 + version: 4.17.0 ueberdb2: - specifier: ^4.2.63 - version: 4.2.63 + specifier: ^4.2.93 + version: 4.2.93 devDependencies: '@types/node': - specifier: ^20.11.27 - version: 20.11.27 + specifier: ^22.4.2 + version: 22.4.2 '@types/semver': specifier: ^7.5.8 version: 7.5.8 typescript: - specifier: ^5.4.2 - version: 5.4.2 + specifier: ^5.5.4 + version: 5.5.4 + + doc: + devDependencies: + vitepress: + specifier: ^1.3.3 + version: 1.3.3(@algolia/client-search@4.23.3)(@types/node@22.4.2)(@types/react@18.3.4)(axios@1.7.5)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) src: dependencies: + '@etherpad/express-session': + specifier: ^1.18.4 + version: 1.18.4 async: - specifier: ^3.2.5 - version: 3.2.5 + specifier: ^3.2.6 + version: 3.2.6 axios: - specifier: ^1.6.8 - version: 1.6.8 - clean-css: - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^1.7.5 + version: 1.7.5 cookie-parser: specifier: ^1.4.6 version: 1.4.6 + cross-env: + specifier: ^7.0.3 + version: 7.0.3 cross-spawn: specifier: ^7.0.3 version: 7.0.3 ejs: - specifier: ^3.1.9 - version: 3.1.9 - etherpad-require-kernel: - specifier: ^1.0.16 - version: 1.0.16 - etherpad-yajsml: - specifier: 0.0.12 - version: 0.0.12 + specifier: ^3.1.10 + version: 3.1.10 + esbuild: + specifier: ^0.23.1 + version: 0.23.1 express: - specifier: 4.18.3 - version: 4.18.3 + specifier: 4.19.2 + version: 4.19.2 express-rate-limit: - specifier: ^7.2.0 - version: 7.2.0(express@4.18.3) - express-session: - specifier: npm:@etherpad/express-session@^1.18.2 - version: /@etherpad/express-session@1.18.2 + specifier: ^7.4.0 + version: 7.4.0(express@4.19.2) fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -169,33 +182,45 @@ importers: http-errors: specifier: ^2.0.0 version: 2.0.0 + jose: + specifier: ^5.7.0 + version: 5.7.0 js-cookie: specifier: ^3.0.5 version: 3.0.5 jsdom: - specifier: ^24.0.0 - version: 24.0.0 + specifier: ^24.1.1 + version: 24.1.1 jsonminify: specifier: 0.4.2 version: 0.4.2 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 languages4translatewiki: specifier: 0.1.3 version: 0.1.3 - live-plugin-manager-pnpm: - specifier: ^0.18.1 - version: 0.18.1 + live-plugin-manager: + specifier: ^1.0.0 + version: 1.0.0 lodash.clonedeep: specifier: 4.5.0 version: 4.5.0 log4js: specifier: ^6.9.1 version: 6.9.1 + lru-cache: + specifier: ^11.0.0 + version: 11.0.0 measured-core: specifier: ^2.0.0 version: 2.0.0 mime-types: specifier: ^2.1.35 version: 2.1.35 + oidc-provider: + specifier: ^8.5.1 + version: 8.5.1 openapi-backend: specifier: ^5.10.6 version: 5.10.6 @@ -203,8 +228,8 @@ importers: specifier: ^2.0.7 version: 2.0.7 rate-limiter-flexible: - specifier: ^5.0.0 - version: 5.0.0 + specifier: ^5.0.3 + version: 5.0.3 rehype: specifier: ^13.0.1 version: 13.0.1 @@ -218,8 +243,8 @@ importers: specifier: 1.0.0 version: 1.0.0 semver: - specifier: ^7.6.0 - version: 7.6.0 + specifier: ^7.6.3 + version: 7.6.3 socket.io: specifier: ^4.7.5 version: 4.7.5 @@ -227,54 +252,66 @@ importers: specifier: ^4.7.5 version: 4.7.5 superagent: - specifier: ^8.1.2 - version: 8.1.2 - terser: - specifier: ^5.29.2 - version: 5.29.2 - threads: - specifier: ^1.7.0 - version: 1.7.0 + specifier: 10.0.2 + version: 10.0.2 tinycon: specifier: 0.6.8 version: 0.6.8 tsx: - specifier: ^4.7.1 - version: 4.7.1 + specifier: 4.17.0 + version: 4.17.0 ueberdb2: - specifier: ^4.2.63 - version: 4.2.63 + specifier: ^4.2.93 + version: 4.2.93 underscore: - specifier: 1.13.6 - version: 1.13.6 + specifier: 1.13.7 + version: 1.13.7 unorm: specifier: 1.6.0 version: 1.6.0 wtfnode: - specifier: ^0.9.1 - version: 0.9.1 + specifier: ^0.9.3 + version: 0.9.3 devDependencies: '@playwright/test': - specifier: ^1.42.1 - version: 1.42.1 + specifier: ^1.46.1 + version: 1.46.1 '@types/async': specifier: ^3.2.24 version: 3.2.24 '@types/express': specifier: ^4.17.21 version: 4.17.21 + '@types/formidable': + specifier: ^3.4.5 + version: 3.4.5 '@types/http-errors': specifier: ^2.0.4 version: 2.0.4 + '@types/jquery': + specifier: ^3.5.30 + version: 3.5.30 + '@types/js-cookie': + specifier: ^3.0.6 + version: 3.0.6 '@types/jsdom': - specifier: ^21.1.6 - version: 21.1.6 + specifier: ^21.1.7 + version: 21.1.7 + '@types/jsonwebtoken': + specifier: ^9.0.6 + version: 9.0.6 + '@types/mime-types': + specifier: ^2.1.4 + version: 2.1.4 '@types/mocha': - specifier: ^10.0.6 - version: 10.0.6 + specifier: ^10.0.7 + version: 10.0.7 '@types/node': - specifier: ^20.11.28 - version: 20.11.28 + specifier: ^22.4.2 + version: 22.4.2 + '@types/oidc-provider': + specifier: ^8.5.2 + version: 8.5.2 '@types/semver': specifier: ^7.5.8 version: 7.5.8 @@ -287,18 +324,21 @@ importers: '@types/underscore': specifier: ^1.11.15 version: 1.11.15 + chokidar: + specifier: ^3.6.0 + version: 3.6.0 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.9.0 + version: 9.9.0 eslint-config-etherpad: - specifier: ^3.0.22 - version: 3.0.22(eslint@8.57.0)(typescript@5.4.2) + specifier: ^4.0.4 + version: 4.0.4(eslint@9.9.0)(typescript@5.5.4) etherpad-cli-client: specifier: ^3.0.2 version: 3.0.2 mocha: - specifier: ^10.3.0 - version: 10.3.0 + specifier: ^10.7.3 + version: 10.7.3 mocha-froth: specifier: ^0.2.10 version: 0.2.10 @@ -309,953 +349,976 @@ importers: specifier: ^0.4.2 version: 0.4.2 set-cookie-parser: - specifier: ^2.6.0 - version: 2.6.0 + specifier: ^2.7.0 + version: 2.7.0 sinon: - specifier: ^17.0.1 - version: 17.0.1 + specifier: ^18.0.0 + version: 18.0.0 split-grid: specifier: ^1.0.11 version: 1.0.11 supertest: - specifier: ^6.3.4 - version: 6.3.4 + specifier: ^7.0.0 + version: 7.0.0 + typescript: + specifier: ^5.5.4 + version: 5.5.4 + vitest: + specifier: ^2.0.5 + version: 2.0.5(@types/node@22.4.2)(jsdom@24.1.1) + + ui: + devDependencies: + ep_etherpad-lite: + specifier: workspace:../src + version: link:../src typescript: + specifier: ^5.5.4 + version: 5.5.4 + vite: specifier: ^5.4.2 - version: 5.4.2 + version: 5.4.2(@types/node@22.4.2) packages: - /@aashutoshrathi/word-wrap@1.2.6: - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} - dev: true + '@algolia/autocomplete-core@1.9.3': + resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==} + + '@algolia/autocomplete-plugin-algolia-insights@1.9.3': + resolution: {integrity: sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.9.3': + resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.9.3': + resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/cache-browser-local-storage@4.23.3': + resolution: {integrity: sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==} + + '@algolia/cache-common@4.23.3': + resolution: {integrity: sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==} + + '@algolia/cache-in-memory@4.23.3': + resolution: {integrity: sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==} + + '@algolia/client-account@4.23.3': + resolution: {integrity: sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==} + + '@algolia/client-analytics@4.23.3': + resolution: {integrity: sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==} + + '@algolia/client-common@4.23.3': + resolution: {integrity: sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==} - /@ampproject/remapping@2.3.0: + '@algolia/client-personalization@4.23.3': + resolution: {integrity: sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==} + + '@algolia/client-search@4.23.3': + resolution: {integrity: sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==} + + '@algolia/logger-common@4.23.3': + resolution: {integrity: sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==} + + '@algolia/logger-console@4.23.3': + resolution: {integrity: sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==} + + '@algolia/recommend@4.23.3': + resolution: {integrity: sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==} + + '@algolia/requester-browser-xhr@4.23.3': + resolution: {integrity: sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==} + + '@algolia/requester-common@4.23.3': + resolution: {integrity: sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==} + + '@algolia/requester-node-http@4.23.3': + resolution: {integrity: sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==} + + '@algolia/transporter@4.23.3': + resolution: {integrity: sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==} + + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - dev: true - /@apidevtools/json-schema-ref-parser@11.1.0: - resolution: {integrity: sha512-g/VW9ZQEFJAOwAyUb8JFf7MLiLy2uEB4rU270rGzDwICxnxMlPy0O11KVePSgS36K1NI29gSlK84n5INGhd4Ag==} + '@apidevtools/json-schema-ref-parser@11.6.4': + resolution: {integrity: sha512-9K6xOqeevacvweLGik6LnZCb1fBtCOSIWQs8d096XGeqoLKC33UVMGz9+77Gw44KvbH4pKcQPWo4ZpxkXYj05w==} engines: {node: '>= 16'} - dependencies: - '@jsdevtools/ono': 7.1.3 - '@types/json-schema': 7.0.15 - '@types/lodash.clonedeep': 4.5.9 - js-yaml: 4.1.0 - lodash.clonedeep: 4.5.0 - dev: false - /@babel/code-frame@7.23.5: - resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.23.4 - chalk: 2.4.2 - dev: true - /@babel/compat-data@7.23.5: - resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + '@babel/compat-data@7.24.7': + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} engines: {node: '>=6.9.0'} - dev: true - /@babel/core@7.24.0: - resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==} + '@babel/core@7.24.7': + resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) - '@babel/helpers': 7.24.0 - '@babel/parser': 7.24.0 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.0 - '@babel/types': 7.24.0 - convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@8.1.1) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/generator@7.23.6: - resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + '@babel/generator@7.24.7': + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.22 - jsesc: 2.5.2 - dev: true - /@babel/helper-compilation-targets@7.23.6: - resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + '@babel/helper-compilation-targets@7.24.7': + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.23.5 - '@babel/helper-validator-option': 7.23.5 - browserslist: 4.23.0 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: true - /@babel/helper-environment-visitor@7.22.20: - resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-function-name@7.23.0: - resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.24.0 - '@babel/types': 7.24.0 - dev: true - /@babel/helper-hoist-variables@7.22.5: - resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - /@babel/helper-module-imports@7.22.15: - resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + '@babel/helper-module-transforms@7.24.7': + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - dev: true - /@babel/helper-simple-access@7.22.5: - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - /@babel/helper-split-export-declaration@7.22.6: - resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - /@babel/helper-string-parser@7.23.4: - resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + '@babel/helper-string-parser@7.24.7': + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-validator-identifier@7.22.20: - resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-validator-option@7.23.5: - resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + '@babel/helper-validator-option@7.24.7': + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helpers@7.24.0: - resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==} + '@babel/helpers@7.24.7': + resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.0 - '@babel/types': 7.24.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/highlight@7.23.4: - resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - /@babel/parser@7.24.0: - resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} + '@babel/parser@7.24.7': + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} engines: {node: '>=6.0.0'} hasBin: true - dependencies: - '@babel/types': 7.24.0 - dev: true - /@babel/runtime@7.24.0: - resolution: {integrity: sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==} + '@babel/runtime@7.24.7': + resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - dev: true - /@babel/template@7.24.0: - resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} + '@babel/runtime@7.24.8': + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/parser': 7.24.0 - '@babel/types': 7.24.0 - dev: true - /@babel/traverse@7.24.0: - resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==} + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.0 - '@babel/types': 7.24.0 - debug: 4.3.4(supports-color@8.1.1) - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/types@7.24.0: - resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + '@babel/traverse@7.24.7': + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.23.4 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - dev: true - /@esbuild/aix-ppc64@0.19.12: - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + '@babel/types@7.24.7': + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} + + '@docsearch/css@3.6.1': + resolution: {integrity: sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==} + + '@docsearch/js@3.6.1': + resolution: {integrity: sha512-erI3RRZurDr1xES5hvYJ3Imp7jtrXj6f1xYIzDzxiS7nNBufYWPbJwrmMqWC5g9y165PmxEmN9pklGCdLi0Iqg==} + + '@docsearch/react@3.6.1': + resolution: {integrity: sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==} + peerDependencies: + '@types/react': '>= 16.8.0 < 19.0.0' + react: '>= 16.8.0 < 19.0.0' + react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - requiresBuild: true - optional: true - /@esbuild/android-arm64@0.19.12: - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + '@esbuild/aix-ppc64@0.23.0': + resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] - requiresBuild: true - optional: true - /@esbuild/android-arm@0.19.12: - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + '@esbuild/android-arm64@0.23.0': + resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] - requiresBuild: true - optional: true - /@esbuild/android-x64@0.19.12: - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + '@esbuild/android-arm@0.23.0': + resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] - requiresBuild: true - optional: true - /@esbuild/darwin-arm64@0.19.12: - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + '@esbuild/android-x64@0.23.0': + resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] - requiresBuild: true - optional: true - /@esbuild/darwin-x64@0.19.12: - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + '@esbuild/darwin-arm64@0.23.0': + resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] - requiresBuild: true - optional: true - /@esbuild/freebsd-arm64@0.19.12: - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + '@esbuild/darwin-x64@0.23.0': + resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - requiresBuild: true - optional: true - /@esbuild/freebsd-x64@0.19.12: - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + '@esbuild/freebsd-arm64@0.23.0': + resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] - requiresBuild: true - optional: true - /@esbuild/linux-arm64@0.19.12: - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + '@esbuild/freebsd-x64@0.23.0': + resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-arm@0.19.12: - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + '@esbuild/linux-arm64@0.23.0': + resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-ia32@0.19.12: - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + '@esbuild/linux-arm@0.23.0': + resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-loong64@0.19.12: - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + '@esbuild/linux-ia32@0.23.0': + resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-mips64el@0.19.12: - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + '@esbuild/linux-loong64@0.23.0': + resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-ppc64@0.19.12: - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + '@esbuild/linux-mips64el@0.23.0': + resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-riscv64@0.19.12: - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + '@esbuild/linux-ppc64@0.23.0': + resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-s390x@0.19.12: - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + '@esbuild/linux-riscv64@0.23.0': + resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-x64@0.19.12: - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + '@esbuild/linux-s390x@0.23.0': + resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/netbsd-x64@0.19.12: - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.23.0': + resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} + engines: {node: '>=18'} cpu: [x64] - os: [netbsd] - requiresBuild: true - optional: true + os: [linux] - /@esbuild/openbsd-x64@0.19.12: - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} cpu: [x64] - os: [openbsd] - requiresBuild: true - optional: true + os: [linux] - /@esbuild/sunos-x64@0.19.12: - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] - os: [sunos] - requiresBuild: true - optional: true + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.0': + resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.0': + resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.0': + resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.23.0': + resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] - /@esbuild/win32-arm64@0.19.12: - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] - requiresBuild: true - optional: true - /@esbuild/win32-ia32@0.19.12: - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + '@esbuild/win32-arm64@0.23.0': + resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] - requiresBuild: true - optional: true - /@esbuild/win32-x64@0.19.12: - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + '@esbuild/win32-ia32@0.23.0': + resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] - requiresBuild: true - optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + '@esbuild/win32-x64@0.23.0': + resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 - dev: true - /@eslint-community/regexpp@4.10.0: - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true - /@eslint/eslintrc@2.1.4: - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4(supports-color@8.1.1) - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.1 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true + '@eslint/config-array@0.17.1': + resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - /@eslint/js@8.57.0: - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - /@etherpad/express-session@1.18.2: - resolution: {integrity: sha512-5S+mdCxqSR5AX9jj1G3Qzxv/EX4jyWr9ikHzwtv+uV9fPu7EPFF6oNkhrUCZ19RJ5975KulPyrBB35hUK5x7LA==} - engines: {node: '>= 0.8.0'} - dependencies: - cookie: 0.4.2 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - on-headers: 1.0.2 - parseurl: 1.3.3 - safe-buffer: 5.2.1 - uid-safe: 2.1.5 - transitivePeerDependencies: - - supports-color - dev: false + '@eslint/js@9.9.0': + resolution: {integrity: sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - /@humanwhocodes/config-array@0.11.14: - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4(supports-color@8.1.1) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true + '@eslint/object-schema@2.1.4': + resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@etherpad/express-session@1.18.4': + resolution: {integrity: sha512-uiUtcfv0hyEA+Lur00V6yINaa/qe09HiFqmc+DzSXYChILFLgOV3G4p4XJkIRrUOGmqaJRiliB1BoQIiY3Tnjw==} + engines: {node: '>= 0.8.0'} - /@humanwhocodes/module-importer@1.0.1: + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - dev: true - /@humanwhocodes/object-schema@2.0.2: - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} - dev: true - - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.22 - dev: true + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + engines: {node: '>=18.18'} - /@jridgewell/gen-mapping@0.3.5: + '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.25 - /@jridgewell/resolve-uri@3.1.2: + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/set-array@1.2.1: + '@jridgewell/set-array@1.2.1': resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - /@jridgewell/source-map@0.3.5: - resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - dev: false - - /@jridgewell/sourcemap-codec@1.4.15: + '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - /@jridgewell/trace-mapping@0.3.22: - resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /@jridgewell/trace-mapping@0.3.25: + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - /@jsdevtools/ono@7.1.3: + '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} - dev: false - /@nodelib/fs.scandir@2.1.5: + '@koa/cors@5.0.0': + resolution: {integrity: sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==} + engines: {node: '>= 14.0.0'} + + '@koa/router@12.0.1': + resolution: {integrity: sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==} + engines: {node: '>= 12'} + + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - dev: true - /@nodelib/fs.stat@2.0.5: + '@nodelib/fs.stat@2.0.5': resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true - /@nodelib/fs.walk@1.2.8: + '@nodelib/fs.walk@1.2.8': resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - dev: true - /@playwright/test@1.42.1: - resolution: {integrity: sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==} - engines: {node: '>=16'} + '@playwright/test@1.46.1': + resolution: {integrity: sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==} + engines: {node: '>=18'} hasBin: true - dependencies: - playwright: 1.42.1 - dev: true - /@radix-ui/primitive@1.0.1: - resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} - dependencies: - '@babel/runtime': 7.24.0 - dev: true + '@radix-ui/primitive@1.1.0': + resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} - /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} + '@radix-ui/react-collection@1.1.0': + resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} + + '@radix-ui/react-compose-refs@1.1.0': + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@types/react': 18.2.65 - react: 18.2.0 - dev: true - /@radix-ui/react-context@1.0.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} + '@radix-ui/react-context@1.1.0': + resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@types/react': 18.2.65 - react: 18.2.0 - dev: true - /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} + '@radix-ui/react-dialog@1.1.1': + resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - aria-hidden: 1.2.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(@types/react@18.2.65)(react@18.2.0) - dev: true - - /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} + + '@radix-ui/react-dismissable-layer@1.1.0': + resolution: {integrity: sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + + '@radix-ui/react-focus-guards@1.1.0': + resolution: {integrity: sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@types/react': 18.2.65 - react: 18.2.0 - dev: true - /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} + '@radix-ui/react-focus-scope@1.1.0': + resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-id@1.0.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - react: 18.2.0 - dev: true - /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} + '@radix-ui/react-portal@1.1.1': + resolution: {integrity: sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + '@radix-ui/react-presence@1.1.0': + resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + + '@radix-ui/react-primitive@2.0.0': + resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@radix-ui/react-slot@1.0.2(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} + '@radix-ui/react-slot@1.1.0': + resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - react: 18.2.0 - dev: true - /@radix-ui/react-toast@1.1.5(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==} + '@radix-ui/react-switch@1.1.0': + resolution: {integrity: sha512-OBzy5WAj641k0AOSpKQtreDMe+isX0MQJ1IVyF03ucdF3DunOnROVrjWs8zsXUxC3zfZ6JL9HFVCUlMghz9dJw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-context': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + + '@radix-ui/react-toast@1.2.1': + resolution: {integrity: sha512-5trl7piMXcZiCq7MW6r8YYmu0bK5qDpTWz+FdEPdKyft2UixkspheYbjbrLXVN5NGKHFbOP7lm8eD0biiSqZqg==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@types/react': 18.2.65 - react: 18.2.0 - dev: true + '@types/react-dom': + optional: true - /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - react: 18.2.0 - dev: true - /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.65)(react@18.2.0) - '@types/react': 18.2.65 - react: 18.2.0 - dev: true - /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@types/react': 18.2.65 - react: 18.2.0 - dev: true - /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.1.0': + resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - dependencies: - '@babel/runtime': 7.24.0 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.21)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.65 - '@types/react-dom': 18.2.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@remix-run/router@1.15.3: - resolution: {integrity: sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==} + '@remix-run/router@1.19.1': + resolution: {integrity: sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==} engines: {node: '>=14.0.0'} - dev: true - /@rollup/pluginutils@5.1.0: + '@rollup/pluginutils@5.1.0': resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1263,1160 +1326,5207 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@types/estree': 1.0.5 - estree-walker: 2.0.2 - picomatch: 2.3.1 - dev: true - /@rollup/rollup-android-arm-eabi@4.13.0: - resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==} + '@rollup/rollup-android-arm-eabi@4.21.0': + resolution: {integrity: sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-android-arm64@4.13.0: - resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==} + '@rollup/rollup-android-arm64@4.21.0': + resolution: {integrity: sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-arm64@4.13.0: - resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==} + '@rollup/rollup-darwin-arm64@4.21.0': + resolution: {integrity: sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-x64@4.13.0: - resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==} + '@rollup/rollup-darwin-x64@4.21.0': + resolution: {integrity: sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.13.0: - resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.21.0': + resolution: {integrity: sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-gnu@4.13.0: - resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==} + '@rollup/rollup-linux-arm-musleabihf@4.21.0': + resolution: {integrity: sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.21.0': + resolution: {integrity: sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-musl@4.13.0: - resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==} + '@rollup/rollup-linux-arm64-musl@4.21.0': + resolution: {integrity: sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-riscv64-gnu@4.13.0: - resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==} + '@rollup/rollup-linux-powerpc64le-gnu@4.21.0': + resolution: {integrity: sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.21.0': + resolution: {integrity: sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-gnu@4.13.0: - resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} + '@rollup/rollup-linux-s390x-gnu@4.21.0': + resolution: {integrity: sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.21.0': + resolution: {integrity: sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-musl@4.13.0: - resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==} + '@rollup/rollup-linux-x64-musl@4.21.0': + resolution: {integrity: sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-arm64-msvc@4.13.0: - resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==} + '@rollup/rollup-win32-arm64-msvc@4.21.0': + resolution: {integrity: sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-ia32-msvc@4.13.0: - resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==} + '@rollup/rollup-win32-ia32-msvc@4.21.0': + resolution: {integrity: sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-x64-msvc@4.13.0: - resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==} + '@rollup/rollup-win32-x64-msvc@4.21.0': + resolution: {integrity: sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rushstack/eslint-patch@1.7.2: - resolution: {integrity: sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==} - dev: true + '@rushstack/eslint-patch@1.10.3': + resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==} + + '@shikijs/core@1.14.1': + resolution: {integrity: sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==} + + '@shikijs/transformers@1.14.1': + resolution: {integrity: sha512-JJqL8QBVCJh3L61jqqEXgFq1cTycwjcGj7aSmqOEsbxnETM9hRlaB74QuXvY/fVJNjbNt8nvWo0VwAXKvMSLRg==} + + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} - /@sinonjs/commons@2.0.0: + '@sinonjs/commons@2.0.0': resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} - dependencies: - type-detect: 4.0.8 - dev: true - /@sinonjs/commons@3.0.1: + '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - dependencies: - type-detect: 4.0.8 - dev: true - /@sinonjs/fake-timers@11.2.2: + '@sinonjs/fake-timers@11.2.2': resolution: {integrity: sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==} - dependencies: - '@sinonjs/commons': 3.0.1 - dev: true - /@sinonjs/samsam@8.0.0: + '@sinonjs/samsam@8.0.0': resolution: {integrity: sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==} - dependencies: - '@sinonjs/commons': 2.0.0 - lodash.get: 4.4.2 - type-detect: 4.0.8 - dev: true - /@sinonjs/text-encoding@0.7.2: + '@sinonjs/text-encoding@0.7.2': resolution: {integrity: sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==} - dev: true - /@socket.io/component-emitter@3.1.0: - resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - /@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.24.0): + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - dev: true - /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.24.0): + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - dev: true - /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.24.0): + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - dev: true - /@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.24.0): + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - dev: true - /@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.24.0): + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - dev: true - /@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.24.0): + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - dev: true - /@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.24.0): + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - dev: true - /@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.24.0): + '@svgr/babel-plugin-transform-svg-component@8.0.0': resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} engines: {node: '>=12'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - dev: true - /@svgr/babel-preset@8.1.0(@babel/core@7.24.0): + '@svgr/babel-preset@8.1.0': resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.24.0) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.24.0) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.24.0) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.24.0) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.24.0) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.24.0) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.24.0) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.24.0) - dev: true - - /@svgr/core@8.1.0(typescript@5.4.2): + + '@svgr/core@8.1.0': resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} engines: {node: '>=14'} - dependencies: - '@babel/core': 7.24.0 - '@svgr/babel-preset': 8.1.0(@babel/core@7.24.0) - camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.4.2) - snake-case: 3.0.4 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - /@svgr/hast-util-to-babel-ast@8.0.0: + '@svgr/hast-util-to-babel-ast@8.0.0': resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} engines: {node: '>=14'} - dependencies: - '@babel/types': 7.24.0 - entities: 4.5.0 - dev: true - /@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0): + '@svgr/plugin-jsx@8.1.0': resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} engines: {node: '>=14'} peerDependencies: '@svgr/core': '*' - dependencies: - '@babel/core': 7.24.0 - '@svgr/babel-preset': 8.1.0(@babel/core@7.24.0) - '@svgr/core': 8.1.0(typescript@5.4.2) - '@svgr/hast-util-to-babel-ast': 8.0.0 - svg-parser: 2.0.4 - transitivePeerDependencies: - - supports-color - dev: true - /@swc/core-darwin-arm64@1.4.7: - resolution: {integrity: sha512-IhfP2Mrrh9WcdlBJQbPNBhfdOhW/SC910SiuzvxaLgJmzq1tw6TVDNUz4Zf85TbK5uzgR0emtPc9hTGxynl57A==} + '@swc/core-darwin-arm64@1.5.28': + resolution: {integrity: sha512-sP6g63ybzIdOWNDbn51tyHN8EMt7Mb4RMeHQEsXB7wQfDvzhpWB+AbfK6Gs3Q8fwP/pmWIrWW9csKOc1K2Mmkg==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@swc/core-darwin-x64@1.4.7: - resolution: {integrity: sha512-MO01pnxJDS6st5IiqyTnAOz9kpAPP/O4lzEUH9E80XdXBzwptS5hNTM0egBlqueWDFrPM26RI81JLtyTU7kR8w==} + '@swc/core-darwin-x64@1.5.28': + resolution: {integrity: sha512-Bd/agp/g7QocQG5AuorOzSC78t8OzeN+pCN/QvJj1CvPhvppjJw6e1vAbOR8vO2vvGi2pvtf3polrYQStJtSiA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-arm-gnueabihf@1.4.7: - resolution: {integrity: sha512-+cDaXW6PZqGhXIq9C4xE+/QuyUsLkXf8d8uSXep+rZYDl4YHS9Fi7HpZQnqLX6al/iVhwe3VnxHMGw50gxcr/g==} + '@swc/core-linux-arm-gnueabihf@1.5.28': + resolution: {integrity: sha512-Wr3TwPGIveS9/OBWm0r9VAL8wkCR0zQn46J8K01uYCmVhUNK3Muxjs0vQBZaOrGu94mqbj9OXY+gB3W7aDvGdA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-arm64-gnu@1.4.7: - resolution: {integrity: sha512-RNnVHRKhEtA3pM34wgb3Vumf5M6/XlWzFdkHEMZIkOKyNSUhZiv8X3tsEK+n1rZQWIDkvlw4YyHtB8vK18WdCA==} + '@swc/core-linux-arm64-gnu@1.5.28': + resolution: {integrity: sha512-8G1ZwVTuLgTAVTMPD+M97eU6WeiRIlGHwKZ5fiJHPBcz1xqIC7jQcEh7XBkobkYoU5OILotls3gzjRt8CMNyDQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-arm64-musl@1.4.7: - resolution: {integrity: sha512-p7Xm4Pib02d1SFS9XXMoOcCTDIkFWMspspptPX00VcjAdZYnXWujWGuD2W+KN1gq5syHB1g3TsYs9LP2dGsKqw==} + '@swc/core-linux-arm64-musl@1.5.28': + resolution: {integrity: sha512-0Ajdzb5Fzvz+XUbN5ESeHAz9aHHSYiQcm+vmsDi0TtPHmsalfnqEPZmnK0zPALPJPLQP2dDo4hELeDg3/c3xgA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-x64-gnu@1.4.7: - resolution: {integrity: sha512-ViI5jy03cFYPETsye1J+oPbHE4v8oIDN34qebzvgHUlNKOXfc1ig0Zha5oQnKp3zj1rmjcSLIMqK++WR021G5A==} + '@swc/core-linux-x64-gnu@1.5.28': + resolution: {integrity: sha512-ueQ9VejnQUM2Pt+vT0IAKoF4vYBWUP6n1KHGdILpoGe3LuafQrqu7RoyQ15C7/AYii7hAeNhTFdf6gLbg8cjFg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-linux-x64-musl@1.4.7: - resolution: {integrity: sha512-Nf3Axcx/ILl7XE44eidNNPF39rg/KIeqg2545vrOXJG02iu7pEjZuu8wm6w+23BpP4COjZJymlg9LzPT1ZBD5Q==} + '@swc/core-linux-x64-musl@1.5.28': + resolution: {integrity: sha512-G5th8Mg0az8CbY4GQt9/m5hg2Y0kGIwvQBeVACuLQB6q2Y4txzdiTpjmFqUUhEvvl7Klyx1IHvNhfXs3zpt7PA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@swc/core-win32-arm64-msvc@1.4.7: - resolution: {integrity: sha512-MFkJEaC59AO2HpndmHhCkaj8NJus5etjMtBphOe9em7jmmfdQ7mLenKHbZ/CspHNl8yNPO9Qzpa/at2838x+RQ==} + '@swc/core-win32-arm64-msvc@1.5.28': + resolution: {integrity: sha512-JezwCGavZ7CkNXx4yInI4kpb71L0zxzxA9BFlmnsGKEEjVQcKc3hFpmIzfFVs+eotlBUwDNb0+Yo9m6Cb7lllA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@swc/core-win32-ia32-msvc@1.4.7: - resolution: {integrity: sha512-nwrfERocUei9sxqd6URrWcEC3KDcTBD+beMerB9idvuzy4rcm5k1O1ClUlZ9pJOZn+vMN1tqZjLze4hJMT9STQ==} + '@swc/core-win32-ia32-msvc@1.5.28': + resolution: {integrity: sha512-q8tW5J4RkOkl7vYShnWS//VAb2Ngolfm9WOMaF2GRJUr2Y/Xeb/+cNjdsNOqea2BzW049D5vdP7XPmir3/zUZw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@swc/core-win32-x64-msvc@1.4.7: - resolution: {integrity: sha512-d5T8Z/axAml8FTA+T9RS2mwJDNIbSSz5jcEiWaGuKVDIoSZib2HpMvnMydOGsIrmjfS1Z4ZhdAawivPhAZ3M8Q==} + '@swc/core-win32-x64-msvc@1.5.28': + resolution: {integrity: sha512-jap6EiB3wG1YE1hyhNr9KLPpH4PGm+5tVMfN0l7fgKtV0ikgpcEN/YF94tru+z5m2HovqYW009+Evq9dcVGmpg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@swc/core@1.4.7: - resolution: {integrity: sha512-I7a9sUxB+z+UCf6KudqrQH/RgLal/S+E+t4uBdbggycLyJe7WvBgPrQlcN5UpEuD9YC2PJ0CN6kgD6ARStg+pg==} + '@swc/core@1.5.28': + resolution: {integrity: sha512-muCdNIqOTURUgYeyyOLYE3ShL8SZO6dw6bhRm6dCvxWzCZOncPc5fB0kjcPXTML+9KJoHL7ks5xg+vsQK+v6ig==} engines: {node: '>=10'} - requiresBuild: true peerDependencies: - '@swc/helpers': ^0.5.0 + '@swc/helpers': '*' peerDependenciesMeta: '@swc/helpers': optional: true - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.5 - optionalDependencies: - '@swc/core-darwin-arm64': 1.4.7 - '@swc/core-darwin-x64': 1.4.7 - '@swc/core-linux-arm-gnueabihf': 1.4.7 - '@swc/core-linux-arm64-gnu': 1.4.7 - '@swc/core-linux-arm64-musl': 1.4.7 - '@swc/core-linux-x64-gnu': 1.4.7 - '@swc/core-linux-x64-musl': 1.4.7 - '@swc/core-win32-arm64-msvc': 1.4.7 - '@swc/core-win32-ia32-msvc': 1.4.7 - '@swc/core-win32-x64-msvc': 1.4.7 - dev: true - - /@swc/counter@0.1.3: + + '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - dev: true - /@swc/types@0.1.5: - resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} - dev: true + '@swc/types@0.1.8': + resolution: {integrity: sha512-RNFA3+7OJFNYY78x0FYwi1Ow+iF1eF5WvmfY1nXPOEH4R2p/D4Cr1vzje7dNAI2aLFqpv8Wyz4oKSWqIZArpQA==} - /@types/async@3.2.24: - resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} - dev: true + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} - /@types/body-parser@1.19.5: + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@types/accepts@1.3.7': + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + + '@types/async@3.2.24': + resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} + + '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/content-disposition@0.5.8': + resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} + + '@types/cookie@0.4.1': + resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + + '@types/cookies@0.9.0': + resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==} + + '@types/cors@2.8.17': + resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/express-serve-static-core@4.19.5': + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + + '@types/express@4.17.21': + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + + '@types/formidable@3.4.5': + resolution: {integrity: sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==} + + '@types/fs-extra@9.0.13': + resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/http-assert@1.5.5': + resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + + '@types/jquery@3.5.30': + resolution: {integrity: sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==} + + '@types/js-cookie@3.0.6': + resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + + '@types/jsdom@21.1.7': + resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/jsonwebtoken@9.0.6': + resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + + '@types/keygrip@1.0.6': + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + + '@types/koa-compose@3.2.8': + resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} + + '@types/koa@2.15.0': + resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/lockfile@1.0.4': + resolution: {integrity: sha512-Q8oFIHJHr+htLrTXN2FuZfg+WXVHQRwU/hC2GpUu+Q8e3FUM9EDkS2pE3R2AO1ZGu56f479ybdMCNF1DAu8cAQ==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + + '@types/mime-types@2.1.4': + resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/mocha@10.0.7': + resolution: {integrity: sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==} + + '@types/ms@0.7.34': + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + + '@types/node-fetch@2.6.11': + resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} + + '@types/node@22.4.2': + resolution: {integrity: sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==} + + '@types/oidc-provider@8.5.2': + resolution: {integrity: sha512-NiD3VG49+cRCAAe8+uZLM4onOcX8y9+cwaml8JG1qlgc98rWoCRgsnOB4Ypx+ysays5jiwzfUgT0nWyXPB/9uQ==} + + '@types/prop-types@15.7.12': + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + + '@types/react@18.3.4': + resolution: {integrity: sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + + '@types/sinon@17.0.3': + resolution: {integrity: sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==} + + '@types/sinonjs__fake-timers@8.1.5': + resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + + '@types/sizzle@2.3.8': + resolution: {integrity: sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==} + + '@types/superagent@8.1.7': + resolution: {integrity: sha512-NmIsd0Yj4DDhftfWvvAku482PZum4DBW7U51OvS8gvOkDDY0WT1jsVyDV3hK+vplrsYw8oDwi9QxOM7U68iwww==} + + '@types/supertest@6.0.2': + resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==} + + '@types/tar@6.1.13': + resolution: {integrity: sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@types/underscore@1.11.15': + resolution: {integrity: sha512-HP38xE+GuWGlbSRq9WrZkousaQ7dragtZCruBVMi0oX1migFZavZ3OROKHSkNp/9ouq82zrWtZpg18jFnVN96g==} + + '@types/unist@3.0.2': + resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} + + '@types/url-join@4.0.3': + resolution: {integrity: sha512-3l1qMm3wqO0iyC5gkADzT95UVW7C/XXcdvUcShOideKF0ddgVRErEQQJXBd2kvQm+aSgqhBGHGB38TgMeT57Ww==} + + '@types/web-bluetooth@0.0.20': + resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + + '@typescript-eslint/eslint-plugin@7.17.0': + resolution: {integrity: sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/eslint-plugin@8.2.0': + resolution: {integrity: sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@7.17.0': + resolution: {integrity: sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@8.2.0': + resolution: {integrity: sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@7.17.0': + resolution: {integrity: sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/scope-manager@8.2.0': + resolution: {integrity: sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@7.17.0': + resolution: {integrity: sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/type-utils@8.2.0': + resolution: {integrity: sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@7.17.0': + resolution: {integrity: sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/types@8.2.0': + resolution: {integrity: sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@7.17.0': + resolution: {integrity: sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@8.2.0': + resolution: {integrity: sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@7.17.0': + resolution: {integrity: sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/utils@8.2.0': + resolution: {integrity: sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + '@typescript-eslint/visitor-keys@7.17.0': + resolution: {integrity: sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/visitor-keys@8.2.0': + resolution: {integrity: sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@vitejs/plugin-react-swc@3.7.0': + resolution: {integrity: sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==} + peerDependencies: + vite: ^4 || ^5 + + '@vitejs/plugin-vue@5.1.2': + resolution: {integrity: sha512-nY9IwH12qeiJqumTCLJLE7IiNx7HZ39cbHaysEUd+Myvbz9KAqd2yq+U01Kab1R/H1BmiyM2ShTYlNH32Fzo3A==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + + '@vitest/expect@2.0.5': + resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} + + '@vitest/pretty-format@2.0.5': + resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} + + '@vitest/runner@2.0.5': + resolution: {integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==} + + '@vitest/snapshot@2.0.5': + resolution: {integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==} + + '@vitest/spy@2.0.5': + resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + + '@vitest/utils@2.0.5': + resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} + + '@vue/compiler-core@3.4.38': + resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==} + + '@vue/compiler-dom@3.4.38': + resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==} + + '@vue/compiler-sfc@3.4.38': + resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==} + + '@vue/compiler-ssr@3.4.38': + resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==} + + '@vue/devtools-api@7.3.8': + resolution: {integrity: sha512-NURFwmxz4WukFU54IHgyGI2KSejdgHG5JC4xTcWmTWEBIc8aelj9fBy4qsboObGHFp3JIdRxxANO9s2wZA/pVQ==} + + '@vue/devtools-kit@7.3.8': + resolution: {integrity: sha512-HYy3MQP1nZ6GbE4vrgJ/UB+MvZnhYmEwCa/UafrEpdpwa+jNCkz1ZdUrC5I7LpkH1ShREEV2/pZlAQdBj+ncLQ==} + + '@vue/devtools-shared@7.3.8': + resolution: {integrity: sha512-1NiJbn7Yp47nPDWhFZyEKpB2+5/+7JYv8IQnU0ccMrgslPR2dL7u1DIyI7mLqy4HN1ll36gQy0k8GqBYSFgZJw==} + + '@vue/reactivity@3.4.38': + resolution: {integrity: sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==} + + '@vue/runtime-core@3.4.38': + resolution: {integrity: sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==} + + '@vue/runtime-dom@3.4.38': + resolution: {integrity: sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==} + + '@vue/server-renderer@3.4.38': + resolution: {integrity: sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==} + peerDependencies: + vue: 3.4.38 + + '@vue/shared@3.4.38': + resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==} + + '@vueuse/core@11.0.1': + resolution: {integrity: sha512-YTrekI18WwEyP3h168Fir94G/HNC27wvXJI21Alm0sPOwvhihfkrvHIe+5PNJq+MpgWdRcsjvE/38JaoKrgZhQ==} + + '@vueuse/integrations@11.0.1': + resolution: {integrity: sha512-V/FQTS/aiV6RTFXOj8cXgqhtNJBvxvbHeLElOUR7N7F3Kr0btS+dkymLB54mFd0Or6uEGpgwwb41cs/q2/rdOg==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@11.0.1': + resolution: {integrity: sha512-dTFvuHFAjLYOiSd+t9Sk7xUiuL6jbfay/eX+g+jaipXXlwKur2VCqBCZX+jfu+2vROUGcUsdn3fJR9KkpadIOg==} + + '@vueuse/shared@11.0.1': + resolution: {integrity: sha512-eAPf5CQB3HR0S76HqrhjBqFYstZfiHWZq8xF9EQmobGBkrhPfErJEhr8aMNQMqd6MkENIx2pblIEfJGlHpClug==} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.12.0: + resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + algoliasearch@4.23.3: + resolution: {integrity: sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.4: + resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} + engines: {node: '>=10'} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios@1.7.5: + resolution: {integrity: sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + + bath-es5@3.0.3: + resolution: {integrity: sha512-PdCioDToH3t84lP40kUFCKWCOCH389Dl1kbC8FGoqOwamxsmqxxnJSXdkTOsPoNHXjem4+sJ+bbNoQm5zeCqxg==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + binary-search@1.3.6: + resolution: {integrity: sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==} + + birpc@0.2.17: + resolution: {integrity: sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==} + + body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + browserslist@4.23.1: + resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + + builtins@5.1.0: + resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cache-content-type@1.0.1: + resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} + engines: {node: '>= 6.0.0'} + + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001632: + resolution: {integrity: sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@5.1.1: + resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} + engines: {node: '>=12'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-parser@1.4.6: + resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==} + engines: {node: '>= 0.8.0'} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.4.1: + resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==} + engines: {node: '>= 0.6'} + + cookie@0.4.2: + resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + engines: {node: '>= 0.6'} + + cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + + cookies@0.9.1: + resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} + engines: {node: '>= 0.8'} + + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + cssstyle@4.0.1: + resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + engines: {node: '>=18'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + date-format@4.0.14: + resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} + engines: {node: '>=4.0'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-equal@1.0.1: + resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + dereference-json-schema@0.2.1: + resolution: {integrity: sha512-uzJsrg225owJyRQ8FNTPHIuBOdSzIZlHhss9u6W8mp7jJldHqGuLv9cULagP/E26QVJDnjtG8U7Dw139mM1ydA==} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.4.798: + resolution: {integrity: sha512-by9J2CiM9KPGj9qfp5U4FcPSbXJG7FNzqnYaY4WLzX+v2PHieVGmnsA4dxfpGE3QEC7JofpPZmn7Vn1B9NR2+Q==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + engine.io-client@6.5.4: + resolution: {integrity: sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.5.5: + resolution: {integrity: sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==} + engines: {node: '>=10.2.0'} + + enhanced-resolve@5.17.0: + resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.23.0: + resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-config-etherpad@4.0.4: + resolution: {integrity: sha512-y1riT+lmFwd+TZR9LzTlF4ntcTWRUpjqspdJ8kekLY9gcwyBsKTaW/Jj8mO4DyfDR72/3o4t6v7A8d8SqXybUQ==} + engines: {node: '>=12.17.0'} + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.6.1: + resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + + eslint-module-utils@2.8.1: + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-cypress@2.15.2: + resolution: {integrity: sha512-CtcFEQTDKyftpI22FVGpx8bkpKyYXBlNge6zSo0pl5/qJvBAnzaD76Vu2AsP16d6mTj478Ldn2mhgrWV+Xr0vQ==} + peerDependencies: + eslint: '>= 3.2.1' + + eslint-plugin-es-x@7.8.0: + resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + + eslint-plugin-eslint-comments@3.2.0: + resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} + engines: {node: '>=6.5.0'} + peerDependencies: + eslint: '>=4.19.1' + + eslint-plugin-import@2.29.1: + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-mocha@10.4.3: + resolution: {integrity: sha512-emc4TVjq5Ht0/upR+psftuz6IBG5q279p+1dSRDeHf+NS9aaerBi3lXKo1SEzwC29hFIW21gO89CEWSvRsi8IQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-n@16.6.2: + resolution: {integrity: sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prefer-arrow@1.2.3: + resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} + peerDependencies: + eslint: '>=2.0.0' + + eslint-plugin-promise@6.6.0: + resolution: {integrity: sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-react-hooks@4.6.2: + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react-refresh@0.4.10: + resolution: {integrity: sha512-I39s6G9We7ZxbCRxTTM5XX4KJV2cfWhFbHF4kTuL0ygdEVdQXtCNGqUQ43sBOCbTC/N6dEZXoQKFHr8gp1VHrQ==} + peerDependencies: + eslint: '>=7' + + eslint-plugin-you-dont-need-lodash-underscore@6.14.0: + resolution: {integrity: sha512-3zkkU/O1agczP7szJGHmisZJS/AknfVl6mb0Zqoc95dvFsdmfK+cbhrn+Ffy0UWB1pgDJwQr7kIO3rPstWs3Dw==} + engines: {node: '>=4.0'} + + eslint-scope@8.0.2: + resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-utils@3.0.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + + eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.9.0: + resolution: {integrity: sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.1.0: + resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eta@3.4.0: + resolution: {integrity: sha512-tCsc7WXTjrTx4ZjYLplcqrI3o4mYJ+Z6YspeuGL8tbt/hHoMchwBwtKfwM09svEY86iRapY93vUqQttcNuIO5Q==} + engines: {node: '>=6.0.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + etherpad-cli-client@3.0.2: + resolution: {integrity: sha512-3uJxz8cx1WSVAPe7haqfwQj4N4wWDrei5BXBjH+e1BLPiEpqWtnMX29Qo9jWhqO9ctyPPHO0nZ1x/ZCNxMVCqA==} + engines: {node: '>=18.0.0'} + hasBin: true + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + express-rate-limit@7.4.0: + resolution: {integrity: sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==} + engines: {node: '>= 16'} + peerDependencies: + express: 4 || 5 || ^5.0.0-beta.1 + + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + fetch-blob@4.0.0: + resolution: {integrity: sha512-nPmnhRmpNMjYWnp9EBMGs6z5lq9RXed5W1vuZcECrsDVQInM8AMQSooVb3X183Aole60adzjWbH9qlRFWzDDTA==} + engines: {node: '>=16.7'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + focus-trap@7.5.4: + resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + formidable@2.1.2: + resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} + + formidable@3.5.1: + resolution: {integrity: sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.7.6: + resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} + + get-uri@6.0.3: + resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} + engines: {node: '>= 14'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + got@13.0.0: + resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} + engines: {node: '>=16'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-embedded@3.0.0: + resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + + hast-util-from-html@2.0.1: + resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==} + + hast-util-from-parse5@8.0.1: + resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.0.4: + resolution: {integrity: sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==} + + hast-util-to-html@9.0.1: + resolution: {integrity: sha512-hZOofyZANbyWo+9RP75xIDV/gq+OUKx+T46IlwERnKmfpwp81XBFbT9mi26ws+SJchA4RVUQwIBJpqEOBhMzEQ==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@8.0.0: + resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hexoid@1.0.0: + resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} + engines: {node: '>=8'} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-parse-stringify@3.0.1: + resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + http-assert@1.5.0: + resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} + engines: {node: '>= 0.8'} + + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + i18next-browser-languagedetector@8.0.0: + resolution: {integrity: sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==} + + i18next@23.14.0: + resolution: {integrity: sha512-Y5GL4OdA8IU2geRrt2+Uc1iIhsjICdHZzT9tNwQ3TVqdNzgxHToGCKf/TPRP80vTCAP6svg2WbbJL+Gx5MFQVA==} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.15.0: + resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-promise@1.0.1: + resolution: {integrity: sha512-mjWH5XxnhMA8cFnDchr6qRP9S/kLntKuEfIYku+PaN1CnS8v+OG9O/BKpRCVRJvpIkgAZm0Pf5Is3iSSOILlcg==} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + + jose@5.7.0: + resolution: {integrity: sha512-3P9qfTYDVnNn642LCAqIKbTGb9a1TBxZ9ti5zEVEr48aDdflgRjhspWFb6WM4PzAfFbGMJYC4+803v8riCRAKw==} + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + jsdom@24.1.1: + resolution: {integrity: sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonminify@0.4.2: + resolution: {integrity: sha512-mEtP5ECD0293D+s45JhDutqF5mFCkWY8ClrPFxjSFR2KUoantofky7noSzyKnAnD9Gd8pXHZSUd5bgzLDUBbfA==} + engines: {node: '>=0.8.0', npm: '>=1.1.0'} + + jsonschema-draft4@1.0.0: + resolution: {integrity: sha512-sBV3UnQPRiyCTD6uzY/Oao2Yohv6KKgQq7zjPwjFHeR6scg/QSXnzDxdugsGaLQDmFUrUlTbMYdEE+72PizhGA==} + + jsonschema@1.2.4: + resolution: {integrity: sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==} + + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + just-extend@6.2.0: + resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + kebab-case@1.0.2: + resolution: {integrity: sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q==} + + keygrip@1.1.0: + resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} + engines: {node: '>= 0.6'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + koa-compose@4.1.0: + resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + + koa-convert@2.0.0: + resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} + engines: {node: '>= 10'} + + koa@2.15.3: + resolution: {integrity: sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==} + engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} + + languages4translatewiki@0.1.3: + resolution: {integrity: sha512-Z7+IM3FF+VyRbWl2CPWQoRf498zGy/qnolP5wJhMny4W3v0SL9rUCIPDHPao9rrw2yg2KIKnHIzopuWvGdnooQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + live-plugin-manager@1.0.0: + resolution: {integrity: sha512-ZzSagtubz5lrlyRZzpJ4L3KCZwdPeYKQJqz3py7hO3R4BDbt6NH2Dn70rWoQTF6ichM8P91xji1cN6ytmZ2VjQ==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lockfile@1.0.4: + resolution: {integrity: sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log4js@6.9.1: + resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} + engines: {node: '>=8.0'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.1.1: + resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lru-cache@11.0.0: + resolution: {integrity: sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lucide-react@0.429.0: + resolution: {integrity: sha512-548DahFy7Ey+0OPlOyZ6ipnbGJzewv8gq2DxDrnAzWZ4RN4+3XyIwQXhD4AQvuREFjS1EnbAgOMaYB0VQyaK1g==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + measured-core@2.0.0: + resolution: {integrity: sha512-SIzGtX1WGDvR59FqcJaGEAqDueBvLBh6W4T/gQaHr5ufcqvQkUHGcfQhlmq77mkeF5Mo+UpD+8hm69CwUVibGw==} + engines: {node: '>= 5.12'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromark-util-character@2.1.0: + resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + + micromark-util-encode@2.0.0: + resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + + micromark-util-sanitize-uri@2.0.0: + resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + + micromark-util-symbol@2.0.0: + resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + + micromark-util-types@2.0.0: + resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minisearch@7.1.0: + resolution: {integrity: sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mocha-froth@0.2.10: + resolution: {integrity: sha512-xyJqAYtm2zjrkG870hjeSVvGgS4Dc9tRokmN6R7XLgBKhdtAJ1ytU6zL045djblfHaPyTkSerQU4wqcjsv7Aew==} + + mocha@10.7.3: + resolution: {integrity: sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==} + engines: {node: '>= 14.0.0'} + hasBin: true + + mock-json-schema@1.1.1: + resolution: {integrity: sha512-YV23vlsLP1EEOy0EviUvZTluXjLR+rhMzeayP2rcDiezj3RW01MhOSQkbQskdtg0K2fnGas5LKbSXgNjAOSX4A==} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.0.7: + resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==} + engines: {node: ^18 || >=20} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + nise@6.0.0: + resolution: {integrity: sha512-K8ePqo9BFvN31HXwEtTNGzgrPpmvgciDsFz8aztFjt4LqKO/JeFD8tBOeuDiCMXrIl/m1YvfH8auSpxfaD09wg==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch-commonjs@3.3.2: + resolution: {integrity: sha512-VBlAiynj3VMLrotgwOS3OyECFxas5y7ltLcK4t41lMUZeaK15Ym4QRkqN0EQKAFL42q9i21EPKjzLUPfltR72A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + nodeify@1.0.1: + resolution: {integrity: sha512-n7C2NyEze8GCo/z73KdbjRsBiLbv6eBn1FxwYKQ23IqGo7pQY3mhQan61Sv7eEDJCiyUjTVrVkXTzJCo1dW7Aw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nwsapi@2.2.12: + resolution: {integrity: sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + oidc-provider@8.5.1: + resolution: {integrity: sha512-Bm3EyxN68/KS76IlciJ3+4pnVtfdRWL+NghWpIF0XQbiRT1gzc6Qf/cyFmpL9yieko/jXYZ/uLHUv77jD00qww==} + + oidc-token-hash@5.0.3: + resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} + engines: {node: ^10.13.0 || >=12.0.0} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + only@0.0.2: + resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} + + openapi-backend@5.10.6: + resolution: {integrity: sha512-vTjBRys/O4JIHdlRHUKZ7pxS+gwIJreAAU9dvYRFrImtPzQ5qxm5a6B8BTVT9m6I8RGGsShJv35MAc3Tu2/y/A==} + engines: {node: '>=12.0.0'} + + openapi-schema-validation@0.4.2: + resolution: {integrity: sha512-K8LqLpkUf2S04p2Nphq9L+3bGFh/kJypxIG2NVGKX0ffzT4NQI9HirhiY6Iurfej9lCu7y4Ndm4tv+lm86Ck7w==} + + openapi-schema-validator@12.1.3: + resolution: {integrity: sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ==} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + + optional-js@2.3.0: + resolution: {integrity: sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + pac-proxy-agent@7.0.2: + resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + + path-to-regexp@6.2.2: + resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + playwright-core@1.46.1: + resolution: {integrity: sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.46.1: + resolution: {integrity: sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==} + engines: {node: '>=18'} + hasBin: true + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss@8.4.41: + resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.22.0: + resolution: {integrity: sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + promise@1.3.0: + resolution: {integrity: sha512-R9WrbTF3EPkVtWjp7B7umQGVndpsi+rsDAfrR4xAALQpFLa/+2OriecLhawxzvii2gd9+DZFwROWDuUUaqS5yA==} + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-agent@6.4.0: + resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + + qs@6.12.3: + resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} + engines: {node: '>=0.6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + quick-lru@7.0.0: + resolution: {integrity: sha512-MX8gB7cVYTrYcFfAnfLlhRd0+Toyl8yX8uBx1MrX7K0jegiz9TumwOK27ldXrgDlHRdVi+MqU9Ssw6dr4BNreg==} + engines: {node: '>=18'} + + rambda@7.5.0: + resolution: {integrity: sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==} + + random-bytes@1.0.0: + resolution: {integrity: sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==} + engines: {node: '>= 0.8'} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + rate-limiter-flexible@5.0.3: + resolution: {integrity: sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-hook-form@7.52.2: + resolution: {integrity: sha512-pqfPEbERnxxiNMPd0bzmt1tuaPcVccywFDpyk2uV5xCIBphHV5T8SVnX9/o3kplPE1zzKt77+YIoq+EMwJp56A==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-i18next@15.0.1: + resolution: {integrity: sha512-NwxLqNM6CLbeGA9xPsjits0EnXdKgCRSS6cgkgOdNcPXqL+1fYNl8fBg1wmnnHvFy812Bt4IWTPE9zjoPmFj3w==} + peerDependencies: + i18next: '>= 23.2.3' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + + react-remove-scroll-bar@2.3.6: + resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.5.7: + resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-router-dom@6.26.1: + resolution: {integrity: sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.26.1: + resolution: {integrity: sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-style-singleton@2.2.1: + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + + rehype-minify-whitespace@6.0.0: + resolution: {integrity: sha512-i9It4YHR0Sf3GsnlR5jFUKXRr9oayvEk9GKQUkwZv6hs70OH9q3OCZrq9PpLvIGKt3W+JxBOxCidNVpH/6rWdA==} + + rehype-parse@9.0.0: + resolution: {integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==} + + rehype-stringify@10.0.0: + resolution: {integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==} + + rehype@13.0.1: + resolution: {integrity: sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup@4.21.0: + resolution: {integrity: sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + + rrweb-cssom@0.7.1: + resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + security@1.0.0: + resolution: {integrity: sha512-5qfoAgfRWS1sUn+fUJtdbbqM1BD/LoQGa+smPTDjf9OqHyuJqi6ewtbYL0+V1S1RaU6OCOCMWGZocIfz2YK4uw==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + + set-cookie-parser@2.7.0: + resolution: {integrity: sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shiki@1.14.1: + resolution: {integrity: sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sinon@18.0.0: + resolution: {integrity: sha512-+dXDXzD1sBO6HlmZDd7mXZCR/y5ECiEiGCBSGuFD/kZ0bDTofPYc6JaeGmPSF+1j1MejGUWkORbYOLDyvqCWpA==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-client@4.7.5: + resolution: {integrity: sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.7.5: + resolution: {integrity: sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==} + engines: {node: '>=10.2.0'} + + socks-proxy-agent@8.0.4: + resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} + engines: {node: '>= 14'} + + socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + split-grid@1.0.11: + resolution: {integrity: sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + streamroller@3.1.5: + resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} + engines: {node: '>=8.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + superagent@10.0.2: + resolution: {integrity: sha512-rvcN5ciYmVRaZLyM/9thpmNSDGscx1FXNwT5LJjsl+2nhdE4ppd53L2c9iSQSM6FA1poBVwlw+0h2Ofxwr3DoQ==} + engines: {node: '>=14.18.0'} + + superagent@8.1.2: + resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} + engines: {node: '>=6.4.0 <13 || >=14'} + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net + + superagent@9.0.2: + resolution: {integrity: sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==} + engines: {node: '>=14.18.0'} + + superjson@2.2.1: + resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==} + engines: {node: '>=16'} + + supertest@7.0.0: + resolution: {integrity: sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==} + engines: {node: '>=14.18.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + swagger-schema-official@2.0.0-bab6bed: + resolution: {integrity: sha512-rCC0NWGKr/IJhtRuPq/t37qvZHI/mH4I4sxflVM+qgVe5Z2uOCivzWaVbuioJaB61kvm5UvB7b49E+oBY0M8jA==} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinycon@0.6.8: + resolution: {integrity: sha512-bF8Lxm4JUXF6Cw0XlZdugJ44GV575OinZ0Pt8vQPr8ooNqd2yyNkoFdCHzmdpHlgoqfSLfcyk4HDP1EyllT+ug==} + + tinypool@1.0.0: + resolution: {integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.0: + resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} + engines: {node: '>=14.0.0'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tsscmp@1.0.6: + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + engines: {node: '>=0.6.x'} + + tsx@4.17.0: + resolution: {integrity: sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} + hasBin: true + + ueberdb2@4.2.93: + resolution: {integrity: sha512-inWXazVuEJc33aRBm6kCT8/ymgSvHcXbakKf4nxVf8Wp86Le0cQd3URn3cCUhxRV05qP14kDdvAQho9LUiXWwg==} + engines: {node: '>=16.20.1'} + + uid-safe@2.1.5: + resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==} + engines: {node: '>= 0.8'} + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + underscore@1.13.7: + resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==} + + undici-types@6.19.6: + resolution: {integrity: sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unorm@1.6.0: + resolution: {integrity: sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==} + engines: {node: '>= 0.4.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.0.16: + resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + use-callback-ref@1.3.2: + resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.2: + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.2.2: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.2: + resolution: {integrity: sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==} + + vite-node@2.0.5: + resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite-plugin-static-copy@1.0.6: + resolution: {integrity: sha512-3uSvsMwDVFZRitqoWHj0t4137Kz7UynnJeq1EZlRW7e25h2068fyIZX4ORCCOAkfp1FklGxJNVJBkBOD+PZIew==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + + vite-plugin-svgr@4.2.0: + resolution: {integrity: sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==} + peerDependencies: + vite: ^2.6.0 || 3 || 4 || 5 + + vite@5.4.2: + resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitepress@1.3.3: + resolution: {integrity: sha512-6UzEw/wZ41S/CATby7ea7UlffvRER/uekxgN6hbEvSys9ukmLOKsz87Ehq9yOx1Rwiw+Sj97yjpivP8w1sUmng==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + + vitest@2.0.5: + resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.0.5 + '@vitest/ui': 2.0.5 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue@3.4.38: + resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workerpool@6.5.1: + resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wtfnode@0.9.3: + resolution: {integrity: sha512-MXjgxJovNVYUkD85JBZTKT5S5ng/e56sNuRZlid7HcGTNrIODa5UPtqE3i0daj7fJ2SGj5Um2VmiphQVyVKK5A==} + hasBin: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xmlhttprequest-ssl@2.0.0: + resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} + engines: {node: '>=0.4.0'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + ylru@1.4.0: + resolution: {integrity: sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==} + engines: {node: '>= 4.0.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zustand@4.5.5: + resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + dependencies: + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + dependencies: + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + '@algolia/client-search': 4.23.3 + algoliasearch: 4.23.3 + + '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + dependencies: + '@algolia/client-search': 4.23.3 + algoliasearch: 4.23.3 + + '@algolia/cache-browser-local-storage@4.23.3': + dependencies: + '@algolia/cache-common': 4.23.3 + + '@algolia/cache-common@4.23.3': {} + + '@algolia/cache-in-memory@4.23.3': + dependencies: + '@algolia/cache-common': 4.23.3 + + '@algolia/client-account@4.23.3': + dependencies: + '@algolia/client-common': 4.23.3 + '@algolia/client-search': 4.23.3 + '@algolia/transporter': 4.23.3 + + '@algolia/client-analytics@4.23.3': + dependencies: + '@algolia/client-common': 4.23.3 + '@algolia/client-search': 4.23.3 + '@algolia/requester-common': 4.23.3 + '@algolia/transporter': 4.23.3 + + '@algolia/client-common@4.23.3': + dependencies: + '@algolia/requester-common': 4.23.3 + '@algolia/transporter': 4.23.3 + + '@algolia/client-personalization@4.23.3': + dependencies: + '@algolia/client-common': 4.23.3 + '@algolia/requester-common': 4.23.3 + '@algolia/transporter': 4.23.3 + + '@algolia/client-search@4.23.3': + dependencies: + '@algolia/client-common': 4.23.3 + '@algolia/requester-common': 4.23.3 + '@algolia/transporter': 4.23.3 + + '@algolia/logger-common@4.23.3': {} + + '@algolia/logger-console@4.23.3': + dependencies: + '@algolia/logger-common': 4.23.3 + + '@algolia/recommend@4.23.3': + dependencies: + '@algolia/cache-browser-local-storage': 4.23.3 + '@algolia/cache-common': 4.23.3 + '@algolia/cache-in-memory': 4.23.3 + '@algolia/client-common': 4.23.3 + '@algolia/client-search': 4.23.3 + '@algolia/logger-common': 4.23.3 + '@algolia/logger-console': 4.23.3 + '@algolia/requester-browser-xhr': 4.23.3 + '@algolia/requester-common': 4.23.3 + '@algolia/requester-node-http': 4.23.3 + '@algolia/transporter': 4.23.3 + + '@algolia/requester-browser-xhr@4.23.3': + dependencies: + '@algolia/requester-common': 4.23.3 + + '@algolia/requester-common@4.23.3': {} + + '@algolia/requester-node-http@4.23.3': + dependencies: + '@algolia/requester-common': 4.23.3 + + '@algolia/transporter@4.23.3': + dependencies: + '@algolia/cache-common': 4.23.3 + '@algolia/logger-common': 4.23.3 + '@algolia/requester-common': 4.23.3 + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@apidevtools/json-schema-ref-parser@11.6.4': + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + js-yaml: 4.1.0 + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + + '@babel/compat-data@7.24.7': {} + + '@babel/core@7.24.7': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helpers': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + convert-source-map: 2.0.0 + debug: 4.3.5(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.24.7': + dependencies: + '@babel/types': 7.24.7 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-compilation-targets@7.24.7': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + browserslist: 4.23.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-function-name@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/helper-hoist-variables@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-string-parser@7.24.7': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-option@7.24.7': {} + + '@babel/helpers@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/parser@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/runtime@7.24.7': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/runtime@7.24.8': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.24.7': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/traverse@7.24.7': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + debug: 4.3.5(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.24.7': + dependencies: + '@babel/helper-string-parser': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@docsearch/css@3.6.1': {} + + '@docsearch/js@3.6.1(@algolia/client-search@4.23.3)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@docsearch/react': 3.6.1(@algolia/client-search@4.23.3)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + preact: 10.22.0 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - react + - react-dom + - search-insights + + '@docsearch/react@3.6.1(@algolia/client-search@4.23.3)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + '@docsearch/css': 3.6.1 + algoliasearch: 4.23.3 + optionalDependencies: + '@types/react': 18.3.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@algolia/client-search' + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.23.0': + optional: true + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.23.0': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-arm@0.23.0': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.23.0': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.23.0': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.23.0': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.23.0': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.23.0': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.23.0': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-arm@0.23.0': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.23.0': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.23.0': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.23.0': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.23.0': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.23.0': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.23.0': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/linux-x64@0.23.0': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.23.0': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.0': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.23.0': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.23.0': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.23.0': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.23.0': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@esbuild/win32-x64@0.23.0': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@9.9.0)': + dependencies: + eslint: 9.9.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.0': {} + + '@eslint/config-array@0.17.1': + dependencies: + '@eslint/object-schema': 2.1.4 + debug: 4.3.5(supports-color@8.1.1) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@3.1.0': + dependencies: + ajv: 6.12.6 + debug: 4.3.5(supports-color@8.1.1) + espree: 10.1.0 + globals: 14.0.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.9.0': {} + + '@eslint/object-schema@2.1.4': {} + + '@etherpad/express-session@1.18.4': + dependencies: + cookie: 0.4.2 + cookie-signature: 1.0.6 + debug: 4.3.5(supports-color@8.1.1) + depd: 2.0.0 + on-headers: 1.0.2 + parseurl: 1.3.3 + safe-buffer: 5.2.1 + uid-safe: 2.1.5 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.0': {} + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.4.15': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + '@jsdevtools/ono@7.1.3': {} + + '@koa/cors@5.0.0': + dependencies: + vary: 1.1.2 + + '@koa/router@12.0.1': + dependencies: + debug: 4.3.5(supports-color@8.1.1) + http-errors: 2.0.0 + koa-compose: 4.1.0 + methods: 1.1.2 + path-to-regexp: 6.2.2 + transitivePeerDependencies: + - supports-color + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@playwright/test@1.46.1': + dependencies: + playwright: 1.46.1 + + '@radix-ui/primitive@1.1.0': {} + + '@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-context@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-dialog@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1) + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.5.7(@types/react@18.3.4)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-dismissable-layer@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-focus-guards@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-focus-scope@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-id@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-portal@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-presence@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-slot@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-switch@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-toast@1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-use-size@1.1.0(@types/react@18.3.4)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.4 + + '@radix-ui/react-visually-hidden@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + + '@remix-run/router@1.19.1': {} + + '@rollup/pluginutils@5.1.0(rollup@4.21.0)': + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.21.0 + + '@rollup/rollup-android-arm-eabi@4.21.0': + optional: true + + '@rollup/rollup-android-arm64@4.21.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.21.0': + optional: true + + '@rollup/rollup-darwin-x64@4.21.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.21.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.21.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.21.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.21.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.21.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.21.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.21.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.21.0': + optional: true + + '@rushstack/eslint-patch@1.10.3': {} + + '@shikijs/core@1.14.1': + dependencies: + '@types/hast': 3.0.4 + + '@shikijs/transformers@1.14.1': + dependencies: + shiki: 1.14.1 + + '@sindresorhus/is@5.6.0': {} + + '@sinonjs/commons@2.0.0': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@11.2.2': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@sinonjs/samsam@8.0.0': + dependencies: + '@sinonjs/commons': 2.0.0 + lodash.get: 4.4.2 + type-detect: 4.0.8 + + '@sinonjs/text-encoding@0.7.2': {} + + '@socket.io/component-emitter@3.1.2': {} + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@svgr/babel-preset@8.1.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.24.7) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.24.7) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.24.7) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.24.7) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.24.7) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.24.7) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.24.7) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.24.7) + + '@svgr/core@8.1.0(typescript@5.5.4)': + dependencies: + '@babel/core': 7.24.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.24.7) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.5.4) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.24.7 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.5.4))': + dependencies: + '@babel/core': 7.24.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.24.7) + '@svgr/core': 8.1.0(typescript@5.5.4) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@swc/core-darwin-arm64@1.5.28': + optional: true + + '@swc/core-darwin-x64@1.5.28': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.5.28': + optional: true + + '@swc/core-linux-arm64-gnu@1.5.28': + optional: true + + '@swc/core-linux-arm64-musl@1.5.28': + optional: true + + '@swc/core-linux-x64-gnu@1.5.28': + optional: true + + '@swc/core-linux-x64-musl@1.5.28': + optional: true + + '@swc/core-win32-arm64-msvc@1.5.28': + optional: true + + '@swc/core-win32-ia32-msvc@1.5.28': + optional: true + + '@swc/core-win32-x64-msvc@1.5.28': + optional: true + + '@swc/core@1.5.28': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.8 + optionalDependencies: + '@swc/core-darwin-arm64': 1.5.28 + '@swc/core-darwin-x64': 1.5.28 + '@swc/core-linux-arm-gnueabihf': 1.5.28 + '@swc/core-linux-arm64-gnu': 1.5.28 + '@swc/core-linux-arm64-musl': 1.5.28 + '@swc/core-linux-x64-gnu': 1.5.28 + '@swc/core-linux-x64-musl': 1.5.28 + '@swc/core-win32-arm64-msvc': 1.5.28 + '@swc/core-win32-ia32-msvc': 1.5.28 + '@swc/core-win32-x64-msvc': 1.5.28 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.8': + dependencies: + '@swc/counter': 0.1.3 + + '@szmarczak/http-timer@5.0.1': + dependencies: + defer-to-connect: 2.0.1 + + '@tootallnate/quickjs-emscripten@0.23.0': {} + + '@types/accepts@1.3.7': + dependencies: + '@types/node': 22.4.2 + + '@types/async@3.2.24': {} + + '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.11.28 - dev: true + '@types/node': 22.4.2 - /@types/connect@3.4.38: - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/connect@3.4.38': dependencies: - '@types/node': 20.11.28 - dev: true + '@types/node': 22.4.2 - /@types/cookie@0.4.1: - resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} - dev: false + '@types/content-disposition@0.5.8': {} - /@types/cookiejar@2.1.5: - resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} - dev: true + '@types/cookie@0.4.1': {} - /@types/cors@2.8.17: - resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + '@types/cookiejar@2.1.5': {} + + '@types/cookies@0.9.0': + dependencies: + '@types/connect': 3.4.38 + '@types/express': 4.17.21 + '@types/keygrip': 1.0.6 + '@types/node': 22.4.2 + + '@types/cors@2.8.17': dependencies: - '@types/node': 20.11.28 - dev: false + '@types/node': 22.4.2 - /@types/debug@4.1.12: - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/debug@4.1.12': dependencies: '@types/ms': 0.7.34 - dev: false - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true + '@types/estree@1.0.5': {} - /@types/express-serve-static-core@4.17.43: - resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} + '@types/express-serve-static-core@4.19.5': dependencies: - '@types/node': 20.11.28 - '@types/qs': 6.9.11 + '@types/node': 22.4.2 + '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - dev: true - /@types/express@4.17.21: - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.17.43 - '@types/qs': 6.9.11 - '@types/serve-static': 1.15.5 - dev: true + '@types/express-serve-static-core': 4.19.5 + '@types/qs': 6.9.15 + '@types/serve-static': 1.15.7 - /@types/fs-extra@9.0.13: - resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + '@types/formidable@3.4.5': dependencies: - '@types/node': 20.11.28 - dev: false + '@types/node': 22.4.2 - /@types/hast@3.0.4: - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/fs-extra@9.0.13': + dependencies: + '@types/node': 22.4.2 + + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.2 - dev: false - /@types/http-errors@2.0.4: - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - dev: true + '@types/http-assert@1.5.5': {} + + '@types/http-cache-semantics@4.0.4': {} + + '@types/http-errors@2.0.4': {} - /@types/jsdom@21.1.6: - resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==} + '@types/jquery@3.5.30': dependencies: - '@types/node': 20.11.28 + '@types/sizzle': 2.3.8 + + '@types/js-cookie@3.0.6': {} + + '@types/jsdom@21.1.7': + dependencies: + '@types/node': 22.4.2 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 - dev: true - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/json-schema@7.0.15': {} - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: true + '@types/json5@0.0.29': {} - /@types/lockfile@1.0.4: - resolution: {integrity: sha512-Q8oFIHJHr+htLrTXN2FuZfg+WXVHQRwU/hC2GpUu+Q8e3FUM9EDkS2pE3R2AO1ZGu56f479ybdMCNF1DAu8cAQ==} - dev: false + '@types/jsonwebtoken@9.0.6': + dependencies: + '@types/node': 22.4.2 + + '@types/keygrip@1.0.6': {} - /@types/lodash.clonedeep@4.5.9: - resolution: {integrity: sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==} + '@types/koa-compose@3.2.8': dependencies: - '@types/lodash': 4.14.202 - dev: false + '@types/koa': 2.15.0 + + '@types/koa@2.15.0': + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.8 + '@types/cookies': 0.9.0 + '@types/http-assert': 1.5.5 + '@types/http-errors': 2.0.4 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.8 + '@types/node': 22.4.2 + + '@types/linkify-it@5.0.0': {} - /@types/lodash@4.14.202: - resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} - dev: false + '@types/lockfile@1.0.4': {} - /@types/mdast@4.0.3: - resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.2 - dev: false - /@types/methods@1.1.4: - resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} - dev: true + '@types/mdurl@2.0.0': {} - /@types/mime@1.3.5: - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - dev: true + '@types/methods@1.1.4': {} - /@types/mime@3.0.4: - resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} - dev: true + '@types/mime-types@2.1.4': {} - /@types/mocha@10.0.6: - resolution: {integrity: sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==} - dev: true + '@types/mime@1.3.5': {} - /@types/ms@0.7.34: - resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - dev: false + '@types/mocha@10.0.7': {} - /@types/node-fetch@2.6.11: - resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} + '@types/ms@0.7.34': {} + + '@types/node-fetch@2.6.11': dependencies: - '@types/node': 20.11.28 + '@types/node': 22.4.2 form-data: 4.0.0 - dev: false - /@types/node@20.11.27: - resolution: {integrity: sha512-qyUZfMnCg1KEz57r7pzFtSGt49f6RPkPBis3Vo4PbS7roQEDn22hiHzl/Lo1q4i4hDEgBJmBF/NTNg2XR0HbFg==} + '@types/node@22.4.2': dependencies: - undici-types: 5.26.5 - dev: true + undici-types: 6.19.6 - /@types/node@20.11.28: - resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==} + '@types/oidc-provider@8.5.2': dependencies: - undici-types: 5.26.5 + '@types/koa': 2.15.0 + '@types/node': 22.4.2 - /@types/prop-types@15.7.11: - resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} - dev: true + '@types/prop-types@15.7.12': {} - /@types/qs@6.9.11: - resolution: {integrity: sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==} - dev: true + '@types/qs@6.9.15': {} - /@types/range-parser@1.2.7: - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - dev: true + '@types/range-parser@1.2.7': {} - /@types/react-dom@18.2.21: - resolution: {integrity: sha512-gnvBA/21SA4xxqNXEwNiVcP0xSGHh/gi1VhWv9Bl46a0ItbTT5nFY+G9VSQpaG/8N/qdJpJ+vftQ4zflTtnjLw==} + '@types/react-dom@18.3.0': dependencies: - '@types/react': 18.2.65 - dev: true + '@types/react': 18.3.4 - /@types/react@18.2.65: - resolution: {integrity: sha512-98TsY0aW4jqx/3RqsUXwMDZSWR1Z4CUlJNue8ueS2/wcxZOsz4xmW1X8ieaWVRHcmmQM3R8xVA4XWB3dJnWwDQ==} + '@types/react@18.3.4': dependencies: - '@types/prop-types': 15.7.11 - '@types/scheduler': 0.16.8 + '@types/prop-types': 15.7.12 csstype: 3.1.3 - dev: true - /@types/scheduler@0.16.8: - resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} - dev: true + '@types/semver@7.5.8': {} - /@types/semver@7.5.8: - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - - /@types/send@0.17.4: - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.11.28 - dev: true + '@types/node': 22.4.2 - /@types/serve-static@1.15.5: - resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/mime': 3.0.4 - '@types/node': 20.11.28 - dev: true + '@types/node': 22.4.2 + '@types/send': 0.17.4 - /@types/sinon@17.0.3: - resolution: {integrity: sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==} + '@types/sinon@17.0.3': dependencies: - '@types/sinonjs__fake-timers': 8.1.1 - dev: true + '@types/sinonjs__fake-timers': 8.1.5 + + '@types/sinonjs__fake-timers@8.1.5': {} - /@types/sinonjs__fake-timers@8.1.1: - resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} - dev: true + '@types/sizzle@2.3.8': {} - /@types/superagent@8.1.3: - resolution: {integrity: sha512-R/CfN6w2XsixLb1Ii8INfn+BT9sGPvw74OavfkW4SwY+jeUcAwLZv2+bXLJkndnimxjEBm0RPHgcjW9pLCa8cw==} + '@types/superagent@8.1.7': dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 20.11.28 - dev: true + '@types/node': 22.4.2 - /@types/supertest@6.0.2: - resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==} + '@types/supertest@6.0.2': dependencies: '@types/methods': 1.1.4 - '@types/superagent': 8.1.3 - dev: true + '@types/superagent': 8.1.7 - /@types/tar@6.1.11: - resolution: {integrity: sha512-ThA1WD8aDdVU4VLuyq5NEqriwXErF5gEIJeyT6gHBWU7JtSmW2a5qjNv3/vR82O20mW+1vhmeZJfBQPT3HCugg==} + '@types/tar@6.1.13': dependencies: - '@types/node': 20.11.28 + '@types/node': 22.4.2 minipass: 4.2.8 - dev: false - /@types/tough-cookie@4.0.5: - resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} - dev: true + '@types/tough-cookie@4.0.5': {} - /@types/underscore@1.11.15: - resolution: {integrity: sha512-HP38xE+GuWGlbSRq9WrZkousaQ7dragtZCruBVMi0oX1migFZavZ3OROKHSkNp/9ouq82zrWtZpg18jFnVN96g==} - dev: true + '@types/underscore@1.11.15': {} - /@types/unist@3.0.2: - resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} - dev: false + '@types/unist@3.0.2': {} - /@types/url-join@4.0.1: - resolution: {integrity: sha512-wDXw9LEEUHyV+7UWy7U315nrJGJ7p1BzaCxDpEoLr789Dk1WDVMMlf3iBfbG2F8NdWnYyFbtTxUn2ZNbm1Q4LQ==} - dev: false + '@types/url-join@4.0.3': {} - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@types/web-bluetooth@0.0.20': {} + + '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.2) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/type-utils': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/utils': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.17.0 + eslint: 9.9.0 graphemer: 1.4.0 ignore: 5.3.1 - natural-compare-lite: 1.4.0 - semver: 7.6.0 - tsutils: 3.21.0(typescript@5.4.2) - typescript: 5.4.2 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/eslint-plugin@8.2.0(@typescript-eslint/parser@8.2.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/type-utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 8.2.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/type-utils': 8.2.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.2.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.2.0 + eslint: 9.9.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.2) - typescript: 5.4.2 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.2) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 - typescript: 5.4.2 + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.17.0 + debug: 4.3.5(supports-color@8.1.1) + eslint: 9.9.0 + optionalDependencies: + typescript: 5.5.4 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser@8.2.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 - typescript: 5.4.2 + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.2.0 + debug: 4.3.5(supports-color@8.1.1) + eslint: 9.9.0 + optionalDependencies: + typescript: 5.5.4 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/scope-manager@5.62.0: - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/scope-manager@7.17.0': dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - dev: true + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/visitor-keys': 7.17.0 - /@typescript-eslint/scope-manager@7.2.0: - resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/scope-manager@8.2.0': dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 - dev: true + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/visitor-keys': 8.2.0 - /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/type-utils@7.17.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.2) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 - tsutils: 3.21.0(typescript@5.4.2) - typescript: 5.4.2 + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + '@typescript-eslint/utils': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + debug: 4.3.5(supports-color@8.1.1) + eslint: 9.9.0 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/type-utils@8.2.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) - '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.4.2) - typescript: 5.4.2 + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + '@typescript-eslint/utils': 8.2.0(eslint@9.9.0)(typescript@5.5.4) + debug: 4.3.5(supports-color@8.1.1) + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 transitivePeerDependencies: + - eslint - supports-color - dev: true - /@typescript-eslint/types@5.62.0: - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + '@typescript-eslint/types@7.17.0': {} - /@typescript-eslint/types@7.2.0: - resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} - engines: {node: ^16.0.0 || >=18.0.0} - dev: true + '@typescript-eslint/types@8.2.0': {} - /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.2): - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/typescript-estree@7.17.0(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.4(supports-color@8.1.1) + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/visitor-keys': 7.17.0 + debug: 4.3.5(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.0 - tsutils: 3.21.0(typescript@5.4.2) - typescript: 5.4.2 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.2): - resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/typescript-estree@8.2.0(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.3.4(supports-color@8.1.1) + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/visitor-keys': 8.2.0 + debug: 4.3.5(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.2) - typescript: 5.4.2 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/utils@7.17.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.2) - eslint: 8.57.0 - eslint-scope: 5.1.1 - semver: 7.6.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + eslint: 9.9.0 transitivePeerDependencies: - supports-color - typescript - dev: true - /@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^8.56.0 + '@typescript-eslint/utils@8.2.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) - eslint: 8.57.0 - semver: 7.6.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + eslint: 9.9.0 transitivePeerDependencies: - supports-color - typescript - dev: true - /@typescript-eslint/visitor-keys@5.62.0: - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/visitor-keys@7.17.0': dependencies: - '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/types': 7.17.0 eslint-visitor-keys: 3.4.3 - dev: true - /@typescript-eslint/visitor-keys@7.2.0: - resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/visitor-keys@8.2.0': dependencies: - '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/types': 8.2.0 eslint-visitor-keys: 3.4.3 - dev: true - /@ungap/structured-clone@1.2.0: - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@ungap/structured-clone@1.2.0': {} - /@vitejs/plugin-react-swc@3.6.0(vite@5.1.6): - resolution: {integrity: sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==} - peerDependencies: - vite: ^4 || ^5 + '@vitejs/plugin-react-swc@3.7.0(vite@5.4.2(@types/node@22.4.2))': dependencies: - '@swc/core': 1.4.7 - vite: 5.1.6 + '@swc/core': 1.5.28 + vite: 5.4.2(@types/node@22.4.2) transitivePeerDependencies: - '@swc/helpers' - dev: true - /accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} + '@vitejs/plugin-vue@5.1.2(vite@5.4.2(@types/node@22.4.2))(vue@3.4.38(typescript@5.5.4))': + dependencies: + vite: 5.4.2(@types/node@22.4.2) + vue: 3.4.38(typescript@5.5.4) + + '@vitest/expect@2.0.5': + dependencies: + '@vitest/spy': 2.0.5 + '@vitest/utils': 2.0.5 + chai: 5.1.1 + tinyrainbow: 1.2.0 + + '@vitest/pretty-format@2.0.5': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.0.5': + dependencies: + '@vitest/utils': 2.0.5 + pathe: 1.1.2 + + '@vitest/snapshot@2.0.5': + dependencies: + '@vitest/pretty-format': 2.0.5 + magic-string: 0.30.10 + pathe: 1.1.2 + + '@vitest/spy@2.0.5': + dependencies: + tinyspy: 3.0.0 + + '@vitest/utils@2.0.5': + dependencies: + '@vitest/pretty-format': 2.0.5 + estree-walker: 3.0.3 + loupe: 3.1.1 + tinyrainbow: 1.2.0 + + '@vue/compiler-core@3.4.38': + dependencies: + '@babel/parser': 7.24.7 + '@vue/shared': 3.4.38 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + + '@vue/compiler-dom@3.4.38': + dependencies: + '@vue/compiler-core': 3.4.38 + '@vue/shared': 3.4.38 + + '@vue/compiler-sfc@3.4.38': + dependencies: + '@babel/parser': 7.24.7 + '@vue/compiler-core': 3.4.38 + '@vue/compiler-dom': 3.4.38 + '@vue/compiler-ssr': 3.4.38 + '@vue/shared': 3.4.38 + estree-walker: 2.0.2 + magic-string: 0.30.10 + postcss: 8.4.41 + source-map-js: 1.2.0 + + '@vue/compiler-ssr@3.4.38': + dependencies: + '@vue/compiler-dom': 3.4.38 + '@vue/shared': 3.4.38 + + '@vue/devtools-api@7.3.8': + dependencies: + '@vue/devtools-kit': 7.3.8 + + '@vue/devtools-kit@7.3.8': + dependencies: + '@vue/devtools-shared': 7.3.8 + birpc: 0.2.17 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.1 + + '@vue/devtools-shared@7.3.8': + dependencies: + rfdc: 1.4.1 + + '@vue/reactivity@3.4.38': + dependencies: + '@vue/shared': 3.4.38 + + '@vue/runtime-core@3.4.38': + dependencies: + '@vue/reactivity': 3.4.38 + '@vue/shared': 3.4.38 + + '@vue/runtime-dom@3.4.38': + dependencies: + '@vue/reactivity': 3.4.38 + '@vue/runtime-core': 3.4.38 + '@vue/shared': 3.4.38 + csstype: 3.1.3 + + '@vue/server-renderer@3.4.38(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@vue/compiler-ssr': 3.4.38 + '@vue/shared': 3.4.38 + vue: 3.4.38(typescript@5.5.4) + + '@vue/shared@3.4.38': {} + + '@vueuse/core@11.0.1(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 11.0.1 + '@vueuse/shared': 11.0.1(vue@3.4.38(typescript@5.5.4)) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/integrations@11.0.1(axios@1.7.5)(focus-trap@7.5.4)(vue@3.4.38(typescript@5.5.4))': + dependencies: + '@vueuse/core': 11.0.1(vue@3.4.38(typescript@5.5.4)) + '@vueuse/shared': 11.0.1(vue@3.4.38(typescript@5.5.4)) + vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) + optionalDependencies: + axios: 1.7.5 + focus-trap: 7.5.4 + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/metadata@11.0.1': {} + + '@vueuse/shared@11.0.1(vue@3.4.38(typescript@5.5.4))': + dependencies: + vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 - dev: false - /acorn-jsx@5.3.2(acorn@8.11.3): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-jsx@5.3.2(acorn@8.12.0): dependencies: - acorn: 8.11.3 - dev: true + acorn: 8.12.0 - /acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} - engines: {node: '>=0.4.0'} - hasBin: true + acorn@8.12.0: {} - /agent-base@7.1.0: - resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} - engines: {node: '>= 14'} + agent-base@7.1.1: dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color - dev: false - /ajv-formats@2.1.1(ajv@8.12.0): - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - dependencies: - ajv: 8.12.0 - dev: false + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - dev: true - /ajv@8.12.0: - resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - uri-js: 4.4.1 - dev: false - /ansi-colors@4.1.1: - resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} - engines: {node: '>=6'} - dev: true + algoliasearch@4.23.3: + dependencies: + '@algolia/cache-browser-local-storage': 4.23.3 + '@algolia/cache-common': 4.23.3 + '@algolia/cache-in-memory': 4.23.3 + '@algolia/client-account': 4.23.3 + '@algolia/client-analytics': 4.23.3 + '@algolia/client-common': 4.23.3 + '@algolia/client-personalization': 4.23.3 + '@algolia/client-search': 4.23.3 + '@algolia/logger-common': 4.23.3 + '@algolia/logger-console': 4.23.3 + '@algolia/recommend': 4.23.3 + '@algolia/requester-browser-xhr': 4.23.3 + '@algolia/requester-common': 4.23.3 + '@algolia/requester-node-http': 4.23.3 + '@algolia/transporter': 4.23.3 - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true + ansi-colors@4.1.3: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-regex@5.0.1: {} + + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - dev: true - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argparse@2.0.1: {} - /aria-hidden@1.2.3: - resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} - engines: {node: '>=10'} + aria-hidden@1.2.4: dependencies: - tslib: 2.6.2 - dev: true + tslib: 2.6.3 - /array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 is-array-buffer: 3.0.4 - dev: true - /array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - dev: false + array-flatten@1.1.1: {} - /array-includes@3.1.7: - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} - engines: {node: '>= 0.4'} + array-includes@3.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 is-string: 1.0.7 - dev: true - - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: true - /array.prototype.filter@1.0.3: - resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - es-array-method-boxes-properly: 1.0.0 - is-string: 1.0.7 - dev: true + array-union@2.1.0: {} - /array.prototype.findlastindex@1.2.4: - resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==} - engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 es-errors: 1.3.0 + es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} + array.prototype.flatmap@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.3: dependencies: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 es-errors: 1.3.0 get-intrinsic: 1.2.4 is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.2 - dev: true + is-shared-array-buffer: 1.0.3 - /asap@2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + asap@2.0.6: {} - /async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + assertion-error@2.0.1: {} - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + ast-types@0.13.4: + dependencies: + tslib: 2.6.3 - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + async@3.2.6: {} + + asynckit@0.4.0: {} + + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - dev: true - /axios@1.6.8: - resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} + axios@1.7.5: dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: false - /bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - dev: false + bail@2.0.2: {} - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@1.0.2: {} - /base64id@2.0.0: - resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} - engines: {node: ^4.5.0 || >= 5.9} - dev: false + base64id@2.0.0: {} - /bath-es5@3.0.3: - resolution: {integrity: sha512-PdCioDToH3t84lP40kUFCKWCOCH389Dl1kbC8FGoqOwamxsmqxxnJSXdkTOsPoNHXjem4+sJ+bbNoQm5zeCqxg==} - dev: false + basic-ftp@5.0.5: {} - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} - dev: true + bath-es5@3.0.3: {} + + binary-extensions@2.3.0: {} + + binary-search@1.3.6: {} - /binary-search@1.3.6: - resolution: {integrity: sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==} - dev: false + birpc@0.2.17: {} - /body-parser@1.20.2: - resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@1.20.2: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -2432,118 +6542,103 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - /braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: - fill-range: 7.0.1 - dev: true + fill-range: 7.1.1 - /browser-stdout@1.3.1: - resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - dev: true + browser-stdout@1.3.1: {} - /browserslist@4.23.0: - resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + browserslist@4.23.1: dependencies: - caniuse-lite: 1.0.30001597 - electron-to-chromium: 1.4.703 + caniuse-lite: 1.0.30001632 + electron-to-chromium: 1.4.798 node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) - dev: true + update-browserslist-db: 1.0.16(browserslist@4.23.1) - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: false + buffer-equal-constant-time@1.0.1: {} - /builtin-modules@3.3.0: - resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} - engines: {node: '>=6'} - dev: true + builtin-modules@3.3.0: {} - /builtins@5.0.1: - resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + builtins@5.1.0: dependencies: - semver: 7.6.0 - dev: true + semver: 7.6.3 - /bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - dev: false + bytes@3.1.2: {} - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} + cac@6.7.14: {} + + cache-content-type@1.0.1: + dependencies: + mime-types: 2.1.35 + ylru: 1.4.0 + + cacheable-lookup@7.0.0: {} + + cacheable-request@10.2.14: + dependencies: + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + mimic-response: 4.0.0 + normalize-url: 8.0.1 + responselike: 3.0.0 + + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 - set-function-length: 1.2.1 + set-function-length: 1.2.2 - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + callsites@3.1.0: {} - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - dev: true + camelcase@6.3.0: {} - /caniuse-lite@1.0.30001597: - resolution: {integrity: sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==} - dev: true + caniuse-lite@1.0.30001632: {} - /ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - dev: false + ccount@2.0.1: {} - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chai@5.1.1: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.1 + pathval: 2.0.0 + + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: true - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - dev: false + character-entities-html4@2.1.0: {} - /character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - dev: false + character-entities-legacy@3.0.0: {} - /chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} - engines: {node: '>= 8.10.0'} + check-error@2.1.1: {} + + chokidar@3.6.0: dependencies: anymatch: 3.1.3 - braces: 3.0.2 + braces: 3.0.3 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -2551,944 +6646,715 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true - - /chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - dev: false - /clean-css@5.3.3: - resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} - engines: {node: '>= 10.0'} - dependencies: - source-map: 0.6.1 - dev: false + chownr@2.0.0: {} - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@7.0.4: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + co@4.6.0: {} + + color-convert@1.9.3: dependencies: color-name: 1.1.3 - dev: true - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - /comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - dev: false - - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false + comma-separated-tokens@2.0.3: {} - /component-emitter@1.3.1: - resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + component-emitter@1.3.1: {} - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-map@0.0.1: {} - /content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 - dev: false - /content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - dev: false + content-type@1.0.5: {} - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true + convert-source-map@2.0.0: {} - /cookie-parser@1.4.6: - resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==} - engines: {node: '>= 0.8.0'} + cookie-parser@1.4.6: dependencies: cookie: 0.4.1 cookie-signature: 1.0.6 - dev: false - /cookie-signature@1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - dev: false + cookie-signature@1.0.6: {} - /cookie@0.4.1: - resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==} - engines: {node: '>= 0.6'} - dev: false + cookie@0.4.1: {} - /cookie@0.4.2: - resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} - engines: {node: '>= 0.6'} - dev: false + cookie@0.4.2: {} - /cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} - dev: false + cookie@0.5.0: {} - /cookiejar@2.1.4: - resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + cookie@0.6.0: {} - /cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} + cookiejar@2.1.4: {} + + cookies@0.9.1: + dependencies: + depd: 2.0.0 + keygrip: 1.1.0 + + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + + cors@2.8.5: dependencies: object-assign: 4.1.1 vary: 1.1.2 - dev: false - /cosmiconfig@8.3.6(typescript@5.4.2): - resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@8.3.6(typescript@5.5.4): dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 - typescript: 5.4.2 - dev: true + optionalDependencies: + typescript: 5.5.4 - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.3 + + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - /cssstyle@4.0.1: - resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} - engines: {node: '>=18'} + cssstyle@4.0.1: dependencies: rrweb-cssom: 0.6.0 - dev: false - /csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dev: true + csstype@3.1.3: {} - /data-urls@5.0.0: - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} - engines: {node: '>=18'} + data-uri-to-buffer@6.0.2: {} + + data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 whatwg-url: 14.0.0 - dev: false - /date-format@4.0.14: - resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} - engines: {node: '>=4.0'} - dev: false + data-view-buffer@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 - /debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + data-view-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-offset@1.0.0: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + date-format@4.0.14: {} + + debug@2.6.9: dependencies: ms: 2.0.0 - dev: false - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@3.2.7: dependencies: ms: 2.1.3 - dev: true - /debug@4.3.4(supports-color@8.1.1): - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.5(supports-color@8.1.1): dependencies: ms: 2.1.2 + optionalDependencies: supports-color: 8.1.1 - /decamelize@4.0.0: - resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} - engines: {node: '>=10'} - dev: true + decamelize@4.0.0: {} - /decimal.js@10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - dev: false + decimal.js@10.4.3: {} - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} + deep-eql@5.0.2: {} + + deep-equal@1.0.1: {} + + deep-is@0.1.4: {} + + defer-to-connect@2.0.1: {} + + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 gopd: 1.0.1 - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - dev: true - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 - /depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dev: false + delayed-stream@1.0.0: {} - /dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - dev: false + delegates@1.0.0: {} - /dereference-json-schema@0.2.1: - resolution: {integrity: sha512-uzJsrg225owJyRQ8FNTPHIuBOdSzIZlHhss9u6W8mp7jJldHqGuLv9cULagP/E26QVJDnjtG8U7Dw139mM1ydA==} - dev: false + depd@1.1.2: {} - /destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dev: false + depd@2.0.0: {} - /detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - dev: true + dequal@2.0.3: {} - /devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dereference-json-schema@0.2.1: {} + + destroy@1.2.0: {} + + detect-node-es@1.1.0: {} + + devlop@1.1.0: dependencies: dequal: 2.0.3 - dev: false - /dezalgo@1.0.4: - resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + dezalgo@1.0.4: dependencies: asap: 2.0.6 wrappy: 1.0.2 - /diff@5.0.0: - resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} - engines: {node: '>=0.3.1'} - dev: true - - /diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} - dev: true + diff@5.2.0: {} - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - dev: true - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} + doctrine@2.1.0: dependencies: esutils: 2.0.3 - dev: true - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + dot-case@3.0.4: dependencies: - esutils: 2.0.3 - dev: true + no-case: 3.0.4 + tslib: 2.6.3 - /dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + ecdsa-sig-formatter@1.0.11: dependencies: - no-case: 3.0.4 - tslib: 2.6.2 - dev: true + safe-buffer: 5.2.1 - /ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - dev: false + ee-first@1.1.1: {} - /ejs@3.1.9: - resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} - engines: {node: '>=0.10.0'} - hasBin: true + ejs@3.1.10: dependencies: - jake: 10.8.7 - dev: false + jake: 10.9.2 - /electron-to-chromium@1.4.703: - resolution: {integrity: sha512-094ZZC4nHXPKl/OwPinSMtLN9+hoFkdfQGKnvXbY+3WEAYtVDpz9UhJIViiY6Zb8agvqxiaJzNG9M+pRZWvSZw==} - dev: true + electron-to-chromium@1.4.798: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true + emoji-regex@8.0.0: {} - /encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - dev: false + encodeurl@1.0.2: {} - /engine.io-client@6.5.3: - resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==} + engine.io-client@6.5.4: dependencies: - '@socket.io/component-emitter': 3.1.0 - debug: 4.3.4(supports-color@8.1.1) - engine.io-parser: 5.2.2 - ws: 8.11.0 + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5(supports-color@8.1.1) + engine.io-parser: 5.2.3 + ws: 8.17.1 xmlhttprequest-ssl: 2.0.0 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - /engine.io-parser@5.2.2: - resolution: {integrity: sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==} - engines: {node: '>=10.0.0'} + engine.io-parser@5.2.3: {} - /engine.io@6.5.4: - resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==} - engines: {node: '>=10.2.0'} + engine.io@6.5.5: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 20.11.28 + '@types/node': 22.4.2 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 cors: 2.8.5 - debug: 4.3.4(supports-color@8.1.1) - engine.io-parser: 5.2.2 - ws: 8.11.0 + debug: 4.3.5(supports-color@8.1.1) + engine.io-parser: 5.2.3 + ws: 8.17.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - dev: false - /enhanced-resolve@5.15.0: - resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} - engines: {node: '>=10.13.0'} + enhanced-resolve@5.17.0: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 - dev: true - /entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} + entities@4.5.0: {} - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - dev: true - /es-abstract@1.22.4: - resolution: {integrity: sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==} - engines: {node: '>= 0.4'} + es-abstract@1.23.3: dependencies: array-buffer-byte-length: 1.0.1 arraybuffer.prototype.slice: 1.0.3 available-typed-arrays: 1.0.7 call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 es-define-property: 1.0.0 es-errors: 1.3.0 - es-set-tostringtag: 2.0.2 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 es-to-primitive: 1.2.1 function.prototype.name: 1.1.6 get-intrinsic: 1.2.4 get-symbol-description: 1.0.2 - globalthis: 1.0.3 + globalthis: 1.0.4 gopd: 1.0.1 has-property-descriptors: 1.0.2 has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.1 + hasown: 2.0.2 internal-slot: 1.0.7 is-array-buffer: 3.0.4 is-callable: 1.2.7 + is-data-view: 1.0.1 is-negative-zero: 2.0.3 is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 + is-shared-array-buffer: 1.0.3 is-string: 1.0.7 is-typed-array: 1.1.13 is-weakref: 1.0.2 - object-inspect: 1.13.1 + object-inspect: 1.13.2 object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.0 + safe-array-concat: 1.1.2 safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 - string.prototype.trimstart: 1.0.7 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.0 - typed-array-byte-offset: 1.0.1 - typed-array-length: 1.0.4 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 unbox-primitive: 1.0.2 - which-typed-array: 1.1.14 - dev: true - - /es-array-method-boxes-properly@1.0.0: - resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} - dev: true + which-typed-array: 1.1.15 - /es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + es-errors@1.3.0: {} - /es-set-tostringtag@2.0.2: - resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} - engines: {node: '>= 0.4'} + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.0.3: dependencies: get-intrinsic: 1.2.4 has-tostringtag: 1.0.2 - hasown: 2.0.1 - dev: true + hasown: 2.0.2 - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.0.2: dependencies: - hasown: 2.0.1 - dev: true + hasown: 2.0.2 - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - dev: true - /esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true + esbuild@0.21.5: optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - dev: true - - /escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - dev: false - - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - dev: true - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true - - /eslint-compat-utils@0.1.2(eslint@8.57.0): - resolution: {integrity: sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - eslint: 8.57.0 - dev: true + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.23.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.0 + '@esbuild/android-arm': 0.23.0 + '@esbuild/android-arm64': 0.23.0 + '@esbuild/android-x64': 0.23.0 + '@esbuild/darwin-arm64': 0.23.0 + '@esbuild/darwin-x64': 0.23.0 + '@esbuild/freebsd-arm64': 0.23.0 + '@esbuild/freebsd-x64': 0.23.0 + '@esbuild/linux-arm': 0.23.0 + '@esbuild/linux-arm64': 0.23.0 + '@esbuild/linux-ia32': 0.23.0 + '@esbuild/linux-loong64': 0.23.0 + '@esbuild/linux-mips64el': 0.23.0 + '@esbuild/linux-ppc64': 0.23.0 + '@esbuild/linux-riscv64': 0.23.0 + '@esbuild/linux-s390x': 0.23.0 + '@esbuild/linux-x64': 0.23.0 + '@esbuild/netbsd-x64': 0.23.0 + '@esbuild/openbsd-arm64': 0.23.0 + '@esbuild/openbsd-x64': 0.23.0 + '@esbuild/sunos-x64': 0.23.0 + '@esbuild/win32-arm64': 0.23.0 + '@esbuild/win32-ia32': 0.23.0 + '@esbuild/win32-x64': 0.23.0 + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + escalade@3.1.2: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 - /eslint-config-etherpad@3.0.22(eslint@8.57.0)(typescript@5.4.2): - resolution: {integrity: sha512-RLB4Ry7gvdd+wkifxq16sgRGjRCLk3IofDDlbIHDQNWBhWH7avAu+tdHExj/MxJylZHIYQVhUO2uVG6WdqinVA==} - engines: {node: '>=12.17.0'} - peerDependencies: - eslint: ^8.46.0 - typescript: ^4.5.5 - dependencies: - '@rushstack/eslint-patch': 1.7.2 - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.2) - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2) - eslint: 8.57.0 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-cypress: 2.15.1(eslint@8.57.0) - eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-mocha: 10.3.0(eslint@8.57.0) - eslint-plugin-n: 16.6.2(eslint@8.57.0) - eslint-plugin-prefer-arrow: 1.2.3(eslint@8.57.0) - eslint-plugin-promise: 6.1.1(eslint@8.57.0) - eslint-plugin-you-dont-need-lodash-underscore: 6.13.0 - typescript: 5.4.2 + eslint-compat-utils@0.5.1(eslint@9.9.0): + dependencies: + eslint: 9.9.0 + semver: 7.6.3 + + eslint-config-etherpad@4.0.4(eslint@9.9.0)(typescript@5.5.4): + dependencies: + '@rushstack/eslint-patch': 1.10.3 + '@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/parser': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0) + eslint-plugin-cypress: 2.15.2(eslint@9.9.0) + eslint-plugin-eslint-comments: 3.2.0(eslint@9.9.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.9.0) + eslint-plugin-mocha: 10.4.3(eslint@9.9.0) + eslint-plugin-n: 16.6.2(eslint@9.9.0) + eslint-plugin-prefer-arrow: 1.2.3(eslint@9.9.0) + eslint-plugin-promise: 6.6.0(eslint@9.9.0) + eslint-plugin-you-dont-need-lodash-underscore: 6.14.0 transitivePeerDependencies: + - eslint - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - dev: true + - typescript - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 - is-core-module: 2.13.1 + is-core-module: 2.15.0 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: true - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0): - resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0): dependencies: - debug: 4.3.4(supports-color@8.1.1) - enhanced-resolve: 5.15.0 - eslint: 8.57.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + debug: 4.3.5(supports-color@8.1.1) + enhanced-resolve: 5.17.0 + eslint: 9.9.0 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0))(eslint@9.9.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.9.0) fast-glob: 3.3.2 - get-tsconfig: 4.7.2 - is-core-module: 2.13.1 + get-tsconfig: 4.7.6 + is-core-module: 2.15.0 is-glob: 4.0.3 transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): - resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0))(eslint@9.9.0): dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2) debug: 3.2.7 - eslint: 8.57.0 + optionalDependencies: + '@typescript-eslint/parser': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + eslint: 9.9.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0) transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-cypress@2.15.1(eslint@8.57.0): - resolution: {integrity: sha512-eLHLWP5Q+I4j2AWepYq0PgFEei9/s5LvjuSqWrxurkg1YZ8ltxdvMNmdSf0drnsNo57CTgYY/NIHHLRSWejR7w==} - peerDependencies: - eslint: '>= 3.2.1' + eslint-plugin-cypress@2.15.2(eslint@9.9.0): dependencies: - eslint: 8.57.0 + eslint: 9.9.0 globals: 13.24.0 - dev: true - - /eslint-plugin-es-x@7.5.0(eslint@8.57.0): - resolution: {integrity: sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '>=8' + + eslint-plugin-es-x@7.8.0(eslint@9.9.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.0 - eslint: 8.57.0 - eslint-compat-utils: 0.1.2(eslint@8.57.0) - dev: true + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@eslint-community/regexpp': 4.11.0 + eslint: 9.9.0 + eslint-compat-utils: 0.5.1(eslint@9.9.0) - /eslint-plugin-eslint-comments@3.2.0(eslint@8.57.0): - resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} - engines: {node: '>=6.5.0'} - peerDependencies: - eslint: '>=4.19.1' + eslint-plugin-eslint-comments@3.2.0(eslint@9.9.0): dependencies: escape-string-regexp: 1.0.5 - eslint: 8.57.0 + eslint: 9.9.0 ignore: 5.3.1 - dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.9.0): dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2) - array-includes: 3.1.7 - array.prototype.findlastindex: 1.2.4 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.57.0 + eslint: 9.9.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - hasown: 2.0.1 - is-core-module: 2.13.1 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0))(eslint@9.9.0) + hasown: 2.0.2 + is-core-module: 2.15.0 is-glob: 4.0.3 minimatch: 3.1.2 - object.fromentries: 2.0.7 - object.groupby: 1.0.2 - object.values: 1.1.7 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 7.17.0(eslint@9.9.0)(typescript@5.5.4) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: true - /eslint-plugin-mocha@10.3.0(eslint@8.57.0): - resolution: {integrity: sha512-IWzbg2K6B1Q7h37Ih4zMyW+nhmw1JvUlHlbCUUUu6PfOOAUGCB0gxmvv7/U+TQQ6e8yHUv+q7KMdIIum4bx+PA==} - engines: {node: '>=14.0.0'} - peerDependencies: - eslint: '>=7.0.0' + eslint-plugin-mocha@10.4.3(eslint@9.9.0): dependencies: - eslint: 8.57.0 - eslint-utils: 3.0.0(eslint@8.57.0) + eslint: 9.9.0 + eslint-utils: 3.0.0(eslint@9.9.0) + globals: 13.24.0 rambda: 7.5.0 - dev: true - /eslint-plugin-n@16.6.2(eslint@8.57.0): - resolution: {integrity: sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - eslint: '>=7.0.0' + eslint-plugin-n@16.6.2(eslint@9.9.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - builtins: 5.0.1 - eslint: 8.57.0 - eslint-plugin-es-x: 7.5.0(eslint@8.57.0) - get-tsconfig: 4.7.2 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + builtins: 5.1.0 + eslint: 9.9.0 + eslint-plugin-es-x: 7.8.0(eslint@9.9.0) + get-tsconfig: 4.7.6 globals: 13.24.0 ignore: 5.3.1 is-builtin-module: 3.2.1 - is-core-module: 2.13.1 + is-core-module: 2.15.0 minimatch: 3.1.2 resolve: 1.22.8 - semver: 7.6.0 - dev: true + semver: 7.6.3 - /eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): - resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} - peerDependencies: - eslint: '>=2.0.0' + eslint-plugin-prefer-arrow@1.2.3(eslint@9.9.0): dependencies: - eslint: 8.57.0 - dev: true + eslint: 9.9.0 - /eslint-plugin-promise@6.1.1(eslint@8.57.0): - resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint-plugin-promise@6.6.0(eslint@9.9.0): dependencies: - eslint: 8.57.0 - dev: true + eslint: 9.9.0 - /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint-plugin-react-hooks@4.6.2(eslint@9.9.0): dependencies: - eslint: 8.57.0 - dev: true + eslint: 9.9.0 - /eslint-plugin-react-refresh@0.4.6(eslint@8.57.0): - resolution: {integrity: sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==} - peerDependencies: - eslint: '>=7' + eslint-plugin-react-refresh@0.4.10(eslint@9.9.0): dependencies: - eslint: 8.57.0 - dev: true + eslint: 9.9.0 - /eslint-plugin-you-dont-need-lodash-underscore@6.13.0: - resolution: {integrity: sha512-6FkFLp/R/QlgfJl5NrxkIXMQ36jMVLczkWDZJvMd7/wr/M3K0DS7mtX7plZ3giTDcbDD7VBfNYUfUVaBCZOXKA==} - engines: {node: '>=4.0'} + eslint-plugin-you-dont-need-lodash-underscore@6.14.0: dependencies: kebab-case: 1.0.2 - dev: true - - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - dev: true - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.0.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - dev: true - /eslint-utils@3.0.0(eslint@8.57.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' + eslint-utils@3.0.0(eslint@9.9.0): dependencies: - eslint: 8.57.0 + eslint: 9.9.0 eslint-visitor-keys: 2.1.0 - dev: true - /eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - dev: true + eslint-visitor-keys@2.1.0: {} - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + eslint-visitor-keys@3.4.3: {} - /eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true + eslint-visitor-keys@4.0.0: {} + + eslint@9.9.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@eslint-community/regexpp': 4.11.0 + '@eslint/config-array': 0.17.1 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.9.0 '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) - doctrine: 3.0.0 + debug: 4.3.5(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 + eslint-scope: 8.0.2 + eslint-visitor-keys: 4.0.0 + espree: 10.1.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - 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.3 + optionator: 0.9.4 strip-ansi: 6.0.1 text-table: 0.2.0 transitivePeerDependencies: - supports-color - dev: true - - /esm@3.2.25: - resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} - engines: {node: '>=6'} - requiresBuild: true - dev: false - optional: true - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@10.1.0: dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 - dev: true + acorn: 8.12.0 + acorn-jsx: 5.3.2(acorn@8.12.0) + eslint-visitor-keys: 4.0.0 - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} + esprima@4.0.1: {} + + esquery@1.6.0: dependencies: estraverse: 5.3.0 - dev: true - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 - dev: true - /estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - dev: true + estraverse@5.3.0: {} - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - dev: true + estree-walker@2.0.2: {} - /estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: true + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.5 - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: true + esutils@2.0.3: {} - /etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - dev: false + eta@3.4.0: {} - /etherpad-cli-client@3.0.2: - resolution: {integrity: sha512-3uJxz8cx1WSVAPe7haqfwQj4N4wWDrei5BXBjH+e1BLPiEpqWtnMX29Qo9jWhqO9ctyPPHO0nZ1x/ZCNxMVCqA==} - engines: {node: '>=18.0.0'} - hasBin: true + etag@1.8.1: {} + + etherpad-cli-client@3.0.2: dependencies: - async: 3.2.5 + async: 3.2.6 socket.io-client: 4.7.5 superagent: 8.1.2 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - dev: true - /etherpad-require-kernel@1.0.16: - resolution: {integrity: sha512-Zym7acX8tsB0mjZmQgcHnx9W+8djpFGeSA6/LhbKEGALaoaKBxCH/QQwJmsqfbvfYQBEg+NbUfDp9gD8QVjhsg==} - engines: {node: '>=12.13.0'} - dev: false - - /etherpad-yajsml@0.0.12: - resolution: {integrity: sha512-lVCqsZYpFsuIz417h+O83I7eadNXJ3MnQavriFa52/KTwj6xPAzEYr0PvH7KTxcqyAFtW7ItoTNVXe2h7zGxlw==} - engines: {node: '>=12.13.0'} - optionalDependencies: - mime: 1.6.0 - dev: false + execa@8.0.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 - /express-rate-limit@7.2.0(express@4.18.3): - resolution: {integrity: sha512-T7nul1t4TNyfZMJ7pKRKkdeVJWa2CqB8NA1P8BwYaoDI5QSBZARv5oMS43J7b7I5P+4asjVXjb7ONuwDKucahg==} - engines: {node: '>= 16'} - peerDependencies: - express: 4 || 5 || ^5.0.0-beta.1 + express-rate-limit@7.4.0(express@4.19.2): dependencies: - express: 4.18.3 - dev: false + express: 4.19.2 - /express@4.18.3: - resolution: {integrity: sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==} - engines: {node: '>= 0.10.0'} + express@4.19.2: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 body-parser: 1.20.2 content-disposition: 0.5.4 content-type: 1.0.5 - cookie: 0.5.0 + cookie: 0.6.0 cookie-signature: 1.0.6 debug: 2.6.9 depd: 2.0.0 @@ -3516,66 +7382,53 @@ packages: vary: 1.1.2 transitivePeerDependencies: - supports-color - dev: false - /extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - dev: false + extend@3.0.2: {} - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 - dev: true + micromatch: 4.0.7 - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true + fast-json-stable-stringify@2.1.0: {} - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true + fast-levenshtein@2.0.6: {} - /fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-safe-stringify@2.1.1: {} - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fast-uri@3.0.1: {} + + fastq@1.17.1: dependencies: reusify: 1.0.4 - dev: true - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + fetch-blob@3.2.0: dependencies: - flat-cache: 3.2.0 - dev: true + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 - /filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + fetch-blob@4.0.0: + dependencies: + node-domexception: 1.0.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + filelist@1.0.4: dependencies: minimatch: 5.1.6 - dev: false - /fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - dev: true - /finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} - engines: {node: '>= 0.8'} + finalhandler@1.2.0: dependencies: debug: 2.6.9 encodeurl: 1.0.2 @@ -3586,254 +7439,172 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /find-root@1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - dev: false + find-root@1.1.0: {} - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: dependencies: - flatted: 3.2.9 + flatted: 3.3.1 keyv: 4.5.4 - rimraf: 3.0.2 - dev: true - /flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - dev: true + flat@5.0.2: {} - /flatted@3.2.9: - resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + flatted@3.3.1: {} - /follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false + focus-trap@7.5.4: + dependencies: + tabbable: 6.2.0 - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + follow-redirects@1.15.6: {} + + for-each@0.3.3: dependencies: is-callable: 1.2.7 - dev: true - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} + form-data-encoder@2.1.4: {} + + form-data@4.0.0: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - /formidable@2.1.2: - resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + formidable@2.1.2: dependencies: dezalgo: 1.0.4 hexoid: 1.0.0 once: 1.4.0 - qs: 6.11.2 + qs: 6.12.3 - /formidable@3.5.1: - resolution: {integrity: sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==} + formidable@3.5.1: dependencies: dezalgo: 1.0.4 hexoid: 1.0.0 once: 1.4.0 - dev: false - /forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - dev: false + forwarded@0.2.0: {} - /fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - dev: false + fresh@0.5.2: {} - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - dev: false - /fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - dev: true - /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: false - /fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 - dev: false - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + fs.realpath@1.0.0: {} - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true + fsevents@2.3.2: optional: true - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} + function.prototype.name@1.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 functions-have-names: 1.2.3 - dev: true - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - dev: true + functions-have-names@1.2.3: {} - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: true + gensync@1.0.0-beta.2: {} - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true + get-caller-file@2.0.5: {} - /get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} + get-func-name@2.0.2: {} + + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.1 + hasown: 2.0.2 - /get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - dev: true + get-nonce@1.0.1: {} - /get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} + get-stream@6.0.1: {} + + get-stream@8.0.1: {} + + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - dev: true - /get-tsconfig@4.7.2: - resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + get-tsconfig@4.7.6: dependencies: resolve-pkg-maps: 1.0.0 - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + get-uri@6.0.3: dependencies: - is-glob: 4.0.3 - dev: true + basic-ftp: 5.0.5 + data-uri-to-buffer: 6.0.2 + debug: 4.3.5(supports-color@8.1.1) + fs-extra: 11.2.0 + transitivePeerDependencies: + - supports-color - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + glob-parent@6.0.2: dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true + is-glob: 4.0.3 - /glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} + glob@8.1.0: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 5.0.1 + minimatch: 5.1.6 once: 1.4.0 - dev: true - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - dev: true + globals@11.12.0: {} - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@13.24.0: dependencies: type-fest: 0.20.2 - dev: true - /globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} + globals@14.0.0: {} + + globalthis@1.0.4: dependencies: define-properties: 1.2.1 - dev: true + gopd: 1.0.1 - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -3841,104 +7612,85 @@ packages: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - dev: true - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + got@13.0.0: + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: true + graceful-fs@4.2.11: {} - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true + graphemer@1.4.0: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - dev: true + has-bigints@1.0.2: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@3.0.0: {} - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.0 - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} + has-proto@1.0.3: {} - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} + has-symbols@1.0.3: {} - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 - dev: true - /hasown@2.0.1: - resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /hast-util-embedded@3.0.0: - resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + hast-util-embedded@3.0.0: dependencies: '@types/hast': 3.0.4 hast-util-is-element: 3.0.0 - dev: false - /hast-util-from-html@2.0.1: - resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==} + hast-util-from-html@2.0.1: dependencies: '@types/hast': 3.0.4 devlop: 1.1.0 hast-util-from-parse5: 8.0.1 parse5: 7.1.2 - vfile: 6.0.1 + vfile: 6.0.2 vfile-message: 4.0.2 - dev: false - /hast-util-from-parse5@8.0.1: - resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} + hast-util-from-parse5@8.0.1: dependencies: '@types/hast': 3.0.4 '@types/unist': 3.0.2 devlop: 1.1.0 hastscript: 8.0.0 - property-information: 6.4.1 - vfile: 6.0.1 - vfile-location: 5.0.2 + property-information: 6.5.0 + vfile: 6.0.2 + vfile-location: 5.0.3 web-namespaces: 2.0.1 - dev: false - /hast-util-is-element@3.0.0: - resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + hast-util-is-element@3.0.0: dependencies: '@types/hast': 3.0.4 - dev: false - /hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 - dev: false - /hast-util-raw@9.0.2: - resolution: {integrity: sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==} + hast-util-raw@9.0.4: dependencies: '@types/hast': 3.0.4 '@types/unist': 3.0.2 @@ -3946,397 +7698,283 @@ packages: hast-util-from-parse5: 8.0.1 hast-util-to-parse5: 8.0.0 html-void-elements: 3.0.0 - mdast-util-to-hast: 13.1.0 + mdast-util-to-hast: 13.2.0 parse5: 7.1.2 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 - vfile: 6.0.1 + vfile: 6.0.2 web-namespaces: 2.0.1 zwitch: 2.0.4 - dev: false - /hast-util-to-html@9.0.0: - resolution: {integrity: sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==} + hast-util-to-html@9.0.1: dependencies: '@types/hast': 3.0.4 '@types/unist': 3.0.2 ccount: 2.0.1 comma-separated-tokens: 2.0.3 - hast-util-raw: 9.0.2 + hast-util-raw: 9.0.4 hast-util-whitespace: 3.0.0 html-void-elements: 3.0.0 - mdast-util-to-hast: 13.1.0 - property-information: 6.4.1 + mdast-util-to-hast: 13.2.0 + property-information: 6.5.0 space-separated-tokens: 2.0.2 - stringify-entities: 4.0.3 + stringify-entities: 4.0.4 zwitch: 2.0.4 - dev: false - /hast-util-to-parse5@8.0.0: - resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + hast-util-to-parse5@8.0.0: dependencies: '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 devlop: 1.1.0 - property-information: 6.4.1 + property-information: 6.5.0 space-separated-tokens: 2.0.2 web-namespaces: 2.0.1 zwitch: 2.0.4 - dev: false - /hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hast-util-whitespace@3.0.0: dependencies: '@types/hast': 3.0.4 - dev: false - /hastscript@8.0.0: - resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} + hastscript@8.0.0: dependencies: '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 4.0.0 - property-information: 6.4.1 + property-information: 6.5.0 space-separated-tokens: 2.0.2 - dev: false - /he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - dev: true + he@1.2.0: {} - /hexoid@1.0.0: - resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} - engines: {node: '>=8'} + hexoid@1.0.0: {} - /html-encoding-sniffer@4.0.0: - resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} - engines: {node: '>=18'} + hookable@5.5.3: {} + + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 - dev: false - /html-parse-stringify@3.0.1: - resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + html-parse-stringify@3.0.1: dependencies: void-elements: 3.1.0 - dev: true - /html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - dev: false + html-void-elements@3.0.0: {} - /http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} + http-assert@1.5.0: + dependencies: + deep-equal: 1.0.1 + http-errors: 1.8.1 + + http-cache-semantics@4.1.1: {} + + http-errors@1.8.1: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + + http-errors@2.0.0: dependencies: depd: 2.0.0 inherits: 2.0.4 setprototypeof: 1.2.0 statuses: 2.0.1 toidentifier: 1.0.1 - dev: false - /http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} + http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + agent-base: 7.1.1 + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color - dev: false - /https-proxy-agent@7.0.4: - resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} - engines: {node: '>= 14'} + http2-wrapper@2.2.1: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + + https-proxy-agent@7.0.5: dependencies: - agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + agent-base: 7.1.1 + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color - dev: false - /i18next-browser-languagedetector@7.2.0: - resolution: {integrity: sha512-U00DbDtFIYD3wkWsr2aVGfXGAj2TgnELzOX9qv8bT0aJtvPV9CRO77h+vgmHFBMe7LAxdwvT/7VkCWGya6L3tA==} + human-signals@5.0.0: {} + + i18next-browser-languagedetector@8.0.0: dependencies: - '@babel/runtime': 7.24.0 - dev: true + '@babel/runtime': 7.24.7 - /i18next@23.10.1: - resolution: {integrity: sha512-NDiIzFbcs3O9PXpfhkjyf7WdqFn5Vq6mhzhtkXzj51aOcNuPNcTwuYNuXCpHsanZGHlHKL35G7huoFeVic1hng==} + i18next@23.14.0: dependencies: - '@babel/runtime': 7.24.0 - dev: true + '@babel/runtime': 7.24.8 - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: false - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - dev: false - /ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - dev: true + ignore@5.3.1: {} - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - dev: true - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true + imurmurhash@0.1.4: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 - hasown: 2.0.1 - side-channel: 1.0.5 - dev: true + hasown: 2.0.2 + side-channel: 1.0.6 - /invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + ip-address@9.0.5: dependencies: - loose-envify: 1.4.0 - dev: true + jsbn: 1.1.0 + sprintf-js: 1.1.3 - /ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - dev: false + ipaddr.js@1.9.1: {} - /is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: true - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: true + is-arrayish@0.2.1: {} - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 - dev: true - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + is-binary-path@2.1.0: dependencies: - binary-extensions: 2.2.0 - dev: true + binary-extensions: 2.3.0 - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true - /is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 - dev: true - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: true + is-callable@1.2.7: {} - /is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.15.0: dependencies: - hasown: 2.0.1 + hasown: 2.0.2 - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} + is-data-view@1.0.1: + dependencies: + is-typed-array: 1.1.13 + + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + is-extglob@2.1.1: {} - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: true + is-fullwidth-code-point@3.0.0: {} - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-generator-function@1.0.10: + dependencies: + has-tostringtag: 1.0.2 + + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - dev: true - /is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - dev: true + is-negative-zero@2.0.3: {} - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 - dev: true - - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true - /is-observable@2.1.0: - resolution: {integrity: sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==} - engines: {node: '>=8'} - dev: false + is-number@7.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: true + is-path-inside@3.0.3: {} - /is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - dev: true + is-plain-obj@2.1.0: {} - /is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - dev: false + is-plain-obj@4.1.0: {} - /is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - dev: false + is-potential-custom-element-name@1.0.1: {} - /is-promise@1.0.1: - resolution: {integrity: sha512-mjWH5XxnhMA8cFnDchr6qRP9S/kLntKuEfIYku+PaN1CnS8v+OG9O/BKpRCVRJvpIkgAZm0Pf5Is3iSSOILlcg==} - dev: true + is-promise@1.0.1: {} - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 - dev: true - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} + is-stream@3.0.0: {} + + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 - dev: true - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} + is-typed-array@1.1.13: dependencies: - which-typed-array: 1.1.14 - dev: true + which-typed-array: 1.1.15 - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true + is-unicode-supported@0.1.0: {} - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.0.2: dependencies: call-bind: 1.0.7 - dev: true - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true + is-what@4.1.16: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isarray@2.0.5: {} - /jake@10.8.7: - resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} - engines: {node: '>=10'} - hasBin: true + isexe@2.0.0: {} + + jake@10.9.2: dependencies: - async: 3.2.5 + async: 3.2.6 chalk: 4.1.2 filelist: 1.0.4 minimatch: 3.1.2 - dev: false - /js-cookie@3.0.5: - resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} - engines: {node: '>=14'} - dev: false + jose@5.7.0: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true + js-cookie@3.0.5: {} - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-tokens@4.0.0: {} + + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - /jsdom@24.0.0: - resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==} - engines: {node: '>=18'} - peerDependencies: - canvas: ^2.11.2 - peerDependenciesMeta: - canvas: - optional: true + jsbn@1.1.0: {} + + jsdom@24.1.1: dependencies: cssstyle: 4.0.1 data-urls: 5.0.0 @@ -4344,576 +7982,495 @@ packages: form-data: 4.0.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.7 + nwsapi: 2.2.12 parse5: 7.1.2 - rrweb-cssom: 0.6.0 + rrweb-cssom: 0.7.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 4.1.3 + tough-cookie: 4.1.4 w3c-xmlserializer: 5.0.0 webidl-conversions: 7.0.0 whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.0.0 - ws: 8.16.0 + ws: 8.18.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - dev: false - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - dev: true + jsesc@2.5.2: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true + jsesc@3.0.2: {} - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: true + json-buffer@3.0.1: {} - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true + json-parse-even-better-errors@2.3.1: {} - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: false + json-schema-traverse@0.4.1: {} - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true + json-schema-traverse@1.0.0: {} - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: dependencies: minimist: 1.2.8 - dev: true - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true + json5@2.2.3: {} - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - dev: false - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - /jsonminify@0.4.2: - resolution: {integrity: sha512-mEtP5ECD0293D+s45JhDutqF5mFCkWY8ClrPFxjSFR2KUoantofky7noSzyKnAnD9Gd8pXHZSUd5bgzLDUBbfA==} - engines: {node: '>=0.8.0', npm: '>=1.1.0'} - dev: false + jsonminify@0.4.2: {} - /jsonschema-draft4@1.0.0: - resolution: {integrity: sha512-sBV3UnQPRiyCTD6uzY/Oao2Yohv6KKgQq7zjPwjFHeR6scg/QSXnzDxdugsGaLQDmFUrUlTbMYdEE+72PizhGA==} - dev: true + jsonschema-draft4@1.0.0: {} - /jsonschema@1.2.4: - resolution: {integrity: sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==} - dev: true + jsonschema@1.2.4: {} - /just-extend@6.2.0: - resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} - dev: true + jsonwebtoken@9.0.2: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.6.3 - /kebab-case@1.0.2: - resolution: {integrity: sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q==} - dev: true + just-extend@6.2.0: {} - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + jwa@1.4.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + + kebab-case@1.0.2: {} + + keygrip@1.1.0: + dependencies: + tsscmp: 1.0.6 + + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - dev: true - /languages4translatewiki@0.1.3: - resolution: {integrity: sha512-Z7+IM3FF+VyRbWl2CPWQoRf498zGy/qnolP5wJhMny4W3v0SL9rUCIPDHPao9rrw2yg2KIKnHIzopuWvGdnooQ==} - dev: false + koa-compose@4.1.0: {} - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} + koa-convert@2.0.0: + dependencies: + co: 4.6.0 + koa-compose: 4.1.0 + + koa@2.15.3: + dependencies: + accepts: 1.3.8 + cache-content-type: 1.0.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookies: 0.9.1 + debug: 4.3.5(supports-color@8.1.1) + delegates: 1.0.0 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + fresh: 0.5.2 + http-assert: 1.5.0 + http-errors: 1.8.1 + is-generator-function: 1.0.10 + koa-compose: 4.1.0 + koa-convert: 2.0.0 + on-finished: 2.4.1 + only: 0.0.2 + parseurl: 1.3.3 + statuses: 1.5.0 + type-is: 1.6.18 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + languages4translatewiki@0.1.3: {} + + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - dev: true - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true + lines-and-columns@1.2.4: {} - /live-plugin-manager-pnpm@0.18.1: - resolution: {integrity: sha512-B+H8lL1LrXS8VIXyyVAoX9NPQx4aVFRvjbQEfBLiaxEMEH/MqI/+Yxyetps9/TfVdes8LCLzDMTWoo/lxZpnSw==} + live-plugin-manager@1.0.0: dependencies: '@types/debug': 4.1.12 '@types/fs-extra': 9.0.13 '@types/lockfile': 1.0.4 '@types/node-fetch': 2.6.11 '@types/semver': 7.5.8 - '@types/tar': 6.1.11 - '@types/url-join': 4.0.1 - debug: 4.3.4(supports-color@8.1.1) + '@types/tar': 6.1.13 + '@types/url-join': 4.0.3 + debug: 4.3.5(supports-color@8.1.1) + fetch-blob: 4.0.0 + formdata-polyfill: 4.0.10 fs-extra: 10.1.0 lockfile: 1.0.4 - node-fetch: 2.7.0 - semver: 7.6.0 - tar: 6.2.0 + node-fetch-commonjs: 3.3.2 + proxy-agent: 6.4.0 + semver: 7.6.3 + tar: 6.2.1 url-join: 4.0.1 transitivePeerDependencies: - - encoding - supports-color - dev: false - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - /lockfile@1.0.4: - resolution: {integrity: sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==} + lockfile@1.0.4: dependencies: signal-exit: 3.0.7 - dev: false - /lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - dev: false + lodash.clonedeep@4.5.0: {} - /lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - dev: true + lodash.get@4.4.2: {} - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.includes@4.3.0: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: false + lodash.isboolean@3.0.3: {} - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true - /log4js@6.9.1: - resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} - engines: {node: '>=8.0'} + log4js@6.9.1: dependencies: date-format: 4.0.14 - debug: 4.3.4(supports-color@8.1.1) - flatted: 3.2.9 - rfdc: 1.3.1 + debug: 4.3.5(supports-color@8.1.1) + flatted: 3.3.1 + rfdc: 1.4.1 streamroller: 3.1.5 transitivePeerDependencies: - supports-color - dev: false - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - dev: true - /lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + loupe@3.1.1: dependencies: - tslib: 2.6.2 - dev: true + get-func-name: 2.0.2 - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lower-case@2.0.2: + dependencies: + tslib: 2.6.3 + + lowercase-keys@3.0.0: {} + + lru-cache@11.0.0: {} + + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - dev: true - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + lru-cache@7.18.3: {} + + lucide-react@0.429.0(react@18.3.1): dependencies: - yallist: 4.0.0 + react: 18.3.1 - /lucide-react@0.356.0(react@18.2.0): - resolution: {integrity: sha512-MDInjLrmZToccH2UxEshntujBlFwtOofGB22FN/eg39FfGVYV1TT1eMIv2j4rdaTJBpYjUuX7fEo9pwYkNFgwA==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 + magic-string@0.30.10: dependencies: - react: 18.2.0 - dev: true + '@jridgewell/sourcemap-codec': 1.4.15 - /mdast-util-to-hast@13.1.0: - resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==} + mark.js@8.11.1: {} + + mdast-util-to-hast@13.2.0: dependencies: '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 '@ungap/structured-clone': 1.2.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.0 trim-lines: 3.0.1 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 - vfile: 6.0.1 - dev: false + vfile: 6.0.2 - /measured-core@2.0.0: - resolution: {integrity: sha512-SIzGtX1WGDvR59FqcJaGEAqDueBvLBh6W4T/gQaHr5ufcqvQkUHGcfQhlmq77mkeF5Mo+UpD+8hm69CwUVibGw==} - engines: {node: '>= 5.12'} + measured-core@2.0.0: dependencies: binary-search: 1.3.6 optional-js: 2.3.0 - dev: false - /media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - dev: false + media-typer@0.3.0: {} - /merge-descriptors@1.0.1: - resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} - dev: false + merge-descriptors@1.0.1: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true + merge-stream@2.0.0: {} - /methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} + merge2@1.4.1: {} - /micromark-util-character@2.1.0: - resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + methods@1.1.2: {} + + micromark-util-character@2.1.0: dependencies: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - dev: false - /micromark-util-encode@2.0.0: - resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} - dev: false + micromark-util-encode@2.0.0: {} - /micromark-util-sanitize-uri@2.0.0: - resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + micromark-util-sanitize-uri@2.0.0: dependencies: micromark-util-character: 2.1.0 micromark-util-encode: 2.0.0 micromark-util-symbol: 2.0.0 - dev: false - /micromark-util-symbol@2.0.0: - resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} - dev: false + micromark-util-symbol@2.0.0: {} - /micromark-util-types@2.0.0: - resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} - dev: false + micromark-util-types@2.0.0: {} - /micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} + micromatch@4.0.7: dependencies: - braces: 3.0.2 + braces: 3.0.3 picomatch: 2.3.1 - dev: true - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} + mime-db@1.52.0: {} - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - dev: false + mime@1.6.0: {} - /mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true + mime@2.6.0: {} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 + mimic-fn@4.0.0: {} - /minimatch@5.0.1: - resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} - engines: {node: '>=10'} + mimic-response@3.1.0: {} + + mimic-response@4.0.0: {} + + minimatch@3.1.2: dependencies: - brace-expansion: 2.0.1 - dev: true + brace-expansion: 1.1.11 - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 - dev: false - /minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: true - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true + minimist@1.2.8: {} - /minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} + minipass@3.3.6: dependencies: yallist: 4.0.0 - dev: false - /minipass@4.2.8: - resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} - engines: {node: '>=8'} - dev: false + minipass@4.2.8: {} - /minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - dev: false + minipass@5.0.0: {} - /minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + minisearch@7.1.0: {} + + minizlib@2.1.2: dependencies: minipass: 3.3.6 yallist: 4.0.0 - dev: false - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: false + mitt@3.0.1: {} - /mocha-froth@0.2.10: - resolution: {integrity: sha512-xyJqAYtm2zjrkG870hjeSVvGgS4Dc9tRokmN6R7XLgBKhdtAJ1ytU6zL045djblfHaPyTkSerQU4wqcjsv7Aew==} - dev: true + mkdirp@1.0.4: {} - /mocha@10.3.0: - resolution: {integrity: sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==} - engines: {node: '>= 14.0.0'} - hasBin: true + mocha-froth@0.2.10: {} + + mocha@10.7.3: dependencies: - ansi-colors: 4.1.1 + ansi-colors: 4.1.3 browser-stdout: 1.3.1 - chokidar: 3.5.3 - debug: 4.3.4(supports-color@8.1.1) - diff: 5.0.0 + chokidar: 3.6.0 + debug: 4.3.5(supports-color@8.1.1) + diff: 5.2.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 glob: 8.1.0 he: 1.2.0 js-yaml: 4.1.0 log-symbols: 4.1.0 - minimatch: 5.0.1 + minimatch: 5.1.6 ms: 2.1.3 - serialize-javascript: 6.0.0 + serialize-javascript: 6.0.2 strip-json-comments: 3.1.1 supports-color: 8.1.1 - workerpool: 6.2.1 + workerpool: 6.5.1 yargs: 16.2.0 - yargs-parser: 20.2.4 + yargs-parser: 20.2.9 yargs-unparser: 2.0.0 - dev: true - /mock-json-schema@1.1.1: - resolution: {integrity: sha512-YV23vlsLP1EEOy0EviUvZTluXjLR+rhMzeayP2rcDiezj3RW01MhOSQkbQskdtg0K2fnGas5LKbSXgNjAOSX4A==} + mock-json-schema@1.1.1: dependencies: lodash: 4.17.21 - dev: false - /ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - dev: false + ms@2.0.0: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.2: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true + nanoid@3.3.7: {} - /natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true + nanoid@5.0.7: {} - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true + natural-compare@1.4.0: {} - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: false + negotiator@0.6.3: {} + + netmask@2.0.2: {} - /nise@5.1.9: - resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} + nise@6.0.0: dependencies: '@sinonjs/commons': 3.0.1 '@sinonjs/fake-timers': 11.2.2 '@sinonjs/text-encoding': 0.7.2 just-extend: 6.2.0 - path-to-regexp: 6.2.1 - dev: true + path-to-regexp: 6.2.2 - /no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.6.2 - dev: true + tslib: 2.6.3 - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + node-domexception@1.0.0: {} + + node-fetch-commonjs@3.3.2: dependencies: - whatwg-url: 5.0.0 - dev: false + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 - /node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - dev: true + node-releases@2.0.14: {} - /nodeify@1.0.1: - resolution: {integrity: sha512-n7C2NyEze8GCo/z73KdbjRsBiLbv6eBn1FxwYKQ23IqGo7pQY3mhQan61Sv7eEDJCiyUjTVrVkXTzJCo1dW7Aw==} + nodeify@1.0.1: dependencies: is-promise: 1.0.1 promise: 1.3.0 - dev: true - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /nwsapi@2.2.7: - resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} - dev: false + normalize-url@8.0.1: {} - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - dev: false + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 - /object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + nwsapi@2.2.12: {} - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - dev: true + object-assign@4.1.1: {} - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} + object-hash@3.0.0: {} + + object-inspect@1.13.2: {} + + object-keys@1.1.1: {} + + object.assign@4.1.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - dev: true - /object.fromentries@2.0.7: - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} - engines: {node: '>= 0.4'} + object.fromentries@2.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 - /object.groupby@1.0.2: - resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==} + object.groupby@1.0.3: dependencies: - array.prototype.filter: 1.0.3 call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - es-errors: 1.3.0 - dev: true + es-abstract: 1.23.3 - /object.values@1.1.7: - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} - engines: {node: '>= 0.4'} + object.values@1.2.0: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true + es-object-atoms: 1.0.0 + + oidc-provider@8.5.1: + dependencies: + '@koa/cors': 5.0.0 + '@koa/router': 12.0.1 + debug: 4.3.5(supports-color@8.1.1) + eta: 3.4.0 + got: 13.0.0 + jose: 5.7.0 + jsesc: 3.0.2 + koa: 2.15.3 + nanoid: 5.0.7 + object-hash: 3.0.0 + oidc-token-hash: 5.0.3 + quick-lru: 7.0.0 + raw-body: 2.5.2 + transitivePeerDependencies: + - supports-color - /observable-fns@0.6.1: - resolution: {integrity: sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==} - dev: false + oidc-token-hash@5.0.3: {} - /on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 - dev: false - /on-headers@1.0.2: - resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} - engines: {node: '>= 0.8'} - dev: false + on-headers@1.0.2: {} - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - /openapi-backend@5.10.6: - resolution: {integrity: sha512-vTjBRys/O4JIHdlRHUKZ7pxS+gwIJreAAU9dvYRFrImtPzQ5qxm5a6B8BTVT9m6I8RGGsShJv35MAc3Tu2/y/A==} - engines: {node: '>=12.0.0'} + onetime@6.0.0: dependencies: - '@apidevtools/json-schema-ref-parser': 11.1.0 - ajv: 8.12.0 + mimic-fn: 4.0.0 + + only@0.0.2: {} + + openapi-backend@5.10.6: + dependencies: + '@apidevtools/json-schema-ref-parser': 11.6.4 + ajv: 8.17.1 bath-es5: 3.0.3 cookie: 0.5.0 dereference-json-schema: 0.2.1 @@ -4921,560 +8478,379 @@ packages: mock-json-schema: 1.1.1 openapi-schema-validator: 12.1.3 openapi-types: 12.1.3 - qs: 6.11.2 - dev: false + qs: 6.12.3 - /openapi-schema-validation@0.4.2: - resolution: {integrity: sha512-K8LqLpkUf2S04p2Nphq9L+3bGFh/kJypxIG2NVGKX0ffzT4NQI9HirhiY6Iurfej9lCu7y4Ndm4tv+lm86Ck7w==} + openapi-schema-validation@0.4.2: dependencies: jsonschema: 1.2.4 jsonschema-draft4: 1.0.0 swagger-schema-official: 2.0.0-bab6bed - dev: true - /openapi-schema-validator@12.1.3: - resolution: {integrity: sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ==} + openapi-schema-validator@12.1.3: dependencies: - ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) lodash.merge: 4.6.2 openapi-types: 12.1.3 - dev: false - /openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - dev: false + openapi-types@12.1.3: {} - /optional-js@2.3.0: - resolution: {integrity: sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==} - dev: false + optional-js@2.3.0: {} - /optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} - engines: {node: '>= 0.8.0'} + optionator@0.9.4: dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 - dev: true + word-wrap: 1.2.5 - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-cancelable@3.0.0: {} + + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} + pac-proxy-agent@7.0.2: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.1 + debug: 4.3.5(supports-color@8.1.1) + get-uri: 6.0.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.5 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.4 + transitivePeerDependencies: + - supports-color + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + + parent-module@1.0.1: dependencies: callsites: 3.1.0 - dev: true - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} + parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - dev: true - /parse5@7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + parse5@7.1.2: dependencies: entities: 4.5.0 - /parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - dev: false + parseurl@1.3.3: {} - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true + path-exists@4.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + path-key@3.1.1: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} + path-key@4.0.0: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-parse@1.0.7: {} - /path-to-regexp@0.1.7: - resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} - dev: false + path-to-regexp@0.1.7: {} - /path-to-regexp@6.2.1: - resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} - dev: true + path-to-regexp@6.2.2: {} - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true + path-type@4.0.0: {} - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true + pathe@1.1.2: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true + pathval@2.0.0: {} - /playwright-core@1.42.1: - resolution: {integrity: sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==} - engines: {node: '>=16'} - hasBin: true - dev: true + perfect-debounce@1.0.0: {} - /playwright@1.42.1: - resolution: {integrity: sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==} - engines: {node: '>=16'} - hasBin: true + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + playwright-core@1.46.1: {} + + playwright@1.46.1: dependencies: - playwright-core: 1.42.1 + playwright-core: 1.46.1 optionalDependencies: fsevents: 2.3.2 - dev: true - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - dev: true + possible-typed-array-names@1.0.0: {} - /postcss@8.4.35: - resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.41: dependencies: nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: true + picocolors: 1.0.1 + source-map-js: 1.2.0 - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true + preact@10.22.0: {} - /promise@1.3.0: - resolution: {integrity: sha512-R9WrbTF3EPkVtWjp7B7umQGVndpsi+rsDAfrR4xAALQpFLa/+2OriecLhawxzvii2gd9+DZFwROWDuUUaqS5yA==} + prelude-ls@1.2.1: {} + + promise@1.3.0: dependencies: is-promise: 1.0.1 - dev: true - /property-information@6.4.1: - resolution: {integrity: sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==} - dev: false + property-information@6.5.0: {} - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - dev: false - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: false + proxy-agent@6.4.0: + dependencies: + agent-base: 7.1.1 + debug: 4.3.5(supports-color@8.1.1) + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.5 + lru-cache: 7.18.3 + pac-proxy-agent: 7.0.2 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.4 + transitivePeerDependencies: + - supports-color - /psl@1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - dev: false + proxy-from-env@1.1.0: {} - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + psl@1.9.0: {} - /qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} + punycode@2.3.1: {} + + qs@6.11.0: dependencies: - side-channel: 1.0.5 - dev: false + side-channel: 1.0.6 - /qs@6.11.2: - resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} - engines: {node: '>=0.6'} + qs@6.12.3: dependencies: - side-channel: 1.0.5 + side-channel: 1.0.6 - /querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - dev: false + querystringify@2.2.0: {} - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true + queue-microtask@1.2.3: {} - /rambda@7.5.0: - resolution: {integrity: sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==} - dev: true + quick-lru@5.1.1: {} - /random-bytes@1.0.0: - resolution: {integrity: sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==} - engines: {node: '>= 0.8'} - dev: false + quick-lru@7.0.0: {} - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + rambda@7.5.0: {} + + random-bytes@1.0.0: {} + + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - dev: true - /range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - dev: false + range-parser@1.2.1: {} - /rate-limiter-flexible@5.0.0: - resolution: {integrity: sha512-ivCyLBwPtR5IRrz+aZnztVwX16ZK3iAjdlW21I/vjHq56at5Zb8eIefDzODg8R7hwPOHpBtb6Pj9Zdmn0nRb8g==} - dev: false + rate-limiter-flexible@5.0.3: {} - /raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} + raw-body@2.5.2: dependencies: bytes: 3.1.2 http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - dev: false - /react-dom@18.2.0(react@18.2.0): - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} - peerDependencies: - react: ^18.2.0 + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 - dev: true + react: 18.3.1 + scheduler: 0.23.2 - /react-hook-form@7.51.0(react@18.2.0): - resolution: {integrity: sha512-BggOy5j58RdhdMzzRUHGOYhSz1oeylFAv6jUSG86OvCIvlAvS7KvnRY7yoAf2pfEiPN7BesnR0xx73nEk3qIiw==} - engines: {node: '>=12.22.0'} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 + react-hook-form@7.52.2(react@18.3.1): dependencies: - react: 18.2.0 - dev: true + react: 18.3.1 - /react-i18next@14.1.0(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-3KwX6LHpbvGQ+sBEntjV4sYW3Zovjjl3fpoHbUwSgFHf0uRBcbeCBLR5al6ikncI5+W0EFb71QXZmfop+J6NrQ==} - peerDependencies: - i18next: '>= 23.2.3' - react: '>= 16.8.0' - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true + react-i18next@15.0.1(i18next@23.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.24.0 + '@babel/runtime': 7.24.8 html-parse-stringify: 3.0.1 - i18next: 23.10.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true + i18next: 23.14.0 + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) - /react-remove-scroll-bar@2.3.5(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + react-remove-scroll-bar@2.3.6(@types/react@18.3.4)(react@18.3.1): dependencies: - '@types/react': 18.2.65 - react: 18.2.0 - react-style-singleton: 2.2.1(@types/react@18.2.65)(react@18.2.0) - tslib: 2.6.2 - dev: true + react: 18.3.1 + react-style-singleton: 2.2.1(@types/react@18.3.4)(react@18.3.1) + tslib: 2.6.3 + optionalDependencies: + '@types/react': 18.3.4 - /react-remove-scroll@2.5.5(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + react-remove-scroll@2.5.7(@types/react@18.3.4)(react@18.3.1): dependencies: - '@types/react': 18.2.65 - react: 18.2.0 - react-remove-scroll-bar: 2.3.5(@types/react@18.2.65)(react@18.2.0) - react-style-singleton: 2.2.1(@types/react@18.2.65)(react@18.2.0) - tslib: 2.6.2 - use-callback-ref: 1.3.1(@types/react@18.2.65)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.2.65)(react@18.2.0) - dev: true - - /react-router-dom@6.22.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' + react: 18.3.1 + react-remove-scroll-bar: 2.3.6(@types/react@18.3.4)(react@18.3.1) + react-style-singleton: 2.2.1(@types/react@18.3.4)(react@18.3.1) + tslib: 2.6.3 + use-callback-ref: 1.3.2(@types/react@18.3.4)(react@18.3.1) + use-sidecar: 1.1.2(@types/react@18.3.4)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + + react-router-dom@6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@remix-run/router': 1.15.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-router: 6.22.3(react@18.2.0) - dev: true + '@remix-run/router': 1.19.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.26.1(react@18.3.1) - /react-router@6.22.3(react@18.2.0): - resolution: {integrity: sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' + react-router@6.26.1(react@18.3.1): dependencies: - '@remix-run/router': 1.15.3 - react: 18.2.0 - dev: true + '@remix-run/router': 1.19.1 + react: 18.3.1 - /react-style-singleton@2.2.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + react-style-singleton@2.2.1(@types/react@18.3.4)(react@18.3.1): dependencies: - '@types/react': 18.2.65 get-nonce: 1.0.1 invariant: 2.2.4 - react: 18.2.0 - tslib: 2.6.2 - dev: true + react: 18.3.1 + tslib: 2.6.3 + optionalDependencies: + '@types/react': 18.3.4 - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} - engines: {node: '>=0.10.0'} + react@18.3.1: dependencies: loose-envify: 1.4.0 - dev: true - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - dev: true - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: true + regenerator-runtime@0.14.1: {} - /regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 - dev: true - /rehype-minify-whitespace@6.0.0: - resolution: {integrity: sha512-i9It4YHR0Sf3GsnlR5jFUKXRr9oayvEk9GKQUkwZv6hs70OH9q3OCZrq9PpLvIGKt3W+JxBOxCidNVpH/6rWdA==} + rehype-minify-whitespace@6.0.0: dependencies: '@types/hast': 3.0.4 hast-util-embedded: 3.0.0 hast-util-is-element: 3.0.0 hast-util-whitespace: 3.0.0 unist-util-is: 6.0.0 - dev: false - /rehype-parse@9.0.0: - resolution: {integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==} + rehype-parse@9.0.0: dependencies: '@types/hast': 3.0.4 hast-util-from-html: 2.0.1 - unified: 11.0.4 - dev: false + unified: 11.0.5 - /rehype-stringify@10.0.0: - resolution: {integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==} + rehype-stringify@10.0.0: dependencies: '@types/hast': 3.0.4 - hast-util-to-html: 9.0.0 - unified: 11.0.4 - dev: false + hast-util-to-html: 9.0.1 + unified: 11.0.5 - /rehype@13.0.1: - resolution: {integrity: sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg==} + rehype@13.0.1: dependencies: '@types/hast': 3.0.4 rehype-parse: 9.0.0 rehype-stringify: 10.0.0 - unified: 11.0.4 - dev: false + unified: 11.0.5 - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - dev: false + require-from-string@2.0.2: {} - /requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - dev: false + requires-port@1.0.0: {} - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - dev: true + resolve-alpn@1.2.1: {} - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-from@4.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.8: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.15.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true + responselike@3.0.0: + dependencies: + lowercase-keys: 3.0.0 - /rfdc@1.3.1: - resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} - dev: false + reusify@1.0.4: {} - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: true + rfdc@1.4.1: {} - /rollup@4.13.0: - resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.21.0: dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.13.0 - '@rollup/rollup-android-arm64': 4.13.0 - '@rollup/rollup-darwin-arm64': 4.13.0 - '@rollup/rollup-darwin-x64': 4.13.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.13.0 - '@rollup/rollup-linux-arm64-gnu': 4.13.0 - '@rollup/rollup-linux-arm64-musl': 4.13.0 - '@rollup/rollup-linux-riscv64-gnu': 4.13.0 - '@rollup/rollup-linux-x64-gnu': 4.13.0 - '@rollup/rollup-linux-x64-musl': 4.13.0 - '@rollup/rollup-win32-arm64-msvc': 4.13.0 - '@rollup/rollup-win32-ia32-msvc': 4.13.0 - '@rollup/rollup-win32-x64-msvc': 4.13.0 + '@rollup/rollup-android-arm-eabi': 4.21.0 + '@rollup/rollup-android-arm64': 4.21.0 + '@rollup/rollup-darwin-arm64': 4.21.0 + '@rollup/rollup-darwin-x64': 4.21.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.21.0 + '@rollup/rollup-linux-arm-musleabihf': 4.21.0 + '@rollup/rollup-linux-arm64-gnu': 4.21.0 + '@rollup/rollup-linux-arm64-musl': 4.21.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.21.0 + '@rollup/rollup-linux-riscv64-gnu': 4.21.0 + '@rollup/rollup-linux-s390x-gnu': 4.21.0 + '@rollup/rollup-linux-x64-gnu': 4.21.0 + '@rollup/rollup-linux-x64-musl': 4.21.0 + '@rollup/rollup-win32-arm64-msvc': 4.21.0 + '@rollup/rollup-win32-ia32-msvc': 4.21.0 + '@rollup/rollup-win32-x64-msvc': 4.21.0 fsevents: 2.3.3 - dev: true - /rrweb-cssom@0.6.0: - resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} - dev: false + rrweb-cssom@0.6.0: {} - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rrweb-cssom@0.7.1: {} + + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - dev: true - /safe-array-concat@1.1.0: - resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==} - engines: {node: '>=0.4'} + safe-array-concat@1.1.2: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 has-symbols: 1.0.3 isarray: 2.0.5 - dev: true - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-regex: 1.1.4 - dev: true - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: false + safer-buffer@2.1.2: {} - /saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 - dev: false - /scheduler@0.23.0: - resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 - dev: true - /security@1.0.0: - resolution: {integrity: sha512-5qfoAgfRWS1sUn+fUJtdbbqM1BD/LoQGa+smPTDjf9OqHyuJqi6ewtbYL0+V1S1RaU6OCOCMWGZocIfz2YK4uw==} - dev: false + security@1.0.0: {} - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - dev: true + semver@6.3.1: {} - /semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 + semver@7.6.3: {} - /send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} + send@0.18.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -5491,17 +8867,12 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /serialize-javascript@6.0.0: - resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 - dev: true - /serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} - engines: {node: '>= 0.8.0'} + serve-static@1.15.0: dependencies: encodeurl: 1.0.2 escape-html: 1.0.3 @@ -5509,15 +8880,10 @@ packages: send: 0.18.0 transitivePeerDependencies: - supports-color - dev: false - /set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - dev: true + set-cookie-parser@2.7.0: {} - /set-function-length@1.2.1: - resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} - engines: {node: '>= 0.4'} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 @@ -5526,299 +8892,259 @@ packages: gopd: 1.0.1 has-property-descriptors: 1.0.2 - /set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} + set-function-name@2.0.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 - dev: true - /setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - dev: false + setprototypeof@1.2.0: {} - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} + shebang-regex@3.0.0: {} - /side-channel@1.0.5: - resolution: {integrity: sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==} - engines: {node: '>= 0.4'} + shiki@1.14.1: + dependencies: + '@shikijs/core': 1.14.1 + '@types/hast': 3.0.4 + + side-channel@1.0.6: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + object-inspect: 1.13.2 - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: false + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} - /sinon@17.0.1: - resolution: {integrity: sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==} + sinon@18.0.0: dependencies: '@sinonjs/commons': 3.0.1 '@sinonjs/fake-timers': 11.2.2 '@sinonjs/samsam': 8.0.0 diff: 5.2.0 - nise: 5.1.9 + nise: 6.0.0 supports-color: 7.2.0 - dev: true - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true + slash@3.0.0: {} - /snake-case@3.0.4: - resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - dependencies: - dot-case: 3.0.4 - tslib: 2.6.2 - dev: true + smart-buffer@4.2.0: {} - /socket.io-adapter@2.5.4: - resolution: {integrity: sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==} + snake-case@3.0.4: dependencies: - debug: 4.3.4(supports-color@8.1.1) - ws: 8.11.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: false + dot-case: 3.0.4 + tslib: 2.6.3 - /socket.io-client@4.7.4: - resolution: {integrity: sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==} - engines: {node: '>=10.0.0'} + socket.io-adapter@2.5.5: dependencies: - '@socket.io/component-emitter': 3.1.0 - debug: 4.3.4(supports-color@8.1.1) - engine.io-client: 6.5.3 - socket.io-parser: 4.2.4 + debug: 4.3.5(supports-color@8.1.1) + ws: 8.17.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - dev: true - /socket.io-client@4.7.5: - resolution: {integrity: sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==} - engines: {node: '>=10.0.0'} + socket.io-client@4.7.5: dependencies: - '@socket.io/component-emitter': 3.1.0 - debug: 4.3.4(supports-color@8.1.1) - engine.io-client: 6.5.3 + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5(supports-color@8.1.1) + engine.io-client: 6.5.4 socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - /socket.io-parser@4.2.4: - resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} - engines: {node: '>=10.0.0'} + socket.io-parser@4.2.4: dependencies: - '@socket.io/component-emitter': 3.1.0 - debug: 4.3.4(supports-color@8.1.1) + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color - /socket.io@4.7.5: - resolution: {integrity: sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==} - engines: {node: '>=10.2.0'} + socket.io@4.7.5: dependencies: accepts: 1.3.8 base64id: 2.0.0 cors: 2.8.5 - debug: 4.3.4(supports-color@8.1.1) - engine.io: 6.5.4 - socket.io-adapter: 2.5.4 + debug: 4.3.5(supports-color@8.1.1) + engine.io: 6.5.5 + socket.io-adapter: 2.5.5 socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - dev: false - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - dev: true + socks-proxy-agent@8.0.4: + dependencies: + agent-base: 7.1.1 + debug: 4.3.5(supports-color@8.1.1) + socks: 2.8.3 + transitivePeerDependencies: + - supports-color - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + socks@2.8.3: dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: false + ip-address: 9.0.5 + smart-buffer: 4.2.0 - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: false + source-map-js@1.2.0: {} - /space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - dev: false + source-map@0.6.1: + optional: true - /split-grid@1.0.11: - resolution: {integrity: sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==} - dev: true + space-separated-tokens@2.0.2: {} - /statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - dev: false + speakingurl@14.0.1: {} - /streamroller@3.1.5: - resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} - engines: {node: '>=8.0'} + split-grid@1.0.11: {} + + sprintf-js@1.1.3: {} + + stackback@0.0.2: {} + + statuses@1.5.0: {} + + statuses@2.0.1: {} + + std-env@3.7.0: {} + + streamroller@3.1.5: dependencies: date-format: 4.0.14 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) fs-extra: 8.1.0 transitivePeerDependencies: - supports-color - dev: false - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true - /string.prototype.trim@1.2.8: - resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} - engines: {node: '>= 0.4'} + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 - /string.prototype.trimend@1.0.7: - resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + string.prototype.trimend@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true + es-object-atoms: 1.0.0 - /string.prototype.trimstart@1.0.7: - resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true + es-object-atoms: 1.0.0 - /stringify-entities@4.0.3: - resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 - dev: false - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true + strip-bom@3.0.0: {} - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true + strip-final-newline@3.0.0: {} - /superagent@8.1.2: - resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} - engines: {node: '>=6.4.0 <13 || >=14'} + strip-json-comments@3.1.1: {} + + superagent@10.0.2: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.3.5(supports-color@8.1.1) + fast-safe-stringify: 2.1.1 + form-data: 4.0.0 + formidable: 3.5.1 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.12.3 + transitivePeerDependencies: + - supports-color + + superagent@8.1.2: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) fast-safe-stringify: 2.1.1 form-data: 4.0.0 formidable: 2.1.2 methods: 1.1.2 mime: 2.6.0 - qs: 6.11.2 - semver: 7.6.0 + qs: 6.12.3 + semver: 7.6.3 transitivePeerDependencies: - supports-color - /supertest@6.3.4: - resolution: {integrity: sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==} - engines: {node: '>=6.4.0'} + superagent@9.0.2: dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.3.5(supports-color@8.1.1) + fast-safe-stringify: 2.1.1 + form-data: 4.0.0 + formidable: 3.5.1 methods: 1.1.2 - superagent: 8.1.2 + mime: 2.6.0 + qs: 6.12.3 transitivePeerDependencies: - supports-color - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + superjson@2.2.1: + dependencies: + copy-anything: 3.0.5 + + supertest@7.0.0: + dependencies: + methods: 1.1.2 + superagent: 9.0.2 + transitivePeerDependencies: + - supports-color + + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - dev: true - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} + supports-color@8.1.1: dependencies: has-flag: 4.0.0 - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + supports-preserve-symlinks-flag@1.0.0: {} - /svg-parser@2.0.4: - resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} - dev: true + svg-parser@2.0.4: {} - /swagger-schema-official@2.0.0-bab6bed: - resolution: {integrity: sha512-rCC0NWGKr/IJhtRuPq/t37qvZHI/mH4I4sxflVM+qgVe5Z2uOCivzWaVbuioJaB61kvm5UvB7b49E+oBY0M8jA==} - dev: true + swagger-schema-official@2.0.0-bab6bed: {} - /symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - dev: false + symbol-tree@3.2.4: {} - /tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - dev: true + tabbable@6.2.0: {} - /tar@6.2.0: - resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} - engines: {node: '>=10'} + tapable@2.2.1: {} + + tar@6.2.1: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 @@ -5826,188 +9152,92 @@ packages: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 - dev: false - /terser@5.29.2: - resolution: {integrity: sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==} - engines: {node: '>=10'} - hasBin: true - dependencies: - '@jridgewell/source-map': 0.3.5 - acorn: 8.11.3 - commander: 2.20.3 - source-map-support: 0.5.21 - dev: false + text-table@0.2.0: {} - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true + tinybench@2.9.0: {} - /threads@1.7.0: - resolution: {integrity: sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==} - dependencies: - callsites: 3.1.0 - debug: 4.3.4(supports-color@8.1.1) - is-observable: 2.1.0 - observable-fns: 0.6.1 - optionalDependencies: - tiny-worker: 2.3.0 - transitivePeerDependencies: - - supports-color - dev: false + tinycon@0.6.8: {} - /tiny-worker@2.3.0: - resolution: {integrity: sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==} - requiresBuild: true - dependencies: - esm: 3.2.25 - dev: false - optional: true + tinypool@1.0.0: {} - /tinycon@0.6.8: - resolution: {integrity: sha512-bF8Lxm4JUXF6Cw0XlZdugJ44GV575OinZ0Pt8vQPr8ooNqd2yyNkoFdCHzmdpHlgoqfSLfcyk4HDP1EyllT+ug==} - dev: false + tinyrainbow@1.2.0: {} - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - dev: true + tinyspy@3.0.0: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - dev: true - /toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - dev: false + toidentifier@1.0.1: {} - /tough-cookie@4.1.3: - resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} - engines: {node: '>=6'} + tough-cookie@4.1.4: dependencies: psl: 1.9.0 punycode: 2.3.1 universalify: 0.2.0 url-parse: 1.5.10 - dev: false - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: false - - /tr46@5.0.0: - resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} - engines: {node: '>=18'} + tr46@5.0.0: dependencies: punycode: 2.3.1 - dev: false - /trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - dev: false + trim-lines@3.0.1: {} - /trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - dev: false + trough@2.2.0: {} - /ts-api-utils@1.3.0(typescript@5.4.2): - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' + ts-api-utils@1.3.0(typescript@5.5.4): dependencies: - typescript: 5.4.2 - dev: true + typescript: 5.5.4 - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 - dev: true - /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true + tslib@2.6.3: {} - /tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - dev: true - - /tsutils@3.21.0(typescript@5.4.2): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 5.4.2 - dev: true + tsscmp@1.0.6: {} - /tsx@4.7.1: - resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} - engines: {node: '>=18.0.0'} - hasBin: true + tsx@4.17.0: dependencies: - esbuild: 0.19.12 - get-tsconfig: 4.7.2 + esbuild: 0.23.0 + get-tsconfig: 4.7.6 optionalDependencies: fsevents: 2.3.3 - dev: false - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - dev: true - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true + type-detect@4.0.8: {} - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - dev: true + type-fest@0.20.2: {} - /type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} + type-is@1.6.18: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - dev: false - /typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} + typed-array-buffer@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-typed-array: 1.1.13 - dev: true - /typed-array-byte-length@1.0.0: - resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} - engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 for-each: 0.3.3 + gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true - /typed-array-byte-offset@1.0.1: - resolution: {integrity: sha512-tcqKMrTRXjqvHN9S3553NPCaGL0VPgFI92lXszmrE8DMhiDPLBYLlvo8Uu4WZAAX/aGqp/T1sbA4ph8EWjDF9Q==} - engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.2: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 @@ -6015,52 +9245,36 @@ packages: gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + typed-array-length@1.0.6: dependencies: call-bind: 1.0.7 for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true + possible-typed-array-names: 1.0.0 - /typescript@5.4.2: - resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} - engines: {node: '>=14.17'} - hasBin: true - dev: true + typescript@5.5.4: {} - /ueberdb2@4.2.63: - resolution: {integrity: sha512-5zmp6vkBNcfRbhXG623pgHtdhbxNi2B4d8PqLXPa65hC8vdWJrjPQbldpl9Y49LuUF7n50k+nOuseVKAeTkgyA==} - engines: {node: '>=16.20.1'} - dev: false + ueberdb2@4.2.93: {} - /uid-safe@2.1.5: - resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==} - engines: {node: '>= 0.8'} + uid-safe@2.1.5: dependencies: random-bytes: 1.0.0 - dev: false - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - dev: true - /underscore@1.13.6: - resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} - dev: false + underscore@1.13.7: {} - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.6: {} - /unified@11.0.4: - resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + unified@11.0.5: dependencies: '@types/unist': 3.0.2 bail: 2.0.2 @@ -6068,396 +9282,327 @@ packages: extend: 3.0.2 is-plain-obj: 4.1.0 trough: 2.2.0 - vfile: 6.0.1 - dev: false + vfile: 6.0.2 - /unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + unist-util-is@6.0.0: dependencies: '@types/unist': 3.0.2 - dev: false - /unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-position@5.0.0: dependencies: '@types/unist': 3.0.2 - dev: false - /unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.2 - dev: false - /unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + unist-util-visit-parents@6.0.1: dependencies: '@types/unist': 3.0.2 unist-util-is: 6.0.0 - dev: false - /unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + unist-util-visit@5.0.0: dependencies: '@types/unist': 3.0.2 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - dev: false - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: false + universalify@0.1.2: {} - /universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - dev: false + universalify@0.2.0: {} - /universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} + universalify@2.0.1: {} - /unorm@1.6.0: - resolution: {integrity: sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==} - engines: {node: '>= 0.4.0'} - dev: false + unorm@1.6.0: {} - /unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - dev: false + unpipe@1.0.0: {} - /update-browserslist-db@1.0.13(browserslist@4.23.0): - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + update-browserslist-db@1.0.16(browserslist@4.23.1): dependencies: - browserslist: 4.23.0 + browserslist: 4.23.1 escalade: 3.1.2 - picocolors: 1.0.0 - dev: true + picocolors: 1.0.1 - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /url-join@4.0.1: - resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} - dev: false + url-join@4.0.1: {} - /url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + url-parse@1.5.10: dependencies: querystringify: 2.2.0 requires-port: 1.0.0 - dev: false - /use-callback-ref@1.3.1(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + use-callback-ref@1.3.2(@types/react@18.3.4)(react@18.3.1): dependencies: - '@types/react': 18.2.65 - react: 18.2.0 - tslib: 2.6.2 - dev: true + react: 18.3.1 + tslib: 2.6.3 + optionalDependencies: + '@types/react': 18.3.4 - /use-sidecar@1.1.2(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + use-sidecar@1.1.2(@types/react@18.3.4)(react@18.3.1): dependencies: - '@types/react': 18.2.65 detect-node-es: 1.1.0 - react: 18.2.0 - tslib: 2.6.2 - dev: true + react: 18.3.1 + tslib: 2.6.3 + optionalDependencies: + '@types/react': 18.3.4 - /use-sync-external-store@1.2.0(react@18.2.0): - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + use-sync-external-store@1.2.2(react@18.3.1): dependencies: - react: 18.2.0 - dev: true + react: 18.3.1 - /utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - dev: false + utils-merge@1.0.1: {} - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - dev: false + vary@1.1.2: {} - /vfile-location@5.0.2: - resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==} + vfile-location@5.0.3: dependencies: '@types/unist': 3.0.2 - vfile: 6.0.1 - dev: false + vfile: 6.0.2 - /vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + vfile-message@4.0.2: dependencies: '@types/unist': 3.0.2 unist-util-stringify-position: 4.0.0 - dev: false - /vfile@6.0.1: - resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} + vfile@6.0.2: dependencies: '@types/unist': 3.0.2 unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - dev: false - /vite-plugin-static-copy@1.0.1(vite@5.1.6): - resolution: {integrity: sha512-3eGL4mdZoPJMDBT68pv/XKIHR4MgVolStIxxv1gIBP4R8TpHn9C9EnaU0hesqlseJ4ycLGUxckFTu/jpuJXQlA==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 + vite-node@2.0.5(@types/node@22.4.2): + dependencies: + cac: 6.7.14 + debug: 4.3.5(supports-color@8.1.1) + pathe: 1.1.2 + tinyrainbow: 1.2.0 + vite: 5.4.2(@types/node@22.4.2) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite-plugin-static-copy@1.0.6(vite@5.4.2(@types/node@22.4.2)): dependencies: - chokidar: 3.5.3 + chokidar: 3.6.0 fast-glob: 3.3.2 fs-extra: 11.2.0 - picocolors: 1.0.0 - vite: 5.1.6 - dev: true + picocolors: 1.0.1 + vite: 5.4.2(@types/node@22.4.2) - /vite-plugin-svgr@4.2.0(typescript@5.4.2)(vite@5.1.6): - resolution: {integrity: sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==} - peerDependencies: - vite: ^2.6.0 || 3 || 4 || 5 + vite-plugin-svgr@4.2.0(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.4.2)): dependencies: - '@rollup/pluginutils': 5.1.0 - '@svgr/core': 8.1.0(typescript@5.4.2) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0) - vite: 5.1.6 + '@rollup/pluginutils': 5.1.0(rollup@4.21.0) + '@svgr/core': 8.1.0(typescript@5.5.4) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4)) + vite: 5.4.2(@types/node@22.4.2) transitivePeerDependencies: - rollup - supports-color - typescript - dev: true - /vite@5.1.6: - resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite@5.4.2(@types/node@22.4.2): dependencies: - esbuild: 0.19.12 - postcss: 8.4.35 - rollup: 4.13.0 + esbuild: 0.21.5 + postcss: 8.4.41 + rollup: 4.21.0 optionalDependencies: + '@types/node': 22.4.2 fsevents: 2.3.3 - dev: true - /void-elements@3.1.0: - resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} - engines: {node: '>=0.10.0'} - dev: true + vitepress@1.3.3(@algolia/client-search@4.23.3)(@types/node@22.4.2)(@types/react@18.3.4)(axios@1.7.5)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): + dependencies: + '@docsearch/css': 3.6.1 + '@docsearch/js': 3.6.1(@algolia/client-search@4.23.3)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@shikijs/core': 1.14.1 + '@shikijs/transformers': 1.14.1 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.1.2(vite@5.4.2(@types/node@22.4.2))(vue@3.4.38(typescript@5.5.4)) + '@vue/devtools-api': 7.3.8 + '@vue/shared': 3.4.38 + '@vueuse/core': 11.0.1(vue@3.4.38(typescript@5.5.4)) + '@vueuse/integrations': 11.0.1(axios@1.7.5)(focus-trap@7.5.4)(vue@3.4.38(typescript@5.5.4)) + focus-trap: 7.5.4 + mark.js: 8.11.1 + minisearch: 7.1.0 + shiki: 1.14.1 + vite: 5.4.2(@types/node@22.4.2) + vue: 3.4.38(typescript@5.5.4) + optionalDependencies: + postcss: 8.4.41 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - '@vue/composition-api' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie - /w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} + vitest@2.0.5(@types/node@22.4.2)(jsdom@24.1.1): + dependencies: + '@ampproject/remapping': 2.3.0 + '@vitest/expect': 2.0.5 + '@vitest/pretty-format': 2.0.5 + '@vitest/runner': 2.0.5 + '@vitest/snapshot': 2.0.5 + '@vitest/spy': 2.0.5 + '@vitest/utils': 2.0.5 + chai: 5.1.1 + debug: 4.3.5(supports-color@8.1.1) + execa: 8.0.1 + magic-string: 0.30.10 + pathe: 1.1.2 + std-env: 3.7.0 + tinybench: 2.9.0 + tinypool: 1.0.0 + tinyrainbow: 1.2.0 + vite: 5.4.2(@types/node@22.4.2) + vite-node: 2.0.5(@types/node@22.4.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.4.2 + jsdom: 24.1.1 + transitivePeerDependencies: + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + void-elements@3.1.0: {} + + vue-demi@0.14.10(vue@3.4.38(typescript@5.5.4)): + dependencies: + vue: 3.4.38(typescript@5.5.4) + + vue@3.4.38(typescript@5.5.4): + dependencies: + '@vue/compiler-dom': 3.4.38 + '@vue/compiler-sfc': 3.4.38 + '@vue/runtime-dom': 3.4.38 + '@vue/server-renderer': 3.4.38(vue@3.4.38(typescript@5.5.4)) + '@vue/shared': 3.4.38 + optionalDependencies: + typescript: 5.5.4 + + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 - dev: false - /web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - dev: false + web-namespaces@2.0.1: {} - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: false + web-streams-polyfill@3.3.3: {} - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: false + webidl-conversions@7.0.0: {} - /whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 - dev: false - /whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - dev: false + whatwg-mimetype@4.0.0: {} - /whatwg-url@14.0.0: - resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} - engines: {node: '>=18'} + whatwg-url@14.0.0: dependencies: tr46: 5.0.0 webidl-conversions: 7.0.0 - dev: false - - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - dev: false - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 is-boolean-object: 1.1.2 is-number-object: 1.0.7 is-string: 1.0.7 is-symbol: 1.0.4 - dev: true - /which-typed-array@1.1.14: - resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} - engines: {node: '>= 0.4'} + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.2 - dev: true - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - /workerpool@6.2.1: - resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} - dev: true + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + word-wrap@1.2.5: {} + + workerpool@6.5.1: {} + + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + wrappy@1.0.2: {} - /ws@8.11.0: - resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + ws@8.17.1: {} - /ws@8.16.0: - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: false + ws@8.18.0: {} - /wtfnode@0.9.1: - resolution: {integrity: sha512-Ip6C2KeQPl/F3aP1EfOnPoQk14Udd9lffpoqWDNH3Xt78svxPbv53ngtmtfI0q2Te3oTq79XKTnRNXVIn/GsPA==} - hasBin: true - dev: false + wtfnode@0.9.3: {} - /xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} - engines: {node: '>=18'} - dev: false + xml-name-validator@5.0.0: {} - /xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - dev: false + xmlchars@2.2.0: {} - /xmlhttprequest-ssl@2.0.0: - resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} - engines: {node: '>=0.4.0'} + xmlhttprequest-ssl@2.0.0: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true + y18n@5.0.8: {} - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true + yallist@3.1.1: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@4.0.0: {} - /yargs-parser@20.2.4: - resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} - engines: {node: '>=10'} - dev: true + yargs-parser@20.2.9: {} - /yargs-unparser@2.0.0: - resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} - engines: {node: '>=10'} + yargs-unparser@2.0.0: dependencies: camelcase: 6.3.0 decamelize: 4.0.0 flat: 5.0.2 is-plain-obj: 2.1.0 - dev: true - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} + yargs@16.2.0: dependencies: cliui: 7.0.4 escalade: 3.1.2 @@ -6465,34 +9610,17 @@ packages: require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 - yargs-parser: 20.2.4 - dev: true + yargs-parser: 20.2.9 - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true + ylru@1.4.0: {} - /zustand@4.5.2(@types/react@18.2.65)(react@18.2.0): - resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==} - engines: {node: '>=12.7.0'} - peerDependencies: - '@types/react': '>=16.8' - immer: '>=9.0.6' - react: '>=16.8' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true + yocto-queue@0.1.0: {} + + zustand@4.5.5(@types/react@18.3.4)(react@18.3.1): dependencies: - '@types/react': 18.2.65 - react: 18.2.0 - use-sync-external-store: 1.2.0(react@18.2.0) - dev: true + use-sync-external-store: 1.2.2(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.4 + react: 18.3.1 - /zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - dev: false + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dd590d5171e..9ebd5c672dd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,3 +2,5 @@ packages: - src - admin - bin + - doc + - ui diff --git a/settings.json.docker b/settings.json.docker index 921c9314023..6eec7c6be0e 100644 --- a/settings.json.docker +++ b/settings.json.docker @@ -171,6 +171,13 @@ */ "showSettingsInAdminPage": "${SHOW_SETTINGS_IN_ADMIN_PAGE:true}", + /* + The authentication method used by the server. + The default value is sso + If you want to use the old authentication system, change this to apikey + */ + "authenticationMethod": "${AUTHENTICATION_METHOD:sso}", + /* * Node native SSL support * @@ -524,6 +531,9 @@ } }, + /* + * Restrict socket.io transport methods + */ "socketTransportProtocols" : ["websocket", "polling"], "socketIo": { @@ -534,7 +544,7 @@ * value to work properly, but increasing the value increases susceptibility * to denial of service attacks (malicious clients can exhaust memory). */ - "maxHttpBufferSize": "${SOCKETIO_MAX_HTTP_BUFFER_SIZE:10000}" + "maxHttpBufferSize": "${SOCKETIO_MAX_HTTP_BUFFER_SIZE:50000}" }, /* @@ -648,7 +658,36 @@ * Enable/Disable case-insensitive pad names. */ "lowerCasePadIds": "${LOWER_CASE_PAD_IDS:false}", - + "sso": { + "issuer": "${SSO_ISSUER:http://localhost:9001}", + "clients": [ + { + "client_id": "${ADMIN_CLIENT:admin_client}", + "client_secret": "${ADMIN_SECRET:admin}", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "redirect_uris": ["${ADMIN_REDIRECT:http://localhost:9001/admin/}"] + }, + { + "client_id": "${USER_CLIENT:user_client}", + "client_secret": "${USER_SECRET:user}", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"] + } + ] + }, + + /* Set the time to live for the tokens + This is the time of seconds a user is logged into Etherpad + "ttl": { + "AccessToken": 3600, + "AuthorizationCode": 600, + "ClientCredentials": 3600, + "IdToken": 3600, + "RefreshToken": 86400 + } + */ /* * Allow only some file formats */ diff --git a/settings.json.template b/settings.json.template index 9c9150394e7..66c9a7df2fd 100644 --- a/settings.json.template +++ b/settings.json.template @@ -537,7 +537,7 @@ * value to work properly, but increasing the value increases susceptibility * to denial of service attacks (malicious clients can exhaust memory). */ - "maxHttpBufferSize": 10000 + "maxHttpBufferSize": 50000 }, /* @@ -586,6 +586,13 @@ */ "importMaxFileSize": 52428800, // 50 * 1024 * 1024 + /* + The authentication method used by the server. + The default value is sso + If you want to use the old authentication system, change this to apikey + */ + "authenticationMethod": "${AUTHENTICATION_METHOD:sso}", + /* * From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited * @@ -641,6 +648,13 @@ */ "loglevel": "INFO", + /* + * The log layout type to use. + * + * Valid values: basic, colored + */ + "logLayoutType": "colored", + /* Override any strings found in locale directories */ "customLocaleStrings": {}, @@ -650,5 +664,36 @@ /* * Enable/Disable case-insensitive pad names. */ - "lowerCasePadIds": false + "lowerCasePadIds": false, + + "sso": { + "issuer": "${SSO_ISSUER:http://localhost:9001}", + "clients": [ + { + "client_id": "${ADMIN_CLIENT:admin_client}", + "client_secret": "${ADMIN_SECRET:admin}", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "redirect_uris": ["${ADMIN_REDIRECT:http://localhost:9001/admin/}"] + }, + { + "client_id": "${USER_CLIENT:user_client}", + "client_secret": "${USER_SECRET:user}", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"] + } + ] + } + + /* Set the time to live for the tokens + This is the time of seconds a user is logged into Etherpad + "ttl": { + "AccessToken": 3600, + "AuthorizationCode": 600, + "ClientCredentials": 3600, + "IdToken": 3600, + "RefreshToken": 86400 + } + */ } diff --git a/src/ep.json b/src/ep.json index f6d41e203ce..c9b26c175aa 100644 --- a/src/ep.json +++ b/src/ep.json @@ -42,15 +42,28 @@ "name": "specialpages", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/specialpages", - "expressPreSession": "ep_etherpad-lite/node/hooks/express/specialpages" + "expressPreSession": "ep_etherpad-lite/node/hooks/express/specialpages", + "socketio": "ep_etherpad-lite/node/hooks/express/specialpages" } }, + { + "name": "oauth2", + "hooks": { + "expressCreateServer": "ep_etherpad-lite/node/security/OAuth2Provider" + } + }, { "name": "padurlsanitize", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/padurlsanitize" } }, + { + "name": "pwa", + "hooks": { + "expressCreateServer": "ep_etherpad-lite/node/hooks/express/pwa" + } + }, { "name": "apicalls", "hooks": { diff --git a/src/locales/az.json b/src/locales/az.json index 07083d06412..fbcd38635b0 100644 --- a/src/locales/az.json +++ b/src/locales/az.json @@ -8,6 +8,7 @@ "MuratTheTurkish", "Mushviq Abdulla", "NMW03", + "Nemoralis", "Neriman2003", "Vesely35", "Wertuose" diff --git a/src/locales/bn.json b/src/locales/bn.json index ac422714b29..472eb27b04e 100644 --- a/src/locales/bn.json +++ b/src/locales/bn.json @@ -8,6 +8,7 @@ "Bellayet", "Greatder", "Nasir8891", + "RiazACU", "Sankarshan", "Sibabrata Banerjee", "আজিজ", diff --git a/src/locales/fa.json b/src/locales/fa.json index 0d3c2011a67..6be60fa790f 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -4,6 +4,7 @@ "BMRG14", "Beginneruser", "Dalba", + "Ebrahim", "Ebraminio", "FarsiNevis", "Jeeputer", @@ -70,13 +71,13 @@ "pad.colorpicker.cancel": "لغو", "pad.loading": "در حال بارگذاری...", "pad.noCookie": "کلوچک یافت نشد. لطفاً اجارهٔ استفاده از کلوچک را در مرورگر خود تأیید کنید! نشست و تنظیمات شما در میان بازدیدها ذخیره نخواهد شد. این میتواند به این دلیل باشد که اترپد در برخی مرورگرها در یک iFrame قرار میگیرد. لطفاً مطمئن شوید که اترپد در زیردامنه/دامنهٔ یکسان با iFrame والد قرار دارد", - "pad.permissionDenied": "شما اجازهی دسترسی به این دفترچه یادداشت را ندارید", + "pad.permissionDenied": "شما اجازهٔ دسترسی به این دفترچه یادداشت را ندارید", "pad.settings.padSettings": "تنظیمات دفترچه یادداشت", "pad.settings.myView": "نمای من", "pad.settings.stickychat": "گفتگو همیشه روی صفحه نمایش باشد", "pad.settings.chatandusers": "نمایش چت و کاربران", "pad.settings.colorcheck": "رنگهای نویسندگی", - "pad.settings.linenocheck": "شمارهی خطوط", + "pad.settings.linenocheck": "شمارهٔ خطوط", "pad.settings.rtlcheck": "خواندن محتوا از راست به چپ؟", "pad.settings.fontType": "نوع قلم:", "pad.settings.fontType.normal": "ساده", @@ -84,7 +85,7 @@ "pad.settings.about": "درباره", "pad.settings.poweredBy": "قدرستگرفته از", "pad.importExport.import_export": "درونریزی/برونبری", - "pad.importExport.import": "بارگذاری پروندهی متنی یا سند", + "pad.importExport.import": "بارگذاری پروندهٔ متنی یا سند", "pad.importExport.importSuccessful": "موفقیت آمیز بود!", "pad.importExport.export": "برونبری این دفترچه یادداشت با قالب:", "pad.importExport.exportetherpad": "اترپد", @@ -100,10 +101,10 @@ "pad.modals.reconnecttimer": "تلاش برای اتصال مجدد", "pad.modals.cancel": "لغو", "pad.modals.userdup": "در پنجرهای دیگر باز شد", - "pad.modals.userdup.explanation": "گمان میرود این دفترچه یادداشت در بیش از یک پنجرهی مرورگر باز شدهاست.", + "pad.modals.userdup.explanation": "گمان میرود این دفترچه یادداشت در بیش از یک پنجرهٔ مرورگر باز شده است.", "pad.modals.userdup.advice": "برای استفاده از این پنجره دوباره وصل شوید.", "pad.modals.unauth": "مجاز نیست", - "pad.modals.unauth.explanation": "دسترسی شما در حین مشاهدهی این برگه تغییر یافتهاست. دوباره متصل شوید.", + "pad.modals.unauth.explanation": "دسترسی شما در حین مشاهدهٔ این برگه تغییر یافته است. دوباره متصل شوید.", "pad.modals.looping.explanation": "مشکلاتی ارتباطی با سرور همگامسازی وجود دارد.", "pad.modals.looping.cause": "شاید شما از طریق یک فایروال یا پروکسی ناسازگار متصل شدهاید.", "pad.modals.initsocketfail": "سرور در دسترس نیست.", @@ -116,10 +117,10 @@ "pad.modals.corruptPad.explanation": "پدی که شما سعی دارید دسترسی پیدا کنید خراب است.", "pad.modals.corruptPad.cause": "این احتمالاً به دلیل تنظیمات اشتباه کارساز یا سایر رفتارهای غیرمنتظره است. لطفاً با مدیر خدمت تماس حاصل کنید.", "pad.modals.deleted": "پاک شد.", - "pad.modals.deleted.explanation": "این دفترچه یادداشت پاک شدهاست.", - "pad.modals.rateLimited": "نرخ محدود شدهاست.", - "pad.modals.disconnected": "اتصال شما قطع شدهاست.", - "pad.modals.disconnected.explanation": "اتصال به سرور قطع شدهاست.", + "pad.modals.deleted.explanation": "این دفترچه یادداشت پاک شده است.", + "pad.modals.rateLimited": "نرخ محدود شده است.", + "pad.modals.disconnected": "اتصال شما قطع شده است.", + "pad.modals.disconnected.explanation": "اتصال به سرور قطع شده است.", "pad.modals.disconnected.cause": "ممکن است سرور در دسترس نباشد. اگر این مشکل باز هم رخ داد مدیر حدمت را آگاه کنید.", "pad.share": "به اشتراکگذاری این دفترچه یادداشت", "pad.share.readonly": "فقط خواندنی", @@ -159,12 +160,12 @@ "pad.savedrevs.timeslider": "شما میتوانید نسخههای ذخیره شده را با دیدن نوار زمان ببنید", "pad.userlist.entername": "نام خود را بنویسید", "pad.userlist.unnamed": "بدون نام", - "pad.editbar.clearcolors": "رنگ نویسندگی از همهی سند پاک شود؟", + "pad.editbar.clearcolors": "رنگ نویسندگی از همهٔ سند پاک شود؟", "pad.impexp.importbutton": "هم اکنون درونریزی کن", "pad.impexp.importing": "در حال درونریزی...", "pad.impexp.confirmimport": "با درونریزی یک پرونده نوشتهٔ کنونی دفترچه پاک میشود. آیا میخواهید ادامه دهید؟", "pad.impexp.convertFailed": "ما نمیتوانیم این پرونده را درونریزی کنیم. خواهشمندیم قالب دیگری برای سندتان انتخاب کرده یا بصورت دستی آنرا کپی کنید", - "pad.impexp.padHasData": "امکان درونریزی این پرونده نیست زیرا این پد تغییر کردهاست. لطفاً در پد جدید درونریزی کنید.", + "pad.impexp.padHasData": "امکان درونریزی این پرونده نیست زیرا این پد تغییر کرده است. لطفاً در پد جدید درونریزی کنید.", "pad.impexp.uploadFailed": "آپلود انجام نشد، دوباره تلاش کنید", "pad.impexp.importfailed": "درونریزی انجام نشد", "pad.impexp.copypaste": "کپی پیست کنید", diff --git a/src/locales/id.json b/src/locales/id.json index 49b8f34fa0d..f068633ff2d 100644 --- a/src/locales/id.json +++ b/src/locales/id.json @@ -1,6 +1,7 @@ { "@metadata": { "authors": [ + "Atriwidada", "Bennylin", "IvanLanin", "Marwan Mohamad", @@ -10,13 +11,38 @@ "admin.page-title": "Dasbor Pengurus - Etherpad", "admin_plugins": "Manajer plugin", "admin_plugins.available": "Plugin yang tersedia", + "admin_plugins.available_not-found": "Tidak ada plugin yang ditemukan.", + "admin_plugins.available_fetching": "Mengambil…", "admin_plugins.available_install.value": "Instal", + "admin_plugins.available_search.placeholder": "Cari plugin yang akan dipasang", + "admin_plugins.description": "Deskripsi", + "admin_plugins.installed": "Plugin terpasang", + "admin_plugins.installed_fetching": "Mengambil plugin yang terpasang…", + "admin_plugins.installed_nothing": "Anda belum memasang plugin apa pun.", + "admin_plugins.last-update": "Pembaruan terakhir", + "admin_plugins.name": "Nama", + "admin_plugins.page-title": "Manajer plugin - Etherpad", "admin_plugins.version": "Versi", + "admin_plugins_info": "Informasi penelusuran masalah", + "admin_plugins_info.hooks": "Kait terpasang", + "admin_plugins_info.hooks_client": "Kait sisi klien", + "admin_plugins_info.hooks_server": "Kait sisi server", + "admin_plugins_info.parts": "Bagian terpasang", + "admin_plugins_info.plugins": "Plugin terpasang", + "admin_plugins_info.page-title": "Informasi plugin - Etherpad", + "admin_plugins_info.version": "Versi Etherpad", + "admin_plugins_info.version_latest": "Versi terakhir yang tersedia", + "admin_plugins_info.version_number": "Nomor versi", "admin_settings": "Pengaturan", + "admin_settings.current": "Konfigurasi kini", + "admin_settings.current_example-devel": "Contoh templat pengaturan pengembangan", + "admin_settings.current_example-prod": "Contoh templat pengaturan produksi", + "admin_settings.current_restart.value": "Jalankan ulang Etherpad", "admin_settings.current_save.value": "Simpan pengaturan", "admin_settings.page-title": "Pengaturan - Etherpad", "index.newPad": "Pad baru", "index.createOpenPad": "atau buat/buka Pad dengan nama:", + "index.openPad": "buka Pad yang ada dengan nama:", "pad.toolbar.bold.title": "Tebal (Ctrl-B)", "pad.toolbar.italic.title": "Miring (Ctrl-I)", "pad.toolbar.underline.title": "Garis bawah (Ctrl-U)", @@ -37,7 +63,7 @@ "pad.colorpicker.save": "Simpan", "pad.colorpicker.cancel": "Batalkan", "pad.loading": "Memuat...", - "pad.noCookie": "Kuki tidak dapat ditemukan. Izinkan kuki di peramban Anda!", + "pad.noCookie": "Kuki tidak dapat ditemukan. Izinkan kuki di peramban Anda! Sesi dan pengaturan Anda tidak akan disimpan antar kunjungan. Ini mungkin karena Etherpad disertakan dalam suatu iFrame dalam beberapa Peramban. Harap pastikan Etherpad ada pada sub domain/domain dengan iFrame induk", "pad.permissionDenied": "Anda tidak memiliki izin untuk mengakses pad ini", "pad.settings.padSettings": "Pengaturan Pad", "pad.settings.myView": "Tampilan Saya", @@ -49,6 +75,7 @@ "pad.settings.fontType": "Jenis fonta:", "pad.settings.language": "Bahasa:", "pad.settings.about": "Tentang", + "pad.settings.poweredBy": "Ditenagai oleh", "pad.importExport.import_export": "Impor/Ekspor", "pad.importExport.import": "Unggah setiap berkas teks atau dokumen", "pad.importExport.importSuccessful": "Berhasil!", @@ -59,9 +86,9 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.abiword.innerHTML": "Anda hanya dapat mengimpor dari format teks biasa atau HTML. Untuk fitur impor yang lebih canggih, <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">pasanglah AbiWord</a>.", + "pad.importExport.abiword.innerHTML": "Anda hanya dapat mengimpor dari format teks polos atau HTML. Untuk fitur impor yang lebih canggih, <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">pasanglah AbiWord atau LibreOffice</a>.", "pad.modals.connected": "Tersambung.", - "pad.modals.reconnecting": "Menyambungkan kembali ke pad Anda...", + "pad.modals.reconnecting": "Menyambungkan kembali ke pad Anda…", "pad.modals.forcereconnect": "Sambung kembali secara paksa", "pad.modals.reconnecttimer": "Mencoba menghubungkan ulang", "pad.modals.cancel": "Batalkan", @@ -83,6 +110,10 @@ "pad.modals.corruptPad.cause": "Hal ini mungkin disebabkan oleh konfigurasi peladen salah atau sesuatu perilaku yang tidak diperkirakan. Silahkan hubungi administrator Anda jika Anda merasakan ini adalah satu kesalahan.", "pad.modals.deleted": "Dihapus", "pad.modals.deleted.explanation": "Pad ini telah dibuang.", + "pad.modals.rateLimited": "Laju Dibatasi.", + "pad.modals.rateLimited.explanation": "Anda mengirim terlalu banyak pesan ke pad ini sehingga itu memutus Anda.", + "pad.modals.rejected.explanation": "Server menolak suatu pesan yang dikirim oleh peramban Anda.", + "pad.modals.rejected.cause": "Server mungkin telah diperbarui ketika Anda sedang melihat pad, atau mungkin ada bug dalam Etherpad. Cobalah memuat ulang halaman.", "pad.modals.disconnected": "Sambungan Anda telah diputuskan.", "pad.modals.disconnected.explanation": "Sambungan ke peladen terputus", "pad.modals.disconnected.cause": "Peladen ini mungkin tidak tersedia. Silakan beritahu administrator jika masalah ini berkelanjutan.", @@ -95,6 +126,7 @@ "pad.chat.loadmessages": "Muatkan lebih banyak pesan", "pad.chat.stick.title": "Tempelkan chat ke layar", "pad.chat.writeMessage.placeholder": "Tuliskan pesan Anda di sini", + "timeslider.followContents": "Ikuti pembaruan isi pad", "timeslider.pageTitle": "{{appTitle}} Timeslider", "timeslider.toolbar.returnbutton": "Kembali ke pad", "timeslider.toolbar.authors": "Pembuat:", @@ -124,7 +156,7 @@ "pad.savedrevs.timeslider": "Anda bisa melihat revisi yang tersimpan dengan mengunjungi timeslider", "pad.userlist.entername": "Masukkan nama Anda", "pad.userlist.unnamed": "tanpa nama", - "pad.editbar.clearcolors": "Padamkan warna penulis pada seluruh dokumen?", + "pad.editbar.clearcolors": "Bersihkan warna penulis pada seluruh dokumen? Ini tidak dapat dibatalkan", "pad.impexp.importbutton": "Impor Sekarang", "pad.impexp.importing": "Mengimpor...", "pad.impexp.confirmimport": "Mengimpor berkas akan menimpa teks saat ini di pad ini. Apakah Anda benar-benar ingin melakukannya?", @@ -133,5 +165,6 @@ "pad.impexp.uploadFailed": "Penunggahan gagal, silakan mencoba lagi", "pad.impexp.importfailed": "Impor gagal", "pad.impexp.copypaste": "Silahkan salin tempel", - "pad.impexp.exportdisabled": "Mengekspor dalam format {{type}} dilarang. Silakan hubungi administrator untuk detilnya." + "pad.impexp.exportdisabled": "Mengekspor dalam format {{type}} dilarang. Silakan hubungi administrator untuk detilnya.", + "pad.impexp.maxFileSize": "Berkas terlalu besar. Hubungi administrator situs Anda untuk menaikkan ukuran berkas yang diizinkan untuk impor" } diff --git a/src/locales/ko.json b/src/locales/ko.json index d8bcc3e973c..6eac3047e44 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -8,6 +8,7 @@ "Kurousagi", "Revi", "SeoJeongHo", + "Suleiman the Magnificent Television", "Ykhwong", "그냥기여자", "아라" @@ -19,13 +20,13 @@ "admin_plugins.available_not-found": "플러그인이 없습니다.", "admin_plugins.available_fetching": "검색 중...", "admin_plugins.available_install.value": "설치", - "admin_plugins.available_search.placeholder": "설치할 플러그인을 검색합니다", + "admin_plugins.available_search.placeholder": "설치할 플러그인을 검색", "admin_plugins.description": "설명", "admin_plugins.installed": "설치된 플러그인", "admin_plugins.installed_fetching": "설치된 플러그인을 검색하는 중...", "admin_plugins.installed_nothing": "아직 플러인을 설치하지 않으셨습니다.", - "admin_plugins.installed_uninstall.value": "설치 제거", - "admin_plugins.last-update": "최근 업데이트", + "admin_plugins.installed_uninstall.value": "제거", + "admin_plugins.last-update": "마지막 업데이트", "admin_plugins.name": "이름", "admin_plugins.page-title": "플러그인 관리자 - 이더패드", "admin_plugins.version": "버전", diff --git a/src/locales/krc.json b/src/locales/krc.json index e90f9520c1f..fdf400a8073 100644 --- a/src/locales/krc.json +++ b/src/locales/krc.json @@ -36,7 +36,7 @@ "admin_settings.current_example-devel": "Юлгю хазырлау джарашдырыуланы шаблону", "admin_settings.current_example-prod": "Юлгю чыгъарыу джарашдырыуланы шаблону", "admin_settings.current_restart.value": "Etherpad-ны джангыдан башлат", - "admin_settings.current_save.value": "Джарашдырыуланы Сакъла", + "admin_settings.current_save.value": "Джарашдырыуланы Сакъландыр", "admin_settings.page-title": "Джарашдырыула — Etherpad", "index.newPad": "Джангы Блокнот", "index.createOpenPad": "неда бу ат бла Блокнот болдур/ач:", @@ -55,10 +55,10 @@ "pad.toolbar.import_export.title": "Файлланы башха форматларын (а/дан) импорт/экспорт", "pad.toolbar.timeslider.title": "Заман шкала", "pad.toolbar.savedRevision.title": "Версияны сакъла", - "pad.toolbar.settings.title": "Джарашдырыула", + "pad.toolbar.settings.title": "Джарашдырыўла", "pad.toolbar.embed.title": "Бу блокнотну Джай эмда Ичине сал", "pad.toolbar.showusers.title": "Хайырланыучуланы бу блокнотда кёргюзт", - "pad.colorpicker.save": "Сакъла", + "pad.colorpicker.save": "Сакъландыр", "pad.colorpicker.cancel": "Ызына ал", "pad.loading": "Джюклениу...", "pad.noCookie": "Куки табылмадыла. Бразуеригизде кукилени бир джандырсагъыз! Сизни кириулеригизни арасында сессиягъыз эмда джарашдырыуларыгъыз сакъланныкъ тюлдюле. Буну чуруму, бир къауум браузерледе Etherpad iFrame ичинде болгъаны болургъа болур. Тилейбиз, Etherpad эмда аны башындагъы iFrame бир тюбдоменде/доменде болгъанындан ишексиз болугъуз.", @@ -90,7 +90,7 @@ "pad.modals.reconnecting": "Блокнотугъузгъа джангыдан байлана турады...", "pad.modals.forcereconnect": "Джангыдан зор бла байланыу", "pad.modals.reconnecttimer": "Джангыдан байланыргъа кюрешеди", - "pad.modals.cancel": "Ызына алыу", + "pad.modals.cancel": "Ызына ал", "pad.modals.userdup": "Башха терезеде ачыкъды", "pad.modals.userdup.explanation": "Бу блокнот, бу компьютерде бирден аслам бразуре терезеде ачылгъаннга ушайды.", "pad.modals.userdup.advice": "Бу терезени хайырланыб джангыдан байлан", diff --git a/src/locales/ms.json b/src/locales/ms.json index a5d8d1debd3..8fe02980266 100644 --- a/src/locales/ms.json +++ b/src/locales/ms.json @@ -41,6 +41,7 @@ "pad.settings.fontType": "Jenis fon:", "pad.settings.fontType.normal": "Normal", "pad.settings.language": "Bahasa:", + "pad.settings.poweredBy": "Dikuasakan oleh", "pad.importExport.import_export": "Import/Eksport", "pad.importExport.import": "Muat naik sebarang fail teks atau dokumen", "pad.importExport.importSuccessful": "Berjaya!", diff --git a/src/locales/ne.json b/src/locales/ne.json index 93b31c0eb12..17bb951acd2 100644 --- a/src/locales/ne.json +++ b/src/locales/ne.json @@ -4,12 +4,14 @@ "Bada Kaji", "Nirajan pant", "Nirjal stha", + "पर्वत सुबेदी", "बडा काजी", "राम प्रसाद जोशी", "सरोज कुमार ढकाल", "हिमाल सुबेदी" ] }, + "admin_plugins.description": "विवरण", "index.newPad": "नयाँ प्याड", "index.createOpenPad": "नाम सहितको नयाँ प्याड सिर्जना गर्ने / खोल्ने :", "pad.toolbar.bold.title": "मोटो (Ctrl-B)", @@ -57,6 +59,7 @@ "pad.modals.connected": "जोडीएको।", "pad.modals.reconnecting": "तपाईंको प्याडमा पुन: जडान गर्दै", "pad.modals.forcereconnect": "जडानको लागि जोडगर्ने", + "pad.modals.cancel": "रद्द गर्नुहोस्", "pad.modals.userdup": "अर्को सन्झ्यालमा खोल्ने", "pad.modals.unauth": "अनुमती नदिइएको", "pad.modals.initsocketfail": "सर्भरमा पहुँच पुर्याउन सकिएन ।", diff --git a/src/locales/pa.json b/src/locales/pa.json index 071498f9850..0f0da75bbf8 100644 --- a/src/locales/pa.json +++ b/src/locales/pa.json @@ -3,6 +3,7 @@ "authors": [ "Aalam", "Babanwalia", + "Cabal", "Tow", "ਗੁਰਪ੍ਰੀਤ ਹੁੰਦਲ", "ਪ੍ਰਚਾਰਕ" @@ -26,7 +27,7 @@ "pad.toolbar.savedRevision.title": "ਦੁਹਰਾਅ ਸਾਂਭੋ", "pad.toolbar.settings.title": "ਸੈਟਿੰਗ", "pad.toolbar.embed.title": "ਇਹ ਪੈਡ ਸਾਂਝਾ ਤੇ ਇੰਬੈੱਡ ਕਰੋ", - "pad.toolbar.showusers.title": "ਇਹ ਪੈਡ ਉੱਤੇ ਯੂਜ਼ਰ ਵੇਖਾਓ", + "pad.toolbar.showusers.title": "ਇਸ ਫੱਟੀ ਉੱਤੇ ਵਰਤੋਂਕਾਰ ਵਿਖਾਓ", "pad.colorpicker.save": "ਸੰਭਾਲੋ", "pad.colorpicker.cancel": "ਰੱਦ ਕਰੋ", "pad.loading": "…ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ", @@ -37,7 +38,7 @@ "pad.settings.stickychat": "ਹਮੇਸ਼ਾ ਸਕਰੀਨ ਉੱਤੇ ਗੱਲ ਕਰੋ", "pad.settings.chatandusers": "ਗੱਲ-ਬਾਤ ਅਤੇ ਵਰਤੋਂਕਾਰ ਦਿਖਾਵੋ", "pad.settings.colorcheck": "ਲੇਖਕੀ ਰੰਗ", - "pad.settings.linenocheck": "ਲਾਈਨ ਨੰਬਰ", + "pad.settings.linenocheck": "ਲਕੀਰ ਨੰਬਰ", "pad.settings.rtlcheck": "ਸਮੱਗਰੀ ਸੱਜੇ ਤੋਂ ਖੱਬੇ ਪੜ੍ਹਨੀ ਹੈ?", "pad.settings.fontType": "ਫੋਂਟ ਕਿਸਮ:", "pad.settings.fontType.normal": "ਸਧਾਰਨ", @@ -88,7 +89,7 @@ "timeslider.toolbar.returnbutton": "ਪੈਡ ਉੱਤੇ ਵਾਪਸ", "timeslider.toolbar.authors": "ਲੇਖਕ:", "timeslider.toolbar.authorsList": "ਕੋਈ ਲੇਖਕ ਨਹੀਂ", - "timeslider.toolbar.exportlink.title": "ਐਕਸਪੋਰਟ", + "timeslider.toolbar.exportlink.title": "ਬਰਾਮਦ", "timeslider.exportCurrent": "ਮੌਜੂਦਾ ਵਰਜਨ ਇੰਝ ਐਕਸਪੋਰਟ ਕਰੋ:", "timeslider.version": "ਵਰਜ਼ਨ {{version}}", "timeslider.saved": "{{day}} {{month}} {{year}} ਨੂੰ ਸੰਭਾਲਿਆ", diff --git a/src/locales/sl.json b/src/locales/sl.json index e553905ade1..3f0f17e8b6d 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -73,7 +73,7 @@ "pad.settings.chatandusers": "Prikaži klepet in uporabnike", "pad.settings.colorcheck": "Barve avtorstva", "pad.settings.linenocheck": "Številke vrstic", - "pad.settings.rtlcheck": "Ali naj se vsebina bera od desne proti levi?", + "pad.settings.rtlcheck": "Ali naj se vsebina bere od desne proti levi?", "pad.settings.fontType": "Vrsta pisave:", "pad.settings.fontType.normal": "Normalno", "pad.settings.language": "Jezik:", diff --git a/src/locales/th.json b/src/locales/th.json index 7de1e4fdce4..8b44b2f20f0 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -3,6 +3,7 @@ "authors": [ "Aefgh39622", "Andibecker", + "Ekminarin", "Patsagorn Y.", "Trisorn Triboon" ] @@ -121,7 +122,7 @@ "pad.share.readonly": "อ่านเท่านั้น", "pad.share.link": "ลิงก์", "pad.share.emebdcode": "URL แบบฝังตัว", - "pad.chat": "แชท", + "pad.chat": "แชต", "pad.chat.title": "เปิดการแชทสำหรับแผ่นจดบันทึกนี้", "pad.chat.loadmessages": "โหลดข้อความเพิ่มเติม", "pad.chat.stick.title": "ปักการสนทนาไว้บนหน้าจอ", diff --git a/src/locales/vec.json b/src/locales/vec.json index 97892523e18..3128dbb11db 100644 --- a/src/locales/vec.json +++ b/src/locales/vec.json @@ -1,13 +1,14 @@ { "@metadata": { "authors": [ + "Candalua", "Fierodelveneto" ] }, "index.newPad": "Novo Pad", "index.createOpenPad": "O creare o verxare on Pad co'l nome:", "pad.toolbar.bold.title": "Groseto (Ctrl-B)", - "pad.toolbar.italic.title": "Corivo (Ctrl-I)", + "pad.toolbar.italic.title": "Corsivo (Ctrl-I)", "pad.toolbar.underline.title": "Sotolineà (Ctrl-U)", "pad.toolbar.strikethrough.title": "Barà (Ctrl+5)", "pad.toolbar.ol.title": "Ełenco numarà (Ctrl+Shift+N)", diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json index ae454be4b10..84b11e59569 100644 --- a/src/locales/zh-hans.json +++ b/src/locales/zh-hans.json @@ -10,6 +10,7 @@ "Hzy980512", "JuneAugust", "Lakejason0", + "LittlePaw365", "Liuxinyu970226", "Qiyue2001", "Shangkuanlc", @@ -57,7 +58,7 @@ "admin_settings.current_save.value": "保存设置", "admin_settings.page-title": "设置 - Etherpad", "index.newPad": "新记事本", - "index.createOpenPad": "或创建/打开一个名为以下的记事本:", + "index.createOpenPad": "或创建/打开以下名称的记事本:", "index.openPad": "打开一个现有的记事本,名称为:", "pad.toolbar.bold.title": "粗体(Ctrl-B)", "pad.toolbar.italic.title": "斜体(Ctrl-I)", diff --git a/src/node/db/API.ts b/src/node/db/API.ts index 8b84693ec2f..88910ca68e7 100644 --- a/src/node/db/API.ts +++ b/src/node/db/API.ts @@ -19,8 +19,10 @@ * limitations under the License. */ -const Changeset = require('../../static/js/Changeset'); -const ChatMessage = require('../../static/js/ChatMessage'); +import {deserializeOps} from '../../static/js/Changeset'; +import ChatMessage from '../../static/js/ChatMessage'; +import {Builder} from "../../static/js/Builder"; +import {Attribute} from "../../static/js/types/Attribute"; const CustomError = require('../utils/customError'); const padManager = require('./PadManager'); const padMessageHandler = require('../handler/PadMessageHandler'); @@ -355,7 +357,7 @@ exports.getChatHistory = async (padID: string, start:number, end:number) => { throw new CustomError('end is higher than the current chatHead', 'apierror'); } - // the the whole message-log and return it to the client + // the whole message-log and return it to the client const messages = await pad.getChatMessages(start, end); return {messages}; @@ -563,11 +565,11 @@ exports.restoreRevision = async (padID: string, rev: number, authorId = '') => { const oldText = pad.text(); atext.text += '\n'; - const eachAttribRun = (attribs: string[], func:Function) => { + const eachAttribRun = (attribs: string, func:Function) => { let textIndex = 0; const newTextStart = 0; const newTextEnd = atext.text.length; - for (const op of Changeset.deserializeOps(attribs)) { + for (const op of deserializeOps(attribs)) { const nextIndex = textIndex + op.chars; if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) { func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); @@ -577,10 +579,10 @@ exports.restoreRevision = async (padID: string, rev: number, authorId = '') => { }; // create a new changeset with a helper builder object - const builder = Changeset.builder(oldText.length); + const builder = new Builder(oldText.length); // assemble each line into the builder - eachAttribRun(atext.attribs, (start: number, end: number, attribs:string[]) => { + eachAttribRun(atext.attribs, (start: number, end: number, attribs:Attribute[]) => { builder.insert(atext.text.substring(start, end), attribs); }); diff --git a/src/node/db/AuthorManager.ts b/src/node/db/AuthorManager.ts index 5b349ef101c..a67cd0ebb38 100644 --- a/src/node/db/AuthorManager.ts +++ b/src/node/db/AuthorManager.ts @@ -21,8 +21,8 @@ const db = require('./DB'); const CustomError = require('../utils/customError'); -const hooks = require('../../static/js/pluginfw/hooks.js'); -const {randomString, padutils: {warnDeprecated}} = require('../../static/js/pad_utils'); +const hooks = require('../../static/js/pluginfw/hooks'); +import padutils, {randomString} from "../../static/js/pad_utils"; exports.getColorPalette = () => [ '#ffc7c7', @@ -169,7 +169,7 @@ exports.getAuthorId = async (token: string, user: object) => { * @param {String} token The token */ exports.getAuthor4Token = async (token: string) => { - warnDeprecated( + padutils.warnDeprecated( 'AuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead'); return await getAuthor4Token(token); }; diff --git a/src/node/db/DB.ts b/src/node/db/DB.ts index 542da673544..663946cd6ad 100644 --- a/src/node/db/DB.ts +++ b/src/node/db/DB.ts @@ -21,7 +21,7 @@ * limitations under the License. */ -import ueberDB from 'ueberdb2'; +import {Database} from 'ueberdb2'; const settings = require('../utils/Settings'); import log4js from 'log4js'; const stats = require('../stats') @@ -37,7 +37,7 @@ exports.db = null; * Initializes the database with the settings provided by the settings module */ exports.init = async () => { - exports.db = new ueberDB.Database(settings.dbType, settings.dbSettings, null, logger); + exports.db = new Database(settings.dbType, settings.dbSettings, null, logger); await exports.db.init(); if (exports.db.metrics != null) { for (const [metric, value] of Object.entries(exports.db.metrics)) { diff --git a/src/node/db/GroupManager.ts b/src/node/db/GroupManager.ts index 0524c4edaa0..b8cb6db02b4 100644 --- a/src/node/db/GroupManager.ts +++ b/src/node/db/GroupManager.ts @@ -20,7 +20,7 @@ */ const CustomError = require('../utils/customError'); -const randomString = require('../../static/js/pad_utils').randomString; +import {randomString} from "../../static/js/pad_utils"; const db = require('./DB'); const padManager = require('./PadManager'); const sessionManager = require('./SessionManager'); diff --git a/src/node/db/Pad.ts b/src/node/db/Pad.ts index fa4af994d57..8937fc0eb59 100644 --- a/src/node/db/Pad.ts +++ b/src/node/db/Pad.ts @@ -7,10 +7,10 @@ import {MapArrayType} from "../types/MapType"; * The pad object, defined with joose */ -const AttributeMap = require('../../static/js/AttributeMap'); -const Changeset = require('../../static/js/Changeset'); -const ChatMessage = require('../../static/js/ChatMessage'); -const AttributePool = require('../../static/js/AttributePool'); +import AttributeMap from '../../static/js/AttributeMap'; +import {applyToAText, checkRep, copyAText, deserializeOps, makeAText, makeSplice, opsFromAText, pack, unpack} from '../../static/js/Changeset'; +import ChatMessage from '../../static/js/ChatMessage'; +import AttributePool from '../../static/js/AttributePool'; const Stream = require('../utils/Stream'); const assert = require('assert').strict; const db = require('./DB'); @@ -23,8 +23,10 @@ const CustomError = require('../utils/customError'); const readOnlyManager = require('./ReadOnlyManager'); const randomString = require('../utils/randomstring'); const hooks = require('../../static/js/pluginfw/hooks'); -const {padutils: {warnDeprecated}} = require('../../static/js/pad_utils'); -const promises = require('../utils/promises'); +import pad_utils from "../../static/js/pad_utils"; +import {SmartOpAssembler} from "../../static/js/SmartOpAssembler"; +import {} from '../utils/promises'; +import {timesLimit} from "async"; /** * Copied from the Etherpad source code. It converts Windows line breaks to Unix @@ -40,7 +42,7 @@ exports.cleanText = (txt:string): string => txt.replace(/\r\n/g, '\n') class Pad { private db: Database; private atext: AText; - private pool: APool; + private pool: AttributePool; private head: number; private chatHead: number; private publicStatus: boolean; @@ -56,7 +58,7 @@ class Pad { */ constructor(id:string, database = db) { this.db = database; - this.atext = Changeset.makeAText('\n'); + this.atext = makeAText('\n'); this.pool = new AttributePool(); this.head = -1; this.chatHead = -1; @@ -93,13 +95,13 @@ class Pad { * @param {String} authorId The id of the author * @return {Promise<number|string>} */ - async appendRevision(aChangeset:AChangeSet, authorId = '') { - const newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool); + async appendRevision(aChangeset:string, authorId = '') { + const newAText = applyToAText(aChangeset, this.atext, this.pool); if (newAText.text === this.atext.text && newAText.attribs === this.atext.attribs && this.head !== -1) { return this.head; } - Changeset.copyAText(newAText, this.atext); + copyAText(newAText, this.atext); const newRev = ++this.head; @@ -126,11 +128,11 @@ class Pad { pad: this, authorId, get author() { - warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`); + pad_utils.warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`); return this.authorId; }, set author(authorId) { - warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`); + pad_utils.warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`); this.authorId = authorId; }, ...this.head === 0 ? {} : { @@ -215,7 +217,7 @@ class Pad { ]); const apool = this.apool(); let atext = keyAText; - for (const cs of changesets) atext = Changeset.applyToAText(cs, atext, apool); + for (const cs of changesets) atext = applyToAText(cs, atext, apool); return atext; } @@ -293,7 +295,7 @@ class Pad { (!ins && start > 0 && orig[start - 1] === '\n'); if (!willEndWithNewline) ins += '\n'; if (ndel === 0 && ins.length === 0) return; - const changeset = Changeset.makeSplice(orig, start, ndel, ins); + const changeset = makeSplice(orig, start, ndel, ins); await this.appendRevision(changeset, authorId); } @@ -330,7 +332,7 @@ class Pad { * @param {?number} [time] - Message timestamp (milliseconds since epoch). Deprecated; use * `msgOrText.time` instead. */ - async appendChatMessage(msgOrText: string|typeof ChatMessage, authorId = null, time = null) { + async appendChatMessage(msgOrText: string| ChatMessage, authorId = null, time = null) { const msg = msgOrText instanceof ChatMessage ? msgOrText : new ChatMessage(msgOrText, authorId, time); this.chatHead++; @@ -393,7 +395,7 @@ class Pad { if (context.type !== 'text') throw new Error(`unsupported content type: ${context.type}`); text = exports.cleanText(context.content); } - const firstChangeset = Changeset.makeSplice('\n', 0, 0, text); + const firstChangeset = makeSplice('\n', 0, 0, text); await this.appendRevision(firstChangeset, authorId); } await hooks.aCallAll('padLoad', {pad: this}); @@ -437,11 +439,11 @@ class Pad { // let the plugins know the pad was copied await hooks.aCallAll('padCopy', { get originalPad() { - warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead'); + pad_utils.warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead'); return this.srcPad; }, get destinationID() { - warnDeprecated( + pad_utils.warnDeprecated( 'padCopy destinationID context property is deprecated; use dstPad.id instead'); return this.dstPad.id; }, @@ -520,8 +522,8 @@ class Pad { const oldAText = this.atext; // based on Changeset.makeSplice - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.opsFromAText(oldAText)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of opsFromAText(oldAText)) assem.append(op); assem.endDocument(); // although we have instantiated the dstPad with '\n', an additional '\n' is @@ -533,16 +535,16 @@ class Pad { // create a changeset that removes the previous text and add the newText with // all atributes present on the source pad - const changeset = Changeset.pack(oldLength, newLength, assem.toString(), newText); + const changeset = pack(oldLength, newLength, assem.toString(), newText); dstPad.appendRevision(changeset, authorId); await hooks.aCallAll('padCopy', { get originalPad() { - warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead'); + pad_utils.warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead'); return this.srcPad; }, get destinationID() { - warnDeprecated( + pad_utils.warnDeprecated( 'padCopy destinationID context property is deprecated; use dstPad.id instead'); return this.dstPad.id; }, @@ -585,12 +587,14 @@ class Pad { p.push(db.remove(`pad2readonly:${padID}`)); // delete all chat messages - p.push(promises.timesLimit(this.chatHead + 1, 500, async (i: string) => { + // @ts-ignore + p.push(timesLimit(this.chatHead + 1, 500, async (i: string) => { await this.db.remove(`pad:${this.id}:chat:${i}`, null); })); // delete all revisions - p.push(promises.timesLimit(this.head + 1, 500, async (i: string) => { + // @ts-ignore + p.push(timesLimit(this.head + 1, 500, async (i: string) => { await this.db.remove(`pad:${this.id}:revs:${i}`, null); })); @@ -603,7 +607,7 @@ class Pad { p.push(padManager.removePad(padID)); p.push(hooks.aCallAll('padRemove', { get padID() { - warnDeprecated('padRemove padID context property is deprecated; use pad.id instead'); + pad_utils.warnDeprecated('padRemove padID context property is deprecated; use pad.id instead'); return this.pad.id; }, pad: this, @@ -706,7 +710,7 @@ class Pad { } }) .batch(100).buffer(99); - let atext = Changeset.makeAText('\n'); + let atext = makeAText('\n'); for await (const [r, changeset, authorId, timestamp, isKeyRev, keyAText] of revs) { try { assert(authorId != null); @@ -717,10 +721,10 @@ class Pad { assert(timestamp > 0); assert(changeset != null); assert.equal(typeof changeset, 'string'); - Changeset.checkRep(changeset); - const unpacked = Changeset.unpack(changeset); + checkRep(changeset); + const unpacked = unpack(changeset); let text = atext.text; - for (const op of Changeset.deserializeOps(unpacked.ops)) { + for (const op of deserializeOps(unpacked.ops)) { if (['=', '-'].includes(op.opcode)) { assert(text.length >= op.chars); const consumed = text.slice(0, op.chars); @@ -731,7 +735,7 @@ class Pad { } assert.equal(op.attribs, AttributeMap.fromString(op.attribs, pool).toString()); } - atext = Changeset.applyToAText(changeset, atext, pool); + atext = applyToAText(changeset, atext, pool); if (isKeyRev) assert.deepEqual(keyAText, atext); } catch (err:any) { err.message = `(pad ${this.id} revision ${r}) ${err.message}`; diff --git a/src/node/db/SecurityManager.ts b/src/node/db/SecurityManager.ts index 326bf36595d..c2f209a01ae 100644 --- a/src/node/db/SecurityManager.ts +++ b/src/node/db/SecurityManager.ts @@ -22,7 +22,7 @@ import {UserSettingsObject} from "../types/UserSettingsObject"; const authorManager = require('./AuthorManager'); -const hooks = require('../../static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks'); const padManager = require('./PadManager'); const readOnlyManager = require('./ReadOnlyManager'); const sessionManager = require('./SessionManager'); @@ -30,7 +30,7 @@ const settings = require('../utils/Settings'); const webaccess = require('../hooks/express/webaccess'); const log4js = require('log4js'); const authLogger = log4js.getLogger('auth'); -const {padutils} = require('../../static/js/pad_utils'); +import padutils from '../../static/js/pad_utils' const DENY = Object.freeze({accessStatus: 'deny'}); diff --git a/src/node/db/SessionManager.ts b/src/node/db/SessionManager.ts index c0e43a6592c..2d1327aa6c0 100644 --- a/src/node/db/SessionManager.ts +++ b/src/node/db/SessionManager.ts @@ -21,7 +21,7 @@ */ const CustomError = require('../utils/customError'); -const promises = require('../utils/promises'); +import {firstSatisfies} from '../utils/promises'; const randomString = require('../utils/randomstring'); const db = require('./DB'); const groupManager = require('./GroupManager'); @@ -79,7 +79,7 @@ exports.findAuthorID = async (groupID:string, sessionCookie: string) => { groupID: string; validUntil: number; }|null) => (si != null && si.groupID === groupID && now < si.validUntil); - const sessionInfo = await promises.firstSatisfies(sessionInfoPromises, isMatch); + const sessionInfo = await firstSatisfies(sessionInfoPromises, isMatch) as any; if (sessionInfo == null) return undefined; return sessionInfo.authorID; }; diff --git a/src/node/db/SessionStore.ts b/src/node/db/SessionStore.ts index 5dca5e2015e..0b398efad6d 100644 --- a/src/node/db/SessionStore.ts +++ b/src/node/db/SessionStore.ts @@ -1,7 +1,7 @@ 'use strict'; const DB = require('./DB'); -const Store = require('express-session').Store; +const Store = require('@etherpad/express-session').Store; const log4js = require('log4js'); const util = require('util'); diff --git a/src/node/eejs/index.ts b/src/node/eejs/index.ts index b7f2cf99836..5d57e475132 100644 --- a/src/node/eejs/index.ts +++ b/src/node/eejs/index.ts @@ -22,7 +22,7 @@ const ejs = require('ejs'); const fs = require('fs'); -const hooks = require('../../static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks'); const path = require('path'); const resolve = require('resolve'); const settings = require('../utils/Settings'); diff --git a/src/node/handler/APIHandler.ts b/src/node/handler/APIHandler.ts index 616d2067574..5feb74eb965 100644 --- a/src/node/handler/APIHandler.ts +++ b/src/node/handler/APIHandler.ts @@ -21,31 +21,13 @@ import {MapArrayType} from "../types/MapType"; -const absolutePaths = require('../utils/AbsolutePaths'); -import fs from 'fs'; const api = require('../db/API'); -import log4js from 'log4js'; const padManager = require('../db/PadManager'); -const randomString = require('../utils/randomstring'); -const argv = require('../utils/Cli').argv; import createHTTPError from 'http-errors'; - -const apiHandlerLogger = log4js.getLogger('APIHandler'); - -// ensure we have an apikey -let apikey:string|null = null; -const apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || './APIKEY.txt'); - -try { - apikey = fs.readFileSync(apikeyFilename, 'utf8'); - apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`); -} catch (e) { - apiHandlerLogger.info( - `Api key file "${apikeyFilename}" not found. Creating with random contents.`); - apikey = randomString(32); - fs.writeFileSync(apikeyFilename, apikey!, 'utf8'); -} - +import {Http2ServerRequest, Http2ServerResponse} from "node:http2"; +import {publicKeyExported} from "../security/OAuth2Provider"; +import {jwtVerify} from "jose"; +import {apikey} from './APIKeyHandler' // a list of all functions const version:MapArrayType<any> = {}; @@ -171,17 +153,18 @@ type APIFields = { api_key: string; padID: string; padName: string; + authorization: string; } /** - * Handles a HTTP API call + * Handles an HTTP API call * @param {String} apiVersion the version of the api * @param {String} functionName the name of the called function * @param fields the params of the called function - * @req express request object - * @res express response object + * @param req express request object */ -exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields) { +exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields, + req: Http2ServerRequest) { // say goodbye if this is an unknown API version if (!(apiVersion in version)) { throw new createHTTPError.NotFound('no such api version'); @@ -192,11 +175,24 @@ exports.handle = async function (apiVersion: string, functionName: string, field throw new createHTTPError.NotFound('no such function'); } - // check the api key! - fields.apikey = fields.apikey || fields.api_key; - if (fields.apikey !== apikey!.trim()) { - throw new createHTTPError.Unauthorized('no or wrong API Key'); + + if (apikey !== null && apikey.trim().length > 0) { + fields.apikey = fields.apikey || fields.api_key || fields.authorization; + // API key is configured, check if it is valid + if (fields.apikey !== apikey!.trim()) { + throw new createHTTPError.Unauthorized('no or wrong API Key'); + } + } else { + if(!req.headers.authorization) { + throw new createHTTPError.Unauthorized('no or wrong API Key'); + } + try { + await jwtVerify(req.headers.authorization!.replace("Bearer ", ""), publicKeyExported!, {algorithms: ['RS256'], + requiredClaims: ["admin"]}) + } catch (e) { + throw new createHTTPError.Unauthorized('no or wrong OAuth token'); + } } // sanitize any padIDs before continuing @@ -217,7 +213,3 @@ exports.handle = async function (apiVersion: string, functionName: string, field // call the api function return api[functionName].apply(this, functionParams); }; - -exports.exportedForTestingOnly = { - apiKey: apikey, -}; diff --git a/src/node/handler/APIKeyHandler.ts b/src/node/handler/APIKeyHandler.ts new file mode 100644 index 00000000000..b4e70f6e4b0 --- /dev/null +++ b/src/node/handler/APIKeyHandler.ts @@ -0,0 +1,25 @@ +const absolutePaths = require('../utils/AbsolutePaths'); +import fs from 'fs'; +import log4js from 'log4js'; +const randomString = require('../utils/randomstring'); +const argv = require('../utils/Cli').argv; +const settings = require('../utils/Settings'); + +const apiHandlerLogger = log4js.getLogger('APIHandler'); + +// ensure we have an apikey +export let apikey:string|null = null; +const apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || './APIKEY.txt'); + + +if(settings.authenticationMethod === 'apikey') { + try { + apikey = fs.readFileSync(apikeyFilename, 'utf8'); + apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`); + } catch (e) { + apiHandlerLogger.info( + `Api key file "${apikeyFilename}" not found. Creating with random contents.`); + apikey = randomString(32); + fs.writeFileSync(apikeyFilename, apikey!, 'utf8'); + } +} diff --git a/src/node/handler/ImportHandler.ts b/src/node/handler/ImportHandler.ts index 330a2d6f97b..286b4fb5699 100644 --- a/src/node/handler/ImportHandler.ts +++ b/src/node/handler/ImportHandler.ts @@ -31,7 +31,7 @@ import os from 'os'; const importHtml = require('../utils/ImportHtml'); const importEtherpad = require('../utils/ImportEtherpad'); import log4js from 'log4js'; -const hooks = require('../../static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks'); const logger = log4js.getLogger('ImportHandler'); diff --git a/src/node/handler/PadMessageHandler.ts b/src/node/handler/PadMessageHandler.ts index c19e4fe6e73..6d2ee0d5785 100644 --- a/src/node/handler/PadMessageHandler.ts +++ b/src/node/handler/PadMessageHandler.ts @@ -21,28 +21,30 @@ import {MapArrayType} from "../types/MapType"; -const AttributeMap = require('../../static/js/AttributeMap'); +import AttributeMap from '../../static/js/AttributeMap'; const padManager = require('../db/PadManager'); -const Changeset = require('../../static/js/Changeset'); -const ChatMessage = require('../../static/js/ChatMessage'); -const AttributePool = require('../../static/js/AttributePool'); +import {checkRep, cloneAText, compose, deserializeOps, follow, identity, inverse, makeAText, makeSplice, moveOpsToNewPool, mutateAttributionLines, mutateTextLines, oldLen, prepareForWire, splitAttributionLines, splitTextLines, unpack} from '../../static/js/Changeset'; +import ChatMessage from '../../static/js/ChatMessage'; +import AttributePool from '../../static/js/AttributePool'; const AttributeManager = require('../../static/js/AttributeManager'); const authorManager = require('../db/AuthorManager'); -const {padutils} = require('../../static/js/pad_utils'); +import padutils from '../../static/js/pad_utils'; const readOnlyManager = require('../db/ReadOnlyManager'); const settings = require('../utils/Settings'); const securityManager = require('../db/SecurityManager'); -const plugins = require('../../static/js/pluginfw/plugin_defs.js'); +const plugins = require('../../static/js/pluginfw/plugin_defs'); import log4js from 'log4js'; const messageLogger = log4js.getLogger('message'); const accessLogger = log4js.getLogger('access'); -const hooks = require('../../static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks'); const stats = require('../stats') const assert = require('assert').strict; import {RateLimiterMemory} from 'rate-limiter-flexible'; import {ChangesetRequest, PadUserInfo, SocketClientRequest} from "../types/SocketClientRequest"; import {APool, AText, PadAuthor, PadType} from "../types/PadType"; import {ChangeSet} from "../types/ChangeSet"; +import {ChatMessageMessage, ClientReadyMessage, ClientSaveRevisionMessage, ClientSuggestUserName, ClientUserChangesMessage, ClientVarMessage, CustomMessage, UserNewInfoMessage} from "../../static/js/types/SocketIOMessage"; +import {Builder} from "../../static/js/Builder"; const webaccess = require('../hooks/express/webaccess'); const { checkValidRev } = require('../utils/checkValidRev'); @@ -88,7 +90,7 @@ exports.socketio = () => { const sessioninfos:MapArrayType<any> = {}; exports.sessioninfos = sessioninfos; -stats.gauge('totalUsers', () => socketio ? socketio.sockets.size : 0); +stats.gauge('totalUsers', () => socketio ? socketio.engine.clientsCount : 0); stats.gauge('activePads', () => { const padIds = new Set(); for (const {padId} of Object.values(sessioninfos)) { @@ -163,7 +165,7 @@ exports.handleConnect = (socket:any) => { */ exports.kickSessionsFromPad = (padID: string) => { - if(socketio?.sockets == null) return; + if(socketio.sockets == null) return; // skip if there is nobody on this pad if (_getRoomSockets(padID).length === 0) return; @@ -214,7 +216,7 @@ exports.handleDisconnect = async (socket:any) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -exports.handleMessage = async (socket:any, message:typeof ChatMessage) => { +exports.handleMessage = async (socket:any, message: ClientVarMessage) => { const env = process.env.NODE_ENV || 'development'; if (env === 'production') { @@ -348,15 +350,15 @@ exports.handleMessage = async (socket:any, message:typeof ChatMessage) => { stats.counter('pendingEdits').inc(); await padChannels.enqueue(thisSession.padId, {socket, message}); break; - case 'USERINFO_UPDATE': await handleUserInfoUpdate(socket, message); break; - case 'CHAT_MESSAGE': await handleChatMessage(socket, message); break; + case 'USERINFO_UPDATE': await handleUserInfoUpdate(socket, message as unknown as UserNewInfoMessage); break; + case 'CHAT_MESSAGE': await handleChatMessage(socket, message as unknown as ChatMessageMessage); break; case 'GET_CHAT_MESSAGES': await handleGetChatMessages(socket, message); break; - case 'SAVE_REVISION': await handleSaveRevisionMessage(socket, message); break; + case 'SAVE_REVISION': await handleSaveRevisionMessage(socket, message as unknown as ClientSaveRevisionMessage); break; case 'CLIENT_MESSAGE': { const {type} = message.data.payload; try { switch (type) { - case 'suggestUserName': handleSuggestUserName(socket, message); break; + case 'suggestUserName': handleSuggestUserName(socket, message as unknown as ClientSuggestUserName); break; default: throw new Error('unknown message type'); } } catch (err) { @@ -384,7 +386,7 @@ exports.handleMessage = async (socket:any, message:typeof ChatMessage) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleSaveRevisionMessage = async (socket:any, message: string) => { +const handleSaveRevisionMessage = async (socket:any, message: ClientSaveRevisionMessage) => { const {padId, author: authorId} = sessioninfos[socket.id]; const pad = await padManager.getPad(padId, null, authorId); await pad.addSavedRevision(pad.head, authorId); @@ -397,7 +399,7 @@ const handleSaveRevisionMessage = async (socket:any, message: string) => { * @param msg {Object} the message we're sending * @param sessionID {string} the socketIO session to which we're sending this message */ -exports.handleCustomObjectMessage = (msg: typeof ChatMessage, sessionID: string) => { +exports.handleCustomObjectMessage = (msg: CustomMessage, sessionID: string) => { if (msg.data.type === 'CUSTOM') { if (sessionID) { // a sessionID is targeted: directly to this sessionID @@ -432,7 +434,7 @@ exports.handleCustomMessage = (padID: string, msgString:string) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleChatMessage = async (socket:any, message: typeof ChatMessage) => { +const handleChatMessage = async (socket:any, message: ChatMessageMessage) => { const chatMessage = ChatMessage.fromObject(message.data.message); const {padId, author: authorId} = sessioninfos[socket.id]; // Don't trust the user-supplied values. @@ -452,7 +454,7 @@ const handleChatMessage = async (socket:any, message: typeof ChatMessage) => { * @param {string} [padId] - The destination pad ID. Deprecated; pass a chat message * object as the first argument and the destination pad ID as the second argument instead. */ -exports.sendChatMessageToPadClients = async (mt: typeof ChatMessage|number, puId: string, text:string|null = null, padId:string|null = null) => { +exports.sendChatMessageToPadClients = async (mt: ChatMessage|number, puId: string, text:string|null = null, padId:string|null = null) => { const message = mt instanceof ChatMessage ? mt : new ChatMessage(text, puId, mt); padId = mt instanceof ChatMessage ? puId : padId; const pad = await padManager.getPad(padId, null, message.authorId); @@ -499,7 +501,7 @@ const handleGetChatMessages = async (socket:any, {data: {start, end}}:any) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleSuggestUserName = (socket:any, message: typeof ChatMessage) => { +const handleSuggestUserName = (socket:any, message: ClientSuggestUserName) => { const {newName, unnamedId} = message.data.payload; if (newName == null) throw new Error('missing newName'); if (unnamedId == null) throw new Error('missing unnamedId'); @@ -519,7 +521,7 @@ const handleSuggestUserName = (socket:any, message: typeof ChatMessage) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId}}}: PadUserInfo) => { +const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId}}}: UserNewInfoMessage) => { if (colorId == null) throw new Error('missing colorId'); if (!name) name = null; const session = sessioninfos[socket.id]; @@ -567,7 +569,9 @@ const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleUserChanges = async (socket:any, message: typeof ChatMessage) => { +const handleUserChanges = async (socket:any, message: { + data: ClientUserChangesMessage +}) => { // This one's no longer pending, as we're gonna process it now stats.counter('pendingEdits').dec(); @@ -591,10 +595,10 @@ const handleUserChanges = async (socket:any, message: typeof ChatMessage) => { const pad = await padManager.getPad(thisSession.padId, null, thisSession.author); // Verify that the changeset has valid syntax and is in canonical form - Changeset.checkRep(changeset); + checkRep(changeset); // Validate all added 'author' attribs to be the same value as the current user - for (const op of Changeset.deserializeOps(Changeset.unpack(changeset).ops)) { + for (const op of deserializeOps(unpack(changeset).ops)) { // + can add text with attribs // = can change or add attribs // - can have attribs, but they are discarded and don't show up in the attribs - @@ -613,7 +617,7 @@ const handleUserChanges = async (socket:any, message: typeof ChatMessage) => { // ex. adoptChangesetAttribs // Afaik, it copies the new attributes from the changeset, to the global Attribute Pool - let rebasedChangeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool); + let rebasedChangeset = moveOpsToNewPool(changeset, wireApool, pad.pool); // ex. applyUserChanges let r = baseRev; @@ -626,21 +630,21 @@ const handleUserChanges = async (socket:any, message: typeof ChatMessage) => { const {changeset: c, meta: {author: authorId}} = await pad.getRevision(r); if (changeset === c && thisSession.author === authorId) { // Assume this is a retransmission of an already applied changeset. - rebasedChangeset = Changeset.identity(Changeset.unpack(changeset).oldLen); + rebasedChangeset = identity(unpack(changeset).oldLen); } // At this point, both "c" (from the pad) and "changeset" (from the // client) are relative to revision r - 1. The follow function // rebases "changeset" so that it is relative to revision r // and can be applied after "c". - rebasedChangeset = Changeset.follow(c, rebasedChangeset, false, pad.pool); + rebasedChangeset = follow(c, rebasedChangeset, false, pad.pool); } const prevText = pad.text(); - if (Changeset.oldLen(rebasedChangeset) !== prevText.length) { + if (oldLen(rebasedChangeset) !== prevText.length) { throw new Error( `Can't apply changeset ${rebasedChangeset} with oldLen ` + - `${Changeset.oldLen(rebasedChangeset)} to document of length ${prevText.length}`); + `${oldLen(rebasedChangeset)} to document of length ${prevText.length}`); } const newRev = await pad.appendRevision(rebasedChangeset, thisSession.author); @@ -655,7 +659,7 @@ const handleUserChanges = async (socket:any, message: typeof ChatMessage) => { // Make sure the pad always ends with an empty line. if (pad.text().lastIndexOf('\n') !== pad.text().length - 1) { - const nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length - 1, 0, '\n'); + const nlChangeset = makeSplice(pad.text(), pad.text().length - 1, 0, '\n'); await pad.appendRevision(nlChangeset, thisSession.author); } @@ -710,7 +714,7 @@ exports.updatePadClients = async (pad: PadType) => { const revChangeset = revision.changeset; const currentTime = revision.meta.timestamp; - const forWire = Changeset.prepareForWire(revChangeset, pad.pool); + const forWire = prepareForWire(revChangeset, pad.pool); const msg = { type: 'COLLABROOM', data: { @@ -738,14 +742,14 @@ exports.updatePadClients = async (pad: PadType) => { /** * Copied from the Etherpad Source Code. Don't know what this method does excatly... */ -const _correctMarkersInPad = (atext: AText, apool: APool) => { +const _correctMarkersInPad = (atext: AText, apool: AttributePool) => { const text = atext.text; // collect char positions of line markers (e.g. bullets) in new atext // that aren't at the start of a line const badMarkers = []; let offset = 0; - for (const op of Changeset.deserializeOps(atext.attribs)) { + for (const op of deserializeOps(atext.attribs)) { const attribs = AttributeMap.fromString(op.attribs, apool); const hasMarker = AttributeManager.lineAttributes.some((a: string) => attribs.has(a)); if (hasMarker) { @@ -767,7 +771,7 @@ const _correctMarkersInPad = (atext: AText, apool: APool) => { // create changeset that removes these bad markers offset = 0; - const builder = Changeset.builder(text.length); + const builder = new Builder(text.length); badMarkers.forEach((pos) => { builder.keepText(text.substring(offset, pos)); @@ -785,7 +789,7 @@ const _correctMarkersInPad = (atext: AText, apool: APool) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleClientReady = async (socket:any, message: typeof ChatMessage) => { +const handleClientReady = async (socket:any, message: ClientReadyMessage) => { const sessionInfo = sessioninfos[socket.id]; if (sessionInfo == null) throw new Error('client disconnected'); assert(sessionInfo.author); @@ -793,8 +797,9 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => { await hooks.aCallAll('clientReady', message); // Deprecated due to awkward context. let {colorId: authorColorId, name: authorName} = message.userInfo || {}; - if (authorColorId && !/^#(?:[0-9A-F]{3}){1,2}$/i.test(authorColorId)) { + if (authorColorId && !/^#(?:[0-9A-F]{3}){1,2}$/i.test(authorColorId as string)) { messageLogger.warn(`Ignoring invalid colorId in CLIENT_READY message: ${authorColorId}`); + // @ts-ignore authorColorId = null; } await Promise.all([ @@ -872,7 +877,7 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => { const revisionsNeeded = []; const changesets:MapArrayType<any> = {}; - let startNum = message.client_rev + 1; + let startNum = message.client_rev! + 1; let endNum = pad.getHeadRevisionNumber() + 1; const headNum = pad.getHeadRevisionNumber(); @@ -901,7 +906,7 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => { // return pending changesets for (const r of revisionsNeeded) { - const forWire = Changeset.prepareForWire(changesets[r].changeset, pad.pool); + const forWire = prepareForWire(changesets[r].changeset, pad.pool); const wireMsg = {type: 'COLLABROOM', data: {type: 'CLIENT_RECONNECT', headRev: pad.getHeadRevisionNumber(), @@ -926,8 +931,8 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => { let apool; // prepare all values for the wire, there's a chance that this throws, if the pad is corrupted try { - atext = Changeset.cloneAText(pad.atext); - const attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool); + atext = cloneAText(pad.atext); + const attribsForWire = prepareForWire(atext.attribs, pad.pool); apool = attribsForWire.pool.toJsonable(); atext.attribs = attribsForWire.translated; } catch (e:any) { @@ -996,7 +1001,8 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => { percentageToScrollWhenUserPressesArrowUp: settings.scrollWhenFocusLineIsOutOfViewport.percentageToScrollWhenUserPressesArrowUp, }, - initialChangesets: [], // FIXME: REMOVE THIS SHIT + initialChangesets: [], // FIXME: REMOVE THIS SHIT, + mode: process.env.NODE_ENV }; // Add a username to the clientVars if one avaiable @@ -1162,13 +1168,13 @@ const getChangesetInfo = async (pad: PadType, startNum: number, endNum:number, g if (compositeEnd > endNum || compositeEnd > headRevision + 1) break; const forwards = composedChangesets[`${compositeStart}/${compositeEnd}`]; - const backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool()); + const backwards = inverse(forwards, lines.textlines, lines.alines, pad.apool()); - Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool()); - Changeset.mutateTextLines(forwards, lines.textlines); + mutateAttributionLines(forwards, lines.alines, pad.apool()); + mutateTextLines(forwards, lines.textlines); - const forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool); - const backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool); + const forwards2 = moveOpsToNewPool(forwards, pad.apool(), apool); + const backwards2 = moveOpsToNewPool(backwards, pad.apool(), apool); const t1 = (compositeStart === 0) ? revisionDate[0] : revisionDate[compositeStart - 1]; const t2 = revisionDate[compositeEnd - 1]; @@ -1194,12 +1200,12 @@ const getPadLines = async (pad: PadType, revNum: number) => { if (revNum >= 0) { atext = await pad.getInternalRevisionAText(revNum); } else { - atext = Changeset.makeAText('\n'); + atext = makeAText('\n'); } return { - textlines: Changeset.splitTextLines(atext.text), - alines: Changeset.splitAttributionLines(atext.attribs, atext.text), + textlines: splitTextLines(atext.text), + alines: splitAttributionLines(atext.attribs, atext.text), }; }; @@ -1234,7 +1240,7 @@ const composePadChangesets = async (pad: PadType, startNum: number, endNum: numb for (r = startNum + 1; r < endNum; r++) { const cs = changesets[r]; - changeset = Changeset.compose(changeset, cs, pool); + changeset = compose(changeset as string, cs as string, pool); } return changeset; } catch (e) { diff --git a/src/node/hooks/express.ts b/src/node/hooks/express.ts index 29da71ac315..d4e92dd35e3 100644 --- a/src/node/hooks/express.ts +++ b/src/node/hooks/express.ts @@ -9,7 +9,7 @@ import cookieParser from 'cookie-parser'; import events from 'events'; import express from 'express'; // @ts-ignore -import expressSession from 'express-session'; +import expressSession from '@etherpad/express-session'; import fs from 'fs'; const hooks = require('../../static/js/pluginfw/hooks'); import log4js from 'log4js'; diff --git a/src/node/hooks/express/admin.ts b/src/node/hooks/express/admin.ts index 5a88379f27b..e802750f25c 100644 --- a/src/node/hooks/express/admin.ts +++ b/src/node/hooks/express/admin.ts @@ -2,11 +2,13 @@ import {ArgsExpressType} from "../../types/ArgsExpressType"; import path from "path"; import fs from "fs"; -import express from "express"; -const settings = require('ep_etherpad-lite/node/utils/Settings'); +import * as url from "node:url"; +import {MapArrayType} from "../../types/MapType"; -const ADMIN_PATH = path.join(settings.root, 'src', 'templates', 'admin'); +const settings = require('ep_etherpad-lite/node/utils/Settings'); +const ADMIN_PATH = path.join(settings.root, 'src', 'templates'); +const PROXY_HEADER = "x-proxy-path" /** * Add the admin navigation link * @param hookName {String} the name of the hook @@ -14,13 +16,72 @@ const ADMIN_PATH = path.join(settings.root, 'src', 'templates', 'admin'); * @param {Function} cb the callback function * @return {*} */ -exports.expressCreateServer = (hookName:string, args: ArgsExpressType, cb:Function): any => { - args.app.use('/admin/', express.static(path.join(__dirname, '../../../templates/admin'), {maxAge: 1000 * 60 * 60 * 24})); - args.app.get('/admin/*', (_request:any, response:any)=>{ - response.sendFile(path.resolve(__dirname,'../../../templates/admin', 'index.html')); - } ) - args.app.get('/admin', (req:any, res:any, next:Function) => { - if ('/' !== req.path[req.path.length - 1]) return res.redirect('./admin/'); +exports.expressCreateServer = (hookName: string, args: ArgsExpressType, cb: Function): any => { + + if (!fs.existsSync(ADMIN_PATH)) { + console.error('admin template not found, skipping admin interface. You need to rebuild it in /admin with pnpm run build-copy') + return cb(); + } + args.app.get('/admin/*', (req: any, res: any) => { + // parse URL + const parsedUrl = url.parse(req.url); + // extract URL path + let pathname = ADMIN_PATH + `${parsedUrl.pathname}`; + // based on the URL path, extract the file extension. e.g. .js, .doc, ... + let ext = path.parse(pathname).ext; + // maps file extension to MIME typere + const map: MapArrayType<string> = { + '.ico': 'image/x-icon', + '.html': 'text/html', + '.js': 'text/javascript', + '.json': 'application/json', + '.css': 'text/css', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.wav': 'audio/wav', + '.mp3': 'audio/mpeg', + '.svg': 'image/svg+xml', + '.pdf': 'application/pdf', + '.doc': 'application/msword' + }; + + fs.exists(pathname, function (exist) { + if (!exist) { + // if the file is not found, return 404 + res.statusCode = 200; + pathname = ADMIN_PATH + "/admin/index.html" + ext = path.parse(pathname).ext; + } + + // if is a directory search for index file matching the extension + if (fs.statSync(pathname).isDirectory()) { + pathname = pathname + '/index.html'; + ext = path.parse(pathname).ext; + } + + // read file from file system + fs.readFile(pathname, function (err, data) { + if (err) { + res.statusCode = 500; + res.end(`Error getting the file: ${err}.`); + } else { + let dataToSend:Buffer|string = data + // if the file is found, set Content-type and send data + res.setHeader('Content-type', map[ext] || 'text/plain'); + if (ext === ".html" || ext === ".js" || ext === ".css") { + if (req.header(PROXY_HEADER)) { + let string = data.toString() + dataToSend = string.replaceAll("/admin", req.header(PROXY_HEADER) + "/admin") + dataToSend = dataToSend.replaceAll("/socket.io", req.header(PROXY_HEADER) + "/socket.io") + } + } + res.end(dataToSend); + } + }); + }) + }); + args.app.get('/admin', (req: any, res: any, next: Function) => { + if ('/' !== req.path[req.path.length - 1]) return res.redirect('./admin/'); }) return cb(); }; diff --git a/src/node/hooks/express/adminplugins.ts b/src/node/hooks/express/adminplugins.ts index 50279919780..b5049a6e1ef 100644 --- a/src/node/hooks/express/adminplugins.ts +++ b/src/node/hooks/express/adminplugins.ts @@ -9,6 +9,8 @@ import {PackageData} from "../../types/PackageInfo"; const pluginDefs = require('../../../static/js/pluginfw/plugin_defs'); import semver from 'semver'; +import log4js from 'log4js'; +const logger = log4js.getLogger('adminPlugins'); exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => { @@ -61,6 +63,7 @@ exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => { socket.on('search', async (query: QueryType) => { try { + if (query.searchTerm) logger.info(`Plugin search: ${query.searchTerm}'`); const results = await search(query.searchTerm, /* maxCacheAge:*/ 60 * 10); let res = Object.keys(results) .map((pluginName) => results[pluginName]) @@ -68,10 +71,9 @@ exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => { res = sortPluginList(res, query.sortBy, query.sortDir) .slice(query.offset, query.offset + query.limit); socket.emit('results:search', {results: res, query}); - } catch (er) { - console.error(er); - - socket.emit('results:search', {results: {}, query}); + } catch (err: any) { + logger.error(`Error searching plugins: ${err}`); + socket.emit('results:searcherror', {error: err.message, query}); } }); @@ -87,6 +89,7 @@ exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => { }); }); + socket.on('uninstall', (pluginName:string) => { uninstall(pluginName, (err:ErrorCaused) => { if (err) console.warn(err.stack || err.toString()); diff --git a/src/node/hooks/express/adminsettings.ts b/src/node/hooks/express/adminsettings.ts index 03258c58416..63d901f2126 100644 --- a/src/node/hooks/express/adminsettings.ts +++ b/src/node/hooks/express/adminsettings.ts @@ -3,6 +3,7 @@ import {PadQueryResult, PadSearchQuery} from "../../types/PadSearchQuery"; import {PadType} from "../../types/PadType"; +import log4js from 'log4js'; const eejs = require('../../eejs'); const fsp = require('fs').promises; @@ -15,197 +16,253 @@ const api = require('../../db/API'); const queryPadLimit = 12; +const logger = log4js.getLogger('adminSettings'); -exports.socketio = (hookName:string, {io}:any) => { - io.of('/settings').on('connection', (socket: any ) => { - // @ts-ignore - const {session: {user: {is_admin: isAdmin} = {}} = {}} = socket.conn.request; - if (!isAdmin) return; - - socket.on('load', async (query:string):Promise<any> => { - let data; - try { - data = await fsp.readFile(settings.settingsFilename, 'utf8'); - } catch (err) { - return console.log(err); - } - // if showSettingsInAdminPage is set to false, then return NOT_ALLOWED in the result - if (settings.showSettingsInAdminPage === false) { - socket.emit('settings', {results: 'NOT_ALLOWED'}); - } else { - socket.emit('settings', {results: data}); - } - }); +exports.socketio = (hookName: string, {io}: any) => { + io.of('/settings').on('connection', (socket: any) => { + // @ts-ignore + const {session: {user: {is_admin: isAdmin} = {}} = {}} = socket.conn.request; + if (!isAdmin) return; - socket.on('saveSettings', async (newSettings:string) => { - console.log('Admin request to save settings through a socket on /admin/settings'); - await fsp.writeFile(settings.settingsFilename, newSettings); - socket.emit('saveprogress', 'saved'); - }); + socket.on('load', async (query: string): Promise<any> => { + let data; + try { + data = await fsp.readFile(settings.settingsFilename, 'utf8'); + } catch (err) { + return logger.error(`Error loading settings: ${err}`); + } + // if showSettingsInAdminPage is set to false, then return NOT_ALLOWED in the result + if (settings.showSettingsInAdminPage === false) { + socket.emit('settings', {results: 'NOT_ALLOWED'}); + } else { + socket.emit('settings', {results: data}); + } + }); + + socket.on('saveSettings', async (newSettings: string) => { + logger.info('Admin request to save settings through a socket on /admin/settings'); + try { + await fsp.writeFile(settings.settingsFilename, newSettings); + } catch (err) { + logger.error(`Error saving settings: ${err}`); + } + socket.emit('saveprogress', 'saved'); + }); - socket.on('help', ()=> { - const gitCommit = settings.getGitCommit(); - const epVersion = settings.getEpVersion(); + type ShoutMessage = { + message: string, + sticky: boolean, + } - const hooks:Map<string, Map<string,string>> = plugins.getHooks('hooks', false); - const clientHooks:Map<string, Map<string,string>> = plugins.getHooks('client_hooks', false); + socket.on('shout', (message: ShoutMessage) => { + const messageToSend = { + type: "COLLABROOM", + data: { + type: "shoutMessage", + payload: { + message: message, + timestamp: Date.now() + } + } + } - function mapToObject(map: Map<string,any>) { - let obj = Object.create(null); - for (let [k,v] of map) { - if(v instanceof Map) { - obj[k] = mapToObject(v); - } else { - obj[k] = v; - } - } - return obj; - } - - socket.emit('reply:help', { - gitCommit, - epVersion, - installedPlugins: plugins.getPlugins(), - installedParts: plugins.getParts(), - installedServerHooks: mapToObject(hooks), - installedClientHooks: mapToObject(clientHooks), - latestVersion: UpdateCheck.getLatestVersion(), - }) - }); + io.of('/settings').emit('shout', messageToSend); + io.sockets.emit('shout', messageToSend); + }) + + + socket.on('help', () => { + const gitCommit = settings.getGitCommit(); + const epVersion = settings.getEpVersion(); + const hooks: Map<string, Map<string, string>> = plugins.getHooks('hooks', false); + const clientHooks: Map<string, Map<string, string>> = plugins.getHooks('client_hooks', false); - socket.on('padLoad', async (query: PadSearchQuery) => { - const {padIDs} = await padManager.listAllPads(); - - const data:{ - total: number, - results?: PadQueryResult[] - } = { - total: padIDs.length, - }; - let result: string[] = padIDs; - let maxResult; - - // Filter out matches - if (query.pattern) { - result = result.filter((padName: string) => padName.includes(query.pattern)); - } - - data.total = result.length; - - maxResult = result.length - 1; - if (maxResult < 0) { - maxResult = 0; - } - - if (query.offset && query.offset < 0) { - query.offset = 0; - } else if (query.offset > maxResult) { - query.offset = maxResult; - } - - if (query.limit && query.limit < 0) { - query.limit = 0; - } else if (query.limit > queryPadLimit) { - query.limit = queryPadLimit; - } - - if (query.sortBy === 'padName') { - result = result.sort((a,b)=>{ - if(a < b) return query.ascending ? -1 : 1; - if(a > b) return query.ascending ? 1 : -1; - return 0; - }).slice(query.offset, query.offset + query.limit); - - data.results = await Promise.all(result.map(async (padName: string) => { - const pad = await padManager.getPad(padName); - const revisionNumber = pad.getHeadRevisionNumber() - const userCount = api.padUsersCount(padName).padUsersCount; - const lastEdited = await pad.getLastEdit(); - - return { - padName, - lastEdited, - userCount, - revisionNumber - }})); - } else { - const currentWinners: PadQueryResult[] = [] - let queryOffsetCounter = 0 - for (let res of result) { - - const pad = await padManager.getPad(res); - const padType = { - padName: res, - lastEdited: await pad.getLastEdit(), - userCount: api.padUsersCount(res).padUsersCount, - revisionNumber: pad.getHeadRevisionNumber() - }; - - if (currentWinners.length < query.limit) { - if(queryOffsetCounter < query.offset){ - queryOffsetCounter++ - continue + function mapToObject(map: Map<string, any>) { + let obj = Object.create(null); + for (let [k, v] of map) { + if (v instanceof Map) { + obj[k] = mapToObject(v); + } else { + obj[k] = v; + } + } + return obj; } - currentWinners.push({ - padName: res, - lastEdited: await pad.getLastEdit(), - userCount: api.padUsersCount(res).padUsersCount, - revisionNumber: pad.getHeadRevisionNumber() + + socket.emit('reply:help', { + gitCommit, + epVersion, + installedPlugins: plugins.getPlugins(), + installedParts: plugins.getParts(), + installedServerHooks: mapToObject(hooks), + installedClientHooks: mapToObject(clientHooks), + latestVersion: UpdateCheck.getLatestVersion(), }) - } else { - // Kick out worst pad and replace by current pad - let worstPad = currentWinners.sort((a, b) => { - if (a[query.sortBy] < b[query.sortBy]) return query.ascending ? -1 : 1; - if (a[query.sortBy] > b[query.sortBy]) return query.ascending ? 1 : -1; + }); + + + socket.on('padLoad', async (query: PadSearchQuery) => { + const {padIDs} = await padManager.listAllPads(); + + const data: { + total: number, + results?: PadQueryResult[] + } = { + total: padIDs.length, + }; + let result: string[] = padIDs; + let maxResult; + + // Filter out matches + if (query.pattern) { + result = result.filter((padName: string) => padName.includes(query.pattern)); + } + + data.total = result.length; + + maxResult = result.length - 1; + if (maxResult < 0) { + maxResult = 0; + } + + // Reset to default values if out of bounds + if (query.offset && query.offset < 0) { + query.offset = 0; + } else if (query.offset > maxResult) { + query.offset = maxResult; + } + + if (query.limit && query.limit < 0) { + // Too small + query.limit = 0; + } else if (query.limit > queryPadLimit) { + // Too big + query.limit = queryPadLimit; + } + + + if (query.sortBy === 'padName') { + result = result.sort((a, b) => { + if (a < b) return query.ascending ? -1 : 1; + if (a > b) return query.ascending ? 1 : -1; + return 0; + }).slice(query.offset, query.offset + query.limit); + + data.results = await Promise.all(result.map(async (padName: string) => { + const pad = await padManager.getPad(padName); + const revisionNumber = pad.getHeadRevisionNumber() + const userCount = api.padUsersCount(padName).padUsersCount; + const lastEdited = await pad.getLastEdit(); + + return { + padName, + lastEdited, + userCount, + revisionNumber + } + })); + } else if (query.sortBy === "revisionNumber") { + const currentWinners: PadQueryResult[] = [] + const padMapping = [] as {padId: string, revisionNumber: number}[] + for (let res of result) { + const pad = await padManager.getPad(res); + const revisionNumber = pad.getHeadRevisionNumber() + padMapping.push({padId: res, revisionNumber}) + } + padMapping.sort((a, b) => { + if (a.revisionNumber < b.revisionNumber) return query.ascending ? -1 : 1; + if (a.revisionNumber > b.revisionNumber) return query.ascending ? 1 : -1; + return 0; + }) + + for (const padRetrieval of padMapping.slice(query.offset, query.offset + query.limit)) { + let pad = await padManager.getPad(padRetrieval.padId); + currentWinners.push({ + padName: padRetrieval.padId, + lastEdited: await pad.getLastEdit(), + userCount: api.padUsersCount(pad.padName).padUsersCount, + revisionNumber: padRetrieval.revisionNumber + }) + } + + data.results = currentWinners; + } else if (query.sortBy === "userCount") { + const currentWinners: PadQueryResult[] = [] + const padMapping = [] as {padId: string, userCount: number}[] + for (let res of result) { + const userCount = api.padUsersCount(res).padUsersCount + padMapping.push({padId: res, userCount}) + } + padMapping.sort((a, b) => { + if (a.userCount < b.userCount) return query.ascending ? -1 : 1; + if (a.userCount > b.userCount) return query.ascending ? 1 : -1; return 0; - }) - if(worstPad[0]&&worstPad[0][query.sortBy] < padType[query.sortBy]){ - if(queryOffsetCounter < query.offset){ - queryOffsetCounter++ - continue + }) + + for (const padRetrieval of padMapping.slice(query.offset, query.offset + query.limit)) { + let pad = await padManager.getPad(padRetrieval.padId); + currentWinners.push({ + padName: padRetrieval.padId, + lastEdited: await pad.getLastEdit(), + userCount: padRetrieval.userCount, + revisionNumber: pad.getHeadRevisionNumber() + }) + } + data.results = currentWinners; + } else if (query.sortBy === "lastEdited") { + const currentWinners: PadQueryResult[] = [] + const padMapping = [] as {padId: string, lastEdited: string}[] + for (let res of result) { + const pad = await padManager.getPad(res); + const lastEdited = await pad.getLastEdit(); + padMapping.push({padId: res, lastEdited}) } - currentWinners.splice(currentWinners.indexOf(worstPad[0]), 1) - currentWinners.push({ - padName: res, - lastEdited: await pad.getLastEdit(), - userCount: api.padUsersCount(res).padUsersCount, - revisionNumber: pad.getHeadRevisionNumber() + padMapping.sort((a, b) => { + if (a.lastEdited < b.lastEdited) return query.ascending ? -1 : 1; + if (a.lastEdited > b.lastEdited) return query.ascending ? 1 : -1; + return 0; }) + + for (const padRetrieval of padMapping.slice(query.offset, query.offset + query.limit)) { + let pad = await padManager.getPad(padRetrieval.padId); + currentWinners.push({ + padName: padRetrieval.padId, + lastEdited: padRetrieval.lastEdited, + userCount: api.padUsersCount(pad.padName).padUsersCount, + revisionNumber: pad.getHeadRevisionNumber() + }) + } + data.results = currentWinners; } - } - } - data.results = currentWinners; - } - - socket.emit('results:padLoad', data); - }) - - - socket.on('deletePad', async (padId: string) => { - const padExists = await padManager.doesPadExists(padId); - if (padExists) { - const pad = await padManager.getPad(padId); - await pad.remove(); - socket.emit('results:deletePad', padId); - } - }) - - socket.on('restartServer', async () => { - console.log('Admin request to restart server through a socket on /admin/settings'); - settings.reloadSettings(); - await plugins.update(); - await hooks.aCallAll('loadSettings', {settings}); - await hooks.aCallAll('restartServer'); + + socket.emit('results:padLoad', data); + }) + + + socket.on('deletePad', async (padId: string) => { + const padExists = await padManager.doesPadExists(padId); + if (padExists) { + logger.info(`Deleting pad: ${padId}`); + const pad = await padManager.getPad(padId); + await pad.remove(); + socket.emit('results:deletePad', padId); + } + }) + + socket.on('restartServer', async () => { + logger.info('Admin request to restart server through a socket on /admin/settings'); + settings.reloadSettings(); + await plugins.update(); + await hooks.aCallAll('loadSettings', {settings}); + await hooks.aCallAll('restartServer'); + }); }); - }); }; - -const searchPad = async (query:PadSearchQuery) => { +const searchPad = async (query: PadSearchQuery) => { } - diff --git a/src/node/hooks/express/openapi.ts b/src/node/hooks/express/openapi.ts index aa2f1e483f4..8b04adf9363 100644 --- a/src/node/hooks/express/openapi.ts +++ b/src/node/hooks/express/openapi.ts @@ -483,14 +483,24 @@ const generateDefinitionForVersion = (version:string, style = APIPathStyle.FLAT) ...defaultResponses, }, securitySchemes: { - ApiKey: { - type: 'apiKey', - in: 'query', - name: 'apikey', + openid: { + type: "oauth2", + flows: { + authorizationCode: { + authorizationUrl: settings.sso.issuer+"/oidc/auth", + tokenUrl: settings.sso.issuer+"/oidc/token", + scopes: { + openid: "openid", + profile: "profile", + email: "email", + admin: "admin" + } + } + }, }, }, }, - security: [{ApiKey: []}], + security: [{openid: []}], }; // build operations @@ -598,7 +608,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { for (const funcName of Object.keys(apiHandler.version[version])) { const handler = async (c: any, req:any, res:any) => { // parse fields from request - const {header, params, query} = c.request; + const {headers, params, query} = c.request; // read form data if method was POST let formData:MapArrayType<any> = {}; @@ -612,8 +622,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { } } - const fields = Object.assign({}, header, params, query, formData); - + const fields = Object.assign({}, headers, params, query, formData); if (logger.isDebugEnabled()) { logger.debug(`REQUEST, v${version}:${funcName}, ${JSON.stringify(fields)}`); } @@ -657,7 +666,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { } // start and bind to express - api.init(); + await api.init(); app.use(apiRoot, async (req:any, res:any) => { let response = null; try { diff --git a/src/node/hooks/express/pwa.ts b/src/node/hooks/express/pwa.ts new file mode 100644 index 00000000000..918efbc0599 --- /dev/null +++ b/src/node/hooks/express/pwa.ts @@ -0,0 +1,32 @@ +import {ArgsExpressType} from "../../types/ArgsExpressType"; +const settings = require('../../utils/Settings'); + +const pwa = { + name: settings.title || "Etherpad", + short_name: settings.title, + description: "A collaborative online editor", + icons: [ + { + "src": "/static/skins/colibris/images/fond.jpg", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "/favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + type: "image/png" + } + ], + start_url: "/", + display: "fullscreen", + theme_color: "#0f775b", + background_color: "#0f775b" +} + +exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => { + args.app.get('/manifest.json', (req:any, res:any) => { + res.json(pwa); + }); + + return cb(); +} diff --git a/src/node/hooks/express/specialpages.ts b/src/node/hooks/express/specialpages.ts index 85a23479fe2..9e4642ca8a8 100644 --- a/src/node/hooks/express/specialpages.ts +++ b/src/node/hooks/express/specialpages.ts @@ -1,14 +1,23 @@ 'use strict'; -const path = require('path'); -const eejs = require('../../eejs'); -const fs = require('fs'); +import path from 'node:path'; +const eejs = require('../../eejs') +import fs from 'node:fs'; const fsp = fs.promises; const toolbar = require('../../utils/toolbar'); const hooks = require('../../../static/js/pluginfw/hooks'); const settings = require('../../utils/Settings'); -const util = require('util'); +import util from 'node:util'; const webaccess = require('./webaccess'); +const plugins = require('../../../static/js/pluginfw/plugin_defs'); + +import {build, buildSync} from 'esbuild' +let ioI: { sockets: { sockets: any[]; }; } | null = null + +exports.socketio = (hookName: string, {io}: any) => { + ioI = io +} + exports.expressPreSession = async (hookName:string, {app}:any) => { // This endpoint is intended to conform to: @@ -73,49 +82,291 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { }); }; -exports.expressCreateServer = (hookName:string, args:any, cb:Function) => { - // serve index.html under / - args.app.get('/', (req:any, res:any) => { - res.send(eejs.require('ep_etherpad-lite/templates/index.html', {req})); + + +const convertTypescript = (content: string) => { + const outputRaw = buildSync({ + stdin: { + contents: content, + resolveDir: path.join(settings.root, 'var','js'), + loader: 'js' + }, + alias:{ + "ep_etherpad-lite/static/js/browser": 'ep_etherpad-lite/static/js/vendors/browser', + "ep_etherpad-lite/static/js/nice-select": 'ep_etherpad-lite/static/js/vendors/nice-select' + }, + bundle: true, // Bundle the files together + minify: process.env.NODE_ENV === "production", // Minify the output + sourcemap: !(process.env.NODE_ENV === "production"), // Generate source maps + sourceRoot: settings.root+"/src/static/js/", + target: ['es2020'], // Target ECMAScript version + metafile: true, + write: false, // Do not write to file system, + }) + const output = outputRaw.outputFiles[0].text + + return { + output, + hash: outputRaw.outputFiles[0].hash.replaceAll('/','2').replaceAll("+",'5').replaceAll("^","7") + } +} + +const handleLiveReload = async (args: any, padString: string, timeSliderString: string, indexString: any) => { + const chokidar = await import('chokidar') + const watcher = chokidar.watch(path.join(settings.root, 'src', 'static', 'js')); + let routeHandlers: { [key: string]: Function } = {}; + + const setRouteHandler = (path: string, newHandler: Function) => { + routeHandlers[path] = newHandler; + }; + args.app.use((req: any, res: any, next: Function) => { + if (req.path.startsWith('/p/') && req.path.split('/').length == 3) { + req.params = { + pad: req.path.split('/')[2] + } + routeHandlers['/p/:pad'](req, res); + } else if (req.path.startsWith('/p/') && req.path.split('/').length == 4) { + req.params = { + pad: req.path.split('/')[2] + } + routeHandlers['/p/:pad/timeslider'](req, res); + } else if (req.path == "/"){ + routeHandlers['/'](req, res); + } else if (routeHandlers[req.path]) { + routeHandlers[req.path](req, res); + } else { + next(); + } }); - // serve pad.html under /p - args.app.get('/p/:pad', (req:any, res:any, next:Function) => { - // The below might break for pads being rewritten - const isReadOnly = !webaccess.userCanModify(req.params.pad, req); + function handleUpdate() { - hooks.callAll('padInitToolbar', { - toolbar, - isReadOnly, - }); + convertTypescriptWatched(indexString, (output, hash) => { + setRouteHandler('/watch/index', (req: any, res: any) => { + res.header('Content-Type', 'application/javascript'); + res.send(output) + }) + setRouteHandler('/', (req: any, res: any) => { + res.send(eejs.require('ep_etherpad-lite/templates/index.html', {req, entrypoint: '/watch/index?hash=' + hash, settings})); + }) + }) + + convertTypescriptWatched(padString, (output, hash) => { + console.log("New pad hash is", hash) + setRouteHandler('/watch/pad', (req: any, res: any) => { + res.header('Content-Type', 'application/javascript'); + res.send(output) + }) - // can be removed when require-kernel is dropped - res.header('Feature-Policy', 'sync-xhr \'self\''); - res.send(eejs.require('ep_etherpad-lite/templates/pad.html', { - req, - toolbar, - isReadOnly, - })); + + + + setRouteHandler("/p/:pad", (req: any, res: any, next: Function) => { + // The below might break for pads being rewritten + const isReadOnly = !webaccess.userCanModify(req.params.pad, req); + + hooks.callAll('padInitToolbar', { + toolbar, + isReadOnly + }); + + const content = eejs.require('ep_etherpad-lite/templates/pad.html', { + req, + toolbar, + isReadOnly, + entrypoint: '/watch/pad?hash=' + hash + }) + res.send(content); + }) + ioI!.sockets.sockets.forEach(socket => socket.emit('liveupdate')) + }) + convertTypescriptWatched(timeSliderString, (output, hash) => { + // serve timeslider.html under /p/$padname/timeslider + console.log("New timeslider hash is", hash) + + setRouteHandler('/watch/timeslider', (req: any, res: any) => { + res.header('Content-Type', 'application/javascript'); + res.send(output) + }) + + setRouteHandler("/p/:pad/timeslider", (req: any, res: any, next: Function) => { + console.log("Reloading pad") + // The below might break for pads being rewritten + const isReadOnly = !webaccess.userCanModify(req.params.pad, req); + + hooks.callAll('padInitToolbar', { + toolbar, + isReadOnly + }); + + const content = eejs.require('ep_etherpad-lite/templates/timeslider.html', { + req, + toolbar, + isReadOnly, + entrypoint: '/watch/timeslider?hash=' + hash + }) + res.send(content); + }) + }) + } + + watcher.on('change', path => { + console.log(`File ${path} has been changed`); + handleUpdate(); }); + handleUpdate() +} + +const convertTypescriptWatched = (content: string, cb: (output:string, hash: string)=>void) => { + build({ + stdin: { + contents: content, + resolveDir: path.join(settings.root, 'var','js'), + loader: 'js' + }, + alias:{ + "ep_etherpad-lite/static/js/browser": 'ep_etherpad-lite/static/js/vendors/browser', + "ep_etherpad-lite/static/js/nice-select": 'ep_etherpad-lite/static/js/vendors/nice-select' + }, + bundle: true, // Bundle the files together + minify: process.env.NODE_ENV === "production", // Minify the output + sourcemap: !(process.env.NODE_ENV === "production"), // Generate source maps + sourceRoot: settings.root+"/src/static/js/", + target: ['es2020'], // Target ECMAScript version + metafile: true, + write: false, // Do not write to file system, + }).then((outputRaw) => { + cb( + outputRaw.outputFiles[0].text, + outputRaw.outputFiles[0].hash.replaceAll('/','2').replaceAll("+",'5').replaceAll("^","7") + ) + }) +} - // serve timeslider.html under /p/$padname/timeslider - args.app.get('/p/:pad/timeslider', (req:any, res:any, next:Function) => { - hooks.callAll('padInitToolbar', { - toolbar, +exports.expressCreateServer = async (hookName: string, args: any, cb: Function) => { + const padString = eejs.require('ep_etherpad-lite/templates/padBootstrap.js', { + pluginModules: (() => { + const pluginModules = new Set(); + for (const part of plugins.parts) { + for (const [, hookFnName] of Object.entries(part.client_hooks || {})) { + // @ts-ignore + pluginModules.add(hookFnName.split(':')[0]); + } + } + return [...pluginModules]; + })(), + settings, + }) + + const indexString = eejs.require('ep_etherpad-lite/templates/indexBootstrap.js', { + }) + + const timeSliderString = eejs.require('ep_etherpad-lite/templates/timeSliderBootstrap.js', { + pluginModules: (() => { + const pluginModules = new Set(); + for (const part of plugins.parts) { + for (const [, hookFnName] of Object.entries(part.client_hooks || {})) { + // @ts-ignore + pluginModules.add(hookFnName.split(':')[0]); + } + } + return [...pluginModules]; + })(), + settings, + }) + + + + const outdir = path.join(settings.root, 'var','js') + // Create the outdir if it doesn't exist + if (!fs.existsSync(outdir)) { + fs.mkdirSync(outdir); + } + + let fileNamePad: string + let fileNameTimeSlider: string + let fileNameIndex: string + if(process.env.NODE_ENV === "production"){ + const padSliderWrite = convertTypescript(padString) + const timeSliderWrite = convertTypescript(timeSliderString) + const indexWrite = convertTypescript(indexString) + + fileNamePad = `padbootstrap-${padSliderWrite.hash}.min.js` + fileNameTimeSlider = `timeSliderBootstrap-${timeSliderWrite.hash}.min.js` + fileNameIndex = `indexBootstrap-${indexWrite.hash}.min.js` + const pathNamePad = path.join(outdir, fileNamePad) + const pathNameTimeSlider = path.join(outdir, fileNameTimeSlider) + const pathNameIndex = path.join(outdir, 'index.js') + + if (!fs.existsSync(pathNamePad)) { + fs.writeFileSync(pathNamePad, padSliderWrite.output); + } + + if (!fs.existsSync(pathNameIndex)) { + fs.writeFileSync(pathNameIndex, indexWrite.output); + } + + if (!fs.existsSync(pathNameTimeSlider)) { + fs.writeFileSync(pathNameTimeSlider,timeSliderWrite.output) + } + + args.app.get("/"+fileNamePad, (req: any, res: any) => { + res.sendFile(pathNamePad) + }) + + args.app.get("/"+fileNameIndex, (req: any, res: any) => { + res.sendFile(pathNameIndex) + }) + + args.app.get("/"+fileNameTimeSlider, (req: any, res: any) => { + res.sendFile(pathNameTimeSlider) + }) + + // serve index.html under / + args.app.get('/', (req: any, res: any) => { + res.send(eejs.require('ep_etherpad-lite/templates/index.html', {req, settings, entrypoint: "/"+fileNameIndex})); }); - res.send(eejs.require('ep_etherpad-lite/templates/timeslider.html', { - req, - toolbar, - })); - }); + + // serve pad.html under /p + args.app.get('/p/:pad', (req: any, res: any, next: Function) => { + // The below might break for pads being rewritten + const isReadOnly = !webaccess.userCanModify(req.params.pad, req); + + hooks.callAll('padInitToolbar', { + toolbar, + isReadOnly + }); + + const content = eejs.require('ep_etherpad-lite/templates/pad.html', { + req, + toolbar, + isReadOnly, + entrypoint: "../../"+fileNamePad + }) + res.send(content); + }); + + // serve timeslider.html under /p/$padname/timeslider + args.app.get('/p/:pad/timeslider', (req: any, res: any, next: Function) => { + hooks.callAll('padInitToolbar', { + toolbar, + }); + + res.send(eejs.require('ep_etherpad-lite/templates/timeslider.html', { + req, + toolbar, + entrypoint: "../../../"+fileNameTimeSlider + })); + }); + } else { + await handleLiveReload(args, padString, timeSliderString, indexString) + } // The client occasionally polls this endpoint to get an updated expiration for the express_sid // cookie. This handler must be installed after the express-session middleware. - args.app.put('/_extendExpressSessionLifetime', (req:any, res:any) => { + args.app.put('/_extendExpressSessionLifetime', (req: any, res: any) => { // express-session automatically calls req.session.touch() so we don't need to do it here. res.json({status: 'ok'}); }); - - return cb(); }; diff --git a/src/node/hooks/express/static.ts b/src/node/hooks/express/static.ts index 6b0b7559355..07a7f3a21cf 100644 --- a/src/node/hooks/express/static.ts +++ b/src/node/hooks/express/static.ts @@ -4,12 +4,10 @@ import {MapArrayType} from "../../types/MapType"; import {PartType} from "../../types/PartType"; const fs = require('fs').promises; -const minify = require('../../utils/Minify'); -const path = require('path'); +import {minify} from '../../utils/Minify'; +import path from 'node:path'; const plugins = require('../../../static/js/pluginfw/plugin_defs'); const settings = require('../../utils/Settings'); -const CachingMiddleware = require('../../utils/caching_middleware'); -const Yajsml = require('etherpad-yajsml'); // Rewrite tar to include modules with no extensions and proper rooted paths. const getTar = async () => { @@ -33,31 +31,10 @@ const getTar = async () => { }; exports.expressPreSession = async (hookName:string, {app}:any) => { - // Cache both minified and static. - const assetCache = new CachingMiddleware(); - app.all(/\/javascripts\/(.*)/, assetCache.handle.bind(assetCache)); // Minify will serve static files compressed (minify enabled). It also has // file-specific hacks for ace/require-kernel/etc. - app.all('/static/:filename(*)', minify.minify); - - // Setup middleware that will package JavaScript files served by minify for - // CommonJS loader on the client-side. - // Hostname "invalid.invalid" is a dummy value to allow parsing as a URI. - const jsServer = new (Yajsml.Server)({ - rootPath: 'javascripts/src/', - rootURI: 'http://invalid.invalid/static/js/', - libraryPath: 'javascripts/lib/', - libraryURI: 'http://invalid.invalid/static/plugins/', - requestURIs: minify.requestURIs, // Loop-back is causing problems, this is a workaround. - }); - - const StaticAssociator = Yajsml.associators.StaticAssociator; - const associations = Yajsml.associators.associationsForSimpleMapping(await getTar()); - const associator = new StaticAssociator(associations); - jsServer.setAssociator(associator); - - app.use(jsServer.handle.bind(jsServer)); + app.all('/static/:filename(*)', minify); // serve plugin definitions // not very static, but served here so that client can do diff --git a/src/node/hooks/express/webaccess.ts b/src/node/hooks/express/webaccess.ts index 0034f87c8b2..10547b046a8 100644 --- a/src/node/hooks/express/webaccess.ts +++ b/src/node/hooks/express/webaccess.ts @@ -169,12 +169,18 @@ const checkAccess = async (req:any, res:any, next: Function) => { if (await aCallFirst0('authnFailure', {req, res})) return; if (await aCallFirst0('authFailure', {req, res, next})) return; // No plugin handled the authentication failure. Fall back to basic authentication. - //res.header('WWW-Authenticate', 'Basic realm="Protected Area"'); + if (!requireAdmin) { + res.header('WWW-Authenticate', 'Basic realm="Protected Area"'); + } // Delay the error response for 1s to slow down brute force attacks. await new Promise((resolve) => setTimeout(resolve, exports.authnFailureDelayMs)); res.status(401).send('Authentication Required'); return; } + if (ctx.username === '__proto__' || ctx.username === 'constructor' || ctx.username === 'prototype') { + res.end(403); + return; + } settings.users[ctx.username].username = ctx.username; // Make a shallow copy so that the password property can be deleted (to prevent it from // appearing in logs or in the database) without breaking future authentication attempts. diff --git a/src/node/hooks/i18n.ts b/src/node/hooks/i18n.ts index 500f1f88784..69c313d0d98 100644 --- a/src/node/hooks/i18n.ts +++ b/src/node/hooks/i18n.ts @@ -7,8 +7,8 @@ const languages = require('languages4translatewiki'); const fs = require('fs'); const path = require('path'); const _ = require('underscore'); -const pluginDefs = require('../../static/js/pluginfw/plugin_defs.js'); -const existsSync = require('../utils/path_exists'); +const pluginDefs = require('../../static/js/pluginfw/plugin_defs'); +import existsSync from '../utils/path_exists'; const settings = require('../utils/Settings'); // returns all existing messages merged together and grouped by langcode diff --git a/src/node/security/OAuth2Provider.ts b/src/node/security/OAuth2Provider.ts new file mode 100644 index 00000000000..e212113504b --- /dev/null +++ b/src/node/security/OAuth2Provider.ts @@ -0,0 +1,282 @@ +import {ArgsExpressType} from "../types/ArgsExpressType"; +import Provider, {Account, Configuration} from 'oidc-provider'; +import {generateKeyPair, exportJWK, KeyLike} from 'jose' +import MemoryAdapter from "./OIDCAdapter"; +import path from "path"; +const settings = require('../utils/Settings'); +import {IncomingForm} from 'formidable' +import express, {Request, Response} from 'express'; +import {format} from 'url' +import {ParsedUrlQuery} from "node:querystring"; +import {Http2ServerRequest, Http2ServerResponse} from "node:http2"; +import {MapArrayType} from "../types/MapType"; + +const configuration: Configuration = { + scopes: ['openid', 'profile', 'email'], + findAccount: async (ctx, id) => { + const users = settings.users as { + [username: string]: { + password: string; + is_admin: boolean; + } + } + const usersArray1 = Object.keys(users).map((username) => ({ + username, + ...users[username] + })); + + const account = usersArray1.find((user) => user.username === id); + + if(account === undefined) { + return undefined + } + if (account.is_admin) { + return { + accountId: id, + claims: () => ({ + sub: id, + admin: true + }) + } as Account + } else { + return { + accountId: id, + claims: () => ({ + sub: id, + }) + } as Account + } + }, + ttl: settings.ttl, + claims: { + openid: ['sub'], + email: ['email'], + profile: ['name'], + admin: ['admin'] + }, + cookies: { + keys: ['oidc'], + }, + features:{ + devInteractions: {enabled: false}, + }, + adapter: MemoryAdapter +}; + + +export let publicKeyExported: KeyLike|null +export let privateKeyExported: KeyLike|null + +/* +This function is used to initialize the OAuth2 provider + */ +export const expressCreateServer = async (hookName: string, args: ArgsExpressType, cb: Function) => { + const {privateKey, publicKey} = await generateKeyPair('RS256'); + const privateKeyJWK = await exportJWK(privateKey); + publicKeyExported = publicKey + privateKeyExported = privateKey + + const oidc = new Provider(settings.sso.issuer, { + ...configuration, jwks: { + keys: [ + privateKeyJWK + ], + }, + conformIdTokenClaims: false, + claims: { + address: ['address'], + email: ['email', 'email_verified'], + phone: ['phone_number', 'phone_number_verified'], + profile: ['birthdate', 'family_name', 'gender', 'given_name', 'locale', 'middle_name', 'name', + 'nickname', 'picture', 'preferred_username', 'profile', 'updated_at', 'website', 'zoneinfo'], + }, + features:{ + userinfo: {enabled: true}, + claimsParameter: {enabled: true}, + clientCredentials: {enabled: true}, + devInteractions: {enabled: false}, + resourceIndicators: {enabled: true, defaultResource(ctx) { + return ctx.origin; + }, + getResourceServerInfo(ctx, resourceIndicator, client) { + return { + scope: "openid", + audience: 'account', + accessTokenFormat: 'jwt', + }; + }, + useGrantedResource(ctx, model) { + return true; + }, + }, + jwtResponseModes: {enabled: true}, + }, + clientBasedCORS: (ctx, origin, client) => { + return true + }, + extraParams: [], + extraTokenClaims: async (ctx, token) => { + if(token.kind === 'AccessToken') { + // Add your custom claims here. For example: + const users = settings.users as { + [username: string]: { + password: string; + is_admin: boolean; + } + } + + const usersArray1 = Object.keys(users).map((username) => ({ + username, + ...users[username] + })); + + const account = usersArray1.find((user) => user.username === token.accountId); + return { + admin: account?.is_admin + }; + } else if (token.kind === "ClientCredentials") { + let extraParams: MapArrayType<string> = {} + + settings.sso.clients + .filter((client:any) => client.client_id === token.clientId) + .forEach((client:any) => { + if(client.extraParams !== undefined) { + client.extraParams.forEach((param:any) => { + extraParams[param.name] = param.value + }) + } + }) + return extraParams + } + }, + clients: settings.sso.clients + }); + + + args.app.post('/interaction/:uid', async (req: Http2ServerRequest, res: Http2ServerResponse, next:Function) => { + const formid = new IncomingForm(); + try { + // @ts-ignore + const {login, password} = (await formid.parse(req))[0] + const {prompt, jti, session,cid, params, grantId} = await oidc.interactionDetails(req, res); + + const client = await oidc.Client.find(params.client_id as string); + + switch (prompt.name) { + case 'login': { + const users = settings.users as { + [username: string]: { + password: string; + admin: boolean; + } + } + const usersArray1 = Object.keys(users).map((username) => ({ + username, + ...users[username] + })); + const account = usersArray1.find((user) => user.username === login as unknown as string && user.password === password as unknown as string); + if (!account) { + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({error: "Invalid login"})); + } + + if (account) { + await oidc.interactionFinished(req, res, { + login: {accountId: account.username} + }, {mergeWithLastSubmission: false}); + } + break; + } + case 'consent': { + let grant; + if (grantId) { + // we'll be modifying existing grant in existing session + grant = await oidc.Grant.find(grantId); + } else { + // we're establishing a new grant + grant = new oidc.Grant({ + accountId: session!.accountId, + clientId: params.client_id as string, + }); + } + + if (prompt.details.missingOIDCScope) { + // @ts-ignore + grant!.addOIDCScope(prompt.details.missingOIDCScope.join(' ')); + } + if (prompt.details.missingOIDCClaims) { + grant!.addOIDCClaims(prompt.details.missingOIDCClaims as string[]); + } + if (prompt.details.missingResourceScopes) { + for (const [indicator, scope] of Object.entries(prompt.details.missingResourceScopes)) { + grant!.addResourceScope(indicator, scope.join(' ')); + } + } + const result = {consent: {grantId: await grant!.save()}}; + await oidc.interactionFinished(req, res, result, { + mergeWithLastSubmission: true, + }); + break; + } + } + await next(); + } catch (err:any) { + return res.writeHead(500).end(err.message); + } + }) + + + args.app.get('/interaction/:uid', async (req: Request, res: Response, next: Function) => { + try { + const { + uid, prompt, params, session, + } = await oidc.interactionDetails(req, res); + + params["state"] = uid + + switch (prompt.name) { + case 'login': { + res.redirect(format({ + pathname: '/views/login.html', + query: params as ParsedUrlQuery + })) + break + } + case 'consent': { + res.redirect(format({ + pathname: '/views/consent.html', + query: params as ParsedUrlQuery + })) + break + } + default: + return res.sendFile(path.join(settings.root,'src','static', 'oidc','login.html')); + } + } catch (err) { + return next(err); + } + }); + + + args.app.use('/views/', express.static(path.join(settings.root,'src','static', 'oidc'), {maxAge: 1000 * 60 * 60 * 24})); + + + oidc.on('authorization.error', (ctx, error) => { + console.log('authorization.error', error); + }) + + oidc.on('server_error', (ctx, error) => { + console.log('server_error', error); + }) + oidc.on('grant.error', (ctx, error) => { + console.log('grant.error', error); + }) + oidc.on('introspection.error', (ctx, error) => { + console.log('introspection.error', error); + }) + oidc.on('revocation.error', (ctx, error) => { + console.log('revocation.error', error); + }) + args.app.use("/oidc", oidc.callback()); + //cb(); +} diff --git a/src/node/security/OAuth2User.ts b/src/node/security/OAuth2User.ts new file mode 100644 index 00000000000..b4305c40133 --- /dev/null +++ b/src/node/security/OAuth2User.ts @@ -0,0 +1,5 @@ +export type OAuth2User = { + username: string; + password: string; + admin: boolean; +} diff --git a/src/node/security/OIDCAdapter.ts b/src/node/security/OIDCAdapter.ts new file mode 100644 index 00000000000..7fb90777665 --- /dev/null +++ b/src/node/security/OIDCAdapter.ts @@ -0,0 +1,115 @@ +import {LRUCache} from 'lru-cache'; +import type {Adapter, AdapterPayload} from "oidc-provider"; + + +const options = { + max: 500, + sizeCalculation: (item:any, key:any) => { + return 1 + }, + // for use with tracking overall storage size + maxSize: 5000, + + // how long to live in ms + ttl: 1000 * 60 * 5, + + // return stale items before removing from cache? + allowStale: false, + + updateAgeOnGet: false, + updateAgeOnHas: false, +} + +const epochTime = (date = Date.now()) => Math.floor(date / 1000); + +const storage = new LRUCache<string,AdapterPayload|string[]|string>(options); + +function grantKeyFor(id: string) { + return `grant:${id}`; +} + +function userCodeKeyFor(userCode:string) { + return `userCode:${userCode}`; +} + +class MemoryAdapter implements Adapter{ + private readonly name: string; + constructor(name:string) { + this.name = name; + } + + key(id:string) { + return `${this.name}:${id}`; + } + + destroy(id:string) { + const key = this.key(id); + + const found = storage.get(key) as AdapterPayload; + const grantId = found && found.grantId; + + storage.delete(key); + + if (grantId) { + const grantKey = grantKeyFor(grantId); + (storage.get(grantKey) as string[])!.forEach(token => storage.delete(token)); + storage.delete(grantKey); + } + + return Promise.resolve(); + } + + consume(id: string) { + (storage.get(this.key(id)) as AdapterPayload)!.consumed = epochTime(); + return Promise.resolve(); + } + + find(id: string): Promise<AdapterPayload | void | undefined> { + if (storage.has(this.key(id))){ + return Promise.resolve<AdapterPayload>(storage.get(this.key(id)) as AdapterPayload); + } + return Promise.resolve<undefined>(undefined) + } + + findByUserCode(userCode: string) { + const id = storage.get(userCodeKeyFor(userCode)) as string; + return this.find(id); + } + + upsert(id: string, payload: { + iat: number; + exp: number; + uid: string; + kind: string; + jti: string; + accountId: string; + loginTs: number; + }, expiresIn: number) { + const key = this.key(id); + + storage.set(key, payload, {ttl: expiresIn * 1000}); + + return Promise.resolve(); + } + + findByUid(uid: string): Promise<AdapterPayload | void | undefined> { + for(const [_, value] of storage.entries()){ + if(typeof value ==="object" && "uid" in value && value.uid === uid){ + return Promise.resolve(value); + } + } + return Promise.resolve(undefined); + } + + revokeByGrantId(grantId: string): Promise<void | undefined> { + const grantKey = grantKeyFor(grantId); + const grant = storage.get(grantKey) as string[]; + if (grant) { + grant.forEach((token) => storage.delete(token)); + storage.delete(grantKey); + } + return Promise.resolve(); + } +} + +export default MemoryAdapter diff --git a/src/node/server.ts b/src/node/server.ts index df6d21ffb18..507ed5f7958 100755 --- a/src/node/server.ts +++ b/src/node/server.ts @@ -27,6 +27,7 @@ import {ErrorCaused} from "./types/ErrorCaused"; import log4js from 'log4js'; import pkg from '../package.json'; import {checkForMigration} from "../static/js/pluginfw/installer"; +import axios from "axios"; const settings = require('./utils/Settings'); @@ -37,6 +38,28 @@ if (settings.dumpOnUncleanExit) { wtfnode = require('wtfnode'); } + +const addProxyToAxios = (url: URL) => { + axios.defaults.proxy = { + host: url.hostname, + port: Number(url.port), + protocol: url.protocol, + } +} + +if(process.env['http_proxy']) { + console.log("Using proxy: " + process.env['http_proxy']) + addProxyToAxios(new URL(process.env['http_proxy'])); +} + + +if (process.env['https_proxy']) { + console.log("Using proxy: " + process.env['https_proxy']) + addProxyToAxios(new URL(process.env['https_proxy'])); +} + + + /* * early check for version compatibility before calling * any modules that require newer versions of NodeJS @@ -51,7 +74,7 @@ const express = require('./hooks/express'); const hooks = require('../static/js/pluginfw/hooks'); const pluginDefs = require('../static/js/pluginfw/plugin_defs'); const plugins = require('../static/js/pluginfw/plugins'); -const {Gate} = require('./utils/promises'); +import {Gate} from './utils/promises'; const stats = require('./stats') const logger = log4js.getLogger('server'); @@ -77,7 +100,7 @@ const removeSignalListener = (signal: NodeJS.Signals, listener: NodeJS.SignalsLi }; -let startDoneGate: { resolve: () => void; } +let startDoneGate: Gate<unknown> exports.start = async () => { switch (state) { case State.INITIAL: @@ -158,12 +181,14 @@ exports.start = async () => { } catch (err) { logger.error('Error occurred while starting Etherpad'); state = State.STATE_TRANSITION_FAILED; + // @ts-ignore startDoneGate.resolve(); return await exports.exit(err); } logger.info('Etherpad is running'); state = State.RUNNING; + // @ts-ignore startDoneGate.resolve(); // Return the HTTP server to make it easier to write tests. @@ -205,11 +230,13 @@ exports.stop = async () => { } catch (err) { logger.error('Error occurred while stopping Etherpad'); state = State.STATE_TRANSITION_FAILED; + // @ts-ignore stopDoneGate.resolve(); return await exports.exit(err); } logger.info('Etherpad stopped'); state = State.STOPPED; + // @ts-ignore stopDoneGate.resolve(); }; diff --git a/src/node/types/PadType.ts b/src/node/types/PadType.ts index b344ed8c555..f35b51361c9 100644 --- a/src/node/types/PadType.ts +++ b/src/node/types/PadType.ts @@ -1,10 +1,11 @@ import {MapArrayType} from "./MapType"; +import AttributePool from "../../static/js/AttributePool"; export type PadType = { id: string, - apool: ()=>APool, + apool: ()=>AttributePool, atext: AText, - pool: APool, + pool: AttributePool, getInternalRevisionAText: (text:number|string)=>Promise<AText>, getValidRevisionRange: (fromRev: string, toRev: string)=>PadRange, getRevisionAuthor: (rev: number)=>Promise<string>, @@ -35,6 +36,7 @@ export type APool = { clone: ()=>APool, check: ()=>Promise<void>, eachAttrib: (callback: (key: string, value: any)=>void)=>void, + getAttrib: (key: number)=>any, } diff --git a/src/node/utils/ExportHelper.ts b/src/node/utils/ExportHelper.ts index f3a438e86e6..4c29534f447 100644 --- a/src/node/utils/ExportHelper.ts +++ b/src/node/utils/ExportHelper.ts @@ -19,8 +19,9 @@ * limitations under the License. */ -const AttributeMap = require('../../static/js/AttributeMap'); -const Changeset = require('../../static/js/Changeset'); +import AttributeMap from '../../static/js/AttributeMap'; +import AttributePool from "../../static/js/AttributePool"; +import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset'; const { checkValidRev } = require('./checkValidRev'); /* @@ -30,7 +31,7 @@ exports.getPadPlainText = (pad: { getInternalRevisionAText: (arg0: any) => any; const _analyzeLine = exports._analyzeLine; const atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(checkValidRev(revNum)) : pad.atext); const textLines = atext.text.slice(0, -1).split('\n'); - const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + const attribLines = splitAttributionLines(atext.attribs, atext.text); const apool = pad.pool; const pieces = []; @@ -51,14 +52,14 @@ type LineModel = { [id:string]:string|number|LineModel } -exports._analyzeLine = (text:string, aline: LineModel, apool: Function) => { +exports._analyzeLine = (text:string, aline: string, apool: AttributePool) => { const line: LineModel = {}; // identify list let lineMarker = 0; line.listLevel = 0; if (aline) { - const [op] = Changeset.deserializeOps(aline); + const [op] = deserializeOps(aline); if (op != null) { const attribs = AttributeMap.fromString(op.attribs, apool); let listType = attribs.get('list'); @@ -78,7 +79,7 @@ exports._analyzeLine = (text:string, aline: LineModel, apool: Function) => { } if (lineMarker) { line.text = text.substring(1); - line.aline = Changeset.subattribution(aline, 1); + line.aline = subattribution(aline, 1); } else { line.text = text; line.aline = aline; diff --git a/src/node/utils/ExportHtml.ts b/src/node/utils/ExportHtml.ts index e21c5453736..6ff03bc00df 100644 --- a/src/node/utils/ExportHtml.ts +++ b/src/node/utils/ExportHtml.ts @@ -18,7 +18,7 @@ import {MapArrayType} from "../types/MapType"; * limitations under the License. */ -const Changeset = require('../../static/js/Changeset'); +import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset'; const attributes = require('../../static/js/attributes'); const padManager = require('../db/PadManager'); const _ = require('underscore'); @@ -27,7 +27,9 @@ const hooks = require('../../static/js/pluginfw/hooks'); const eejs = require('../eejs'); const _analyzeLine = require('./ExportHelper')._analyzeLine; const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace; -const padutils = require('../../static/js/pad_utils').padutils; +import padutils from "../../static/js/pad_utils"; +import {StringIterator} from "../../static/js/StringIterator"; +import {StringAssembler} from "../../static/js/StringAssembler"; const getPadHTML = async (pad: PadType, revNum: string) => { let atext = pad.atext; @@ -44,7 +46,7 @@ const getPadHTML = async (pad: PadType, revNum: string) => { const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string[]) => { const apool = pad.apool(); const textLines = atext.text.slice(0, -1).split('\n'); - const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + const attribLines = splitAttributionLines(atext.attribs, atext.text); const tags = ['h1', 'h2', 'strong', 'em', 'u', 's']; const props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; @@ -80,6 +82,7 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string css += '<style>\n'; for (const a of Object.keys(apool.numToAttrib)) { + // @ts-ignore const attr = apool.numToAttrib[a]; // skip non author attributes @@ -115,6 +118,7 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string // see hook exportHtmlAdditionalTagsWithData attrib = propName; } + // @ts-ignore const propTrueNum = apool.putAttrib(attrib, true); if (propTrueNum >= 0) { anumMap[propTrueNum] = i; @@ -127,8 +131,8 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string // <b>Just bold<b> <b><i>Bold and italics</i></b> <i>Just italics</i> // becomes // <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i> - const taker = Changeset.stringIterator(text); - const assem = Changeset.stringAssembler(); + const taker = new StringIterator(text); + const assem = new StringAssembler(); const openTags:string[] = []; const getSpanClassFor = (i: string) => { @@ -204,7 +208,8 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string return; } - const ops = Changeset.deserializeOps(Changeset.subattribution(attribs, idx, idx + numChars)); + // @ts-ignore + const ops = deserializeOps(subattribution(attribs, idx, idx + numChars)); idx += numChars; // this iterates over every op string and decides which tags to open or to close diff --git a/src/node/utils/ExportTxt.ts b/src/node/utils/ExportTxt.ts index 95e8b045646..ba4d9e19f00 100644 --- a/src/node/utils/ExportTxt.ts +++ b/src/node/utils/ExportTxt.ts @@ -22,7 +22,9 @@ import {AText, PadType} from "../types/PadType"; import {MapType} from "../types/MapType"; -const Changeset = require('../../static/js/Changeset'); +import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset'; +import {StringIterator} from "../../static/js/StringIterator"; +import {StringAssembler} from "../../static/js/StringAssembler"; const attributes = require('../../static/js/attributes'); const padManager = require('../db/PadManager'); const _analyzeLine = require('./ExportHelper')._analyzeLine; @@ -45,13 +47,14 @@ const getPadTXT = async (pad: PadType, revNum: string) => { const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => { const apool = pad.apool(); const textLines = atext.text.slice(0, -1).split('\n'); - const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + const attribLines = splitAttributionLines(atext.attribs, atext.text); const props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; const anumMap: MapType = {}; const css = ''; props.forEach((propName, i) => { + // @ts-ignore const propTrueNum = apool.putAttrib([propName, true], true); if (propTrueNum >= 0) { anumMap[propTrueNum] = i; @@ -69,8 +72,8 @@ const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => { // <b>Just bold<b> <b><i>Bold and italics</i></b> <i>Just italics</i> // becomes // <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i> - const taker = Changeset.stringIterator(text); - const assem = Changeset.stringAssembler(); + const taker = new StringIterator(text); + const assem = new StringAssembler(); let idx = 0; @@ -79,7 +82,7 @@ const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => { return; } - const ops = Changeset.deserializeOps(Changeset.subattribution(attribs, idx, idx + numChars)); + const ops = deserializeOps(subattribution(attribs, idx, idx + numChars)); idx += numChars; for (const o of ops) { diff --git a/src/node/utils/ImportEtherpad.ts b/src/node/utils/ImportEtherpad.ts index 50b9a43d580..cf34107c73e 100644 --- a/src/node/utils/ImportEtherpad.ts +++ b/src/node/utils/ImportEtherpad.ts @@ -18,7 +18,7 @@ import {APool} from "../types/PadType"; * limitations under the License. */ -const AttributePool = require('../../static/js/AttributePool'); +import AttributePool from '../../static/js/AttributePool'; const {Pad} = require('../db/Pad'); const Stream = require('./Stream'); const authorManager = require('../db/AuthorManager'); @@ -26,7 +26,7 @@ const db = require('../db/DB'); const hooks = require('../../static/js/pluginfw/hooks'); import log4js from 'log4js'; const supportedElems = require('../../static/js/contentcollector').supportedElems; -import ueberdb from 'ueberdb2'; +import {Database} from 'ueberdb2'; const logger = log4js.getLogger('ImportEtherpad'); @@ -56,12 +56,12 @@ exports.setPadRaw = async (padId: string, r: string, authorId = '') => { const data = new Map(); const existingAuthors = new Set(); - const padDb = new ueberdb.Database('memory', {data}); + const padDb = new Database('memory', {data}); await padDb.init(); try { const processRecord = async (key:string, value: null|{ padIDs: string|Record<string, unknown>, - pool: APool + pool: AttributePool }) => { if (!value) return; const keyParts = key.split(':'); diff --git a/src/node/utils/ImportHtml.ts b/src/node/utils/ImportHtml.ts index 57d9cbe4f65..941aa767a27 100644 --- a/src/node/utils/ImportHtml.ts +++ b/src/node/utils/ImportHtml.ts @@ -16,10 +16,11 @@ */ import log4js from 'log4js'; -const Changeset = require('../../static/js/Changeset'); +import {deserializeOps} from '../../static/js/Changeset'; const contentcollector = require('../../static/js/contentcollector'); import jsdom from 'jsdom'; import {PadType} from "../types/PadType"; +import {Builder} from "../../static/js/Builder"; const apiLogger = log4js.getLogger('ImportHtml'); let processor:any; @@ -69,13 +70,13 @@ exports.setPadHTML = async (pad: PadType, html:string, authorId = '') => { const newAttribs = `${result.lineAttribs.join('|1+1')}|1+1`; // create a new changeset with a helper builder object - const builder = Changeset.builder(1); + const builder = new Builder(1); // assemble each line into the builder let textIndex = 0; const newTextStart = 0; const newTextEnd = newText.length; - for (const op of Changeset.deserializeOps(newAttribs)) { + for (const op of deserializeOps(newAttribs)) { const nextIndex = textIndex + op.chars; if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) { const start = Math.max(newTextStart, textIndex); diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.ts similarity index 67% rename from src/node/utils/Minify.js rename to src/node/utils/Minify.ts index b8bf6b6154f..47a276100ac 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.ts @@ -21,21 +21,20 @@ * limitations under the License. */ +import {TransformResult} from "esbuild"; +import mime from 'mime-types'; +import log4js from 'log4js'; +import {compressCSS, compressJS} from './MinifyWorker' + const settings = require('./Settings'); -const fs = require('fs').promises; -const path = require('path'); +import {promises as fs} from 'fs'; +import path from 'node:path'; const plugins = require('../../static/js/pluginfw/plugin_defs'); -const RequireKernel = require('etherpad-require-kernel'); -const mime = require('mime-types'); -const Threads = require('threads'); -const log4js = require('log4js'); -const sanitizePathname = require('./sanitizePathname'); - +import sanitizePathname from './sanitizePathname'; const logger = log4js.getLogger('Minify'); const ROOT_DIR = path.join(settings.root, 'src/static/'); -const threadsPool = new Threads.Pool(() => Threads.spawn(new Threads.Worker('./MinifyWorker')), 2); const LIBRARY_WHITELIST = [ 'async', @@ -52,10 +51,10 @@ const LIBRARY_WHITELIST = [ // What follows is a terrible hack to avoid loop-back within the server. // TODO: Serve files from another service, or directly from the file system. -const requestURI = async (url, method, headers) => { +const requestURI = async (url: string | URL, method: any, headers: { [x: string]: any; }) => { const parsedUrl = new URL(url); let status = 500; - const content = []; + const content: any[] = []; const mockRequest = { url, method, @@ -65,7 +64,7 @@ const requestURI = async (url, method, headers) => { let mockResponse; const p = new Promise((resolve) => { mockResponse = { - writeHead: (_status, _headers) => { + writeHead: (_status: number, _headers: { [x: string]: any; }) => { status = _status; for (const header in _headers) { if (Object.prototype.hasOwnProperty.call(_headers, header)) { @@ -73,37 +72,63 @@ const requestURI = async (url, method, headers) => { } } }, - setHeader: (header, value) => { + setHeader: (header: string, value: { toString: () => any; }) => { headers[header.toLowerCase()] = value.toString(); }, - header: (header, value) => { + header: (header: string, value: { toString: () => any; }) => { headers[header.toLowerCase()] = value.toString(); }, - write: (_content) => { + write: (_content: any) => { _content && content.push(_content); }, - end: (_content) => { + end: (_content: any) => { _content && content.push(_content); resolve([status, headers, content.join('')]); }, }; }); - await minify(mockRequest, mockResponse); + await _minify(mockRequest, mockResponse); return await p; }; -const requestURIs = (locations, method, headers, callback) => { +const _requestURIs = (locations: any[], method: any, headers: { + [x: string]: + /** + * This Module manages all /minified/* requests. It controls the + * minification && compression of Javascript and CSS. + */ + /* + * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + any; +}, callback: (arg0: any[], arg1: any[], arg2: any[]) => void) => { Promise.all(locations.map(async (loc) => { try { return await requestURI(loc, method, headers); } catch (err) { logger.debug(`requestURI(${JSON.stringify(loc)}, ${JSON.stringify(method)}, ` + - `${JSON.stringify(headers)}) failed: ${err.stack || err}`); + // @ts-ignore + `${JSON.stringify(headers)}) failed: ${err.stack || err}`); return [500, headers, '']; } })).then((responses) => { + // @ts-ignore const statuss = responses.map((x) => x[0]); + // @ts-ignore const headerss = responses.map((x) => x[1]); + // @ts-ignore const contentss = responses.map((x) => x[2]); callback(statuss, headerss, contentss); }); @@ -123,11 +148,12 @@ const compatPaths = { * @param req the Express request * @param res the Express response */ -const minify = async (req, res) => { +const _minify = async (req:any, res:any) => { let filename = req.params.filename; try { filename = sanitizePathname(filename); } catch (err) { + // @ts-ignore logger.error(`sanitization of pathname "${filename}" failed: ${err.stack || err}`); res.writeHead(404, {}); res.end(); @@ -135,6 +161,7 @@ const minify = async (req, res) => { } // Backward compatibility for plugins that require() files from old paths. + // @ts-ignore const newLocation = compatPaths[filename.replace(/^plugins\/ep_etherpad-lite\/static\//, '')]; if (newLocation != null) { logger.warn(`request for deprecated path "${filename}", replacing with "${newLocation}"`); @@ -197,7 +224,7 @@ const minify = async (req, res) => { res.writeHead(200, {}); res.end(); } else if (req.method === 'GET') { - const content = await getFileCompressed(filename, contentType); + const content = await getFileCompressed(filename, contentType as string); res.header('Content-Type', contentType); res.writeHead(200, {}); res.write(content); @@ -209,7 +236,7 @@ const minify = async (req, res) => { }; // Check for the existance of the file and get the last modification date. -const statFile = async (filename, dirStatLimit) => { +const statFile = async (filename: string, dirStatLimit: number):Promise<(any | boolean)[]> => { /* * The only external call to this function provides an explicit value for * dirStatLimit: this check could be removed. @@ -220,17 +247,12 @@ const statFile = async (filename, dirStatLimit) => { if (dirStatLimit < 1 || filename === '' || filename === '/') { return [null, false]; - } else if (filename === 'js/ace.js') { - // Sometimes static assets are inlined into this file, so we have to stat - // everything. - return [await lastModifiedDateOfEverything(), true]; - } else if (filename === 'js/require-kernel.js') { - return [_requireLastModified, true]; } else { let stats; try { stats = await fs.stat(path.resolve(ROOT_DIR, filename)); } catch (err) { + // @ts-ignore if (['ENOENT', 'ENOTDIR'].includes(err.code)) { // Stat the directory instead. const [date] = await statFile(path.dirname(filename), dirStatLimit - 1); @@ -242,88 +264,66 @@ const statFile = async (filename, dirStatLimit) => { } }; -const lastModifiedDateOfEverything = async () => { - const folders2check = [path.join(ROOT_DIR, 'js/'), path.join(ROOT_DIR, 'css/')]; - let latestModification = null; - // go through this two folders - await Promise.all(folders2check.map(async (dir) => { - // read the files in the folder - const files = await fs.readdir(dir); - - // we wanna check the directory itself for changes too - files.push('.'); - - // go through all files in this folder - await Promise.all(files.map(async (filename) => { - // get the stat data of this file - const stats = await fs.stat(path.join(dir, filename)); - - // compare the modification time to the highest found - if (latestModification == null || stats.mtime > latestModification) { - latestModification = stats.mtime; - } - })); - })); - return latestModification; -}; - -// This should be provided by the module, but until then, just use startup -// time. -const _requireLastModified = new Date(); -const requireDefinition = () => `var require = ${RequireKernel.kernelSource};\n`; +let contentCache = new Map(); -const getFileCompressed = async (filename, contentType) => { - let content = await getFile(filename); +const getFileCompressed = async (filename: any, contentType: string) => { + if (contentCache.has(filename)) { + return contentCache.get(filename); + } + let content: Buffer|string = await getFile(filename); if (!content || !settings.minify) { return content; } else if (contentType === 'application/javascript') { - return await new Promise((resolve) => { - threadsPool.queue(async ({compressJS}) => { - try { - logger.info('Compress JS file %s.', filename); - - content = content.toString(); - const compressResult = await compressJS(content); + return await new Promise(async (resolve) => { + try { + logger.info('Compress JS file %s.', filename); - if (compressResult.error) { - console.error(`Error compressing JS (${filename}) using terser`, compressResult.error); - } else { - content = compressResult.code.toString(); // Convert content obj code to string - } + content = content.toString(); + try { + let compressResult: TransformResult<{ minify: boolean }> + compressResult = await compressJS(content); + content = compressResult.code.toString(); // Convert content obj code to string } catch (error) { - console.error('getFile() returned an error in ' + - `getFileCompressed(${filename}, ${contentType}): ${error}`); + console.error(`Error compressing JS (${filename}) using esbuild`, error); } - resolve(content); - }); + } catch (error) { + console.error('getFile() returned an error in ' + + `getFileCompressed(${filename}, ${contentType}): ${error}`); + } + contentCache.set(filename, content); + resolve(content); }); } else if (contentType === 'text/css') { - return await new Promise((resolve) => { - threadsPool.queue(async ({compressCSS}) => { - try { - logger.info('Compress CSS file %s.', filename); + return await new Promise(async (resolve) => { + try { + logger.info('Compress CSS file %s.', filename); - content = await compressCSS(filename, ROOT_DIR); + try { + content = await compressCSS(path.resolve(ROOT_DIR, filename)); } catch (error) { console.error(`CleanCSS.minify() returned an error on ${filename}: ${error}`); } + contentCache.set(filename, content); resolve(content); - }); - }); + } catch (e) { + console.error('getFile() returned an error in ' + + `getFileCompressed(${filename}, ${contentType}): ${e}`); + } + }) } else { + contentCache.set(filename, content); return content; } }; -const getFile = async (filename) => { - if (filename === 'js/require-kernel.js') return requireDefinition(); +const getFile = async (filename: any) => { return await fs.readFile(path.resolve(ROOT_DIR, filename)); }; -exports.minify = (req, res, next) => minify(req, res).catch((err) => next(err || new Error(err))); +export const minify = (req:any, res:any, next:Function) => _minify(req, res).catch((err) => next(err || new Error(err))); -exports.requestURIs = requestURIs; +export const requestURIs = _requestURIs; -exports.shutdown = async (hookName, context) => { - await threadsPool.terminate(); +export const shutdown = async (hookName: string, context:any) => { + contentCache = new Map(); }; diff --git a/src/node/utils/MinifyWorker.js b/src/node/utils/MinifyWorker.js deleted file mode 100644 index 364ecc96cca..00000000000 --- a/src/node/utils/MinifyWorker.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; -/** - * Worker thread to minify JS & CSS files out of the main NodeJS thread - */ - -const CleanCSS = require('clean-css'); -const Terser = require('terser'); -const fsp = require('fs').promises; -const path = require('path'); -const Threads = require('threads'); - -const compressJS = (content) => Terser.minify(content); - -const compressCSS = async (filename, ROOT_DIR) => { - const absPath = path.resolve(ROOT_DIR, filename); - try { - const basePath = path.dirname(absPath); - const output = await new CleanCSS({ - rebase: true, - rebaseTo: basePath, - }).minify([absPath]); - return output.styles; - } catch (error) { - // on error, just yield the un-minified original, but write a log message - console.error(`Unexpected error minifying ${filename} (${absPath}): ${error}`); - return await fsp.readFile(absPath, 'utf8'); - } -}; - -Threads.expose({ - compressJS, - compressCSS, -}); diff --git a/src/node/utils/MinifyWorker.ts b/src/node/utils/MinifyWorker.ts new file mode 100644 index 00000000000..cb278313c6f --- /dev/null +++ b/src/node/utils/MinifyWorker.ts @@ -0,0 +1,42 @@ +'use strict'; +/** + * Worker thread to minify JS & CSS files out of the main NodeJS thread + */ + +import {build, transform} from 'esbuild'; + +/* + * Minify JS content + * @param {string} content - JS content to minify + */ +export const compressJS = async (content: string) => { + return await transform(content, {minify: true}); +} + +/* + * Minify CSS content + * @param {string} filename - name of the file + * @param {string} ROOT_DIR - the root dir of Etherpad + */ +export const compressCSS = async (content: string) => { + const transformedCSS = await build( + { + entryPoints: [content], + minify: true, + bundle: true, + loader:{ + '.jpg': 'dataurl', + '.png': 'dataurl', + '.gif': 'dataurl', + '.ttf': 'dataurl', + '.otf': 'dataurl', + '.woff': 'dataurl', + '.woff2': 'dataurl', + '.eot': 'dataurl', + '.svg': 'dataurl' + }, + write: false + } + ) + return transformedCSS.outputFiles[0].text +}; diff --git a/src/node/utils/Settings.ts b/src/node/utils/Settings.ts index 8f1e058ea67..d798e10bffc 100644 --- a/src/node/utils/Settings.ts +++ b/src/node/utils/Settings.ts @@ -27,6 +27,10 @@ * limitations under the License. */ +import {MapArrayType} from "../types/MapType"; +import {SettingsNode, SettingsTree} from "./SettingsTree"; +import {coerce} from "semver"; + const absolutePaths = require('./AbsolutePaths'); const deepEqual = require('fast-deep-equal/es6'); const fs = require('fs'); @@ -44,33 +48,36 @@ const logger = log4js.getLogger('settings'); // Exported values that settings.json and credentials.json cannot override. const nonSettings = [ - 'credentialsFilename', - 'settingsFilename', + 'credentialsFilename', + 'settingsFilename', ]; // This is a function to make it easy to create a new instance. It is important to not reuse a // config object after passing it to log4js.configure() because that method mutates the object. :( -const defaultLogConfig = (level:string) => ({appenders: {console: {type: 'console'}}, - categories: { - default: {appenders: ['console'], level}, - }}); +const defaultLogConfig = (level: string, layoutType: string) => ({ + appenders: {console: {type: 'console', layout: {type: layoutType}}}, + categories: { + default: {appenders: ['console'], level}, + } +}); const defaultLogLevel = 'INFO'; - -const initLogging = (config:any) => { - // log4js.configure() modifies exports.logconfig so check for equality first. - log4js.configure(config); - log4js.getLogger('console'); - - // Overwrites for console output methods - console.debug = logger.debug.bind(logger); - console.log = logger.info.bind(logger); - console.warn = logger.warn.bind(logger); - console.error = logger.error.bind(logger); +const defaultLogLayoutType = 'colored'; + +const initLogging = (config: any) => { + // log4js.configure() modifies exports.logconfig so check for equality first. + log4js.configure(config); + log4js.getLogger('console'); + + // Overwrites for console output methods + console.debug = logger.debug.bind(logger); + console.log = logger.info.bind(logger); + console.warn = logger.warn.bind(logger); + console.error = logger.error.bind(logger); }; // Initialize logging as early as possible with reasonable defaults. Logging will be re-initialized // with the user's chosen log level and logger config after the settings have been loaded. -initLogging(defaultLogConfig(defaultLogLevel)); +initLogging(defaultLogConfig(defaultLogLevel, defaultLogLayoutType)); /* Root path of the installation */ exports.root = absolutePaths.findEtherpadRoot(); @@ -92,6 +99,16 @@ exports.title = 'Etherpad'; */ exports.favicon = null; +exports.ttl = { + AccessToken: 1 * 60 * 60, // 1 hour in seconds + AuthorizationCode: 10 * 60, // 10 minutes in seconds + ClientCredentials: 1 * 60 * 60, // 1 hour in seconds + IdToken: 1 * 60 * 60, // 1 hour in seconds + RefreshToken: 1 * 24 * 60 * 60, // 1 day in seconds +} + + + /* * Skin name. * @@ -129,17 +146,26 @@ exports.ssl = false; exports.socketTransportProtocols = ['websocket', 'polling']; exports.socketIo = { - /** - * Maximum permitted client message size (in bytes). - * - * All messages from clients that are larger than this will be rejected. Large values make it - * possible to paste large amounts of text, and plugins may require a larger value to work - * properly, but increasing the value increases susceptibility to denial of service attacks - * (malicious clients can exhaust memory). - */ - maxHttpBufferSize: 10000, + /** + * Maximum permitted client message size (in bytes). + * + * All messages from clients that are larger than this will be rejected. Large values make it + * possible to paste large amounts of text, and plugins may require a larger value to work + * properly, but increasing the value increases susceptibility to denial of service attacks + * (malicious clients can exhaust memory). + */ + maxHttpBufferSize: 50000, }; + +/* + The authentication method used by the server. + The default value is sso + If you want to use the old authentication system, change this to apikey + */ +exports.authenticationMethod = 'sso' + + /* * The Type of the database */ @@ -153,77 +179,77 @@ exports.dbSettings = {filename: path.join(exports.root, 'var/dirty.db')}; * The default Text of a new pad */ exports.defaultPadText = [ - 'Welcome to Etherpad!', - '', - 'This pad text is synchronized as you type, so that everyone viewing this page sees the same ' + - 'text. This allows you to collaborate seamlessly on documents!', - '', - 'Etherpad on Github: https://github.com/ether/etherpad-lite', + 'Welcome to Etherpad!', + '', + 'This pad text is synchronized as you type, so that everyone viewing this page sees the same ' + + 'text. This allows you to collaborate seamlessly on documents!', + '', + 'Etherpad on Github: https://github.com/ether/etherpad-lite', ].join('\n'); /** * The default Pad Settings for a user (Can be overridden by changing the setting */ exports.padOptions = { - noColors: false, - showControls: true, - showChat: true, - showLineNumbers: true, - useMonospaceFont: false, - userName: null, - userColor: null, - rtl: false, - alwaysShowChat: false, - chatAndUsers: false, - lang: null, + noColors: false, + showControls: true, + showChat: true, + showLineNumbers: true, + useMonospaceFont: false, + userName: null, + userColor: null, + rtl: false, + alwaysShowChat: false, + chatAndUsers: false, + lang: null, }; /** * Whether certain shortcut keys are enabled for a user in the pad */ exports.padShortcutEnabled = { - altF9: true, - altC: true, - delete: true, - cmdShift2: true, - return: true, - esc: true, - cmdS: true, - tab: true, - cmdZ: true, - cmdY: true, - cmdB: true, - cmdI: true, - cmdU: true, - cmd5: true, - cmdShiftL: true, - cmdShiftN: true, - cmdShift1: true, - cmdShiftC: true, - cmdH: true, - ctrlHome: true, - pageUp: true, - pageDown: true, + altF9: true, + altC: true, + delete: true, + cmdShift2: true, + return: true, + esc: true, + cmdS: true, + tab: true, + cmdZ: true, + cmdY: true, + cmdB: true, + cmdI: true, + cmdU: true, + cmd5: true, + cmdShiftL: true, + cmdShiftN: true, + cmdShift1: true, + cmdShiftC: true, + cmdH: true, + ctrlHome: true, + pageUp: true, + pageDown: true, }; /** * The toolbar buttons and order. */ exports.toolbar = { - left: [ - ['bold', 'italic', 'underline', 'strikethrough'], - ['orderedlist', 'unorderedlist', 'indent', 'outdent'], - ['undo', 'redo'], - ['clearauthorship'], - ], - right: [ - ['importexport', 'timeslider', 'savedrevision'], - ['settings', 'embed'], - ['showusers'], - ], - timeslider: [ - ['timeslider_export', 'timeslider_settings', 'timeslider_returnToPad'], - ], + left: [ + ['bold', 'italic', 'underline', 'strikethrough'], + ['orderedlist', 'unorderedlist', 'indent', 'outdent'], + ['undo', 'redo'], + ['clearauthorship'], + ], + right: [ + ['importexport', 'timeslider', 'savedrevision'], + ['settings', 'embed'], + ['showusers'], + ], + timeslider: [ + ['timeslider_export', 'timeslider_settings', 'timeslider_returnToPad'], + ], }; /** @@ -266,6 +292,11 @@ exports.allowUnknownFileEnds = true; */ exports.loglevel = defaultLogLevel; +/** + * The log layout type of log4js + */ +exports.logLayoutType = defaultLogLayoutType; + /** * Disable IP logging */ @@ -310,21 +341,21 @@ exports.trustProxy = false; * Settings controlling the session cookie issued by Etherpad. */ exports.cookie = { - keyRotationInterval: 1 * 24 * 60 * 60 * 1000, - /* - * Value of the SameSite cookie property. "Lax" is recommended unless - * Etherpad will be embedded in an iframe from another site, in which case - * this must be set to "None". Note: "None" will not work (the browser will - * not send the cookie to Etherpad) unless https is used to access Etherpad - * (either directly or via a reverse proxy with "trustProxy" set to true). - * - * "Strict" is not recommended because it has few security benefits but - * significant usability drawbacks vs. "Lax". See - * https://stackoverflow.com/q/41841880 for discussion. - */ - sameSite: 'Lax', - sessionLifetime: 10 * 24 * 60 * 60 * 1000, - sessionRefreshInterval: 1 * 24 * 60 * 60 * 1000, + keyRotationInterval: 1 * 24 * 60 * 60 * 1000, + /* + * Value of the SameSite cookie property. "Lax" is recommended unless + * Etherpad will be embedded in an iframe from another site, in which case + * this must be set to "None". Note: "None" will not work (the browser will + * not send the cookie to Etherpad) unless https is used to access Etherpad + * (either directly or via a reverse proxy with "trustProxy" set to true). + * + * "Strict" is not recommended because it has few security benefits but + * significant usability drawbacks vs. "Lax". See + * https://stackoverflow.com/q/41841880 for discussion. + */ + sameSite: 'Lax', + sessionLifetime: 10 * 24 * 60 * 60 * 1000, + sessionRefreshInterval: 1 * 24 * 60 * 60 * 1000, }; /* @@ -336,6 +367,13 @@ exports.requireAuthentication = false; exports.requireAuthorization = false; exports.users = {}; +/* + * This setting is used for configuring sso + */ +exports.sso = { + issuer: "http://localhost:9001" +} + /* * Show settings in admin page, by default it is true */ @@ -346,31 +384,31 @@ exports.showSettingsInAdminPage = true; * height needed to make this line visible. */ exports.scrollWhenFocusLineIsOutOfViewport = { - /* - * Percentage of viewport height to be additionally scrolled. - */ - percentage: { - editionAboveViewport: 0, - editionBelowViewport: 0, - }, - - /* - * Time (in milliseconds) used to animate the scroll transition. Set to 0 to - * disable animation - */ - duration: 0, - - /* - * Percentage of viewport height to be additionally scrolled when user presses arrow up - * in the line of the top of the viewport. - */ - percentageToScrollWhenUserPressesArrowUp: 0, - - /* - * Flag to control if it should scroll when user places the caret in the last - * line of the viewport - */ - scrollWhenCaretIsInTheLastLineOfViewport: false, + /* + * Percentage of viewport height to be additionally scrolled. + */ + percentage: { + editionAboveViewport: 0, + editionBelowViewport: 0, + }, + + /* + * Time (in milliseconds) used to animate the scroll transition. Set to 0 to + * disable animation + */ + duration: 0, + + /* + * Percentage of viewport height to be additionally scrolled when user presses arrow up + * in the line of the top of the viewport. + */ + percentageToScrollWhenUserPressesArrowUp: 0, + + /* + * Flag to control if it should scroll when user places the caret in the last + * line of the viewport + */ + scrollWhenCaretIsInTheLastLineOfViewport: false, }; /* @@ -395,11 +433,11 @@ exports.customLocaleStrings = {}; * See https://github.com/nfriedly/express-rate-limit for more options */ exports.importExportRateLimiting = { - // duration of the rate limit window (milliseconds) - windowMs: 90000, + // duration of the rate limit window (milliseconds) + windowMs: 90000, - // maximum number of requests per IP to allow during the rate limit window - max: 10, + // maximum number of requests per IP to allow during the rate limit window + max: 10, }; /* @@ -411,11 +449,11 @@ exports.importExportRateLimiting = { * See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options */ exports.commitRateLimiting = { - // duration of the rate limit window (seconds) - duration: 1, + // duration of the rate limit window (seconds) + duration: 1, - // maximum number of chanes per IP to allow during the rate limit window - points: 10, + // maximum number of chanes per IP to allow during the rate limit window + points: 10, }; /* @@ -439,63 +477,65 @@ exports.lowerCasePadIds = false; // checks if abiword is avaiable exports.abiwordAvailable = () => { - if (exports.abiword != null) { - return os.type().indexOf('Windows') !== -1 ? 'withoutPDF' : 'yes'; - } else { - return 'no'; - } + if (exports.abiword != null) { + return os.type().indexOf('Windows') !== -1 ? 'withoutPDF' : 'yes'; + } else { + return 'no'; + } }; exports.sofficeAvailable = () => { - if (exports.soffice != null) { - return os.type().indexOf('Windows') !== -1 ? 'withoutPDF' : 'yes'; - } else { - return 'no'; - } + if (exports.soffice != null) { + return os.type().indexOf('Windows') !== -1 ? 'withoutPDF' : 'yes'; + } else { + return 'no'; + } }; exports.exportAvailable = () => { - const abiword = exports.abiwordAvailable(); - const soffice = exports.sofficeAvailable(); - - if (abiword === 'no' && soffice === 'no') { - return 'no'; - } else if ((abiword === 'withoutPDF' && soffice === 'no') || - (abiword === 'no' && soffice === 'withoutPDF')) { - return 'withoutPDF'; - } else { - return 'yes'; - } + const abiword = exports.abiwordAvailable(); + const soffice = exports.sofficeAvailable(); + + if (abiword === 'no' && soffice === 'no') { + return 'no'; + } else if ((abiword === 'withoutPDF' && soffice === 'no') || + (abiword === 'no' && soffice === 'withoutPDF')) { + return 'withoutPDF'; + } else { + return 'yes'; + } }; // Provide git version if available exports.getGitCommit = () => { - let version = ''; - try { - let rootPath = exports.root; - if (fs.lstatSync(`${rootPath}/.git`).isFile()) { - rootPath = fs.readFileSync(`${rootPath}/.git`, 'utf8'); - rootPath = rootPath.split(' ').pop().trim(); - } else { - rootPath += '/.git'; - } - const ref = fs.readFileSync(`${rootPath}/HEAD`, 'utf-8'); - if (ref.startsWith('ref: ')) { - const refPath = `${rootPath}/${ref.substring(5, ref.indexOf('\n'))}`; - version = fs.readFileSync(refPath, 'utf-8'); - } else { - version = ref; + let version = ''; + try { + let rootPath = exports.root; + if (fs.lstatSync(`${rootPath}/.git`).isFile()) { + rootPath = fs.readFileSync(`${rootPath}/.git`, 'utf8'); + rootPath = rootPath.split(' ').pop().trim(); + } else { + rootPath += '/.git'; + } + const ref = fs.readFileSync(`${rootPath}/HEAD`, 'utf-8'); + if (ref.startsWith('ref: ')) { + const refPath = `${rootPath}/${ref.substring(5, ref.indexOf('\n'))}`; + version = fs.readFileSync(refPath, 'utf-8'); + } else { + version = ref; + } + version = version.substring(0, 7); + } catch (e: any) { + logger.warn(`Can't get git version for server header\n${e.message}`); } - version = version.substring(0, 7); - } catch (e:any) { - logger.warn(`Can't get git version for server header\n${e.message}`); - } - return version; + return version; }; // Return etherpad version from package.json exports.getEpVersion = () => require('../../package.json').version; + + /** * Receives a settingsObj and, if the property name is a valid configuration * item, stores it in the module's exported properties via a side effect. @@ -503,31 +543,31 @@ exports.getEpVersion = () => require('../../package.json').version; * This code refactors a previous version that copied & pasted the same code for * both "settings.json" and "credentials.json". */ -const storeSettings = (settingsObj:any) => { - for (const i of Object.keys(settingsObj || {})) { - if (nonSettings.includes(i)) { - logger.warn(`Ignoring setting: '${i}'`); - continue; - } +const storeSettings = (settingsObj: any) => { + for (const i of Object.keys(settingsObj || {})) { + if (nonSettings.includes(i)) { + logger.warn(`Ignoring setting: '${i}'`); + continue; + } - // test if the setting starts with a lowercase character - if (i.charAt(0).search('[a-z]') !== 0) { - logger.warn(`Settings should start with a lowercase character: '${i}'`); - } + // test if the setting starts with a lowercase character + if (i.charAt(0).search('[a-z]') !== 0) { + logger.warn(`Settings should start with a lowercase character: '${i}'`); + } - // we know this setting, so we overwrite it - // or it's a settings hash, specific to a plugin - if (exports[i] !== undefined || i.indexOf('ep_') === 0) { - if (_.isObject(settingsObj[i]) && !Array.isArray(settingsObj[i])) { - exports[i] = _.defaults(settingsObj[i], exports[i]); - } else { - exports[i] = settingsObj[i]; - } - } else { - // this setting is unknown, output a warning and throw it away - logger.warn(`Unknown Setting: '${i}'. This setting doesn't exist or it was removed`); + // we know this setting, so we overwrite it + // or it's a settings hash, specific to a plugin + if (exports[i] !== undefined || i.indexOf('ep_') === 0) { + if (_.isObject(settingsObj[i]) && !Array.isArray(settingsObj[i])) { + exports[i] = _.defaults(settingsObj[i], exports[i]); + } else { + exports[i] = settingsObj[i]; + } + } else { + // this setting is unknown, output a warning and throw it away + logger.warn(`Unknown Setting: '${i}'. This setting doesn't exist or it was removed`); + } } - } }; /* @@ -542,24 +582,29 @@ const storeSettings = (settingsObj:any) => { * short syntax "${ABIWORD}", and not "${ABIWORD:null}": the latter would result * in the literal string "null", instead. */ -const coerceValue = (stringValue:string) => { - // cooked from https://stackoverflow.com/questions/175739/built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number - // @ts-ignore - const isNumeric = !isNaN(stringValue) && !isNaN(parseFloat(stringValue) && isFinite(stringValue)); +const coerceValue = (stringValue: string) => { + // cooked from https://stackoverflow.com/questions/175739/built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number + // @ts-ignore + const isNumeric = !isNaN(stringValue) && !isNaN(parseFloat(stringValue) && isFinite(stringValue)); - if (isNumeric) { - // detected numeric string. Coerce to a number + if (isNumeric) { + // detected numeric string. Coerce to a number - return +stringValue; - } + return +stringValue; + } - switch (stringValue) { - case 'true': return true; - case 'false': return false; - case 'undefined': return undefined; - case 'null': return null; - default: return stringValue; - } + switch (stringValue) { + case 'true': + return true; + case 'false': + return false; + case 'undefined': + return undefined; + case 'null': + return null; + default: + return stringValue; + } }; /** @@ -598,86 +643,130 @@ const coerceValue = (stringValue:string) => { * * see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter */ -const lookupEnvironmentVariables = (obj: object) => { - const stringifiedAndReplaced = JSON.stringify(obj, (key, value) => { - /* - * the first invocation of replacer() is with an empty key. Just go on, or - * we would zap the entire object. - */ - if (key === '') { - return value; - } - - /* - * If we received from the configuration file a number, a boolean or - * something that is not a string, we can be sure that it was a literal - * value. No need to perform any variable substitution. - * - * The environment variable expansion syntax "${ENV_VAR}" is just a string - * of specific form, after all. - */ - if (typeof value !== 'string') { - return value; +const lookupEnvironmentVariables = (obj: MapArrayType<any>) => { + const replaceEnvs = (obj: MapArrayType<any>) => { + for (let [key, value] of Object.entries(obj)) { + /* + * the first invocation of replacer() is with an empty key. Just go on, or + * we would zap the entire object. + */ + if (key === '') { + obj[key] = value; + continue + } + + /* + * If we received from the configuration file a number, a boolean or + * something that is not a string, we can be sure that it was a literal + * value. No need to perform any variable substitution. + * + * The environment variable expansion syntax "${ENV_VAR}" is just a string + * of specific form, after all. + */ + + if(key === 'undefined' || value === undefined) { + delete obj[key] + continue + } + + if ((typeof value !== 'string' && typeof value !== 'object') || value === null) { + obj[key] = value; + continue + } + + if (typeof obj[key] === "object") { + replaceEnvs(obj[key]); + continue + } + + + /* + * Let's check if the string value looks like a variable expansion (e.g.: + * "${ENV_VAR}" or "${ENV_VAR:default_value}") + */ + // MUXATOR 2019-03-21: we could use named capture groups here once we migrate to nodejs v10 + const match = value.match(/^\$\{([^:]*)(:((.|\n)*))?\}$/); + + if (match == null) { + // no match: use the value literally, without any substitution + obj[key] = value; + continue + } + + /* + * We found the name of an environment variable. Let's read its actual value + * and its default value, if given + */ + const envVarName = match[1]; + const envVarValue = process.env[envVarName]; + const defaultValue = match[3]; + + if ((envVarValue === undefined) && (defaultValue === undefined)) { + logger.warn(`Environment variable "${envVarName}" does not contain any value for ` + + `configuration key "${key}", and no default was given. Using null. ` + + 'THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF ETHERPAD; you should ' + + 'explicitly use "null" as the default if you want to continue to use null.'); + + /* + * We have to return null, because if we just returned undefined, the + * configuration item "key" would be stripped from the returned object. + */ + obj[key] = null; + continue + } + + if ((envVarValue === undefined) && (defaultValue !== undefined)) { + logger.debug(`Environment variable "${envVarName}" not found for ` + + `configuration key "${key}". Falling back to default value.`); + + obj[key] = coerceValue(defaultValue); + continue + } + + // envVarName contained some value. + + /* + * For numeric and boolean strings let's convert it to proper types before + * returning it, in order to maintain backward compatibility. + */ + logger.debug( + `Configuration key "${key}" will be read from environment variable "${envVarName}"`); + + obj[key] = coerceValue(envVarValue!); + } + return obj } - /* - * Let's check if the string value looks like a variable expansion (e.g.: - * "${ENV_VAR}" or "${ENV_VAR:default_value}") - */ - // MUXATOR 2019-03-21: we could use named capture groups here once we migrate to nodejs v10 - const match = value.match(/^\$\{([^:]*)(:((.|\n)*))?\}$/); + replaceEnvs(obj); - if (match == null) { - // no match: use the value literally, without any substitution + // Add plugin ENV variables - return value; - } - - /* - * We found the name of an environment variable. Let's read its actual value - * and its default value, if given + /** + * If the key contains a double underscore, it's a plugin variable + * E.g. */ - const envVarName = match[1]; - const envVarValue = process.env[envVarName]; - const defaultValue = match[3]; - - if ((envVarValue === undefined) && (defaultValue === undefined)) { - logger.warn(`Environment variable "${envVarName}" does not contain any value for ` + - `configuration key "${key}", and no default was given. Using null. ` + - 'THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF ETHERPAD; you should ' + - 'explicitly use "null" as the default if you want to continue to use null.'); - - /* - * We have to return null, because if we just returned undefined, the - * configuration item "key" would be stripped from the returned object. - */ - return null; - } - - if ((envVarValue === undefined) && (defaultValue !== undefined)) { - logger.debug(`Environment variable "${envVarName}" not found for ` + - `configuration key "${key}". Falling back to default value.`); + let treeEntries = new Map<string, string | undefined> + const root = new SettingsNode("EP") - return coerceValue(defaultValue); + for (let [env, envVal] of Object.entries(process.env)) { + if (!env.startsWith("EP")) continue + treeEntries.set(env, envVal) } - - // envVarName contained some value. - - /* - * For numeric and boolean strings let's convert it to proper types before - * returning it, in order to maintain backward compatibility. - */ - logger.debug( - `Configuration key "${key}" will be read from environment variable "${envVarName}"`); - - return coerceValue(envVarValue!); - }); - - const newSettings = JSON.parse(stringifiedAndReplaced); - - return newSettings; + treeEntries.forEach((value, key) => { + let pathToKey = key.split("__") + let currentNode = root + let depth = 0 + depth++ + currentNode.addChild(pathToKey, value!) + }) + + //console.log(root.collectFromLeafsUpwards()) + const rooting = root.collectFromLeafsUpwards() + obj = Object.assign(obj, rooting) + return obj; }; + /** * - reads the JSON configuration file settingsFilename from disk * - strips the comments @@ -686,188 +775,196 @@ const lookupEnvironmentVariables = (obj: object) => { * * The isSettings variable only controls the error logging. */ -const parseSettings = (settingsFilename:string, isSettings:boolean) => { - let settingsStr = ''; - - let settingsType, notFoundMessage, notFoundFunction; +const parseSettings = (settingsFilename: string, isSettings: boolean) => { + let settingsStr = ''; - if (isSettings) { - settingsType = 'settings'; - notFoundMessage = 'Continuing using defaults!'; - notFoundFunction = logger.warn.bind(logger); - } else { - settingsType = 'credentials'; - notFoundMessage = 'Ignoring.'; - notFoundFunction = logger.info.bind(logger); - } + let settingsType, notFoundMessage, notFoundFunction; - try { - // read the settings file - settingsStr = fs.readFileSync(settingsFilename).toString(); - } catch (e) { - notFoundFunction(`No ${settingsType} file found in ${settingsFilename}. ${notFoundMessage}`); + if (isSettings) { + settingsType = 'settings'; + notFoundMessage = 'Continuing using defaults!'; + notFoundFunction = logger.warn.bind(logger); + } else { + settingsType = 'credentials'; + notFoundMessage = 'Ignoring.'; + notFoundFunction = logger.info.bind(logger); + } - // or maybe undefined! - return null; - } + try { + // read the settings file + settingsStr = fs.readFileSync(settingsFilename).toString(); + } catch (e) { + notFoundFunction(`No ${settingsType} file found in ${settingsFilename}. ${notFoundMessage}`); - try { - settingsStr = jsonminify(settingsStr).replace(',]', ']').replace(',}', '}'); + // or maybe undefined! + return null; + } - const settings = JSON.parse(settingsStr); + try { + settingsStr = jsonminify(settingsStr).replace(',]', ']').replace(',}', '}'); - logger.info(`${settingsType} loaded from: ${settingsFilename}`); + const settings = JSON.parse(settingsStr); - const replacedSettings = lookupEnvironmentVariables(settings); + logger.info(`${settingsType} loaded from: ${settingsFilename}`); - return replacedSettings; - } catch (e:any) { - logger.error(`There was an error processing your ${settingsType} ` + - `file from ${settingsFilename}: ${e.message}`); + return lookupEnvironmentVariables(settings); + } catch (e: any) { + logger.error(`There was an error processing your ${settingsType} ` + + `file from ${settingsFilename}: ${e.message}`); - process.exit(1); - } + process.exit(1); + } }; exports.reloadSettings = () => { - const settings = parseSettings(exports.settingsFilename, true); - const credentials = parseSettings(exports.credentialsFilename, false); - storeSettings(settings); - storeSettings(credentials); - - // Init logging config - exports.logconfig = defaultLogConfig(exports.loglevel ? exports.loglevel : defaultLogLevel); - initLogging(exports.logconfig); - - if (!exports.skinName) { - logger.warn('No "skinName" parameter found. Please check out settings.json.template and ' + - 'update your settings.json. Falling back to the default "colibris".'); - exports.skinName = 'colibris'; - } - - // checks if skinName has an acceptable value, otherwise falls back to "colibris" - if (exports.skinName) { - const skinBasePath = path.join(exports.root, 'src', 'static', 'skins'); - const countPieces = exports.skinName.split(path.sep).length; - - if (countPieces !== 1) { - logger.error(`skinName must be the name of a directory under "${skinBasePath}". This is ` + - `not valid: "${exports.skinName}". Falling back to the default "colibris".`); - - exports.skinName = 'colibris'; + const settings = parseSettings(exports.settingsFilename, true); + const credentials = parseSettings(exports.credentialsFilename, false); + storeSettings(settings); + storeSettings(credentials); + + // Init logging config + exports.logconfig = defaultLogConfig( + exports.loglevel ? exports.loglevel : defaultLogLevel, + exports.logLayoutType ? exports.logLayoutType : defaultLogLayoutType + ); + logger.warn("loglevel: " + exports.loglevel); + logger.warn("logLayoutType: " + exports.logLayoutType); + initLogging(exports.logconfig); + + if (!exports.skinName) { + logger.warn('No "skinName" parameter found. Please check out settings.json.template and ' + + 'update your settings.json. Falling back to the default "colibris".'); + exports.skinName = 'colibris'; } - // informative variable, just for the log messages - let skinPath = path.join(skinBasePath, exports.skinName); + if (!exports.socketTransportProtocols.includes("websocket") || !exports.socketTransportProtocols.includes("polling")) { + logger.warn("Invalid socketTransportProtocols setting. Please check out settings.json.template and update your settings.json. Falling back to the default ['websocket', 'polling']."); + exports.socketTransportProtocols = ['websocket', 'polling']; + } + + // checks if skinName has an acceptable value, otherwise falls back to "colibris" + if (exports.skinName) { + const skinBasePath = path.join(exports.root, 'src', 'static', 'skins'); + const countPieces = exports.skinName.split(path.sep).length; + + if (countPieces !== 1) { + logger.error(`skinName must be the name of a directory under "${skinBasePath}". This is ` + + `not valid: "${exports.skinName}". Falling back to the default "colibris".`); + + exports.skinName = 'colibris'; + } + + // informative variable, just for the log messages + let skinPath = path.join(skinBasePath, exports.skinName); - // what if someone sets skinName == ".." or "."? We catch him! - if (absolutePaths.isSubdir(skinBasePath, skinPath) === false) { - logger.error(`Skin path ${skinPath} must be a subdirectory of ${skinBasePath}. ` + - 'Falling back to the default "colibris".'); + // what if someone sets skinName == ".." or "."? We catch him! + if (absolutePaths.isSubdir(skinBasePath, skinPath) === false) { + logger.error(`Skin path ${skinPath} must be a subdirectory of ${skinBasePath}. ` + + 'Falling back to the default "colibris".'); + + exports.skinName = 'colibris'; + skinPath = path.join(skinBasePath, exports.skinName); + } - exports.skinName = 'colibris'; - skinPath = path.join(skinBasePath, exports.skinName); + if (fs.existsSync(skinPath) === false) { + logger.error(`Skin path ${skinPath} does not exist. Falling back to the default "colibris".`); + exports.skinName = 'colibris'; + skinPath = path.join(skinBasePath, exports.skinName); + } + + logger.info(`Using skin "${exports.skinName}" in dir: ${skinPath}`); } - if (fs.existsSync(skinPath) === false) { - logger.error(`Skin path ${skinPath} does not exist. Falling back to the default "colibris".`); - exports.skinName = 'colibris'; - skinPath = path.join(skinBasePath, exports.skinName); + if (exports.abiword) { + // Check abiword actually exists + if (exports.abiword != null) { + fs.exists(exports.abiword, (exists: boolean) => { + if (!exists) { + const abiwordError = 'Abiword does not exist at this path, check your settings file.'; + if (!exports.suppressErrorsInPadText) { + exports.defaultPadText += `\nError: ${abiwordError}${suppressDisableMsg}`; + } + logger.error(`${abiwordError} File location: ${exports.abiword}`); + exports.abiword = null; + } + }); + } } - logger.info(`Using skin "${exports.skinName}" in dir: ${skinPath}`); - } + if (exports.soffice) { + fs.exists(exports.soffice, (exists: boolean) => { + if (!exists) { + const sofficeError = + 'soffice (libreoffice) does not exist at this path, check your settings file.'; + + if (!exports.suppressErrorsInPadText) { + exports.defaultPadText += `\nError: ${sofficeError}${suppressDisableMsg}`; + } + logger.error(`${sofficeError} File location: ${exports.soffice}`); + exports.soffice = null; + } + }); + } - if (exports.abiword) { - // Check abiword actually exists - if (exports.abiword != null) { - fs.exists(exports.abiword, (exists: boolean) => { - if (!exists) { - const abiwordError = 'Abiword does not exist at this path, check your settings file.'; - if (!exports.suppressErrorsInPadText) { - exports.defaultPadText += `\nError: ${abiwordError}${suppressDisableMsg}`; - } - logger.error(`${abiwordError} File location: ${exports.abiword}`); - exports.abiword = null; + const sessionkeyFilename = absolutePaths.makeAbsolute(argv.sessionkey || './SESSIONKEY.txt'); + if (!exports.sessionKey) { + try { + exports.sessionKey = fs.readFileSync(sessionkeyFilename, 'utf8'); + logger.info(`Session key loaded from: ${sessionkeyFilename}`); + } catch (err) { /* ignored */ } - }); + const keyRotationEnabled = exports.cookie.keyRotationInterval && exports.cookie.sessionLifetime; + if (!exports.sessionKey && !keyRotationEnabled) { + logger.info( + `Session key file "${sessionkeyFilename}" not found. Creating with random contents.`); + exports.sessionKey = randomString(32); + fs.writeFileSync(sessionkeyFilename, exports.sessionKey, 'utf8'); + } + } else { + logger.warn('Declaring the sessionKey in the settings.json is deprecated. ' + + 'This value is auto-generated now. Please remove the setting from the file. -- ' + + 'If you are seeing this error after restarting using the Admin User ' + + 'Interface then you can ignore this message.'); + } + if (exports.sessionKey) { + logger.warn(`The sessionKey setting and ${sessionkeyFilename} file are deprecated; ` + + 'use automatic key rotation instead (see the cookie.keyRotationInterval setting).'); } - } - - if (exports.soffice) { - fs.exists(exports.soffice, (exists: boolean) => { - if (!exists) { - const sofficeError = - 'soffice (libreoffice) does not exist at this path, check your settings file.'; + if (exports.dbType === 'dirty') { + const dirtyWarning = 'DirtyDB is used. This is not recommended for production.'; if (!exports.suppressErrorsInPadText) { - exports.defaultPadText += `\nError: ${sofficeError}${suppressDisableMsg}`; + exports.defaultPadText += `\nWarning: ${dirtyWarning}${suppressDisableMsg}`; } - logger.error(`${sofficeError} File location: ${exports.soffice}`); - exports.soffice = null; - } - }); - } - - const sessionkeyFilename = absolutePaths.makeAbsolute(argv.sessionkey || './SESSIONKEY.txt'); - if (!exports.sessionKey) { - try { - exports.sessionKey = fs.readFileSync(sessionkeyFilename, 'utf8'); - logger.info(`Session key loaded from: ${sessionkeyFilename}`); - } catch (err) { /* ignored */ } - const keyRotationEnabled = exports.cookie.keyRotationInterval && exports.cookie.sessionLifetime; - if (!exports.sessionKey && !keyRotationEnabled) { - logger.info( - `Session key file "${sessionkeyFilename}" not found. Creating with random contents.`); - exports.sessionKey = randomString(32); - fs.writeFileSync(sessionkeyFilename, exports.sessionKey, 'utf8'); + + exports.dbSettings.filename = absolutePaths.makeAbsolute(exports.dbSettings.filename); + logger.warn(`${dirtyWarning} File location: ${exports.dbSettings.filename}`); } - } else { - logger.warn('Declaring the sessionKey in the settings.json is deprecated. ' + - 'This value is auto-generated now. Please remove the setting from the file. -- ' + - 'If you are seeing this error after restarting using the Admin User ' + - 'Interface then you can ignore this message.'); - } - if (exports.sessionKey) { - logger.warn(`The sessionKey setting and ${sessionkeyFilename} file are deprecated; ` + - 'use automatic key rotation instead (see the cookie.keyRotationInterval setting).'); - } - - if (exports.dbType === 'dirty') { - const dirtyWarning = 'DirtyDB is used. This is not recommended for production.'; - if (!exports.suppressErrorsInPadText) { - exports.defaultPadText += `\nWarning: ${dirtyWarning}${suppressDisableMsg}`; + + if (exports.ip === '') { + // using Unix socket for connectivity + logger.warn('The settings file contains an empty string ("") for the "ip" parameter. The ' + + '"port" parameter will be interpreted as the path to a Unix socket to bind at.'); } - exports.dbSettings.filename = absolutePaths.makeAbsolute(exports.dbSettings.filename); - logger.warn(`${dirtyWarning} File location: ${exports.dbSettings.filename}`); - } - - if (exports.ip === '') { - // using Unix socket for connectivity - logger.warn('The settings file contains an empty string ("") for the "ip" parameter. The ' + - '"port" parameter will be interpreted as the path to a Unix socket to bind at.'); - } - - /* - * At each start, Etherpad generates a random string and appends it as query - * parameter to the URLs of the static assets, in order to force their reload. - * Subsequent requests will be cached, as long as the server is not reloaded. - * - * For the rationale behind this choice, see - * https://github.com/ether/etherpad-lite/pull/3958 - * - * ACHTUNG: this may prevent caching HTTP proxies to work - * TODO: remove the "?v=randomstring" parameter, and replace with hashed filenames instead - */ - exports.randomVersionString = randomString(4); - logger.info(`Random string used for versioning assets: ${exports.randomVersionString}`); + /* + * At each start, Etherpad generates a random string and appends it as query + * parameter to the URLs of the static assets, in order to force their reload. + * Subsequent requests will be cached, as long as the server is not reloaded. + * + * For the rationale behind this choice, see + * https://github.com/ether/etherpad-lite/pull/3958 + * + * ACHTUNG: this may prevent caching HTTP proxies to work + * TODO: remove the "?v=randomstring" parameter, and replace with hashed filenames instead + */ + exports.randomVersionString = randomString(4); + logger.info(`Random string used for versioning assets: ${exports.randomVersionString}`); }; exports.exportedForTestingOnly = { - parseSettings, + parseSettings, }; // initially load settings exports.reloadSettings(); - diff --git a/src/node/utils/SettingsTree.ts b/src/node/utils/SettingsTree.ts new file mode 100644 index 00000000000..c505f2ebb0c --- /dev/null +++ b/src/node/utils/SettingsTree.ts @@ -0,0 +1,112 @@ +import {MapArrayType} from "../types/MapType"; + +export class SettingsTree { + private children: Map<string, SettingsNode>; + constructor() { + this.children = new Map(); + } + + public addChild(key: string, value: string) { + this.children.set(key, new SettingsNode(key, value)); + } + + public removeChild(key: string) { + this.children.delete(key); + } + + public getChild(key: string) { + return this.children.get(key); + } + + public hasChild(key: string) { + return this.children.has(key); + } +} + + +export class SettingsNode { + private readonly key: string; + private value: string | number | boolean | null | undefined; + private children: MapArrayType<SettingsNode>; + + constructor(key: string, value?: string | number | boolean | null | undefined) { + this.key = key; + this.value = value; + this.children = {} + } + + public addChild(path: string[], value: string) { + let currentNode:SettingsNode = this; + for (let i = 0; i < path.length; i++) { + const key = path[i]; + /* + Skip the current node if the key is the same as the current node's key + */ + if (key === this.key ) { + continue + } + /* + If the current node does not have a child with the key, create a new node with the key + */ + if (!currentNode.hasChild(key)) { + currentNode = currentNode.children[key] = new SettingsNode(key, this.coerceValue(value)); + } else { + /* + Else move to the child node + */ + currentNode = currentNode.getChild(key); + } + } + } + + + public collectFromLeafsUpwards() { + let collected:MapArrayType<any> = {}; + for (const key in this.children) { + const child = this.children[key]; + if (child.hasChildren()) { + collected[key] = child.collectFromLeafsUpwards(); + } else { + collected[key] = child.value; + } + } + return collected; + } + + coerceValue = (stringValue: string) => { + // cooked from https://stackoverflow.com/questions/175739/built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number + // @ts-ignore + const isNumeric = !isNaN(stringValue) && !isNaN(parseFloat(stringValue) && isFinite(stringValue)); + + if (isNumeric) { + // detected numeric string. Coerce to a number + + return +stringValue; + } + + switch (stringValue) { + case 'true': + return true; + case 'false': + return false; + case 'undefined': + return undefined; + case 'null': + return null; + default: + return stringValue; + } + }; + + public hasChildren() { + return Object.keys(this.children).length > 0; + } + + public getChild(key: string) { + return this.children[key]; + } + + public hasChild(key: string) { + return this.children[key] !== undefined; + } +} diff --git a/src/node/utils/UpdateCheck.ts b/src/node/utils/UpdateCheck.ts index f4c10182a40..534c5c640fa 100644 --- a/src/node/utils/UpdateCheck.ts +++ b/src/node/utils/UpdateCheck.ts @@ -1,7 +1,7 @@ 'use strict'; const semver = require('semver'); const settings = require('./Settings'); -const axios = require('axios'); +import axios from 'axios'; const headers = { 'User-Agent': 'Etherpad/' + settings.getEpVersion(), } @@ -17,7 +17,7 @@ let lastLoadingTime: number | null = null; const loadEtherpadInformations = () => { if (lastLoadingTime !== null && Date.now() - lastLoadingTime < updateInterval) { - return Promise.resolve(infos); + return infos; } return axios.get('https://static.etherpad.org/info.json', {headers: headers}) @@ -29,10 +29,10 @@ const loadEtherpadInformations = () => { } lastLoadingTime = Date.now(); - return await Promise.resolve(infos); + return infos; }) .catch(async (err: Error) => { - return await Promise.reject(err); + throw err; }); } @@ -43,15 +43,15 @@ exports.getLatestVersion = () => { }; exports.needsUpdate = async (cb?: Function) => { - await loadEtherpadInformations() - .then((info:Infos) => { - if (semver.gt(info.latestVersion, settings.getEpVersion())) { + try { + const info = await loadEtherpadInformations() + if (semver.gt(info!.latestVersion, settings.getEpVersion())) { if (cb) return cb(true); } - }).catch((err: Error) => { + } catch (err) { console.error(`Can not perform Etherpad update check: ${err}`); if (cb) return cb(false); - }); + } }; exports.check = () => { diff --git a/src/node/utils/caching_middleware.ts b/src/node/utils/caching_middleware.ts deleted file mode 100644 index d5866b019a1..00000000000 --- a/src/node/utils/caching_middleware.ts +++ /dev/null @@ -1,210 +0,0 @@ -'use strict'; - -/* - * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS-IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const Buffer = require('buffer').Buffer; -const fs = require('fs'); -const fsp = fs.promises; -const path = require('path'); -const zlib = require('zlib'); -const settings = require('./Settings'); -const existsSync = require('./path_exists'); -const util = require('util'); - -/* - * The crypto module can be absent on reduced node installations. - * - * Here we copy the approach TypeScript guys used for https://github.com/microsoft/TypeScript/issues/19100 - * If importing crypto fails at runtime, we replace sha256 with djb2, which is - * weaker, but works for our case. - * - * djb2 was written in 1991 by Daniel J. Bernstein. - * - */ - - -const _crypto = require('crypto'); - - -let CACHE_DIR = path.join(settings.root, 'var/'); -CACHE_DIR = existsSync(CACHE_DIR) ? CACHE_DIR : undefined; - -type Headers = { - [id: string]: string -} - -type ResponseCache = { - [id: string]: { - statusCode: number - headers: Headers - } -} - -const responseCache: ResponseCache = {}; - -const djb2Hash = (data: string) => { - const chars = data.split('').map((str) => str.charCodeAt(0)); - return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`; -}; - -const generateCacheKeyWithSha256 = - (path: string) => _crypto.createHash('sha256').update(path).digest('hex'); - -const generateCacheKeyWithDjb2 = - (path: string) => Buffer.from(djb2Hash(path)).toString('hex'); - -let generateCacheKey: (path: string)=>string; - -if (_crypto) { - generateCacheKey = generateCacheKeyWithSha256; -} else { - generateCacheKey = generateCacheKeyWithDjb2; - console.warn('No crypto support in this nodejs runtime. Djb2 (weaker) will be used.'); -} - -// MIMIC https://github.com/microsoft/TypeScript/commit/9677b0641cc5ba7d8b701b4f892ed7e54ceaee9a - END - -/* - This caches and compresses 200 and 404 responses to GET and HEAD requests. - TODO: Caching and compressing are solved problems, a middleware configuration - should replace this. -*/ - -module.exports = class CachingMiddleware { - handle(req: any, res: any, next: any) { - this._handle(req, res, next).catch((err) => next(err || new Error(err))); - } - - async _handle(req: any, res: any, next: any) { - if (!(req.method === 'GET' || req.method === 'HEAD') || !CACHE_DIR) { - return next(undefined, req, res); - } - - const oldReq:ResponseCache = {}; - const oldRes:ResponseCache = {}; - - const supportsGzip = - (req.get('Accept-Encoding') || '').indexOf('gzip') !== -1; - - const url = new URL(req.url, 'http://localhost'); - const cacheKey = generateCacheKey(url.pathname + url.search); - - const stats = await fsp.stat(`${CACHE_DIR}minified_${cacheKey}`).catch(() => {}); - const modifiedSince = - req.headers['if-modified-since'] && new Date(req.headers['if-modified-since']); - if (stats != null && stats.mtime && responseCache[cacheKey]) { - req.headers['if-modified-since'] = stats.mtime.toUTCString(); - } else { - delete req.headers['if-modified-since']; - } - - // Always issue get to downstream. - oldReq.method = req.method; - req.method = 'GET'; - - // This handles read/write synchronization as well as its predecessor, - // which is to say, not at all. - // TODO: Implement locking on write or ditch caching of gzip and use - // existing middlewares. - const respond = () => { - req.method = oldReq.method || req.method; - res.write = oldRes.write || res.write; - res.end = oldRes.end || res.end; - - const headers: Headers = {}; - Object.assign(headers, (responseCache[cacheKey].headers || {})); - const statusCode = responseCache[cacheKey].statusCode; - - let pathStr = `${CACHE_DIR}minified_${cacheKey}`; - if (supportsGzip && /application\/javascript/.test(headers['content-type'])) { - pathStr += '.gz'; - headers['content-encoding'] = 'gzip'; - } - - const lastModified = headers['last-modified'] && new Date(headers['last-modified']); - - if (statusCode === 200 && lastModified <= modifiedSince) { - res.writeHead(304, headers); - res.end(); - } else if (req.method === 'GET') { - const readStream = fs.createReadStream(pathStr); - res.writeHead(statusCode, headers); - readStream.pipe(res); - } else { - res.writeHead(statusCode, headers); - res.end(); - } - }; - - const expirationDate = new Date(((responseCache[cacheKey] || {}).headers || {}).expires); - if (expirationDate > new Date()) { - // Our cached version is still valid. - return respond(); - } - - const _headers:Headers = {}; - oldRes.setHeader = res.setHeader; - res.setHeader = (key: string, value: string) => { - // Don't set cookies, see issue #707 - if (key.toLowerCase() === 'set-cookie') return; - - _headers[key.toLowerCase()] = value; - // @ts-ignore - oldRes.setHeader.call(res, key, value); - }; - - oldRes.writeHead = res.writeHead; - res.writeHead = (status: number, headers: Headers) => { - res.writeHead = oldRes.writeHead; - if (status === 200) { - // Update cache - let buffer = ''; - - Object.keys(headers || {}).forEach((key) => { - res.setHeader(key, headers[key]); - }); - headers = _headers; - - oldRes.write = res.write; - oldRes.end = res.end; - res.write = (data: number, encoding: number) => { - buffer += data.toString(encoding); - }; - res.end = async (data: number, encoding: number) => { - await Promise.all([ - fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}`, buffer).catch(() => {}), - util.promisify(zlib.gzip)(buffer) - .then((content: string) => fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}.gz`, content)) - .catch(() => {}), - ]); - responseCache[cacheKey] = {statusCode: status, headers}; - respond(); - }; - } else if (status === 304) { - // Nothing new changed from the cached version. - oldRes.write = res.write; - oldRes.end = res.end; - res.write = (data: number, encoding: number) => {}; - res.end = (data: number, encoding: number) => { respond(); }; - } else { - res.writeHead(status, headers); - } - }; - - next(undefined, req, res); - } -}; diff --git a/src/node/utils/padDiff.ts b/src/node/utils/padDiff.ts index cd29aeb7a23..53f79fa562d 100644 --- a/src/node/utils/padDiff.ts +++ b/src/node/utils/padDiff.ts @@ -3,8 +3,13 @@ import {PadAuthor, PadType} from "../types/PadType"; import {MapArrayType} from "../types/MapType"; -const AttributeMap = require('../../static/js/AttributeMap'); -const Changeset = require('../../static/js/Changeset'); +import AttributeMap from '../../static/js/AttributeMap'; +import {applyToAText, checkRep, compose, deserializeOps, pack, splitAttributionLines, splitTextLines, unpack} from '../../static/js/Changeset'; +import {Builder} from "../../static/js/Builder"; +import {OpAssembler} from "../../static/js/OpAssembler"; +import {numToString} from "../../static/js/ChangesetUtils"; +import Op from "../../static/js/Op"; +import {StringAssembler} from "../../static/js/StringAssembler"; const attributes = require('../../static/js/attributes'); const exportHtml = require('./ExportHtml'); @@ -33,7 +38,7 @@ class PadDiff { } _isClearAuthorship(changeset: any){ // unpack - const unpacked = Changeset.unpack(changeset); + const unpacked = unpack(changeset); // check if there is nothing in the charBank if (unpacked.charBank !== '') { @@ -45,7 +50,7 @@ class PadDiff { return false; } - const [clearOperator, anotherOp] = Changeset.deserializeOps(unpacked.ops); + const [clearOperator, anotherOp] = deserializeOps(unpacked.ops); // check if there is only one operator if (anotherOp != null) return false; @@ -78,7 +83,7 @@ class PadDiff { const atext = await this._pad.getInternalRevisionAText(rev); // build clearAuthorship changeset - const builder = Changeset.builder(atext.text.length); + const builder = new Builder(atext.text.length); builder.keepText(atext.text, [['author', '']], this._pad.pool); const changeset = builder.toString(); @@ -93,7 +98,7 @@ class PadDiff { const changeset = await this._createClearAuthorship(rev); // apply the clearAuthorship changeset - const newAText = Changeset.applyToAText(changeset, atext, this._pad.pool); + const newAText = applyToAText(changeset, atext, this._pad.pool); return newAText; } @@ -157,7 +162,7 @@ class PadDiff { if (superChangeset == null) { superChangeset = changeset; } else { - superChangeset = Changeset.compose(superChangeset, changeset, this._pad.pool); + superChangeset = compose(superChangeset, changeset, this._pad.pool); } } @@ -171,10 +176,10 @@ class PadDiff { const deletionChangeset = this._createDeletionChangeset(superChangeset, atext, this._pad.pool); // apply the superChangeset, which includes all addings - atext = Changeset.applyToAText(superChangeset, atext, this._pad.pool); + atext = applyToAText(superChangeset, atext, this._pad.pool); // apply the deletionChangeset, which adds a deletions - atext = Changeset.applyToAText(deletionChangeset, atext, this._pad.pool); + atext = applyToAText(deletionChangeset, atext, this._pad.pool); } return atext; @@ -209,22 +214,22 @@ class PadDiff { _extendChangesetWithAuthor(changeset: any, author: any, apool: any){ // unpack - const unpacked = Changeset.unpack(changeset); + const unpacked = unpack(changeset); - const assem = Changeset.opAssembler(); + const assem = new OpAssembler(); // create deleted attribs const authorAttrib = apool.putAttrib(['author', author || '']); const deletedAttrib = apool.putAttrib(['removed', true]); - const attribs = `*${Changeset.numToString(authorAttrib)}*${Changeset.numToString(deletedAttrib)}`; + const attribs = `*${numToString(authorAttrib)}*${numToString(deletedAttrib)}`; - for (const operator of Changeset.deserializeOps(unpacked.ops)) { + for (const operator of deserializeOps(unpacked.ops)) { if (operator.opcode === '-') { // this is a delete operator, extend it with the author operator.attribs = attribs; } else if (operator.opcode === '=' && operator.attribs) { // this is operator changes only attributes, let's mark which author did that - operator.attribs += `*${Changeset.numToString(authorAttrib)}`; + operator.attribs += `*${numToString(authorAttrib)}`; } // append the new operator to our assembler @@ -232,26 +237,31 @@ class PadDiff { } // return the modified changeset - return Changeset.pack(unpacked.oldLen, unpacked.newLen, assem.toString(), unpacked.charBank); + return pack(unpacked.oldLen, unpacked.newLen, assem.toString(), unpacked.charBank); } _createDeletionChangeset(cs: any, startAText: any, apool: any){ - const lines = Changeset.splitTextLines(startAText.text); - const alines = Changeset.splitAttributionLines(startAText.attribs, startAText.text); + const lines = splitTextLines(startAText.text); + const alines = splitAttributionLines(startAText.attribs, startAText.text); // lines and alines are what the exports is meant to apply to. // They may be arrays or objects with .get(i) and .length methods. // They include final newlines on lines. const linesGet = (idx: number) => { + // @ts-ignore if (lines.get) { + // @ts-ignore return lines.get(idx); } else { + // @ts-ignore return lines[idx]; } }; const aLinesGet = (idx: number) => { + // @ts-ignore if (alines.get) { + // @ts-ignore return alines.get(idx); } else { return alines[idx]; @@ -263,14 +273,14 @@ class PadDiff { let curLineOps: { next: () => any; } | null = null; let curLineOpsNext: { done: any; value: any; } | null = null; let curLineOpsLine: number; - let curLineNextOp = new Changeset.Op('+'); + let curLineNextOp = new Op('+'); - const unpacked = Changeset.unpack(cs); - const builder = Changeset.builder(unpacked.newLen); + const unpacked = unpack(cs); + const builder = new Builder(unpacked.newLen); const consumeAttribRuns = (numChars: number, func: Function /* (len, attribs, endsLine)*/) => { if (!curLineOps || curLineOpsLine !== curLine) { - curLineOps = Changeset.deserializeOps(aLinesGet(curLine)); + curLineOps = deserializeOps(aLinesGet(curLine)); curLineOpsNext = curLineOps!.next(); curLineOpsLine = curLine; let indexIntoLine = 0; @@ -291,13 +301,13 @@ class PadDiff { curChar = 0; curLineOpsLine = curLine; curLineNextOp.chars = 0; - curLineOps = Changeset.deserializeOps(aLinesGet(curLine)); + curLineOps = deserializeOps(aLinesGet(curLine)); curLineOpsNext = curLineOps!.next(); } if (!curLineNextOp.chars) { if (curLineOpsNext!.done) { - curLineNextOp = new Changeset.Op(); + curLineNextOp = new Op(); } else { curLineNextOp = curLineOpsNext!.value; curLineOpsNext = curLineOps!.next(); @@ -332,7 +342,7 @@ class PadDiff { const nextText = (numChars: number) => { let len = 0; - const assem = Changeset.stringAssembler(); + const assem = new StringAssembler(); const firstString = linesGet(curLine).substring(curChar); len += firstString.length; assem.append(firstString); @@ -360,7 +370,7 @@ class PadDiff { }; }; - for (const csOp of Changeset.deserializeOps(unpacked.ops)) { + for (const csOp of deserializeOps(unpacked.ops)) { if (csOp.opcode === '=') { const textBank = nextText(csOp.chars); @@ -442,14 +452,15 @@ class PadDiff { } } - return Changeset.checkRep(builder.toString()); + return checkRep(builder.toString()); } } // this method is 80% like Changeset.inverse. I just changed so instead of reverting, -// it adds deletions and attribute changes to to the atext. +// it adds deletions and attribute changes to the atext. +// @ts-ignore PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) { }; diff --git a/src/node/utils/path_exists.ts b/src/node/utils/path_exists.ts index 354cd3cc72d..cb48f2fa9e1 100644 --- a/src/node/utils/path_exists.ts +++ b/src/node/utils/path_exists.ts @@ -1,5 +1,5 @@ 'use strict'; -const fs = require('fs'); +import fs from 'node:fs'; const check = (path:string) => { const existsSync = fs.statSync || fs.existsSync; @@ -13,4 +13,4 @@ const check = (path:string) => { return result; }; -module.exports = check; +export default check; diff --git a/src/node/utils/promises.ts b/src/node/utils/promises.ts index 701c5da89f6..9d07ed00b4a 100644 --- a/src/node/utils/promises.ts +++ b/src/node/utils/promises.ts @@ -7,7 +7,7 @@ // `predicate`. Resolves to `undefined` if none of the Promises satisfy `predicate`, or if // `promises` is empty. If `predicate` is nullish, the truthiness of the resolved value is used as // the predicate. -exports.firstSatisfies = <T>(promises: Promise<T>[], predicate: null|Function) => { +export const firstSatisfies = <T>(promises: Promise<T>[], predicate: null|Function) => { if (predicate == null) { predicate = (x: any) => x; } @@ -44,7 +44,7 @@ exports.firstSatisfies = <T>(promises: Promise<T>[], predicate: null|Function) = // `total` is greater than `concurrency`, then `concurrency` Promises will be created right away, // and each remaining Promise will be created once one of the earlier Promises resolves.) This async // function resolves once all `total` Promises have resolved. -exports.timesLimit = async (total: number, concurrency: number, promiseCreator: Function) => { +export const timesLimit = async (total: number, concurrency: number, promiseCreator: Function) => { if (total > 0 && concurrency <= 0) throw new RangeError('concurrency must be positive'); let next = 0; const addAnother = () => promiseCreator(next++).finally(() => { @@ -61,7 +61,7 @@ exports.timesLimit = async (total: number, concurrency: number, promiseCreator: * An ordinary Promise except the `resolve` and `reject` executor functions are exposed as * properties. */ -class Gate<T> extends Promise<T> { +export class Gate<T> extends Promise<T> { // Coax `.then()` into returning an ordinary Promise, not a Gate. See // https://stackoverflow.com/a/65669070 for the rationale. static get [Symbol.species]() { return Promise; } @@ -75,4 +75,3 @@ class Gate<T> extends Promise<T> { Object.assign(this, props); } } -exports.Gate = Gate; diff --git a/src/node/utils/sanitizePathname.ts b/src/node/utils/sanitizePathname.ts index 2932b913d31..72ab88417b1 100644 --- a/src/node/utils/sanitizePathname.ts +++ b/src/node/utils/sanitizePathname.ts @@ -1,10 +1,8 @@ -'use strict'; - -const path = require('path'); +import path from 'path'; // Normalizes p and ensures that it is a relative path that does not reach outside. See // https://nvd.nist.gov/vuln/detail/CVE-2015-3297 for additional context. -module.exports = (p: string, pathApi = path) => { +const sanitizeRoot = (p: string, pathApi = path) => { // The documentation for path.normalize() says that it resolves '..' and '.' segments. The word // "resolve" implies that it examines the filesystem to resolve symbolic links, so 'a/../b' might // not be the same thing as 'b'. Most path normalization functions from other libraries (e.g., @@ -21,3 +19,5 @@ module.exports = (p: string, pathApi = path) => { if (pathApi.sep === '\\') p = p.replace(/\\/g, '/'); return p; }; + +export default sanitizeRoot diff --git a/src/node/utils/toolbar.ts b/src/node/utils/toolbar.ts index aac3fb3d33e..f0ef454791c 100644 --- a/src/node/utils/toolbar.ts +++ b/src/node/utils/toolbar.ts @@ -2,7 +2,7 @@ /** * The Toolbar Module creates and renders the toolbars and buttons */ -const _ = require('underscore'); +import {isString, reduce, each, isUndefined, map, first, last, extend, escape} from 'underscore'; const removeItem = (array: string[], what: string) => { let ax; @@ -21,7 +21,7 @@ const defaultButtonAttributes = (name: string, overrides?: boolean) => ({ const tag = (name: string, attributes: AttributeObj, contents?: string) => { const aStr = tagAttributes(attributes); - if (_.isString(contents) && contents!.length > 0) { + if (isString(contents) && contents!.length > 0) { return `<${name}${aStr}>${contents}</${name}>`; } else { return `<${name}${aStr}></${name}>`; @@ -34,14 +34,14 @@ type AttributeObj = { } const tagAttributes = (attributes: AttributeObj) => { - attributes = _.reduce(attributes || {}, (o: AttributeObj, val: string, name: string) => { - if (!_.isUndefined(val)) { + attributes = reduce(attributes || {}, (o: AttributeObj, val: string, name: string) => { + if (!isUndefined(val)) { o[name] = val; } return o; }, {}); - return ` ${_.map(attributes, (val: string, name: string) => `${name}="${_.escape(val)}"`).join(' ')}`; + return ` ${map(attributes, (val: string, name: string) => `${name}="${escape(val)}"`).join(' ')}`; }; type ButtonGroupType = { @@ -58,7 +58,7 @@ class ButtonGroup { public static fromArray = function (array: string[]) { const btnGroup = new ButtonGroup(); - _.each(array, (btnName: string) => { + each(array, (btnName: string) => { const button = Button.load(btnName) as Button btnGroup.addButton(button); }); @@ -70,18 +70,19 @@ class ButtonGroup { return this; } - render() { + render(): string { if (this.buttons && this.buttons.length === 1) { this.buttons[0].grouping = ''; } else if (this.buttons && this.buttons.length > 1) { - _.first(this.buttons).grouping = 'grouped-left'; - _.last(this.buttons).grouping = 'grouped-right'; - _.each(this.buttons.slice(1, -1), (btn: Button) => { + first(this.buttons)!.grouping = 'grouped-left'; + last(this.buttons)!.grouping = 'grouped-right'; + each(this.buttons.slice(1, -1), (btn: Button) => { btn.grouping = 'grouped-middle'; }); } - return _.map(this.buttons, (btn: ButtonGroup) => { + // @ts-ignore + return map(this.buttons, (btn: ButtonGroup) => { if (btn) return btn.render(); }).join('\n'); } @@ -151,8 +152,8 @@ class SelectButton extends Button { select(attributes: AttributeObj) { const options: string[] = []; - _.each(this.options, (opt: AttributeSelect) => { - const a = _.extend({ + each(this.options, (opt: AttributeSelect) => { + const a = extend({ value: opt.value, }, opt.attributes); @@ -299,7 +300,7 @@ module.exports = { buttons[0].push('savedrevision'); } - const groups = _.map(buttons, (group: string[]) => ButtonGroup.fromArray(group).render()); + const groups = map(buttons, (group: string[]) => ButtonGroup.fromArray(group).render()); return groups.join(this.separator()); }, }; diff --git a/src/package.json b/src/package.json index d10c6acc12d..9fe985a035b 100644 --- a/src/package.json +++ b/src/package.json @@ -30,81 +30,90 @@ } ], "dependencies": { - "async": "^3.2.5", - "axios": "^1.6.8", - "clean-css": "^5.3.3", + "@etherpad/express-session": "^1.18.4", + "async": "^3.2.6", + "axios": "^1.7.5", "cookie-parser": "^1.4.6", + "cross-env": "^7.0.3", "cross-spawn": "^7.0.3", - "ejs": "^3.1.9", - "etherpad-require-kernel": "^1.0.16", - "etherpad-yajsml": "0.0.12", - "express": "4.18.3", - "express-rate-limit": "^7.2.0", - "express-session": "npm:@etherpad/express-session@^1.18.2", + "ejs": "^3.1.10", + "esbuild": "^0.23.1", + "express": "4.19.2", + "express-rate-limit": "^7.4.0", + "dompurify": "2.4.7", "fast-deep-equal": "^3.1.3", "find-root": "1.1.0", "formidable": "^3.5.1", + "html2canvas": "1.4.1", "http-errors": "^2.0.0", + "jose": "^5.7.0", "js-cookie": "^3.0.5", - "jsdom": "^24.0.0", + "jsdom": "^24.1.1", "jsonminify": "0.4.2", + "jsonwebtoken": "^9.0.2", + "jspdf": "2.5.1", "languages4translatewiki": "0.1.3", - "live-plugin-manager-pnpm": "^0.18.1", + "live-plugin-manager": "^1.0.0", "lodash.clonedeep": "4.5.0", "log4js": "^6.9.1", + "lru-cache": "^11.0.0", "measured-core": "^2.0.0", "mime-types": "^2.1.35", + "oidc-provider": "^8.5.1", "openapi-backend": "^5.10.6", "proxy-addr": "^2.0.7", - "rate-limiter-flexible": "^5.0.0", + "rate-limiter-flexible": "^5.0.3", "rehype": "^13.0.1", "rehype-minify-whitespace": "^6.0.0", "resolve": "1.22.8", "security": "1.0.0", - "semver": "^7.6.0", + "semver": "^7.6.3", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", - "superagent": "^8.1.2", - "terser": "^5.29.2", - "threads": "^1.7.0", + "superagent": "10.0.2", "tinycon": "0.6.8", - "tsx": "^4.7.1", - "ueberdb2": "^4.2.63", - "underscore": "1.13.6", + "tsx": "4.17.0", + "ueberdb2": "^4.2.93", + "underscore": "1.13.7", "unorm": "1.6.0", - "wtfnode": "^0.9.1", - "jspdf": "2.5.1", - "dompurify": "2.4.7", - "html2canvas": "1.4.1" + "wtfnode": "^0.9.3" }, "bin": { "etherpad-healthcheck": "../bin/etherpad-healthcheck", "etherpad-lite": "node/server.ts" }, "devDependencies": { + "@playwright/test": "^1.46.1", "@types/async": "^3.2.24", "@types/express": "^4.17.21", + "@types/formidable": "^3.4.5", "@types/http-errors": "^2.0.4", - "@types/jsdom": "^21.1.6", - "@types/mocha": "^10.0.6", - "@types/node": "^20.11.28", + "@types/jquery": "^3.5.30", + "@types/js-cookie": "^3.0.6", + "@types/jsdom": "^21.1.7", + "@types/jsonwebtoken": "^9.0.6", + "@types/mime-types": "^2.1.4", + "@types/mocha": "^10.0.7", + "@types/node": "^22.4.2", + "@types/oidc-provider": "^8.5.2", + "@types/semver": "^7.5.8", "@types/sinon": "^17.0.3", "@types/supertest": "^6.0.2", - "@types/semver": "^7.5.8", "@types/underscore": "^1.11.15", - "eslint": "^8.57.0", - "eslint-config-etherpad": "^4.0.1", + "chokidar": "^3.6.0", + "eslint": "^9.9.0", + "eslint-config-etherpad": "^4.0.4", "etherpad-cli-client": "^3.0.2", - "mocha": "^10.3.0", + "mocha": "^10.7.3", "mocha-froth": "^0.2.10", "nodeify": "^1.0.1", "openapi-schema-validation": "^0.4.2", - "@playwright/test": "^1.42.1", - "set-cookie-parser": "^2.6.0", - "sinon": "^17.0.1", + "set-cookie-parser": "^2.7.0", + "sinon": "^18.0.0", "split-grid": "^1.0.11", - "supertest": "^6.3.4", - "typescript": "^5.4.2" + "supertest": "^7.0.0", + "typescript": "^5.5.4", + "vitest": "^2.0.5" }, "engines": { "node": ">=18.18.2", @@ -117,17 +126,20 @@ }, "scripts": { "lint": "eslint .", - "test": "mocha --import=tsx --timeout 120000 --recursive tests/backend/specs/**.ts ../node_modules/ep_*/static/tests/backend/specs/**", + "test": "cross-env NODE_ENV=production mocha --import=tsx --timeout 120000 --recursive tests/backend/specs/**.ts ../node_modules/ep_*/static/tests/backend/specs/**", + "test-utils": "cross-env NODE_ENV=production mocha --import=tsx --timeout 5000 --recursive tests/backend/specs/*utils.ts", "test-container": "mocha --import=tsx --timeout 5000 tests/container/specs/api", - "dev": "node --import tsx node/server.ts", - "prod": "node --import tsx node/server.ts", + "dev": "cross-env NODE_ENV=development node --require tsx/cjs node/server.ts", + "prod": "cross-env NODE_ENV=production node --require tsx/cjs node/server.ts", "ts-check": "tsc --noEmit", "ts-check:watch": "tsc --noEmit --watch", - "test-ui": "npx playwright test tests/frontend-new/specs", - "test-ui:ui": "npx playwright test tests/frontend-new/specs --ui", - "test-admin": "npx playwright test tests/frontend-new/admin-spec --workers 1", - "test-admin:ui": "npx playwright test tests/frontend-new/admin-spec --ui --workers 1" + "test-ui": "cross-env NODE_ENV=production npx playwright test tests/frontend-new/specs", + "test-ui:ui": "cross-env NODE_ENV=production npx playwright test tests/frontend-new/specs --ui", + "test-admin": "cross-env NODE_ENV=production npx playwright test tests/frontend-new/admin-spec --workers 1", + "test-admin:ui": "cross-env NODE_ENV=production npx playwright test tests/frontend-new/admin-spec --ui --workers 1", + "debug:socketio": "cross-env DEBUG=socket.io* node --require tsx/cjs node/server.ts", + "test:vitest": "vitest" }, - "version": "2.0.1", + "version": "2.2.2", "license": "Apache-2.0" } diff --git a/src/playwright.config.ts b/src/playwright.config.ts index 20b0361ba89..1aae7d7a490 100644 --- a/src/playwright.config.ts +++ b/src/playwright.config.ts @@ -17,7 +17,7 @@ export default defineConfig({ expect: { timeout: defaultExpectTimeout }, timeout: defaultTestTimeout, retries: 2, - workers: 20, + workers: 5, /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.ts similarity index 90% rename from src/static/js/AttributeManager.js rename to src/static/js/AttributeManager.ts index f508af64111..6d90678f3bd 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.ts @@ -1,10 +1,10 @@ -'use strict'; - -const AttributeMap = require('./AttributeMap'); -const Changeset = require('./Changeset'); -const ChangesetUtils = require('./ChangesetUtils'); -const attributes = require('./attributes'); -const _ = require('./underscore'); +// @ts-nocheck +import AttributeMap from './AttributeMap'; +import {compose, deserializeOps, isIdentity} from './Changeset'; +import {Builder} from "./Builder"; +import {buildKeepRange, buildKeepToStartOfRange, buildRemoveRange} from './ChangesetUtils'; +import attributes from './attributes'; +import underscore from "underscore"; const lineMarkerAttribute = 'lmkr'; @@ -45,13 +45,13 @@ const AttributeManager = function (rep, applyChangesetCallback) { AttributeManager.DEFAULT_LINE_ATTRIBUTES = DEFAULT_LINE_ATTRIBUTES; AttributeManager.lineAttributes = lineAttributes; -AttributeManager.prototype = _(AttributeManager.prototype).extend({ +AttributeManager.prototype = underscore.default(AttributeManager.prototype).extend({ applyChangeset(changeset) { if (!this.applyChangesetCallback) return changeset; const cs = changeset.toString(); - if (!Changeset.isIdentity(cs)) { + if (!isIdentity(cs)) { this.applyChangesetCallback(cs); } @@ -85,7 +85,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ // as the range might not be continuous // due to the presence of line markers on the rows if (allChangesets) { - allChangesets = Changeset.compose( + allChangesets = compose( allChangesets.toString(), rowChangeset.toString(), this.rep.apool); } else { allChangesets = rowChangeset; @@ -125,9 +125,9 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ * @param attribs an array of attributes */ _setAttributesOnRangeByLine(row, startCol, endCol, attribs) { - const builder = Changeset.builder(this.rep.lines.totalWidth()); - ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [row, startCol]); - ChangesetUtils.buildKeepRange( + const builder = new Builder(this.rep.lines.totalWidth()); + buildKeepToStartOfRange(this.rep, builder, [row, startCol]); + buildKeepRange( this.rep, builder, [row, startCol], [row, endCol], attribs, this.rep.apool); return builder; }, @@ -150,7 +150,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ // get `attributeName` attribute of first char of line const aline = this.rep.alines[lineNum]; if (!aline) return ''; - const [op] = Changeset.deserializeOps(aline); + const [op] = deserializeOps(aline); if (op == null) return ''; return AttributeMap.fromString(op.attribs, this.rep.apool).get(attributeName) || ''; }, @@ -163,7 +163,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ // get attributes of first char of line const aline = this.rep.alines[lineNum]; if (!aline) return []; - const [op] = Changeset.deserializeOps(aline); + const [op] = deserializeOps(aline); if (op == null) return []; return [...attributes.attribsFromString(op.attribs, this.rep.apool)]; }, @@ -221,7 +221,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ let hasAttrib = true; let indexIntoLine = 0; - for (const op of Changeset.deserializeOps(rep.alines[lineNum])) { + for (const op of deserializeOps(rep.alines[lineNum])) { const opStartInLine = indexIntoLine; const opEndInLine = opStartInLine + op.chars; if (!hasIt(op.attribs)) { @@ -258,7 +258,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ // we need to sum up how much characters each operations take until the wanted position let currentPointer = 0; - for (const currentOperation of Changeset.deserializeOps(aline)) { + for (const currentOperation of deserializeOps(aline)) { currentPointer += currentOperation.chars; if (currentPointer <= column) continue; return [...attributes.attribsFromString(currentOperation.attribs, this.rep.apool)]; @@ -285,13 +285,13 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ */ setAttributeOnLine(lineNum, attributeName, attributeValue) { let loc = [0, 0]; - const builder = Changeset.builder(this.rep.lines.totalWidth()); + const builder = new Builder(this.rep.lines.totalWidth()); const hasMarker = this.lineHasMarker(lineNum); - ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0])); + buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0])); if (hasMarker) { - ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), [ + buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), [ [attributeName, attributeValue], ], this.rep.apool); } else { @@ -314,7 +314,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ * @param attributeValue if given only attributes with equal value will be removed */ removeAttributeOnLine(lineNum, attributeName, attributeValue) { - const builder = Changeset.builder(this.rep.lines.totalWidth()); + const builder = new Builder(this.rep.lines.totalWidth()); const hasMarker = this.lineHasMarker(lineNum); let found = false; @@ -333,16 +333,16 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ return; } - ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [lineNum, 0]); + buildKeepToStartOfRange(this.rep, builder, [lineNum, 0]); - const countAttribsWithMarker = _.chain(attribs).filter((a) => !!a[1]) + const countAttribsWithMarker = underscore.chain(attribs).filter((a) => !!a[1]) .map((a) => a[0]).difference(DEFAULT_LINE_ATTRIBUTES).size().value(); // if we have marker and any of attributes don't need to have marker. we need delete it if (hasMarker && !countAttribsWithMarker) { - ChangesetUtils.buildRemoveRange(this.rep, builder, [lineNum, 0], [lineNum, 1]); + buildRemoveRange(this.rep, builder, [lineNum, 0], [lineNum, 1]); } else { - ChangesetUtils.buildKeepRange( + buildKeepRange( this.rep, builder, [lineNum, 0], [lineNum, 1], attribs, this.rep.apool); } diff --git a/src/static/js/AttributeMap.js b/src/static/js/AttributeMap.ts similarity index 78% rename from src/static/js/AttributeMap.js rename to src/static/js/AttributeMap.ts index 55640eb8bc6..07bc106a501 100644 --- a/src/static/js/AttributeMap.js +++ b/src/static/js/AttributeMap.ts @@ -1,6 +1,9 @@ 'use strict'; -const attributes = require('./attributes'); +import AttributePool from "./AttributePool"; +import {Attribute} from "./types/Attribute"; + +import attributes from './attributes'; /** * A `[key, value]` pair of strings describing a text attribute. @@ -21,6 +24,7 @@ const attributes = require('./attributes'); * Convenience class to convert an Op's attribute string to/from a Map of key, value pairs. */ class AttributeMap extends Map { + private readonly pool? : AttributePool|null /** * Converts an attribute string into an AttributeMap. * @@ -28,14 +32,14 @@ class AttributeMap extends Map { * @param {AttributePool} pool - Attribute pool. * @returns {AttributeMap} */ - static fromString(str, pool) { + public static fromString(str: string, pool?: AttributePool|null): AttributeMap { return new AttributeMap(pool).updateFromString(str); } /** * @param {AttributePool} pool - Attribute pool. */ - constructor(pool) { + constructor(pool?: AttributePool|null) { super(); /** @public */ this.pool = pool; @@ -46,15 +50,15 @@ class AttributeMap extends Map { * @param {string} v - Attribute value. * @returns {AttributeMap} `this` (for chaining). */ - set(k, v) { + set(k: string, v: string):this { k = k == null ? '' : String(k); v = v == null ? '' : String(v); - this.pool.putAttrib([k, v]); + this.pool!.putAttrib([k, v]); return super.set(k, v); } toString() { - return attributes.attribsToString(attributes.sort([...this]), this.pool); + return attributes.attribsToString(attributes.sort([...this]), this.pool!); } /** @@ -63,7 +67,7 @@ class AttributeMap extends Map { * key is removed from this map (if present). * @returns {AttributeMap} `this` (for chaining). */ - update(entries, emptyValueIsDelete = false) { + update(entries: Iterable<Attribute>, emptyValueIsDelete: boolean = false): AttributeMap { for (let [k, v] of entries) { k = k == null ? '' : String(k); v = v == null ? '' : String(v); @@ -83,9 +87,9 @@ class AttributeMap extends Map { * key is removed from this map (if present). * @returns {AttributeMap} `this` (for chaining). */ - updateFromString(str, emptyValueIsDelete = false) { - return this.update(attributes.attribsFromString(str, this.pool), emptyValueIsDelete); + updateFromString(str: string, emptyValueIsDelete: boolean = false): AttributeMap { + return this.update(attributes.attribsFromString(str, this.pool!), emptyValueIsDelete); } } -module.exports = AttributeMap; +export default AttributeMap diff --git a/src/static/js/AttributePool.js b/src/static/js/AttributePool.ts similarity index 91% rename from src/static/js/AttributePool.js rename to src/static/js/AttributePool.ts index ccdd2eb35ca..5bbe52122a4 100644 --- a/src/static/js/AttributePool.js +++ b/src/static/js/AttributePool.ts @@ -44,6 +44,8 @@ * @property {number} nextNum - The attribute ID to assign to the next new attribute. */ +import {Attribute} from "./types/Attribute"; + /** * Represents an attribute pool, which is a collection of attributes (pairs of key and value * strings) along with their identifiers (non-negative integers). @@ -55,6 +57,14 @@ * in the pad. */ class AttributePool { + numToAttrib: { + [key: number]: [string, string] + } + private attribToNum: { + [key: number]: [string, string] + } + private nextNum: number + constructor() { /** * Maps an attribute identifier to the attribute's `[key, value]` string pair. @@ -96,7 +106,10 @@ class AttributePool { */ clone() { const c = new AttributePool(); - for (const [n, a] of Object.entries(this.numToAttrib)) c.numToAttrib[n] = [a[0], a[1]]; + for (const [n, a] of Object.entries(this.numToAttrib)){ + // @ts-ignore + c.numToAttrib[n] = [a[0], a[1]]; + } Object.assign(c.attribToNum, this.attribToNum); c.nextNum = this.nextNum; return c; @@ -111,15 +124,17 @@ class AttributePool { * membership in the pool without mutating the pool. * @returns {number} The attribute's identifier, or -1 if the attribute is not in the pool. */ - putAttrib(attrib, dontAddIfAbsent = false) { + putAttrib(attrib: Attribute, dontAddIfAbsent = false) { const str = String(attrib); if (str in this.attribToNum) { + // @ts-ignore return this.attribToNum[str]; } if (dontAddIfAbsent) { return -1; } const num = this.nextNum++; + // @ts-ignore this.attribToNum[str] = num; this.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')]; return num; @@ -130,7 +145,7 @@ class AttributePool { * @returns {Attribute} The attribute with the given identifier, or nullish if there is no such * attribute. */ - getAttrib(num) { + getAttrib(num: number): Attribute { const pair = this.numToAttrib[num]; if (!pair) { return pair; @@ -143,7 +158,7 @@ class AttributePool { * @returns {string} Eqivalent to `getAttrib(num)[0]` if the attribute exists, otherwise the empty * string. */ - getAttribKey(num) { + getAttribKey(num: number): string { const pair = this.numToAttrib[num]; if (!pair) return ''; return pair[0]; @@ -154,7 +169,7 @@ class AttributePool { * @returns {string} Eqivalent to `getAttrib(num)[1]` if the attribute exists, otherwise the empty * string. */ - getAttribValue(num) { + getAttribValue(num: number) { const pair = this.numToAttrib[num]; if (!pair) return ''; return pair[1]; @@ -166,8 +181,8 @@ class AttributePool { * @param {Function} func - Callback to call with two arguments: key and value. Its return value * is ignored. */ - eachAttrib(func) { - for (const n of Object.keys(this.numToAttrib)) { + eachAttrib(func: (k: string, v: string)=>void) { + for (const n in this.numToAttrib) { const pair = this.numToAttrib[n]; func(pair[0], pair[1]); } @@ -196,11 +211,12 @@ class AttributePool { * `new AttributePool().fromJsonable(pool.toJsonable())` to copy because the resulting shared * state will lead to pool corruption. */ - fromJsonable(obj) { + fromJsonable(obj: this) { this.numToAttrib = obj.numToAttrib; this.nextNum = obj.nextNum; this.attribToNum = {}; for (const n of Object.keys(this.numToAttrib)) { + // @ts-ignore this.attribToNum[String(this.numToAttrib[n])] = Number(n); } return this; @@ -213,6 +229,7 @@ class AttributePool { if (!Number.isInteger(this.nextNum)) throw new Error('nextNum property is not an integer'); if (this.nextNum < 0) throw new Error('nextNum property is negative'); for (const prop of ['numToAttrib', 'attribToNum']) { + // @ts-ignore const obj = this[prop]; if (obj == null) throw new Error(`${prop} property is null`); if (typeof obj !== 'object') throw new TypeError(`${prop} property is not an object`); @@ -231,9 +248,10 @@ class AttributePool { if (v == null) throw new TypeError(`attrib ${i} value is null`); if (typeof v !== 'string') throw new TypeError(`attrib ${i} value is not a string`); const attrStr = String(attr); + // @ts-ignore if (this.attribToNum[attrStr] !== i) throw new Error(`attribToNum for ${attrStr} !== ${i}`); } } } -module.exports = AttributePool; +export default AttributePool diff --git a/src/static/js/Builder.ts b/src/static/js/Builder.ts new file mode 100644 index 00000000000..4543ddc207c --- /dev/null +++ b/src/static/js/Builder.ts @@ -0,0 +1,108 @@ +/** + * Incrementally builds a Changeset. + * + * @typedef {object} Builder + * @property {Function} insert - + * @property {Function} keep - + * @property {Function} keepText - + * @property {Function} remove - + * @property {Function} toString - + */ +import {SmartOpAssembler} from "./SmartOpAssembler"; +import Op from "./Op"; +import {StringAssembler} from "./StringAssembler"; +import AttributeMap from "./AttributeMap"; +import {Attribute} from "./types/Attribute"; +import AttributePool from "./AttributePool"; +import {opsFromText, pack} from "./Changeset"; + +/** + * @param {number} oldLen - Old length + * @returns {Builder} + */ +export class Builder { + private readonly oldLen: number; + private assem: SmartOpAssembler; + private readonly o: Op; + private charBank: StringAssembler; + + constructor(oldLen: number) { + this.oldLen = oldLen + this.assem = new SmartOpAssembler() + this.o = new Op() + this.charBank = new StringAssembler() + } + + /** + * @param {number} N - Number of characters to keep. + * @param {number} L - Number of newlines among the `N` characters. If positive, the last + * character must be a newline. + * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' + * (no pool needed in latter case). + * @param {?AttributePool.ts} pool - Attribute pool, only required if `attribs` is a list of + * attribute key, value pairs. + * @returns {Builder} this + */ + keep = (N: number, L?: number, attribs?: string|Attribute[], pool?: AttributePool): Builder => { + this.o.opcode = '='; + this.o.attribs = typeof attribs === 'string' + ? attribs : new AttributeMap(pool).update(attribs || []).toString(); + this.o.chars = N; + this.o.lines = (L || 0); + this.assem.append(this.o); + return this; + } + + + /** + * @param {string} text - Text to keep. + * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' + * (no pool needed in latter case). + * @param {?AttributePool.ts} pool - Attribute pool, only required if `attribs` is a list of + * attribute key, value pairs. + * @returns {Builder} this + */ + keepText= (text: string, attribs?: string|Attribute[], pool?: AttributePool): Builder=> { + for (const op of opsFromText('=', text, attribs, pool)) this.assem.append(op); + return this; + } + + + /** + * @param {string} text - Text to insert. + * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' + * (no pool needed in latter case). + * @param {?AttributePool.ts} pool - Attribute pool, only required if `attribs` is a list of + * attribute key, value pairs. + * @returns {Builder} this + */ + insert= (text: string, attribs: string | Attribute[] | undefined, pool?: AttributePool | null | undefined): Builder => { + for (const op of opsFromText('+', text, attribs, pool)) this.assem.append(op); + this.charBank.append(text); + return this; + } + + + /** + * @param {number} N - Number of characters to remove. + * @param {number} L - Number of newlines among the `N` characters. If positive, the last + * character must be a newline. + * @returns {Builder} this + */ + remove= (N: number, L?: number): Builder => { + this.o.opcode = '-'; + this.o.attribs = ''; + this.o.chars = N; + this.o.lines = (L || 0); + this.assem.append(this.o); + return this; + } + + toString= () => { + this.assem.endDocument(); + const newLen = this.oldLen + this.assem.getLengthChange(); + return pack(this.oldLen, newLen, this.assem.toString(), this.charBank.toString()); + } +} + + diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.ts similarity index 54% rename from src/static/js/Changeset.js rename to src/static/js/Changeset.ts index 53b3f2c8f09..8d96e157b4e 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.ts @@ -22,10 +22,23 @@ * https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js */ -const AttributeMap = require('./AttributeMap'); -const AttributePool = require('./AttributePool'); -const attributes = require('./attributes'); -const {padutils} = require('./pad_utils'); +import AttributeMap from './AttributeMap' +import AttributePool from "./AttributePool"; +import {attribsFromString} from './attributes'; +import padutils from "./pad_utils"; +import Op, {OpCode} from './Op' +import {numToString, parseNum} from './ChangesetUtils' +import {StringAssembler} from "./StringAssembler"; +import {OpIter} from "./OpIter"; +import {Attribute} from "./types/Attribute"; +import {SmartOpAssembler} from "./SmartOpAssembler"; +import TextLinesMutator from "./TextLinesMutator"; +import {ChangeSet} from "./types/ChangeSet"; +import {AText} from "./types/AText"; +import {ChangeSetBuilder} from "./types/ChangeSetBuilder"; +import {Builder} from "./Builder"; +import {StringIterator} from "./StringIterator"; +import {MergingOpAssembler} from "./MergingOpAssembler"; /** * A `[key, value]` pair of strings describing a text attribute. @@ -47,8 +60,9 @@ const {padutils} = require('./pad_utils'); * * @param {string} msg - Just some message */ -const error = (msg) => { +const error = (msg: string) => { const e = new Error(msg); + // @ts-ignore e.easysync = true; throw e; }; @@ -61,96 +75,10 @@ const error = (msg) => { * @param {string} msg - error message to include in the exception * @type {(b: boolean, msg: string) => asserts b} */ -const assert = (b, msg) => { +export const assert: (b: boolean, msg: string) => asserts b = (b: boolean, msg: string): asserts b => { if (!b) error(`Failed assertion: ${msg}`); }; -/** - * Parses a number from string base 36. - * - * @param {string} str - string of the number in base 36 - * @returns {number} number - */ -exports.parseNum = (str) => parseInt(str, 36); - -/** - * Writes a number in base 36 and puts it in a string. - * - * @param {number} num - number - * @returns {string} string - */ -exports.numToString = (num) => num.toString(36).toLowerCase(); - -/** - * An operation to apply to a shared document. - */ -class Op { - /** - * @param {(''|'='|'+'|'-')} [opcode=''] - Initial value of the `opcode` property. - */ - constructor(opcode = '') { - /** - * The operation's operator: - * - '=': Keep the next `chars` characters (containing `lines` newlines) from the base - * document. - * - '-': Remove the next `chars` characters (containing `lines` newlines) from the base - * document. - * - '+': Insert `chars` characters (containing `lines` newlines) at the current position in - * the document. The inserted characters come from the changeset's character bank. - * - '' (empty string): Invalid operator used in some contexts to signifiy the lack of an - * operation. - * - * @type {(''|'='|'+'|'-')} - * @public - */ - this.opcode = opcode; - - /** - * The number of characters to keep, insert, or delete. - * - * @type {number} - * @public - */ - this.chars = 0; - - /** - * The number of characters among the `chars` characters that are newlines. If non-zero, the - * last character must be a newline. - * - * @type {number} - * @public - */ - this.lines = 0; - - /** - * Identifiers of attributes to apply to the text, represented as a repeated (zero or more) - * sequence of asterisk followed by a non-negative base-36 (lower-case) integer. For example, - * '*2*1o' indicates that attributes 2 and 60 apply to the text affected by the operation. The - * identifiers come from the document's attribute pool. - * - * For keep ('=') operations, the attributes are merged with the base text's existing - * attributes: - * - A keep op attribute with a non-empty value replaces an existing base text attribute that - * has the same key. - * - A keep op attribute with an empty value is interpreted as an instruction to remove an - * existing base text attribute that has the same key, if one exists. - * - * This is the empty string for remove ('-') operations. - * - * @type {string} - * @public - */ - this.attribs = ''; - } - - toString() { - if (!this.opcode) throw new TypeError('null op'); - if (typeof this.attribs !== 'string') throw new TypeError('attribs must be a string'); - const l = this.lines ? `|${exports.numToString(this.lines)}` : ''; - return this.attribs + l + this.opcode + exports.numToString(this.chars); - } -} -exports.Op = Op; /** * Describes changes to apply to a document. Does not include the attribute pool or the original @@ -170,7 +98,7 @@ exports.Op = Op; * @param {string} cs - String representation of the Changeset * @returns {number} oldLen property */ -exports.oldLen = (cs) => exports.unpack(cs).oldLen; +export const oldLen = (cs: string) => unpack(cs).oldLen /** * Returns the length of the text after changeset is applied. @@ -178,7 +106,7 @@ exports.oldLen = (cs) => exports.unpack(cs).oldLen; * @param {string} cs - String representation of the Changeset * @returns {number} newLen property */ -exports.newLen = (cs) => exports.unpack(cs).newLen; +export const newLen = (cs: string) => unpack(cs).newLen /** * Parses a string of serialized changeset operations. @@ -187,63 +115,23 @@ exports.newLen = (cs) => exports.unpack(cs).newLen; * @yields {Op} * @returns {Generator<Op>} */ -exports.deserializeOps = function* (ops) { +export const deserializeOps = function* (ops: string) { // TODO: Migrate to String.prototype.matchAll() once there is enough browser support. const regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|(.)/g; let match; while ((match = regex.exec(ops)) != null) { if (match[5] === '$') return; // Start of the insert operation character bank. if (match[5] != null) error(`invalid operation: ${ops.slice(regex.lastIndex - 1)}`); - const op = new Op(match[3]); - op.lines = exports.parseNum(match[2] || '0'); - op.chars = exports.parseNum(match[4]); + const opMatch = match[3] as ""|"=" | "+" | "-" | undefined + const op = new Op(opMatch); + op.lines = parseNum(match[2] || '0'); + op.chars = parseNum(match[4]); op.attribs = match[1]; yield op; } }; -/** - * Iterator over a changeset's operations. - * - * Note: This class does NOT implement the ECMAScript iterable or iterator protocols. - * - * @deprecated Use `deserializeOps` instead. - */ -class OpIter { - /** - * @param {string} ops - String encoding the change operations to iterate over. - */ - constructor(ops) { - this._gen = exports.deserializeOps(ops); - this._next = this._gen.next(); - } - - /** - * @returns {boolean} Whether there are any remaining operations. - */ - hasNext() { - return !this._next.done; - } - /** - * Returns the next operation object and advances the iterator. - * - * Note: This does NOT implement the ECMAScript iterator protocol. - * - * @param {Op} [opOut] - Deprecated. Operation object to recycle for the return value. - * @returns {Op} The next operation, or an operation with a falsy `opcode` property if there are - * no more operations. - */ - next(opOut = new Op()) { - if (this.hasNext()) { - copyOp(this._next.value, opOut); - this._next = this._gen.next(); - } else { - clearOp(opOut); - } - return opOut; - } -} /** * Creates an iterator which decodes string changeset operations. @@ -252,9 +140,9 @@ class OpIter { * @param {string} opsStr - String encoding of the change operations to perform. * @returns {OpIter} Operator iterator object. */ -exports.opIterator = (opsStr) => { +export const opIterator = (opsStr: string) => { padutils.warnDeprecated( - 'Changeset.opIterator() is deprecated; use Changeset.deserializeOps() instead'); + 'Changeset.opIterator() is deprecated; use Changeset.deserializeOps() instead'); return new OpIter(opsStr); }; @@ -263,7 +151,7 @@ exports.opIterator = (opsStr) => { * * @param {Op} op - object to clear */ -const clearOp = (op) => { +export const clearOp = (op: Op) => { op.opcode = ''; op.chars = 0; op.lines = 0; @@ -277,7 +165,7 @@ const clearOp = (op) => { * @param {('+'|'-'|'='|'')} [optOpcode=''] - The operation's operator. * @returns {Op} */ -exports.newOp = (optOpcode) => { +export const newOp = (optOpcode:'+'|'-'|'='|'' ): Op => { padutils.warnDeprecated('Changeset.newOp() is deprecated; use the Changeset.Op class instead'); return new Op(optOpcode); }; @@ -289,7 +177,7 @@ exports.newOp = (optOpcode) => { * @param {Op} [op2] - dest Op. If not given, a new Op is used. * @returns {Op} `op2` */ -const copyOp = (op1, op2 = new Op()) => Object.assign(op2, op1); +export const copyOp = (op1: Op, op2: Op = new Op()): Op => Object.assign(op2, op1); /** * Serializes a sequence of Ops. @@ -320,12 +208,12 @@ const copyOp = (op1, op2 = new Op()) => Object.assign(op2, op1); * (if necessary) and encode. If an attribute string, no checking is performed to ensure that * the attributes exist in the pool, are in the canonical order, and contain no duplicate keys. * If this is an iterable of attributes, `pool` must be non-null. - * @param {?AttributePool} pool - Attribute pool. Required if `attribs` is an iterable of + * @param {?AttributePool.ts} pool - Attribute pool. Required if `attribs` is an iterable of * attributes, ignored if `attribs` is an attribute string. * @yields {Op} One or two ops (depending on the presense of newlines) that cover the given text. * @returns {Generator<Op>} */ -const opsFromText = function* (opcode, text, attribs = '', pool = null) { +export const opsFromText = function* (opcode: "" | "=" | "+" | "-" | undefined, text: string, attribs: string|Attribute[] = '', pool: AttributePool|null = null) { const op = new Op(opcode); op.attribs = typeof attribs === 'string' ? attribs : new AttributeMap(pool).update(attribs || [], opcode === '+').toString(); @@ -336,7 +224,7 @@ const opsFromText = function* (opcode, text, attribs = '', pool = null) { yield op; } else { op.chars = lastNewlinePos + 1; - op.lines = text.match(/\n/g).length; + op.lines = text.match(/\n/g)!.length; yield op; const op2 = copyOp(op); op2.chars = text.length - (lastNewlinePos + 1); @@ -345,23 +233,7 @@ const opsFromText = function* (opcode, text, attribs = '', pool = null) { } }; -/** - * Creates an object that allows you to append operations (type Op) and also compresses them if - * possible. Like MergingOpAssembler, but able to produce conforming exportss from slightly looser - * input, at the cost of speed. Specifically: - * - merges consecutive operations that can be merged - * - strips final "=" - * - ignores 0-length changes - * - reorders consecutive + and - (which MergingOpAssembler doesn't do) - * - * @typedef {object} SmartOpAssembler - * @property {Function} append - - * @property {Function} appendOpWithText - - * @property {Function} clear - - * @property {Function} endDocument - - * @property {Function} getLengthChange - - * @property {Function} toString - - */ + /** * Used to check if a Changeset is valid. This function does not check things that require access to @@ -370,17 +242,17 @@ const opsFromText = function* (opcode, text, attribs = '', pool = null) { * @param {string} cs - Changeset to check * @returns {string} the checked Changeset */ -exports.checkRep = (cs) => { - const unpacked = exports.unpack(cs); +export const checkRep = (cs: string) => { + const unpacked = unpack(cs); const oldLen = unpacked.oldLen; const newLen = unpacked.newLen; const ops = unpacked.ops; let charBank = unpacked.charBank; - const assem = exports.smartOpAssembler(); + const assem = new SmartOpAssembler(); let oldPos = 0; let calcNewLen = 0; - for (const o of exports.deserializeOps(ops)) { + for (const o of deserializeOps(ops)) { switch (o.opcode) { case '=': oldPos += o.chars; @@ -396,9 +268,9 @@ exports.checkRep = (cs) => { const chars = charBank.slice(0, o.chars); const nlines = (chars.match(/\n/g) || []).length; assert(nlines === o.lines, - 'Invalid changeset: number of newlines in insert op does not match the charBank'); + 'Invalid changeset: number of newlines in insert op does not match the charBank'); assert(o.lines === 0 || chars.endsWith('\n'), - 'Invalid changeset: multiline insert op does not end with a newline'); + 'Invalid changeset: multiline insert op does not end with a newline'); charBank = charBank.slice(o.chars); calcNewLen += o.chars; assert(calcNewLen <= newLen, `${calcNewLen} > ${newLen} in ${cs}`); @@ -413,259 +285,11 @@ exports.checkRep = (cs) => { assert(calcNewLen === newLen, 'Invalid changeset: claimed length does not match actual length'); assert(charBank === '', 'Invalid changeset: excess characters in the charBank'); assem.endDocument(); - const normalized = exports.pack(oldLen, calcNewLen, assem.toString(), unpacked.charBank); + const normalized = pack(oldLen, calcNewLen, assem.toString(), unpacked.charBank); assert(normalized === cs, 'Invalid changeset: not in canonical form'); return cs; }; -/** - * @returns {SmartOpAssembler} - */ -exports.smartOpAssembler = () => { - const minusAssem = exports.mergingOpAssembler(); - const plusAssem = exports.mergingOpAssembler(); - const keepAssem = exports.mergingOpAssembler(); - const assem = exports.stringAssembler(); - let lastOpcode = ''; - let lengthChange = 0; - - const flushKeeps = () => { - assem.append(keepAssem.toString()); - keepAssem.clear(); - }; - - const flushPlusMinus = () => { - assem.append(minusAssem.toString()); - minusAssem.clear(); - assem.append(plusAssem.toString()); - plusAssem.clear(); - }; - - const append = (op) => { - if (!op.opcode) return; - if (!op.chars) return; - - if (op.opcode === '-') { - if (lastOpcode === '=') { - flushKeeps(); - } - minusAssem.append(op); - lengthChange -= op.chars; - } else if (op.opcode === '+') { - if (lastOpcode === '=') { - flushKeeps(); - } - plusAssem.append(op); - lengthChange += op.chars; - } else if (op.opcode === '=') { - if (lastOpcode !== '=') { - flushPlusMinus(); - } - keepAssem.append(op); - } - lastOpcode = op.opcode; - }; - - /** - * Generates operations from the given text and attributes. - * - * @deprecated Use `opsFromText` instead. - * @param {('-'|'+'|'=')} opcode - The operator to use. - * @param {string} text - The text to remove/add/keep. - * @param {(string|Iterable<Attribute>)} attribs - The attributes to apply to the operations. - * @param {?AttributePool} pool - Attribute pool. Only required if `attribs` is an iterable of - * attribute key, value pairs. - */ - const appendOpWithText = (opcode, text, attribs, pool) => { - padutils.warnDeprecated('Changeset.smartOpAssembler().appendOpWithText() is deprecated; ' + - 'use opsFromText() instead.'); - for (const op of opsFromText(opcode, text, attribs, pool)) append(op); - }; - - const toString = () => { - flushPlusMinus(); - flushKeeps(); - return assem.toString(); - }; - - const clear = () => { - minusAssem.clear(); - plusAssem.clear(); - keepAssem.clear(); - assem.clear(); - lengthChange = 0; - }; - - const endDocument = () => { - keepAssem.endDocument(); - }; - - const getLengthChange = () => lengthChange; - - return { - append, - toString, - clear, - endDocument, - appendOpWithText, - getLengthChange, - }; -}; - -/** - * @returns {MergingOpAssembler} - */ -exports.mergingOpAssembler = () => { - const assem = exports.opAssembler(); - const bufOp = new Op(); - - // If we get, for example, insertions [xxx\n,yyy], those don't merge, - // but if we get [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n]. - // This variable stores the length of yyy and any other newline-less - // ops immediately after it. - let bufOpAdditionalCharsAfterNewline = 0; - - /** - * @param {boolean} [isEndDocument] - */ - const flush = (isEndDocument) => { - if (!bufOp.opcode) return; - if (isEndDocument && bufOp.opcode === '=' && !bufOp.attribs) { - // final merged keep, leave it implicit - } else { - assem.append(bufOp); - if (bufOpAdditionalCharsAfterNewline) { - bufOp.chars = bufOpAdditionalCharsAfterNewline; - bufOp.lines = 0; - assem.append(bufOp); - bufOpAdditionalCharsAfterNewline = 0; - } - } - bufOp.opcode = ''; - }; - - const append = (op) => { - if (op.chars <= 0) return; - if (bufOp.opcode === op.opcode && bufOp.attribs === op.attribs) { - if (op.lines > 0) { - // bufOp and additional chars are all mergeable into a multi-line op - bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars; - bufOp.lines += op.lines; - bufOpAdditionalCharsAfterNewline = 0; - } else if (bufOp.lines === 0) { - // both bufOp and op are in-line - bufOp.chars += op.chars; - } else { - // append in-line text to multi-line bufOp - bufOpAdditionalCharsAfterNewline += op.chars; - } - } else { - flush(); - copyOp(op, bufOp); - } - }; - - const endDocument = () => { - flush(true); - }; - - const toString = () => { - flush(); - return assem.toString(); - }; - - const clear = () => { - assem.clear(); - clearOp(bufOp); - }; - return { - append, - toString, - clear, - endDocument, - }; -}; - -/** - * @returns {OpAssembler} - */ -exports.opAssembler = () => { - let serialized = ''; - - /** - * @param {Op} op - Operation to add. Ownership remains with the caller. - */ - const append = (op) => { - assert(op instanceof Op, 'argument must be an instance of Op'); - serialized += op.toString(); - }; - - const toString = () => serialized; - - const clear = () => { - serialized = ''; - }; - return { - append, - toString, - clear, - }; -}; - -/** - * A custom made String Iterator - * - * @typedef {object} StringIterator - * @property {Function} newlines - - * @property {Function} peek - - * @property {Function} remaining - - * @property {Function} skip - - * @property {Function} take - - */ - -/** - * @param {string} str - String to iterate over - * @returns {StringIterator} - */ -exports.stringIterator = (str) => { - let curIndex = 0; - // newLines is the number of \n between curIndex and str.length - let newLines = str.split('\n').length - 1; - const getnewLines = () => newLines; - - const assertRemaining = (n) => { - assert(n <= remaining(), `!(${n} <= ${remaining()})`); - }; - - const take = (n) => { - assertRemaining(n); - const s = str.substr(curIndex, n); - newLines -= s.split('\n').length - 1; - curIndex += n; - return s; - }; - - const peek = (n) => { - assertRemaining(n); - const s = str.substr(curIndex, n); - return s; - }; - - const skip = (n) => { - assertRemaining(n); - curIndex += n; - }; - - const remaining = () => str.length - curIndex; - return { - take, - skip, - remaining, - peek, - newlines: getnewLines, - }; -}; - /** * A custom made StringBuffer * @@ -674,19 +298,6 @@ exports.stringIterator = (str) => { * @property {Function} toString - */ -/** - * @returns {StringAssembler} - */ -exports.stringAssembler = () => ({ - _str: '', - clear() { this._str = ''; }, - /** - * @param {string} x - - */ - append(x) { this._str += String(x); }, - toString() { return this._str; }, -}); - /** * @typedef {object} StringArrayLike * @property {(i: number) => string} get - Returns the line at index `i`. @@ -699,323 +310,6 @@ exports.stringAssembler = () => ({ * `Array.prototype.splice()`. */ -/** - * Class to iterate and modify texts which have several lines. It is used for applying Changesets on - * arrays of lines. - * - * Mutation operations have the same constraints as exports operations with respect to newlines, but - * not the other additional constraints (i.e. ins/del ordering, forbidden no-ops, non-mergeability, - * final newline). Can be used to mutate lists of strings where the last char of each string is not - * actually a newline, but for the purposes of N and L values, the caller should pretend it is, and - * for things to work right in that case, the input to the `insert` method should be a single line - * with no newlines. - */ -class TextLinesMutator { - /** - * @param {(string[]|StringArrayLike)} lines - Lines to mutate (in place). - */ - constructor(lines) { - this._lines = lines; - /** - * this._curSplice holds values that will be passed as arguments to this._lines.splice() to - * insert, delete, or change lines: - * - this._curSplice[0] is an index into the this._lines array. - * - this._curSplice[1] is the number of lines that will be removed from the this._lines array - * starting at the index. - * - The other elements represent mutated (changed by ops) lines or new lines (added by ops) - * to insert at the index. - * - * @type {[number, number?, ...string[]?]} - */ - this._curSplice = [0, 0]; - this._inSplice = false; - // position in lines after curSplice is applied: - this._curLine = 0; - this._curCol = 0; - // invariant: if (inSplice) then (curLine is in curSplice[0] + curSplice.length - {2,3}) && - // curLine >= curSplice[0] - // invariant: if (inSplice && (curLine >= curSplice[0] + curSplice.length - 2)) then - // curCol == 0 - } - - /** - * Get a line from `lines` at given index. - * - * @param {number} idx - an index - * @returns {string} - */ - _linesGet(idx) { - if ('get' in this._lines) { - return this._lines.get(idx); - } else { - return this._lines[idx]; - } - } - - /** - * Return a slice from `lines`. - * - * @param {number} start - the start index - * @param {number} end - the end index - * @returns {string[]} - */ - _linesSlice(start, end) { - // can be unimplemented if removeLines's return value not needed - if (this._lines.slice) { - return this._lines.slice(start, end); - } else { - return []; - } - } - - /** - * Return the length of `lines`. - * - * @returns {number} - */ - _linesLength() { - if (typeof this._lines.length === 'number') { - return this._lines.length; - } else { - return this._lines.length(); - } - } - - /** - * Starts a new splice. - */ - _enterSplice() { - this._curSplice[0] = this._curLine; - this._curSplice[1] = 0; - // TODO(doc) when is this the case? - // check all enterSplice calls and changes to curCol - if (this._curCol > 0) this._putCurLineInSplice(); - this._inSplice = true; - } - - /** - * Changes the lines array according to the values in curSplice and resets curSplice. Called via - * close or TODO(doc). - */ - _leaveSplice() { - this._lines.splice(...this._curSplice); - this._curSplice.length = 2; - this._curSplice[0] = this._curSplice[1] = 0; - this._inSplice = false; - } - - /** - * Indicates if curLine is already in the splice. This is necessary because the last element in - * curSplice is curLine when this line is currently worked on (e.g. when skipping or inserting). - * - * @returns {boolean} true if curLine is in splice - */ - _isCurLineInSplice() { - // The value of `this._curSplice[1]` does not matter when determining the return value because - // `this._curLine` refers to the line number *after* the splice is applied (so after those lines - // are deleted). - return this._curLine - this._curSplice[0] < this._curSplice.length - 2; - } - - /** - * Incorporates current line into the splice and marks its old position to be deleted. - * - * @returns {number} the index of the added line in curSplice - */ - _putCurLineInSplice() { - if (!this._isCurLineInSplice()) { - this._curSplice.push(this._linesGet(this._curSplice[0] + this._curSplice[1])); - this._curSplice[1]++; - } - // TODO should be the same as this._curSplice.length - 1 - return 2 + this._curLine - this._curSplice[0]; - } - - /** - * It will skip some newlines by putting them into the splice. - * - * @param {number} L - - * @param {boolean} includeInSplice - Indicates that attributes are present. - */ - skipLines(L, includeInSplice) { - if (!L) return; - if (includeInSplice) { - if (!this._inSplice) this._enterSplice(); - // TODO(doc) should this count the number of characters that are skipped to check? - for (let i = 0; i < L; i++) { - this._curCol = 0; - this._putCurLineInSplice(); - this._curLine++; - } - } else { - if (this._inSplice) { - if (L > 1) { - // TODO(doc) figure out why single lines are incorporated into splice instead of ignored - this._leaveSplice(); - } else { - this._putCurLineInSplice(); - } - } - this._curLine += L; - this._curCol = 0; - } - // tests case foo in remove(), which isn't otherwise covered in current impl - } - - /** - * Skip some characters. Can contain newlines. - * - * @param {number} N - number of characters to skip - * @param {number} L - number of newlines to skip - * @param {boolean} includeInSplice - indicates if attributes are present - */ - skip(N, L, includeInSplice) { - if (!N) return; - if (L) { - this.skipLines(L, includeInSplice); - } else { - if (includeInSplice && !this._inSplice) this._enterSplice(); - if (this._inSplice) { - // although the line is put into splice curLine is not increased, because - // only some chars are skipped, not the whole line - this._putCurLineInSplice(); - } - this._curCol += N; - } - } - - /** - * Remove whole lines from lines array. - * - * @param {number} L - number of lines to remove - * @returns {string} - */ - removeLines(L) { - if (!L) return ''; - if (!this._inSplice) this._enterSplice(); - - /** - * Gets a string of joined lines after the end of the splice. - * - * @param {number} k - number of lines - * @returns {string} joined lines - */ - const nextKLinesText = (k) => { - const m = this._curSplice[0] + this._curSplice[1]; - return this._linesSlice(m, m + k).join(''); - }; - - let removed = ''; - if (this._isCurLineInSplice()) { - if (this._curCol === 0) { - removed = this._curSplice[this._curSplice.length - 1]; - this._curSplice.length--; - removed += nextKLinesText(L - 1); - this._curSplice[1] += L - 1; - } else { - removed = nextKLinesText(L - 1); - this._curSplice[1] += L - 1; - const sline = this._curSplice.length - 1; - removed = this._curSplice[sline].substring(this._curCol) + removed; - this._curSplice[sline] = this._curSplice[sline].substring(0, this._curCol) + - this._linesGet(this._curSplice[0] + this._curSplice[1]); - this._curSplice[1] += 1; - } - } else { - removed = nextKLinesText(L); - this._curSplice[1] += L; - } - return removed; - } - - /** - * Remove text from lines array. - * - * @param {number} N - characters to delete - * @param {number} L - lines to delete - * @returns {string} - */ - remove(N, L) { - if (!N) return ''; - if (L) return this.removeLines(L); - if (!this._inSplice) this._enterSplice(); - // although the line is put into splice, curLine is not increased, because - // only some chars are removed not the whole line - const sline = this._putCurLineInSplice(); - const removed = this._curSplice[sline].substring(this._curCol, this._curCol + N); - this._curSplice[sline] = this._curSplice[sline].substring(0, this._curCol) + - this._curSplice[sline].substring(this._curCol + N); - return removed; - } - - /** - * Inserts text into lines array. - * - * @param {string} text - the text to insert - * @param {number} L - number of newlines in text - */ - insert(text, L) { - if (!text) return; - if (!this._inSplice) this._enterSplice(); - if (L) { - const newLines = exports.splitTextLines(text); - if (this._isCurLineInSplice()) { - const sline = this._curSplice.length - 1; - /** @type {string} */ - const theLine = this._curSplice[sline]; - const lineCol = this._curCol; - // Insert the chars up to `curCol` and the first new line. - this._curSplice[sline] = theLine.substring(0, lineCol) + newLines[0]; - this._curLine++; - newLines.splice(0, 1); - // insert the remaining new lines - this._curSplice.push(...newLines); - this._curLine += newLines.length; - // insert the remaining chars from the "old" line (e.g. the line we were in - // when we started to insert new lines) - this._curSplice.push(theLine.substring(lineCol)); - this._curCol = 0; // TODO(doc) why is this not set to the length of last line? - } else { - this._curSplice.push(...newLines); - this._curLine += newLines.length; - } - } else { - // There are no additional lines. Although the line is put into splice, curLine is not - // increased because there may be more chars in the line (newline is not reached). - const sline = this._putCurLineInSplice(); - if (!this._curSplice[sline]) { - const err = new Error( - 'curSplice[sline] not populated, actual curSplice contents is ' + - `${JSON.stringify(this._curSplice)}. Possibly related to ` + - 'https://github.com/ether/etherpad-lite/issues/2802'); - console.error(err.stack || err.toString()); - } - this._curSplice[sline] = this._curSplice[sline].substring(0, this._curCol) + text + - this._curSplice[sline].substring(this._curCol); - this._curCol += text.length; - } - } - - /** - * Checks if curLine (the line we are in when curSplice is applied) is the last line in `lines`. - * - * @returns {boolean} indicates if there are lines left - */ - hasMore() { - let docLines = this._linesLength(); - if (this._inSplice) { - docLines += this._curSplice.length - 2 - this._curSplice[1]; - } - return this._curLine < docLines; - } - - /** - * Closes the splice - */ - close() { - if (this._inSplice) this._leaveSplice(); - } -} /** * Apply operations to other operations. @@ -1038,12 +332,12 @@ class TextLinesMutator { * either `opOut` must be nullish or `opOut.opcode` must be the empty string. * @returns {string} the integrated changeset */ -const applyZip = (in1, in2, func) => { - const ops1 = exports.deserializeOps(in1); - const ops2 = exports.deserializeOps(in2); +const applyZip = (in1: string, in2: string, func: Function): string => { + const ops1 = deserializeOps(in1); + const ops2 = deserializeOps(in2); let next1 = ops1.next(); let next2 = ops2.next(); - const assem = exports.smartOpAssembler(); + const assem = new SmartOpAssembler(); while (!next1.done || !next2.done) { if (!next1.done && !next1.value.opcode) next1 = ops1.next(); if (!next2.done && !next2.value.opcode) next2 = ops2.next(); @@ -1063,15 +357,15 @@ const applyZip = (in1, in2, func) => { * @param {string} cs - The encoded changeset. * @returns {Changeset} */ -exports.unpack = (cs) => { +export const unpack = (cs: string): ChangeSet => { const headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/; const headerMatch = headerRegex.exec(cs); if ((!headerMatch) || (!headerMatch[0])) error(`Not a changeset: ${cs}`); - const oldLen = exports.parseNum(headerMatch[1]); - const changeSign = (headerMatch[2] === '>') ? 1 : -1; - const changeMag = exports.parseNum(headerMatch[3]); + const oldLen = parseNum(headerMatch![1]); + const changeSign = (headerMatch![2] === '>') ? 1 : -1; + const changeMag = parseNum(headerMatch![3]); const newLen = oldLen + changeSign * changeMag; - const opsStart = headerMatch[0].length; + const opsStart = headerMatch![0].length; let opsEnd = cs.indexOf('$'); if (opsEnd < 0) opsEnd = cs.length; return { @@ -1091,12 +385,12 @@ exports.unpack = (cs) => { * @param {string} bank - Characters for insert operations. * @returns {string} The encoded changeset. */ -exports.pack = (oldLen, newLen, opsStr, bank) => { +export const pack = (oldLen: number, newLen: number, opsStr: string, bank: string): string => { const lenDiff = newLen - oldLen; - const lenDiffStr = (lenDiff >= 0 ? `>${exports.numToString(lenDiff)}` - : `<${exports.numToString(-lenDiff)}`); + const lenDiffStr = (lenDiff >= 0 ? `>${numToString(lenDiff)}` + : `<${numToString(-lenDiff)}`); const a = []; - a.push('Z:', exports.numToString(oldLen), lenDiffStr, opsStr, '$', bank); + a.push('Z:', numToString(oldLen), lenDiffStr, opsStr, '$', bank); return a.join(''); }; @@ -1107,33 +401,33 @@ exports.pack = (oldLen, newLen, opsStr, bank) => { * @param {string} str - String to which a Changeset should be applied * @returns {string} */ -exports.applyToText = (cs, str) => { - const unpacked = exports.unpack(cs); +export const applyToText = (cs: string, str: string): string => { + const unpacked = unpack(cs); assert(str.length === unpacked.oldLen, `mismatched apply: ${str.length} / ${unpacked.oldLen}`); - const bankIter = exports.stringIterator(unpacked.charBank); - const strIter = exports.stringIterator(str); - const assem = exports.stringAssembler(); - for (const op of exports.deserializeOps(unpacked.ops)) { + const bankIter = new StringIterator(unpacked.charBank); + const strIter = new StringIterator(str); + const assem = new StringAssembler(); + for (const op of deserializeOps(unpacked.ops)) { switch (op.opcode) { case '+': - // op is + and op.lines 0: no newlines must be in op.chars - // op is + and op.lines >0: op.chars must include op.lines newlines + // op is + and op.lines 0: no newlines must be in op.chars + // op is + and op.lines >0: op.chars must include op.lines newlines if (op.lines !== bankIter.peek(op.chars).split('\n').length - 1) { throw new Error(`newline count is wrong in op +; cs:${cs} and text:${str}`); } assem.append(bankIter.take(op.chars)); break; case '-': - // op is - and op.lines 0: no newlines must be in the deleted string - // op is - and op.lines >0: op.lines newlines must be in the deleted string + // op is - and op.lines 0: no newlines must be in the deleted string + // op is - and op.lines >0: op.lines newlines must be in the deleted string if (op.lines !== strIter.peek(op.chars).split('\n').length - 1) { throw new Error(`newline count is wrong in op -; cs:${cs} and text:${str}`); } strIter.skip(op.chars); break; case '=': - // op is = and op.lines 0: no newlines must be in the copied string - // op is = and op.lines >0: op.lines newlines must be in the copied string + // op is = and op.lines 0: no newlines must be in the copied string + // op is = and op.lines >0: op.lines newlines must be in the copied string if (op.lines !== strIter.peek(op.chars).split('\n').length - 1) { throw new Error(`newline count is wrong in op =; cs:${cs} and text:${str}`); } @@ -1151,11 +445,11 @@ exports.applyToText = (cs, str) => { * @param {string} cs - the changeset to apply * @param {string[]} lines - The lines to which the changeset needs to be applied */ -exports.mutateTextLines = (cs, lines) => { - const unpacked = exports.unpack(cs); - const bankIter = exports.stringIterator(unpacked.charBank); - const mut = new TextLinesMutator(lines); - for (const op of exports.deserializeOps(unpacked.ops)) { +export const mutateTextLines = (cs: string, lines: RegExpMatchArray|string[] | null) => { + const unpacked = unpack(cs); + const bankIter = new StringIterator(unpacked.charBank); + const mut = new TextLinesMutator(lines!); + for (const op of deserializeOps(unpacked.ops)) { switch (op.opcode) { case '+': mut.insert(bankIter.take(op.chars), op.lines); @@ -1177,10 +471,10 @@ exports.mutateTextLines = (cs, lines) => { * @param {AttributeString} att1 - first attribute string * @param {AttributeString} att2 - second attribue string * @param {boolean} resultIsMutation - - * @param {AttributePool} pool - attribute pool + * @param {AttributePool.ts} pool - attribute pool * @returns {string} */ -exports.composeAttributes = (att1, att2, resultIsMutation, pool) => { +export const composeAttributes = (att1: string, att2: string, resultIsMutation: boolean, pool?: AttributePool|null): string => { // att1 and att2 are strings like "*3*f*1c", asMutation is a boolean. // Sometimes attribute (key,value) pairs are treated as attribute presence // information, while other times they are treated as operations that @@ -1205,80 +499,6 @@ exports.composeAttributes = (att1, att2, resultIsMutation, pool) => { return AttributeMap.fromString(att1, pool).updateFromString(att2, !resultIsMutation).toString(); }; -/** - * Function used as parameter for applyZip to apply a Changeset to an attribute. - * - * @param {Op} attOp - The op from the sequence that is being operated on, either an attribution - * string or the earlier of two exportss being composed. - * @param {Op} csOp - - * @param {AttributePool} pool - Can be null if definitely not needed. - * @returns {Op} The result of applying `csOp` to `attOp`. - */ -const slicerZipperFunc = (attOp, csOp, pool) => { - const opOut = new Op(); - if (!attOp.opcode) { - copyOp(csOp, opOut); - csOp.opcode = ''; - } else if (!csOp.opcode) { - copyOp(attOp, opOut); - attOp.opcode = ''; - } else if (attOp.opcode === '-') { - copyOp(attOp, opOut); - attOp.opcode = ''; - } else if (csOp.opcode === '+') { - copyOp(csOp, opOut); - csOp.opcode = ''; - } else { - for (const op of [attOp, csOp]) { - assert(op.chars >= op.lines, `op has more newlines than chars: ${op.toString()}`); - } - assert( - attOp.chars < csOp.chars ? attOp.lines <= csOp.lines - : attOp.chars > csOp.chars ? attOp.lines >= csOp.lines - : attOp.lines === csOp.lines, - 'line count mismatch when composing changesets A*B; ' + - `opA: ${attOp.toString()} opB: ${csOp.toString()}`); - assert(['+', '='].includes(attOp.opcode), `unexpected opcode in op: ${attOp.toString()}`); - assert(['-', '='].includes(csOp.opcode), `unexpected opcode in op: ${csOp.toString()}`); - opOut.opcode = { - '+': { - '-': '', // The '-' cancels out (some of) the '+', leaving any remainder for the next call. - '=': '+', - }, - '=': { - '-': '-', - '=': '=', - }, - }[attOp.opcode][csOp.opcode]; - const [fullyConsumedOp, partiallyConsumedOp] = [attOp, csOp].sort((a, b) => a.chars - b.chars); - opOut.chars = fullyConsumedOp.chars; - opOut.lines = fullyConsumedOp.lines; - opOut.attribs = csOp.opcode === '-' - // csOp is a remove op and remove ops normally never have any attributes, so this should - // normally be the empty string. However, padDiff.js adds attributes to remove ops and needs - // them preserved so they are copied here. - ? csOp.attribs - : exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode === '=', pool); - partiallyConsumedOp.chars -= fullyConsumedOp.chars; - partiallyConsumedOp.lines -= fullyConsumedOp.lines; - if (!partiallyConsumedOp.chars) partiallyConsumedOp.opcode = ''; - fullyConsumedOp.opcode = ''; - } - return opOut; -}; - -/** - * Applies a Changeset to the attribs string of a AText. - * - * @param {string} cs - Changeset - * @param {string} astr - the attribs string of a AText - * @param {AttributePool} pool - the attibutes pool - * @returns {string} - */ -exports.applyToAttribution = (cs, astr, pool) => { - const unpacked = exports.unpack(cs); - return applyZip(astr, unpacked.ops, (op1, op2) => slicerZipperFunc(op1, op2, pool)); -}; /** * Applies a changeset to an array of attribute lines. @@ -1287,9 +507,9 @@ exports.applyToAttribution = (cs, astr, pool) => { * @param {Array<string>} lines - Attribute lines. Modified in place. * @param {AttributePool} pool - Attribute pool. */ -exports.mutateAttributionLines = (cs, lines, pool) => { - const unpacked = exports.unpack(cs); - const csOps = exports.deserializeOps(unpacked.ops); +export const mutateAttributionLines = (cs: any, lines: string[] | RegExpMatchArray, pool: AttributePool | null) => { + const unpacked = unpack(cs); + const csOps = deserializeOps(unpacked.ops); let csOpsNext = csOps.next(); const csBank = unpacked.charBank; let csBankIndex = 0; @@ -1301,8 +521,8 @@ exports.mutateAttributionLines = (cs, lines, pool) => { * * @type {?Generator<Op>} */ - let lineOps = null; - let lineOpsNext = null; + let lineOps: { next: () => any; } | null = null; + let lineOpsNext: { done: any; value: any; } | null = null; const lineOpsHasNext = () => lineOpsNext && !lineOpsNext.done; /** @@ -1323,29 +543,29 @@ exports.mutateAttributionLines = (cs, lines, pool) => { // There are more attribute lines in `lines` to do AND either we just started so `lineIter` is // still null or there are no more ops in current `lineIter`. const line = mut.removeLines(1); - lineOps = exports.deserializeOps(line); + lineOps = deserializeOps(line); lineOpsNext = lineOps.next(); } if (!lineOpsHasNext()) return new Op(); // No more ops and no more lines. - const op = lineOpsNext.value; - lineOpsNext = lineOps.next(); + const op = lineOpsNext!.value; + lineOpsNext = lineOps!.next(); return op; }; - let lineAssem = null; + let lineAssem: { append: (arg0: any) => void; toString: () => string; } | null = null; /** * Appends an op to `lineAssem`. In case `lineAssem` includes one single newline, adds it to the * `lines` mutator. */ - const outputMutOp = (op) => { + const outputMutOp = (op: Op) => { if (!lineAssem) { - lineAssem = exports.mergingOpAssembler(); + lineAssem = new MergingOpAssembler(); } - lineAssem.append(op); + lineAssem!.append(op); if (op.lines <= 0) return; assert(op.lines === 1, `Can't have op.lines of ${op.lines} in attribution lines`); // ship it to the mut - mut.insert(lineAssem.toString(), 1); + mut.insert(lineAssem!.toString(), 1); lineAssem = null; }; @@ -1360,7 +580,7 @@ exports.mutateAttributionLines = (cs, lines, pool) => { if (!csOp.opcode && !attOp.opcode && !lineAssem && !lineOpsHasNext()) { break; // done } else if (csOp.opcode === '=' && csOp.lines > 0 && !csOp.attribs && !attOp.opcode && - !lineAssem && !lineOpsHasNext()) { + !lineAssem && !lineOpsHasNext()) { // Skip multiple lines without attributes; this is what makes small changes not order of the // document size. mut.skipLines(csOp.lines); @@ -1391,26 +611,101 @@ exports.mutateAttributionLines = (cs, lines, pool) => { mut.close(); }; +/** + * Function used as parameter for applyZip to apply a Changeset to an attribute. + * + * @param {Op} attOp - The op from the sequence that is being operated on, either an attribution + * string or the earlier of two exportss being composed. + * @param {Op} csOp - + * @param {AttributePool.ts} pool - Can be null if definitely not needed. + * @returns {Op} The result of applying `csOp` to `attOp`. + */ +export const slicerZipperFunc = (attOp: Op, csOp: Op, pool: AttributePool|null):Op => { + const opOut = new Op(); + if (!attOp.opcode) { + copyOp(csOp, opOut); + csOp.opcode = ''; + } else if (!csOp.opcode) { + copyOp(attOp, opOut); + attOp.opcode = ''; + } else if (attOp.opcode === '-') { + copyOp(attOp, opOut); + attOp.opcode = ''; + } else if (csOp.opcode === '+') { + copyOp(csOp, opOut); + csOp.opcode = ''; + } else { + for (const op of [attOp, csOp]) { + assert(op.chars >= op.lines, `op has more newlines than chars: ${op.toString()}`); + } + assert( + attOp.chars < csOp.chars ? attOp.lines <= csOp.lines + : attOp.chars > csOp.chars ? attOp.lines >= csOp.lines + : attOp.lines === csOp.lines, + 'line count mismatch when composing changesets A*B; ' + + `opA: ${attOp.toString()} opB: ${csOp.toString()}`); + assert(['+', '='].includes(attOp.opcode), `unexpected opcode in op: ${attOp.toString()}`); + assert(['-', '='].includes(csOp.opcode), `unexpected opcode in op: ${csOp.toString()}`); + opOut.opcode = { + '+': { + '-': '', // The '-' cancels out (some of) the '+', leaving any remainder for the next call. + '=': '+', + }, + '=': { + '-': '-', + '=': '=', + }, + }[attOp.opcode][csOp.opcode] as OpCode; + const [fullyConsumedOp, partiallyConsumedOp] = [attOp, csOp].sort((a, b) => a.chars - b.chars); + opOut.chars = fullyConsumedOp.chars; + opOut.lines = fullyConsumedOp.lines; + opOut.attribs = csOp.opcode === '-' + // csOp is a remove op and remove ops normally never have any attributes, so this should + // normally be the empty string. However, padDiff.js adds attributes to remove ops and needs + // them preserved so they are copied here. + ? csOp.attribs + : composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode === '=', pool); + partiallyConsumedOp.chars -= fullyConsumedOp.chars; + partiallyConsumedOp.lines -= fullyConsumedOp.lines; + if (!partiallyConsumedOp.chars) partiallyConsumedOp.opcode = ''; + fullyConsumedOp.opcode = ''; + } + return opOut; +}; + +/** + * Applies a Changeset to the attribs string of a AText. + * + * @param {string} cs - Changeset + * @param {string} astr - the attribs string of a AText + * @param {AttributePool.ts} pool - the attibutes pool + * @returns {string} + */ +export const applyToAttribution = (cs: string, astr: string, pool: AttributePool): string => { + const unpacked = unpack(cs); + return applyZip(astr, unpacked.ops, (op1: Op, op2:Op) => slicerZipperFunc(op1, op2, pool)); +}; + /** * Joins several Attribution lines. * * @param {string[]} theAlines - collection of Attribution lines * @returns {string} joined Attribution lines */ -exports.joinAttributionLines = (theAlines) => { - const assem = exports.mergingOpAssembler(); +export const joinAttributionLines = (theAlines: string[]): string => { + const assem = new MergingOpAssembler(); for (const aline of theAlines) { - for (const op of exports.deserializeOps(aline)) assem.append(op); + for (const op of deserializeOps(aline)) assem.append(op); } return assem.toString(); }; -exports.splitAttributionLines = (attrOps, text) => { - const assem = exports.mergingOpAssembler(); - const lines = []; +export const splitAttributionLines = (attrOps: string, text: string) => { + const assem = new MergingOpAssembler(); + const lines: string[] = []; let pos = 0; - const appendOp = (op) => { + const appendOp = (op:Op) => { assem.append(op); if (op.lines > 0) { lines.push(assem.toString()); @@ -1419,7 +714,7 @@ exports.splitAttributionLines = (attrOps, text) => { pos += op.chars; }; - for (const op of exports.deserializeOps(attrOps)) { + for (const op of deserializeOps(attrOps)) { let numChars = op.chars; let numLines = op.lines; while (numLines > 1) { @@ -1447,28 +742,28 @@ exports.splitAttributionLines = (attrOps, text) => { * @param {string} text - text to split * @returns {string[]} */ -exports.splitTextLines = (text) => text.match(/[^\n]*(?:\n|[^\n]$)/g); +export const splitTextLines = (text:string) => text.match(/[^\n]*(?:\n|[^\n]$)/g); /** * Compose two Changesets. * * @param {string} cs1 - first Changeset * @param {string} cs2 - second Changeset - * @param {AttributePool} pool - Attribs pool + * @param {AttributePool.ts} pool - Attribs pool * @returns {string} */ -exports.compose = (cs1, cs2, pool) => { - const unpacked1 = exports.unpack(cs1); - const unpacked2 = exports.unpack(cs2); +export const compose = (cs1: string, cs2:string, pool: AttributePool): string => { + const unpacked1 = unpack(cs1); + const unpacked2 = unpack(cs2); const len1 = unpacked1.oldLen; const len2 = unpacked1.newLen; assert(len2 === unpacked2.oldLen, 'mismatched composition of two changesets'); const len3 = unpacked2.newLen; - const bankIter1 = exports.stringIterator(unpacked1.charBank); - const bankIter2 = exports.stringIterator(unpacked2.charBank); - const bankAssem = exports.stringAssembler(); + const bankIter1 = new StringIterator(unpacked1.charBank); + const bankIter2 = new StringIterator(unpacked2.charBank); + const bankAssem = new StringAssembler(); - const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1, op2) => { + const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1: Op, op2: Op) => { const op1code = op1.opcode; const op2code = op2.opcode; if (op1code === '+' && op2code === '-') { @@ -1485,7 +780,7 @@ exports.compose = (cs1, cs2, pool) => { return opOut; }); - return exports.pack(len1, len3, newOps, bankAssem.toString()); + return pack(len1, len3, newOps, bankAssem.toString()); }; /** @@ -1493,16 +788,16 @@ exports.compose = (cs1, cs2, pool) => { * key,value that is already present in the pool. * * @param {Attribute} attribPair - `[key, value]` pair of strings. - * @param {AttributePool} pool - Attribute pool + * @param {AttributePool.ts} pool - Attribute pool * @returns {Function} */ -exports.attributeTester = (attribPair, pool) => { - const never = (attribs) => false; +export const attributeTester = (attribPair: Attribute, pool: AttributePool): Function => { + const never = (attribs: Attribute[]) => false; if (!pool) return never; const attribNum = pool.putAttrib(attribPair, true); if (attribNum < 0) return never; - const re = new RegExp(`\\*${exports.numToString(attribNum)}(?!\\w)`); - return (attribs) => re.test(attribs); + const re = new RegExp(`\\*${numToString(attribNum)}(?!\\w)`); + return (attribs: string) => re.test(attribs); }; /** @@ -1511,7 +806,7 @@ exports.attributeTester = (attribPair, pool) => { * @param {number} N - length of the identity changeset * @returns {string} */ -exports.identity = (N) => exports.pack(N, N, '', ''); +export const identity = (N: number): string => pack(N, N, '', ''); /** * Creates a Changeset which works on oldFullText and removes text from spliceStart to @@ -1523,24 +818,24 @@ exports.identity = (N) => exports.pack(N, N, '', ''); * @param {number} ndel - Number of characters to delete at `start`. * @param {string} ins - Text to insert at `start` (after deleting `ndel` characters). * @param {string} [attribs] - Optional attributes to apply to the inserted text. - * @param {AttributePool} [pool] - Attribute pool. + * @param {AttributePool.ts} [pool] - Attribute pool. * @returns {string} */ -exports.makeSplice = (orig, start, ndel, ins, attribs, pool) => { +export const makeSplice = (orig: string, start: number, ndel: number, ins: string|null, attribs?: string | Attribute[] | undefined, pool?: AttributePool | null | undefined): string => { if (start < 0) throw new RangeError(`start index must be non-negative (is ${start})`); if (ndel < 0) throw new RangeError(`characters to delete must be non-negative (is ${ndel})`); if (start > orig.length) start = orig.length; if (ndel > orig.length - start) ndel = orig.length - start; const deleted = orig.substring(start, start + ndel); - const assem = exports.smartOpAssembler(); + const assem = new SmartOpAssembler(); const ops = (function* () { yield* opsFromText('=', orig.substring(0, start)); yield* opsFromText('-', deleted); - yield* opsFromText('+', ins, attribs, pool); + yield* opsFromText('+', ins as string, attribs, pool); })(); for (const op of ops) assem.append(op); assem.endDocument(); - return exports.pack(orig.length, orig.length + ins.length - ndel, assem.toString(), ins); + return pack(orig.length, orig.length + ins!.length - ndel, assem.toString(), ins!); }; /** @@ -1550,15 +845,15 @@ exports.makeSplice = (orig, start, ndel, ins, attribs, pool) => { * @param {string} cs - Changeset * @returns {[number, number, string][]} */ -const toSplices = (cs) => { - const unpacked = exports.unpack(cs); +const toSplices = (cs: string): [number, number, string][] => { + const unpacked = unpack(cs); /** @type {[number, number, string][]} */ - const splices = []; + const splices: [number, number, string][] = []; let oldPos = 0; - const charIter = exports.stringIterator(unpacked.charBank); + const charIter = new StringIterator(unpacked.charBank); let inSplice = false; - for (const op of exports.deserializeOps(unpacked.ops)) { + for (const op of deserializeOps(unpacked.ops)) { if (op.opcode === '=') { oldPos += op.chars; inSplice = false; @@ -1586,7 +881,7 @@ const toSplices = (cs) => { * @param {number} insertionsAfter - * @returns {[number, number]} */ -exports.characterRangeFollow = (cs, startChar, endChar, insertionsAfter) => { +export const characterRangeFollow = (cs: string, startChar: number, endChar: number, insertionsAfter: number):[number, number] => { let newStartChar = startChar; let newEndChar = endChar; let lengthChangeSoFar = 0; @@ -1636,7 +931,7 @@ exports.characterRangeFollow = (cs, startChar, endChar, insertionsAfter) => { * @param {AttributePool} newPool - new attributes pool * @returns {string} the new Changeset */ -exports.moveOpsToNewPool = (cs, oldPool, newPool) => { +export const moveOpsToNewPool = (cs: string, oldPool: AttributePool, newPool: AttributePool): string => { // works on exports or attribution string let dollarPos = cs.indexOf('$'); if (dollarPos < 0) { @@ -1646,13 +941,13 @@ exports.moveOpsToNewPool = (cs, oldPool, newPool) => { const fromDollar = cs.substring(dollarPos); // order of attribs stays the same return upToDollar.replace(/\*([0-9a-z]+)/g, (_, a) => { - const oldNum = exports.parseNum(a); + const oldNum = parseNum(a); const pair = oldPool.getAttrib(oldNum); // The attribute might not be in the old pool if the user is viewing the current revision in the // timeslider and text is deleted. See: https://github.com/ether/etherpad-lite/issues/3932 if (!pair) return ''; const newNum = newPool.putAttrib(pair); - return `*${exports.numToString(newNum)}`; + return `*${numToString(newNum)}`; }) + fromDollar; }; @@ -1662,8 +957,8 @@ exports.moveOpsToNewPool = (cs, oldPool, newPool) => { * @param {string} text - text to insert * @returns {string} */ -exports.makeAttribution = (text) => { - const assem = exports.smartOpAssembler(); +export const makeAttribution = (text: string) => { + const assem = new SmartOpAssembler(); for (const op of opsFromText('+', text)) assem.append(op); return assem.toString(); }; @@ -1676,9 +971,9 @@ exports.makeAttribution = (text) => { * @param {string} cs - changeset * @param {Function} func - function to call */ -exports.eachAttribNumber = (cs, func) => { +export const eachAttribNumber = (cs: string, func: Function) => { padutils.warnDeprecated( - 'Changeset.eachAttribNumber() is deprecated; use attributes.decodeAttribString() instead'); + 'Changeset.eachAttribNumber() is deprecated; use attributes.decodeAttribString() instead'); let dollarPos = cs.indexOf('$'); if (dollarPos < 0) { dollarPos = cs.length; @@ -1688,7 +983,7 @@ exports.eachAttribNumber = (cs, func) => { // WARNING: The following cannot be replaced with a call to `attributes.decodeAttribString()` // because that function only works on attribute strings, not serialized operations or changesets. upToDollar.replace(/\*([0-9a-z]+)/g, (_, a) => { - func(exports.parseNum(a)); + func(parseNum(a)); return ''; }); }; @@ -1702,16 +997,16 @@ exports.eachAttribNumber = (cs, func) => { * Changeset * @returns {string} */ -exports.filterAttribNumbers = (cs, filter) => exports.mapAttribNumbers(cs, filter); +export const filterAttribNumbers = (cs: string, filter: Function) => mapAttribNumbers(cs, filter); /** - * Does exactly the same as exports.filterAttribNumbers. + * Does exactly the same as filterAttribNumbers. * * @param {string} cs - * @param {Function} func - * @returns {string} */ -exports.mapAttribNumbers = (cs, func) => { +export const mapAttribNumbers = (cs: string, func: Function): string => { let dollarPos = cs.indexOf('$'); if (dollarPos < 0) { dollarPos = cs.length; @@ -1719,11 +1014,11 @@ exports.mapAttribNumbers = (cs, func) => { const upToDollar = cs.substring(0, dollarPos); const newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, (s, a) => { - const n = func(exports.parseNum(a)); + const n = func(parseNum(a)); if (n === true) { return s; } else if ((typeof n) === 'number') { - return `*${exports.numToString(n)}`; + return `*${numToString(n)}`; } else { return ''; } @@ -1749,9 +1044,9 @@ exports.mapAttribNumbers = (cs, func) => { * attributes * @returns {AText} */ -exports.makeAText = (text, attribs) => ({ +export const makeAText = (text: string, attribs?: string): AText => ({ text, - attribs: (attribs || exports.makeAttribution(text)), + attribs: (attribs || makeAttribution(text)), }); /** @@ -1759,12 +1054,12 @@ exports.makeAText = (text, attribs) => ({ * * @param {string} cs - Changeset to apply * @param {AText} atext - - * @param {AttributePool} pool - Attribute Pool to add to + * @param {AttributePool.ts} pool - Attribute Pool to add to * @returns {AText} */ -exports.applyToAText = (cs, atext, pool) => ({ - text: exports.applyToText(cs, atext.text), - attribs: exports.applyToAttribution(cs, atext.attribs, pool), +export const applyToAText = (cs: string, atext: AText, pool: AttributePool): AText => ({ + text: applyToText(cs, atext.text), + attribs: applyToAttribution(cs, atext.attribs, pool), }); /** @@ -1773,7 +1068,7 @@ exports.applyToAText = (cs, atext, pool) => ({ * @param {AText} atext - * @returns {AText} */ -exports.cloneAText = (atext) => { +export const cloneAText = (atext: AText): AText => { if (!atext) error('atext is null'); return { text: atext.text, @@ -1787,7 +1082,7 @@ exports.cloneAText = (atext) => { * @param {AText} atext1 - * @param {AText} atext2 - */ -exports.copyAText = (atext1, atext2) => { +export const copyAText = (atext1: AText, atext2: AText) => { atext2.text = atext1.text; atext2.attribs = atext1.attribs; }; @@ -1799,10 +1094,10 @@ exports.copyAText = (atext1, atext2) => { * @yields {Op} * @returns {Generator<Op>} */ -exports.opsFromAText = function* (atext) { +export const opsFromAText = function* (atext: AText): Generator<Op> { // intentionally skips last newline char of atext let lastOp = null; - for (const op of exports.deserializeOps(atext.attribs)) { + for (const op of deserializeOps(atext.attribs)) { if (lastOp != null) yield lastOp; lastOp = op; } @@ -1830,22 +1125,27 @@ exports.opsFromAText = function* (atext) { * @param {AText} atext - * @param assem - Assembler like SmartOpAssembler TODO add desc */ -exports.appendATextToAssembler = (atext, assem) => { +export const appendATextToAssembler = (atext: AText, assem: SmartOpAssembler) => { padutils.warnDeprecated( - 'Changeset.appendATextToAssembler() is deprecated; use Changeset.opsFromAText() instead'); - for (const op of exports.opsFromAText(atext)) assem.append(op); + 'Changeset.appendATextToAssembler() is deprecated; use Changeset.opsFromAText() instead'); + for (const op of opsFromAText(atext)) assem.append(op); }; +type WirePrep = { + translated: string, + pool: AttributePool +} + /** * Creates a clone of a Changeset and it's APool. * * @param {string} cs - - * @param {AttributePool} pool - - * @returns {{translated: string, pool: AttributePool}} + * @param {AttributePool.ts} pool - + * @returns {{translated: string, pool: AttributePool.ts}} */ -exports.prepareForWire = (cs, pool) => { +export const prepareForWire = (cs: string, pool: AttributePool): WirePrep => { const newPool = new AttributePool(); - const newCs = exports.moveOpsToNewPool(cs, pool, newPool); + const newCs = moveOpsToNewPool(cs, pool, newPool); return { translated: newCs, pool: newPool, @@ -1858,17 +1158,17 @@ exports.prepareForWire = (cs, pool) => { * @param {string} cs - * @returns {boolean} */ -exports.isIdentity = (cs) => { - const unpacked = exports.unpack(cs); +export const isIdentity = (cs: string): boolean => { + const unpacked = unpack(cs); return unpacked.ops === '' && unpacked.oldLen === unpacked.newLen; }; /** * @deprecated Use an AttributeMap instead. */ -const attribsAttributeValue = (attribs, key, pool) => { +const _attribsAttributeValue = (attribs: string, key: string, pool: AttributePool) => { if (!attribs) return ''; - for (const [k, v] of attributes.attribsFromString(attribs, pool)) { + for (const [k, v] of attribsFromString(attribs, pool)) { if (k === key) return v; } return ''; @@ -1880,13 +1180,13 @@ const attribsAttributeValue = (attribs, key, pool) => { * @deprecated Use an AttributeMap instead. * @param {Op} op - Op * @param {string} key - string to search for - * @param {AttributePool} pool - attribute pool + * @param {AttributePool.ts} pool - attribute pool * @returns {string} */ -exports.opAttributeValue = (op, key, pool) => { +export const opAttributeValue = (op: Op, key: string, pool: AttributePool):string => { padutils.warnDeprecated( - 'Changeset.opAttributeValue() is deprecated; use an AttributeMap instead'); - return attribsAttributeValue(op.attribs, key, pool); + 'Changeset.opAttributeValue() is deprecated; use an AttributeMap instead'); + return _attribsAttributeValue(op.attribs, key, pool); }; /** @@ -1895,107 +1195,16 @@ exports.opAttributeValue = (op, key, pool) => { * @deprecated Use an AttributeMap instead. * @param {AttributeString} attribs - Attribute string * @param {string} key - string to search for - * @param {AttributePool} pool - attribute pool + * @param {AttributePool.ts} pool - attribute pool * @returns {string} */ -exports.attribsAttributeValue = (attribs, key, pool) => { +export const attribsAttributeValue = (attribs: string, key: string, pool: AttributePool) => { padutils.warnDeprecated( - 'Changeset.attribsAttributeValue() is deprecated; use an AttributeMap instead'); - return attribsAttributeValue(attribs, key, pool); + 'Changeset.attribsAttributeValue() is deprecated; use an AttributeMap instead'); + return _attribsAttributeValue(attribs, key, pool); }; -/** - * Incrementally builds a Changeset. - * - * @typedef {object} Builder - * @property {Function} insert - - * @property {Function} keep - - * @property {Function} keepText - - * @property {Function} remove - - * @property {Function} toString - - */ -/** - * @param {number} oldLen - Old length - * @returns {Builder} - */ -exports.builder = (oldLen) => { - const assem = exports.smartOpAssembler(); - const o = new Op(); - const charBank = exports.stringAssembler(); - - const self = { - /** - * @param {number} N - Number of characters to keep. - * @param {number} L - Number of newlines among the `N` characters. If positive, the last - * character must be a newline. - * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' - * (no pool needed in latter case). - * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of - * attribute key, value pairs. - * @returns {Builder} this - */ - keep: (N, L, attribs, pool) => { - o.opcode = '='; - o.attribs = typeof attribs === 'string' - ? attribs : new AttributeMap(pool).update(attribs || []).toString(); - o.chars = N; - o.lines = (L || 0); - assem.append(o); - return self; - }, - - /** - * @param {string} text - Text to keep. - * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' - * (no pool needed in latter case). - * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of - * attribute key, value pairs. - * @returns {Builder} this - */ - keepText: (text, attribs, pool) => { - for (const op of opsFromText('=', text, attribs, pool)) assem.append(op); - return self; - }, - - /** - * @param {string} text - Text to insert. - * @param {(string|Attribute[])} attribs - Either [[key1,value1],[key2,value2],...] or '*0*1...' - * (no pool needed in latter case). - * @param {?AttributePool} pool - Attribute pool, only required if `attribs` is a list of - * attribute key, value pairs. - * @returns {Builder} this - */ - insert: (text, attribs, pool) => { - for (const op of opsFromText('+', text, attribs, pool)) assem.append(op); - charBank.append(text); - return self; - }, - - /** - * @param {number} N - Number of characters to remove. - * @param {number} L - Number of newlines among the `N` characters. If positive, the last - * character must be a newline. - * @returns {Builder} this - */ - remove: (N, L) => { - o.opcode = '-'; - o.attribs = ''; - o.chars = N; - o.lines = (L || 0); - assem.append(o); - return self; - }, - - toString: () => { - assem.endDocument(); - const newLen = oldLen + assem.getLengthChange(); - return exports.pack(oldLen, newLen, assem.toString(), charBank.toString()); - }, - }; - - return self; -}; /** * Constructs an attribute string from a sequence of attributes. @@ -2006,14 +1215,14 @@ exports.builder = (oldLen) => { * (if necessary) and encode. If an attribute string, no checking is performed to ensure that * the attributes exist in the pool, are in the canonical order, and contain no duplicate keys. * If this is an iterable of attributes, `pool` must be non-null. - * @param {AttributePool} pool - Attribute pool. Required if `attribs` is an iterable of attributes, + * @param {AttributePool.ts} pool - Attribute pool. Required if `attribs` is an iterable of attributes, * ignored if `attribs` is an attribute string. * @returns {AttributeString} */ -exports.makeAttribsString = (opcode, attribs, pool) => { +export const makeAttribsString = (opcode: string, attribs: Attribute[]|string, pool: AttributePool | null | undefined): string => { padutils.warnDeprecated( - 'Changeset.makeAttribsString() is deprecated; ' + - 'use AttributeMap.prototype.toString() or attributes.attribsToString() instead'); + 'Changeset.makeAttribsString() is deprecated; ' + + 'use AttributeMap.prototype.toString() or attributes.attribsToString() instead'); if (!attribs || !['=', '+'].includes(opcode)) return ''; if (typeof attribs === 'string') return attribs; return new AttributeMap(pool).update(attribs, opcode === '+').toString(); @@ -2022,10 +1231,10 @@ exports.makeAttribsString = (opcode, attribs, pool) => { /** * Like "substring" but on a single-line attribution string. */ -exports.subattribution = (astr, start, optEnd) => { - const attOps = exports.deserializeOps(astr); +export const subattribution = (astr: string, start: number, optEnd?: number) => { + const attOps = deserializeOps(astr); let attOpsNext = attOps.next(); - const assem = exports.smartOpAssembler(); + const assem = new SmartOpAssembler(); let attOp = new Op(); const csOp = new Op(); @@ -2033,11 +1242,11 @@ exports.subattribution = (astr, start, optEnd) => { if (!csOp.chars) return; while (csOp.opcode && (attOp.opcode || !attOpsNext.done)) { if (!attOp.opcode) { - attOp = attOpsNext.value; + attOp = attOpsNext.value as Op; attOpsNext = attOps.next(); } if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars && - attOp.lines > 0 && csOp.lines <= 0) { + attOp.lines > 0 && csOp.lines <= 0) { csOp.lines++; } const opOut = slicerZipperFunc(attOp, csOp, null); @@ -2067,16 +1276,20 @@ exports.subattribution = (astr, start, optEnd) => { return assem.toString(); }; -exports.inverse = (cs, lines, alines, pool) => { +export const inverse = (cs: string, lines: string|RegExpMatchArray|string[] | null, alines: string[]|{ + get: (idx: number) => string, +}, pool: AttributePool) => { // lines and alines are what the exports is meant to apply to. // They may be arrays or objects with .get(i) and .length methods. // They include final newlines on lines. - const linesGet = (idx) => { - if (lines.get) { + const linesGet = (idx: number) => { + // @ts-ignore + if ("get" in lines) { + // @ts-ignore return lines.get(idx); } else { - return lines[idx]; + return lines![idx]; } }; @@ -2084,8 +1297,9 @@ exports.inverse = (cs, lines, alines, pool) => { * @param {number} idx - * @returns {string} */ - const alinesGet = (idx) => { - if (alines.get) { + const alinesGet = (idx: number): string => { + // @ts-ignore + if ("get" in alines) { return alines.get(idx); } else { return alines[idx]; @@ -2094,17 +1308,17 @@ exports.inverse = (cs, lines, alines, pool) => { let curLine = 0; let curChar = 0; - let curLineOps = null; - let curLineOpsNext = null; - let curLineOpsLine; + let curLineOps: null|Generator<Op> = null; + let curLineOpsNext:IteratorResult<Op>|null = null; + let curLineOpsLine: number; let curLineNextOp = new Op('+'); - const unpacked = exports.unpack(cs); - const builder = exports.builder(unpacked.newLen); + const unpacked = unpack(cs); + const builder = new Builder(unpacked.newLen); - const consumeAttribRuns = (numChars, func /* (len, attribs, endsLine)*/) => { + const consumeAttribRuns = (numChars: number, func: Function /* (len, attribs, endsLine)*/) => { if (!curLineOps || curLineOpsLine !== curLine) { - curLineOps = exports.deserializeOps(alinesGet(curLine)); + curLineOps = deserializeOps(alinesGet(curLine)); curLineOpsNext = curLineOps.next(); curLineOpsLine = curLine; let indexIntoLine = 0; @@ -2120,37 +1334,37 @@ exports.inverse = (cs, lines, alines, pool) => { } while (numChars > 0) { - if (!curLineNextOp.chars && curLineOpsNext.done) { + if (!curLineNextOp.chars && curLineOpsNext!.done) { curLine++; curChar = 0; curLineOpsLine = curLine; curLineNextOp.chars = 0; - curLineOps = exports.deserializeOps(alinesGet(curLine)); - curLineOpsNext = curLineOps.next(); + curLineOps = deserializeOps(alinesGet(curLine)); + curLineOpsNext = curLineOps!.next(); } if (!curLineNextOp.chars) { - if (curLineOpsNext.done) { + if (curLineOpsNext!.done) { curLineNextOp = new Op(); } else { - curLineNextOp = curLineOpsNext.value; + curLineNextOp = curLineOpsNext!.value; curLineOpsNext = curLineOps.next(); } } const charsToUse = Math.min(numChars, curLineNextOp.chars); func(charsToUse, curLineNextOp.attribs, charsToUse === curLineNextOp.chars && - curLineNextOp.lines > 0); + curLineNextOp.lines > 0); numChars -= charsToUse; curLineNextOp.chars -= charsToUse; curChar += charsToUse; } - if (!curLineNextOp.chars && curLineOpsNext.done) { + if (!curLineNextOp.chars && curLineOpsNext!.done) { curLine++; curChar = 0; } }; - const skip = (N, L) => { + const skip = (N: number, L: number) => { if (L) { curLine += L; curChar = 0; @@ -2161,9 +1375,9 @@ exports.inverse = (cs, lines, alines, pool) => { } }; - const nextText = (numChars) => { + const nextText = (numChars: number) => { let len = 0; - const assem = exports.stringAssembler(); + const assem = new StringAssembler(); const firstString = linesGet(curLine).substring(curChar); len += firstString.length; assem.append(firstString); @@ -2179,9 +1393,11 @@ exports.inverse = (cs, lines, alines, pool) => { return assem.toString().substring(0, numChars); }; - const cachedStrFunc = (func) => { - const cache = {}; - return (s) => { + const cachedStrFunc = (func: Function) => { + const cache:{ + [key: string]: string + } = {}; + return (s: string | number) => { if (!cache[s]) { cache[s] = func(s); } @@ -2189,11 +1405,11 @@ exports.inverse = (cs, lines, alines, pool) => { }; }; - for (const csOp of exports.deserializeOps(unpacked.ops)) { + for (const csOp of deserializeOps(unpacked.ops)) { if (csOp.opcode === '=') { if (csOp.attribs) { const attribs = AttributeMap.fromString(csOp.attribs, pool); - const undoBackToAttribs = cachedStrFunc((oldAttribsStr) => { + const undoBackToAttribs = cachedStrFunc((oldAttribsStr: string) => { const oldAttribs = AttributeMap.fromString(oldAttribsStr, pool); const backAttribs = new AttributeMap(pool); for (const [key, value] of attribs) { @@ -2204,7 +1420,7 @@ exports.inverse = (cs, lines, alines, pool) => { // are in oldAttribs but not in attribs). I don't know if that is intentional. return backAttribs.toString(); }); - consumeAttribRuns(csOp.chars, (len, attribs, endsLine) => { + consumeAttribRuns(csOp.chars, (len: number, attribs: string, endsLine: number) => { builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs)); }); } else { @@ -2216,33 +1432,33 @@ exports.inverse = (cs, lines, alines, pool) => { } else if (csOp.opcode === '-') { const textBank = nextText(csOp.chars); let textBankIndex = 0; - consumeAttribRuns(csOp.chars, (len, attribs, endsLine) => { + consumeAttribRuns(csOp.chars, (len: number, attribs: string) => { builder.insert(textBank.substr(textBankIndex, len), attribs); textBankIndex += len; }); } } - return exports.checkRep(builder.toString()); + return checkRep(builder.toString()); }; // %CLIENT FILE ENDS HERE% -exports.follow = (cs1, cs2, reverseInsertOrder, pool) => { - const unpacked1 = exports.unpack(cs1); - const unpacked2 = exports.unpack(cs2); +export const follow = (cs1: string, cs2:string, reverseInsertOrder: boolean, pool: AttributePool) => { + const unpacked1 = unpack(cs1); + const unpacked2 = unpack(cs2); const len1 = unpacked1.oldLen; const len2 = unpacked2.oldLen; assert(len1 === len2, 'mismatched follow - cannot transform cs1 on top of cs2'); - const chars1 = exports.stringIterator(unpacked1.charBank); - const chars2 = exports.stringIterator(unpacked2.charBank); + const chars1 = new StringIterator(unpacked1.charBank); + const chars2 = new StringIterator(unpacked2.charBank); const oldLen = unpacked1.newLen; let oldPos = 0; let newLen = 0; - const hasInsertFirst = exports.attributeTester(['insertorder', 'first'], pool); + const hasInsertFirst = attributeTester(['insertorder', 'first'], pool); - const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1, op2) => { + const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1: Op, op2: Op) => { const opOut = new Op(); if (op1.opcode === '+' || op2.opcode === '+') { let whichToDo; @@ -2365,10 +1581,10 @@ exports.follow = (cs1, cs2, reverseInsertOrder, pool) => { }); newLen += oldLen - oldPos; - return exports.pack(oldLen, newLen, newOps, unpacked2.charBank); + return pack(oldLen, newLen, newOps, unpacked2.charBank); }; -const followAttributes = (att1, att2, pool) => { +const followAttributes = (att1: string, att2: string, pool: AttributePool) => { // The merge of two sets of attribute changes to the same text // takes the lexically-earlier value if there are two values // for the same key. Otherwise, all key/value changes from @@ -2379,25 +1595,25 @@ const followAttributes = (att1, att2, pool) => { if (!att1) return att2; const atts = new Map(); att2.replace(/\*([0-9a-z]+)/g, (_, a) => { - const [key, val] = pool.getAttrib(exports.parseNum(a)); + const [key, val] = pool.getAttrib(parseNum(a)); atts.set(key, val); return ''; }); att1.replace(/\*([0-9a-z]+)/g, (_, a) => { - const [key, val] = pool.getAttrib(exports.parseNum(a)); + const [key, val] = pool.getAttrib(parseNum(a)); if (atts.has(key) && val <= atts.get(key)) atts.delete(key); return ''; }); // we've only removed attributes, so they're already sorted - const buf = exports.stringAssembler(); + const buf = new StringAssembler(); for (const att of atts) { buf.append('*'); - buf.append(exports.numToString(pool.putAttrib(att))); + buf.append(numToString(pool.putAttrib(att))); } return buf.toString(); }; -exports.exportedForTestingOnly = { +export const exportedForTestingOnly = { TextLinesMutator, followAttributes, toSplices, diff --git a/src/static/js/ChangesetUtils.js b/src/static/js/ChangesetUtils.ts similarity index 59% rename from src/static/js/ChangesetUtils.js rename to src/static/js/ChangesetUtils.ts index ef2be2ebe07..33d9749c4c4 100644 --- a/src/static/js/ChangesetUtils.js +++ b/src/static/js/ChangesetUtils.ts @@ -5,6 +5,12 @@ * based on a SkipList */ +import {RepModel} from "./types/RepModel"; +import {ChangeSetBuilder} from "./types/ChangeSetBuilder"; +import {Attribute} from "./types/Attribute"; +import AttributePool from "./AttributePool"; +import {Builder} from "./Builder"; + /** * Copyright 2009 Google Inc. * @@ -20,7 +26,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -exports.buildRemoveRange = (rep, builder, start, end) => { +export const buildRemoveRange = (rep: RepModel, builder: ChangeSetBuilder, start: [number,number], end: [number, number]) => { const startLineOffset = rep.lines.offsetOfIndex(start[0]); const endLineOffset = rep.lines.offsetOfIndex(end[0]); @@ -32,7 +38,7 @@ exports.buildRemoveRange = (rep, builder, start, end) => { } }; -exports.buildKeepRange = (rep, builder, start, end, attribs, pool) => { +export const buildKeepRange = (rep: RepModel, builder: ChangeSetBuilder, start: [number, number], end:[number, number], attribs?: Attribute[], pool?: AttributePool) => { const startLineOffset = rep.lines.offsetOfIndex(start[0]); const endLineOffset = rep.lines.offsetOfIndex(end[0]); @@ -44,9 +50,25 @@ exports.buildKeepRange = (rep, builder, start, end, attribs, pool) => { } }; -exports.buildKeepToStartOfRange = (rep, builder, start) => { +export const buildKeepToStartOfRange = (rep: RepModel, builder: Builder, start: [number, number]) => { const startLineOffset = rep.lines.offsetOfIndex(start[0]); builder.keep(startLineOffset, start[0]); builder.keep(start[1]); }; + +/** + * Parses a number from string base 36. + * + * @param {string} str - string of the number in base 36 + * @returns {number} number + */ +export const parseNum = (str: string): number => parseInt(str, 36); + +/** + * Writes a number in base 36 and puts it in a string. + * + * @param {number} num - number + * @returns {string} string + */ +export const numToString = (num: number): string => num.toString(36).toLowerCase(); diff --git a/src/static/js/ChatMessage.js b/src/static/js/ChatMessage.ts similarity index 66% rename from src/static/js/ChatMessage.js rename to src/static/js/ChatMessage.ts index a627f88f9f3..db2d3403a19 100644 --- a/src/static/js/ChatMessage.js +++ b/src/static/js/ChatMessage.ts @@ -1,6 +1,6 @@ 'use strict'; -const {padutils: {warnDeprecated}} = require('./pad_utils'); +import padUtils from './pad_utils' /** * Represents a chat message stored in the database and transmitted among users. Plugins can extend @@ -8,14 +8,25 @@ const {padutils: {warnDeprecated}} = require('./pad_utils'); * * Supports serialization to JSON. */ -class ChatMessage { - static fromObject(obj) { +export class ChatMessage { + customMetadata: any + text: string|null + public authorId: string|null + displayName: string|null + time: number|null + static fromObject(obj: ChatMessage) { // The userId property was renamed to authorId, and userName was renamed to displayName. Accept // the old names in case the db record was written by an older version of Etherpad. obj = Object.assign({}, obj); // Don't mutate the caller's object. - if ('userId' in obj && !('authorId' in obj)) obj.authorId = obj.userId; + if ('userId' in obj && !('authorId' in obj)) { // @ts-ignore + obj.authorId = obj.userId; + } + // @ts-ignore delete obj.userId; - if ('userName' in obj && !('displayName' in obj)) obj.displayName = obj.userName; + if ('userName' in obj && !('displayName' in obj)) { // @ts-ignore + obj.displayName = obj.userName; + } + // @ts-ignore delete obj.userName; return Object.assign(new ChatMessage(), obj); } @@ -25,7 +36,7 @@ class ChatMessage { * @param {?string} [authorId] - Initial value of the `authorId` property. * @param {?number} [time] - Initial value of the `time` property. */ - constructor(text = null, authorId = null, time = null) { + constructor(text: string | null = null, authorId: string | null = null, time: number | null = null) { /** * The raw text of the user's chat message (before any rendering or processing). * @@ -62,11 +73,11 @@ class ChatMessage { * @type {string} */ get userId() { - warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead'); + padUtils.warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead'); return this.authorId; } set userId(val) { - warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead'); + padUtils.warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead'); this.authorId = val; } @@ -77,11 +88,11 @@ class ChatMessage { * @type {string} */ get userName() { - warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead'); + padUtils.warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead'); return this.displayName; } set userName(val) { - warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead'); + padUtils.warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead'); this.displayName = val; } @@ -89,10 +100,12 @@ class ChatMessage { // doesn't support authorId and displayName. toJSON() { const {authorId, displayName, ...obj} = this; + // @ts-ignore obj.userId = authorId; + // @ts-ignore obj.userName = displayName; return obj; } } -module.exports = ChatMessage; +export default ChatMessage diff --git a/src/static/js/MergingOpAssembler.ts b/src/static/js/MergingOpAssembler.ts new file mode 100644 index 00000000000..791a567f6d4 --- /dev/null +++ b/src/static/js/MergingOpAssembler.ts @@ -0,0 +1,73 @@ +import {OpAssembler} from "./OpAssembler"; +import Op from "./Op"; +import {clearOp, copyOp} from "./Changeset"; + +export class MergingOpAssembler { + private assem: OpAssembler; + private readonly bufOp: Op; + private bufOpAdditionalCharsAfterNewline: number; + + constructor() { + this.assem = new OpAssembler() + this.bufOp = new Op() + // If we get, for example, insertions [xxx\n,yyy], those don't merge, + // but if we get [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n]. + // This variable stores the length of yyy and any other newline-less + // ops immediately after it. + this.bufOpAdditionalCharsAfterNewline = 0; + } + + /** + * @param {boolean} [isEndDocument] + */ + flush = (isEndDocument?: boolean) => { + if (!this.bufOp.opcode) return; + if (isEndDocument && this.bufOp.opcode === '=' && !this.bufOp.attribs) { + // final merged keep, leave it implicit + } else { + this.assem.append(this.bufOp); + if (this.bufOpAdditionalCharsAfterNewline) { + this.bufOp.chars = this.bufOpAdditionalCharsAfterNewline; + this.bufOp.lines = 0; + this.assem.append(this.bufOp); + this.bufOpAdditionalCharsAfterNewline = 0; + } + } + this.bufOp.opcode = ''; + } + + append = (op: Op) => { + if (op.chars <= 0) return; + if (this.bufOp.opcode === op.opcode && this.bufOp.attribs === op.attribs) { + if (op.lines > 0) { + // bufOp and additional chars are all mergeable into a multi-line op + this.bufOp.chars += this.bufOpAdditionalCharsAfterNewline + op.chars; + this.bufOp.lines += op.lines; + this.bufOpAdditionalCharsAfterNewline = 0; + } else if (this.bufOp.lines === 0) { + // both bufOp and op are in-line + this.bufOp.chars += op.chars; + } else { + // append in-line text to multi-line bufOp + this.bufOpAdditionalCharsAfterNewline += op.chars; + } + } else { + this.flush(); + copyOp(op, this.bufOp); + } + } + + endDocument = () => { + this.flush(true); + }; + + toString = () => { + this.flush(); + return this.assem.toString(); + }; + + clear = () => { + this.assem.clear(); + clearOp(this.bufOp); + }; +} diff --git a/src/static/js/Op.ts b/src/static/js/Op.ts new file mode 100644 index 00000000000..b1d038df13d --- /dev/null +++ b/src/static/js/Op.ts @@ -0,0 +1,78 @@ +import {numToString} from "./ChangesetUtils"; + +export type OpCode = ''|'='|'+'|'-'; + + +/** + * An operation to apply to a shared document. + */ +export default class Op { + opcode: ''|'='|'+'|'-' + chars: number + lines: number + attribs: string + /** + * @param {(''|'='|'+'|'-')} [opcode=''] - Initial value of the `opcode` property. + */ + constructor(opcode:''|'='|'+'|'-' = '') { + /** + * The operation's operator: + * - '=': Keep the next `chars` characters (containing `lines` newlines) from the base + * document. + * - '-': Remove the next `chars` characters (containing `lines` newlines) from the base + * document. + * - '+': Insert `chars` characters (containing `lines` newlines) at the current position in + * the document. The inserted characters come from the changeset's character bank. + * - '' (empty string): Invalid operator used in some contexts to signifiy the lack of an + * operation. + * + * @type {(''|'='|'+'|'-')} + * @public + */ + this.opcode = opcode; + + /** + * The number of characters to keep, insert, or delete. + * + * @type {number} + * @public + */ + this.chars = 0; + + /** + * The number of characters among the `chars` characters that are newlines. If non-zero, the + * last character must be a newline. + * + * @type {number} + * @public + */ + this.lines = 0; + + /** + * Identifiers of attributes to apply to the text, represented as a repeated (zero or more) + * sequence of asterisk followed by a non-negative base-36 (lower-case) integer. For example, + * '*2*1o' indicates that attributes 2 and 60 apply to the text affected by the operation. The + * identifiers come from the document's attribute pool. + * + * For keep ('=') operations, the attributes are merged with the base text's existing + * attributes: + * - A keep op attribute with a non-empty value replaces an existing base text attribute that + * has the same key. + * - A keep op attribute with an empty value is interpreted as an instruction to remove an + * existing base text attribute that has the same key, if one exists. + * + * This is the empty string for remove ('-') operations. + * + * @type {string} + * @public + */ + this.attribs = ''; + } + + toString() { + if (!this.opcode) throw new TypeError('null op'); + if (typeof this.attribs !== 'string') throw new TypeError('attribs must be a string'); + const l = this.lines ? `|${numToString(this.lines)}` : ''; + return this.attribs + l + this.opcode + numToString(this.chars); + } +} diff --git a/src/static/js/OpAssembler.ts b/src/static/js/OpAssembler.ts new file mode 100644 index 00000000000..2c354965587 --- /dev/null +++ b/src/static/js/OpAssembler.ts @@ -0,0 +1,21 @@ +import Op from "./Op"; +import {assert} from './Changeset' + +/** + * @returns {OpAssembler} + */ +export class OpAssembler { + private serialized: string; + constructor() { + this.serialized = '' + + } + append = (op: Op) => { + assert(op instanceof Op, 'argument must be an instance of Op'); + this.serialized += op.toString(); + } + toString = () => this.serialized + clear = () => { + this.serialized = ''; + } +} diff --git a/src/static/js/OpIter.ts b/src/static/js/OpIter.ts new file mode 100644 index 00000000000..40b0abaf487 --- /dev/null +++ b/src/static/js/OpIter.ts @@ -0,0 +1,47 @@ +import Op from "./Op"; +import {clearOp, copyOp, deserializeOps} from "./Changeset"; + +/** + * Iterator over a changeset's operations. + * + * Note: This class does NOT implement the ECMAScript iterable or iterator protocols. + * + * @deprecated Use `deserializeOps` instead. + */ +export class OpIter { + private gen + private _next: IteratorResult<Op, void> + /** + * @param {string} ops - String encoding the change operations to iterate over. + */ + constructor(ops: string) { + this.gen = deserializeOps(ops); + this._next = this.gen.next(); + } + + /** + * @returns {boolean} Whether there are any remaining operations. + */ + hasNext(): boolean { + return !this._next.done; + } + + /** + * Returns the next operation object and advances the iterator. + * + * Note: This does NOT implement the ECMAScript iterator protocol. + * + * @param {Op} [opOut] - Deprecated. Operation object to recycle for the return value. + * @returns {Op} The next operation, or an operation with a falsy `opcode` property if there are + * no more operations. + */ + next(opOut: Op = new Op()): Op { + if (this.hasNext()) { + copyOp(this._next.value!, opOut); + this._next = this.gen.next(); + } else { + clearOp(opOut); + } + return opOut; + } +} diff --git a/src/static/js/SmartOpAssembler.ts b/src/static/js/SmartOpAssembler.ts new file mode 100644 index 00000000000..57f07c739a4 --- /dev/null +++ b/src/static/js/SmartOpAssembler.ts @@ -0,0 +1,115 @@ +import {MergingOpAssembler} from "./MergingOpAssembler"; +import {StringAssembler} from "./StringAssembler"; +import padutils from "./pad_utils"; +import Op from "./Op"; +import { Attribute } from "./types/Attribute"; +import AttributePool from "./AttributePool"; +import {opsFromText} from "./Changeset"; + +/** + * Creates an object that allows you to append operations (type Op) and also compresses them if + * possible. Like MergingOpAssembler, but able to produce conforming exportss from slightly looser + * input, at the cost of speed. Specifically: + * - merges consecutive operations that can be merged + * - strips final "=" + * - ignores 0-length changes + * - reorders consecutive + and - (which MergingOpAssembler doesn't do) + * + * @typedef {object} SmartOpAssembler + * @property {Function} append - + * @property {Function} appendOpWithText - + * @property {Function} clear - + * @property {Function} endDocument - + * @property {Function} getLengthChange - + * @property {Function} toString - + */ +export class SmartOpAssembler { + private minusAssem: MergingOpAssembler; + private plusAssem: MergingOpAssembler; + private keepAssem: MergingOpAssembler; + private lastOpcode: string; + private lengthChange: number; + private assem: StringAssembler; + + constructor() { + this.minusAssem = new MergingOpAssembler() + this.plusAssem = new MergingOpAssembler() + this.keepAssem = new MergingOpAssembler() + this.assem = new StringAssembler() + this.lastOpcode = '' + this.lengthChange = 0 + } + + flushKeeps = () => { + this.assem.append(this.keepAssem.toString()); + this.keepAssem.clear(); + }; + + flushPlusMinus = () => { + this.assem.append(this.minusAssem.toString()); + this.minusAssem.clear(); + this.assem.append(this.plusAssem.toString()); + this.plusAssem.clear(); + }; + + append = (op: Op) => { + if (!op.opcode) return; + if (!op.chars) return; + + if (op.opcode === '-') { + if (this.lastOpcode === '=') { + this.flushKeeps(); + } + this.minusAssem.append(op); + this.lengthChange -= op.chars; + } else if (op.opcode === '+') { + if (this.lastOpcode === '=') { + this.flushKeeps(); + } + this.plusAssem.append(op); + this.lengthChange += op.chars; + } else if (op.opcode === '=') { + if (this.lastOpcode !== '=') { + this.flushPlusMinus(); + } + this.keepAssem.append(op); + } + this.lastOpcode = op.opcode; + }; + + /** + * Generates operations from the given text and attributes. + * + * @deprecated Use `opsFromText` instead. + * @param {('-'|'+'|'=')} opcode - The operator to use. + * @param {string} text - The text to remove/add/keep. + * @param {(string|Iterable<Attribute>)} attribs - The attributes to apply to the operations. + * @param {?AttributePool.ts} pool - Attribute pool. Only required if `attribs` is an iterable of + * attribute key, value pairs. + */ + appendOpWithText = (opcode: '-'|'+'|'=', text: string, attribs: Attribute[]|string, pool?: AttributePool) => { + padutils.warnDeprecated('Changeset.smartOpAssembler().appendOpWithText() is deprecated; ' + + 'use opsFromText() instead.'); + for (const op of opsFromText(opcode, text, attribs, pool)) this.append(op); + }; + + toString = () => { + this.flushPlusMinus(); + this.flushKeeps(); + return this.assem.toString(); + }; + + clear = () => { + this.minusAssem.clear(); + this.plusAssem.clear(); + this.keepAssem.clear(); + this.assem.clear(); + this.lengthChange = 0; + }; + + endDocument = () => { + this.keepAssem.endDocument(); + }; + + getLengthChange = () => this.lengthChange; +} diff --git a/src/static/js/StringAssembler.ts b/src/static/js/StringAssembler.ts new file mode 100644 index 00000000000..b6976d21636 --- /dev/null +++ b/src/static/js/StringAssembler.ts @@ -0,0 +1,18 @@ +/** + * @returns {StringAssembler} + */ +export class StringAssembler { + private str = '' + clear = ()=> { + this.str = ''; + } + /** + * @param {string} x - + */ + append(x: string) { + this.str += String(x); + } + toString() { + return this.str + } +} diff --git a/src/static/js/StringIterator.ts b/src/static/js/StringIterator.ts new file mode 100644 index 00000000000..1633008276f --- /dev/null +++ b/src/static/js/StringIterator.ts @@ -0,0 +1,54 @@ +import {assert} from "./Changeset"; + +/** + * A custom made String Iterator + * + * @typedef {object} StringIterator + * @property {Function} newlines - + * @property {Function} peek - + * @property {Function} remaining - + * @property {Function} skip - + * @property {Function} take - + */ + +/** + * @param {string} str - String to iterate over + * @returns {StringIterator} + */ +export class StringIterator { + private curIndex: number; + private newLines: number; + private str: String + + constructor(str: string) { + this.curIndex = 0; + this.str = str + this.newLines = str.split('\n').length - 1; + } + remaining = () => this.str.length - this.curIndex; + + getnewLines = () => this.newLines; + + assertRemaining = (n: number) => { + assert(n <= this.remaining(), `!(${n} <= ${this.remaining()})`); + } + + take = (n: number) => { + this.assertRemaining(n); + const s = this.str.substring(this.curIndex, this.curIndex+n); + this.newLines -= s.split('\n').length - 1; + this.curIndex += n; + return s; + } + + peek = (n: number) => { + this.assertRemaining(n); + return this.str.substring(this.curIndex, this.curIndex+n); + } + + skip = (n: number) => { + this.assertRemaining(n); + this.curIndex += n; + } + +} diff --git a/src/static/js/TextLinesMutator.ts b/src/static/js/TextLinesMutator.ts new file mode 100644 index 00000000000..c6d3c930324 --- /dev/null +++ b/src/static/js/TextLinesMutator.ts @@ -0,0 +1,348 @@ +import {splitTextLines} from "./Changeset"; + +/** + * Class to iterate and modify texts which have several lines. It is used for applying Changesets on + * arrays of lines. + * + * Mutation operations have the same constraints as exports operations with respect to newlines, but + * not the other additional constraints (i.e. ins/del ordering, forbidden no-ops, non-mergeability, + * final newline). Can be used to mutate lists of strings where the last char of each string is not + * actually a newline, but for the purposes of N and L values, the caller should pretend it is, and + * for things to work right in that case, the input to the `insert` method should be a single line + * with no newlines. + */ +class TextLinesMutator { + private _lines: string[]; + private _curSplice: [number, number?]; + private _inSplice: boolean; + private _curLine: number; + private _curCol: number; + /** + * @param {(string[]|StringArrayLike)} lines - Lines to mutate (in place). + */ + constructor(lines: string[]) { + this._lines = lines; + /** + * this._curSplice holds values that will be passed as arguments to this._lines.splice() to + * insert, delete, or change lines: + * - this._curSplice[0] is an index into the this._lines array. + * - this._curSplice[1] is the number of lines that will be removed from the this._lines array + * starting at the index. + * - The other elements represent mutated (changed by ops) lines or new lines (added by ops) + * to insert at the index. + * + * @type {[number, number?, ...string[]?]} + */ + this._curSplice = [0, 0]; + this._inSplice = false; + // position in lines after curSplice is applied: + this._curLine = 0; + this._curCol = 0; + // invariant: if (inSplice) then (curLine is in curSplice[0] + curSplice.length - {2,3}) && + // curLine >= curSplice[0] + // invariant: if (inSplice && (curLine >= curSplice[0] + curSplice.length - 2)) then + // curCol == 0 + } + + /** + * Get a line from `lines` at given index. + * + * @param {number} idx - an index + * @returns {string} + */ + _linesGet(idx: number) { + if ('get' in this._lines) { + // @ts-ignore + return this._lines.get(idx) as string; + } else { + return this._lines[idx]; + } + } + + /** + * Return a slice from `lines`. + * + * @param {number} start - the start index + * @param {number} end - the end index + * @returns {string[]} + */ + _linesSlice(start: number | undefined, end: number | undefined) { + // can be unimplemented if removeLines's return value not needed + if (this._lines.slice) { + return this._lines.slice(start, end); + } else { + return []; + } + } + + /** + * Return the length of `lines`. + * + * @returns {number} + */ + _linesLength() { + if (typeof this._lines.length === 'number') { + return this._lines.length; + } else { + // @ts-ignore + return this._lines.length(); + } + } + + /** + * Starts a new splice. + */ + _enterSplice() { + this._curSplice[0] = this._curLine; + this._curSplice[1] = 0; + // TODO(doc) when is this the case? + // check all enterSplice calls and changes to curCol + if (this._curCol > 0) this._putCurLineInSplice(); + this._inSplice = true; + } + + /** + * Changes the lines array according to the values in curSplice and resets curSplice. Called via + * close or TODO(doc). + */ + _leaveSplice() { + this._lines.splice(...this._curSplice); + this._curSplice.length = 2; + this._curSplice[0] = this._curSplice[1] = 0; + this._inSplice = false; + } + + /** + * Indicates if curLine is already in the splice. This is necessary because the last element in + * curSplice is curLine when this line is currently worked on (e.g. when skipping or inserting). + * + * @returns {boolean} true if curLine is in splice + */ + _isCurLineInSplice() { + // The value of `this._curSplice[1]` does not matter when determining the return value because + // `this._curLine` refers to the line number *after* the splice is applied (so after those lines + // are deleted). + return this._curLine - this._curSplice[0] < this._curSplice.length - 2; + } + + /** + * Incorporates current line into the splice and marks its old position to be deleted. + * + * @returns {number} the index of the added line in curSplice + */ + _putCurLineInSplice() { + if (!this._isCurLineInSplice()) { + // @ts-ignore + this._curSplice.push(this._linesGet(this._curSplice[0] + this._curSplice[1])); + // @ts-ignore + this._curSplice[1]++; + } + // TODO should be the same as this._curSplice.length - 1 + return 2 + this._curLine - this._curSplice[0]; + } + + /** + * It will skip some newlines by putting them into the splice. + * + * @param {number} L - + * @param {boolean} includeInSplice - Indicates that attributes are present. + */ + skipLines(L: number, includeInSplice?: any) { + if (!L) return; + if (includeInSplice) { + if (!this._inSplice) this._enterSplice(); + // TODO(doc) should this count the number of characters that are skipped to check? + for (let i = 0; i < L; i++) { + this._curCol = 0; + this._putCurLineInSplice(); + this._curLine++; + } + } else { + if (this._inSplice) { + if (L > 1) { + // TODO(doc) figure out why single lines are incorporated into splice instead of ignored + this._leaveSplice(); + } else { + this._putCurLineInSplice(); + } + } + this._curLine += L; + this._curCol = 0; + } + // tests case foo in remove(), which isn't otherwise covered in current impl + } + + /** + * Skip some characters. Can contain newlines. + * + * @param {number} N - number of characters to skip + * @param {number} L - number of newlines to skip + * @param {boolean} includeInSplice - indicates if attributes are present + */ + skip(N: number, L: number, includeInSplice?: any) { + if (!N) return; + if (L) { + this.skipLines(L, includeInSplice); + } else { + if (includeInSplice && !this._inSplice) this._enterSplice(); + if (this._inSplice) { + // although the line is put into splice curLine is not increased, because + // only some chars are skipped, not the whole line + this._putCurLineInSplice(); + } + this._curCol += N; + } + } + + /** + * Remove whole lines from lines array. + * + * @param {number} L - number of lines to remove + * @returns {string} + */ + removeLines(L: number) { + if (!L) return ''; + if (!this._inSplice) this._enterSplice(); + + /** + * Gets a string of joined lines after the end of the splice. + * + * @param {number} k - number of lines + * @returns {string} joined lines + */ + const nextKLinesText = (k: number) => { + // @ts-ignore + const m = this._curSplice[0] + this._curSplice[1]; + return this._linesSlice(m, m + k).join(''); + }; + + let removed = ''; + if (this._isCurLineInSplice()) { + if (this._curCol === 0) { + // @ts-ignore + removed = this._curSplice[this._curSplice.length - 1]; + this._curSplice.length--; + removed += nextKLinesText(L - 1); + // @ts-ignore + this._curSplice[1] += L - 1; + } else { + removed = nextKLinesText(L - 1); + // @ts-ignore + this._curSplice[1] += L - 1; + const sline = this._curSplice.length - 1; + // @ts-ignore + removed = this._curSplice[sline].substring(this._curCol) + removed; + // @ts-ignore + this._curSplice[sline] = this._curSplice[sline].substring(0, this._curCol) + + // @ts-ignore + this._linesGet(this._curSplice[0] + this._curSplice[1]); + // @ts-ignore + this._curSplice[1] += 1; + } + } else { + removed = nextKLinesText(L); + this._curSplice[1]! += L; + } + return removed; + } + + /** + * Remove text from lines array. + * + * @param {number} N - characters to delete + * @param {number} L - lines to delete + * @returns {string} + */ + remove(N: number, L: any) { + if (!N) return ''; + if (L) return this.removeLines(L); + if (!this._inSplice) this._enterSplice(); + // although the line is put into splice, curLine is not increased, because + // only some chars are removed not the whole line + const sline = this._putCurLineInSplice(); + // @ts-ignore + const removed = this._curSplice[sline].substring(this._curCol, this._curCol + N); + // @ts-ignore + this._curSplice[sline] = this._curSplice[sline].substring(0, this._curCol) + + // @ts-ignore + this._curSplice[sline].substring(this._curCol + N); + return removed; + } + + /** + * Inserts text into lines array. + * + * @param {string} text - the text to insert + * @param {number} L - number of newlines in text + */ + insert(text: string | any[], L: any) { + if (!text) return; + if (!this._inSplice) this._enterSplice(); + if (L) { + // @ts-ignore + const newLines = splitTextLines(text); + if (this._isCurLineInSplice()) { + const sline = this._curSplice.length - 1; + /** @type {string} */ + const theLine = this._curSplice[sline]; + const lineCol = this._curCol; + // Insert the chars up to `curCol` and the first new line. + // @ts-ignore + this._curSplice[sline] = theLine.substring(0, lineCol) + newLines[0]; + this._curLine++; + newLines!.splice(0, 1); + // insert the remaining new lines + // @ts-ignore + this._curSplice.push(...newLines); + this._curLine += newLines!.length; + // insert the remaining chars from the "old" line (e.g. the line we were in + // when we started to insert new lines) + // @ts-ignore + this._curSplice.push(theLine.substring(lineCol)); + this._curCol = 0; // TODO(doc) why is this not set to the length of last line? + } else { + this._curSplice.push(...newLines); + this._curLine += newLines!.length; + } + } else { + // There are no additional lines. Although the line is put into splice, curLine is not + // increased because there may be more chars in the line (newline is not reached). + const sline = this._putCurLineInSplice(); + if (!this._curSplice[sline]) { + const err = new Error( + 'curSplice[sline] not populated, actual curSplice contents is ' + + `${JSON.stringify(this._curSplice)}. Possibly related to ` + + 'https://github.com/ether/etherpad-lite/issues/2802'); + console.error(err.stack || err.toString()); + } + // @ts-ignore + this._curSplice[sline] = this._curSplice[sline].substring(0, this._curCol) + text + + // @ts-ignore + this._curSplice[sline].substring(this._curCol); + this._curCol += text.length; + } + } + + /** + * Checks if curLine (the line we are in when curSplice is applied) is the last line in `lines`. + * + * @returns {boolean} indicates if there are lines left + */ + hasMore() { + let docLines = this._linesLength(); + if (this._inSplice) { + // @ts-ignore + docLines += this._curSplice.length - 2 - this._curSplice[1]; + } + return this._curLine < docLines; + } + + /** + * Closes the splice + */ + close() { + if (this._inSplice) this._leaveSplice(); + } +} + +export default TextLinesMutator diff --git a/src/static/js/ace.js b/src/static/js/ace.ts similarity index 93% rename from src/static/js/ace.js rename to src/static/js/ace.ts index b0a0425702e..4c062584cde 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. @@ -27,9 +28,10 @@ const hooks = require('./pluginfw/hooks'); const makeCSSManager = require('./cssmanager').makeCSSManager; const pluginUtils = require('./pluginfw/shared'); - +const ace2_inner = require('ep_etherpad-lite/static/js/ace2_inner') const debugLog = (...args) => {}; - +const cl_plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins') +const rJQuery = require('ep_etherpad-lite/static/js/rjquery') // The inner and outer iframe's locations are about:blank, so relative URLs are relative to that. // Firefox and Chrome seem to do what the developer intends if given a relative URL, but Safari // errors out unless given an absolute URL for a JavaScript-created element. @@ -257,19 +259,19 @@ const Ace2Editor = function () { // <head> tag addStyleTagsFor(innerDocument, includedCSS); - const requireKernel = innerDocument.createElement('script'); - requireKernel.type = 'text/javascript'; - requireKernel.src = - absUrl(`../static/js/require-kernel.js?v=${clientVars.randomVersionString}`); - innerDocument.head.appendChild(requireKernel); + //const requireKernel = innerDocument.createElement('script'); + //requireKernel.type = 'text/javascript'; + //requireKernel.src = + // absUrl(`../static/js/require-kernel.js?v=${clientVars.randomVersionString}`); + //innerDocument.head.appendChild(requireKernel); // Pre-fetch modules to improve load performance. - for (const module of ['ace2_inner', 'ace2_common']) { + /*for (const module of ['ace2_inner', 'ace2_common']) { const script = innerDocument.createElement('script'); script.type = 'text/javascript'; script.src = absUrl(`../javascripts/lib/ep_etherpad-lite/static/js/${module}.js` + `?callback=require.define&v=${clientVars.randomVersionString}`); innerDocument.head.appendChild(script); - } + }*/ const innerStyle = innerDocument.createElement('style'); innerStyle.type = 'text/css'; innerStyle.title = 'dynamicsyntax'; @@ -284,7 +286,7 @@ const Ace2Editor = function () { innerDocument.body.classList.add('innerdocbody'); innerDocument.body.setAttribute('spellcheck', 'false'); innerDocument.body.appendChild(innerDocument.createTextNode('\u00A0')); // - +/* debugLog('Ace2Editor.init() waiting for require kernel load'); await eventFired(requireKernel, 'load'); debugLog('Ace2Editor.init() require kernel loaded'); @@ -292,17 +294,16 @@ const Ace2Editor = function () { require.setRootURI(absUrl('../javascripts/src')); require.setLibraryURI(absUrl('../javascripts/lib')); require.setGlobalKeyPath('require'); - +*/ // intentially moved before requiring client_plugins to save a 307 - innerWindow.Ace2Inner = require('ep_etherpad-lite/static/js/ace2_inner'); - innerWindow.plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); - innerWindow.plugins.adoptPluginsFromAncestorsOf(innerWindow); + innerWindow.Ace2Inner = ace2_inner; + innerWindow.plugins = cl_plugins; - innerWindow.$ = innerWindow.jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery; + innerWindow.$ = innerWindow.jQuery = rJQuery.jQuery; debugLog('Ace2Editor.init() waiting for plugins'); - await new Promise((resolve, reject) => innerWindow.plugins.ensure( - (err) => err != null ? reject(err) : resolve())); + /*await new Promise((resolve, reject) => innerWindow.plugins.ensure( + (err) => err != null ? reject(err) : resolve()));*/ debugLog('Ace2Editor.init() waiting for Ace2Inner.init()'); await innerWindow.Ace2Inner.init(info, { inner: makeCSSManager(innerStyle.sheet), diff --git a/src/static/js/ace2_common.js b/src/static/js/ace2_common.ts similarity index 76% rename from src/static/js/ace2_common.js rename to src/static/js/ace2_common.ts index c1dab5cfd8b..0a5f308e6a2 100644 --- a/src/static/js/ace2_common.js +++ b/src/static/js/ace2_common.ts @@ -6,6 +6,8 @@ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED */ +import {MapArrayType} from "../../node/types/MapType"; + /** * Copyright 2009 Google Inc. * @@ -22,11 +24,13 @@ * limitations under the License. */ -const isNodeText = (node) => (node.nodeType === 3); +export const isNodeText = (node: { + nodeType: number +}) => (node.nodeType === 3); -const getAssoc = (obj, name) => obj[`_magicdom_${name}`]; +export const getAssoc = (obj: MapArrayType<any>, name: string) => obj[`_magicdom_${name}`]; -const setAssoc = (obj, name, value) => { +export const setAssoc = (obj: MapArrayType<any>, name: string, value: string) => { // note that in IE designMode, properties of a node can get // copied to new nodes that are spawned during editing; also, // properties representable in HTML text can survive copy-and-paste @@ -38,7 +42,7 @@ const setAssoc = (obj, name, value) => { // between false and true, a number between 0 and numItems inclusive. -const binarySearch = (numItems, func) => { +export const binarySearch = (numItems: number, func: (num: number)=>boolean) => { if (numItems < 1) return 0; if (func(0)) return 0; if (!func(numItems - 1)) return numItems; @@ -52,17 +56,10 @@ const binarySearch = (numItems, func) => { return high; }; -const binarySearchInfinite = (expectedLength, func) => { +export const binarySearchInfinite = (expectedLength: number, func: (num: number)=>boolean) => { let i = 0; while (!func(i)) i += expectedLength; return binarySearch(i, func); }; -const noop = () => {}; - -exports.isNodeText = isNodeText; -exports.getAssoc = getAssoc; -exports.setAssoc = setAssoc; -exports.binarySearch = binarySearch; -exports.binarySearchInfinite = binarySearchInfinite; -exports.noop = noop; +export const noop = () => {}; diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.ts similarity index 95% rename from src/static/js/ace2_inner.js rename to src/static/js/ace2_inner.ts index e64c8695d16..0dce038a89c 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.ts @@ -1,4 +1,5 @@ -'use strict'; +// @ts-nocheck +import {Builder} from "./Builder"; /** * Copyright 2009 Google Inc. @@ -18,31 +19,34 @@ */ let documentAttributeManager; -const AttributeMap = require('./AttributeMap'); +import AttributeMap from './AttributeMap'; const browser = require('./vendors/browser'); -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' const Ace2Common = require('./ace2_common'); const $ = require('./rjquery').$; +import {characterRangeFollow, checkRep, cloneAText, compose, deserializeOps, filterAttribNumbers, inverse, isIdentity, makeAText, makeAttribution, mapAttribNumbers, moveOpsToNewPool, mutateAttributionLines, mutateTextLines, oldLen, opsFromAText, pack, splitAttributionLines} from './Changeset' + const isNodeText = Ace2Common.isNodeText; const getAssoc = Ace2Common.getAssoc; const setAssoc = Ace2Common.setAssoc; const noop = Ace2Common.noop; const hooks = require('./pluginfw/hooks'); +import SkipList from "./skiplist"; +import Scroll from './scroll' +import AttribPool from './AttributePool' +import {SmartOpAssembler} from "./SmartOpAssembler"; +import Op from "./Op"; +import {buildKeepRange, buildKeepToStartOfRange, buildRemoveRange} from './ChangesetUtils' function Ace2Inner(editorInfo, cssManagers) { const makeChangesetTracker = require('./changesettracker').makeChangesetTracker; const colorutils = require('./colorutils').colorutils; const makeContentCollector = require('./contentcollector').makeContentCollector; const domline = require('./domline').domline; - const AttribPool = require('./AttributePool'); - const Changeset = require('./Changeset'); - const ChangesetUtils = require('./ChangesetUtils'); const linestylefilter = require('./linestylefilter').linestylefilter; - const SkipList = require('./skiplist'); const undoModule = require('./undomodule').undoModule; const AttributeManager = require('./AttributeManager'); - const Scroll = require('./scroll'); const DEBUG = false; const THE_TAB = ' '; // 4 @@ -54,13 +58,16 @@ function Ace2Inner(editorInfo, cssManagers) { let thisAuthor = ''; let disposed = false; + const outerWin = document.getElementsByName("ace_outer")[0] + const targetDoc = outerWin.contentWindow.document.getElementsByName("ace_inner")[0].contentWindow.document + const targetBody = targetDoc.body const focus = () => { - window.focus(); + targetBody.focus(); }; - const outerWin = window.parent; - const outerDoc = outerWin.document; + const outerDoc = outerWin.contentWindow.document; + const sideDiv = outerDoc.getElementById('sidediv'); const lineMetricsDiv = outerDoc.getElementById('linemetricsdiv'); const sideDivInner = outerDoc.getElementById('sidedivinner'); @@ -74,7 +81,7 @@ function Ace2Inner(editorInfo, cssManagers) { }; appendNewSideDivLine(); - const scroll = Scroll.init(outerWin); + const scroll = new Scroll(outerWin); let outsideKeyDown = noop; let outsideKeyPress = (e) => true; @@ -170,9 +177,9 @@ function Ace2Inner(editorInfo, cssManagers) { // CCCCCCCCCCCCCCCCCCCC\n // CCCC\n // end[0]: <CCC end[1] CCC>-------\n - const builder = Changeset.builder(rep.lines.totalWidth()); - ChangesetUtils.buildKeepToStartOfRange(rep, builder, start); - ChangesetUtils.buildRemoveRange(rep, builder, start, end); + const builder = new Builder(rep.lines.totalWidth()); + buildKeepToStartOfRange(rep, builder, start); + buildRemoveRange(rep, builder, start, end); builder.insert(newText, [ ['author', thisAuthor], ], rep.apool); @@ -415,7 +422,7 @@ function Ace2Inner(editorInfo, cssManagers) { const setWraps = (newVal) => { doesWrap = newVal; - document.body.classList.toggle('doesWrap', doesWrap); + targetBody.classList.toggle('doesWrap', doesWrap); scheduler.setTimeout(() => { inCallStackIfNecessary('setWraps', () => { fastIncorp(7); @@ -445,7 +452,7 @@ function Ace2Inner(editorInfo, cssManagers) { }; const setTextFace = (face) => { - document.body.style.fontFamily = face; + targetBody.style.fontFamily = face; lineMetricsDiv.style.fontFamily = face; }; @@ -456,8 +463,8 @@ function Ace2Inner(editorInfo, cssManagers) { const setEditable = (newVal) => { isEditable = newVal; - document.body.contentEditable = isEditable ? 'true' : 'false'; - document.body.classList.toggle('static', !isEditable); + targetBody.contentEditable = isEditable ? 'true' : 'false'; + targetBody.classList.toggle('static', !isEditable); }; const enforceEditability = () => setEditable(isEditable); @@ -480,6 +487,7 @@ function Ace2Inner(editorInfo, cssManagers) { newText = `${lines.join('\n')}\n`; } + inCallStackIfNecessary(`importText${undoable ? 'Undoable' : ''}`, () => { setDocText(newText); }); @@ -490,10 +498,10 @@ function Ace2Inner(editorInfo, cssManagers) { }; const importAText = (atext, apoolJsonObj, undoable) => { - atext = Changeset.cloneAText(atext); + atext = cloneAText(atext); if (apoolJsonObj) { const wireApool = (new AttribPool()).fromJsonable(apoolJsonObj); - atext.attribs = Changeset.moveOpsToNewPool(atext.attribs, wireApool, rep.apool); + atext.attribs = moveOpsToNewPool(atext.attribs, wireApool, rep.apool); } inCallStackIfNecessary(`importText${undoable ? 'Undoable' : ''}`, () => { setDocAText(atext); @@ -522,18 +530,18 @@ function Ace2Inner(editorInfo, cssManagers) { const numLines = rep.lines.length(); const upToLastLine = rep.lines.offsetOfIndex(numLines - 1); const lastLineLength = rep.lines.atIndex(numLines - 1).text.length; - const assem = Changeset.smartOpAssembler(); - const o = new Changeset.Op('-'); + const assem = new SmartOpAssembler(); + const o = new Op('-'); o.chars = upToLastLine; o.lines = numLines - 1; assem.append(o); o.chars = lastLineLength; o.lines = 0; assem.append(o); - for (const op of Changeset.opsFromAText(atext)) assem.append(op); + for (const op of opsFromAText(atext)) assem.append(op); const newLen = oldLen + assem.getLengthChange(); - const changeset = Changeset.checkRep( - Changeset.pack(oldLen, newLen, assem.toString(), atext.text.slice(0, -1))); + const changeset = checkRep( + pack(oldLen, newLen, assem.toString(), atext.text.slice(0, -1))); performDocumentApplyChangeset(changeset); performSelectionChange( @@ -547,7 +555,7 @@ function Ace2Inner(editorInfo, cssManagers) { }; const setDocText = (text) => { - setDocAText(Changeset.makeAText(text)); + setDocAText(makeAText(text)); }; const getDocText = () => { @@ -640,8 +648,8 @@ function Ace2Inner(editorInfo, cssManagers) { // These properties are exposed const setters = { wraps: setWraps, - showsauthorcolors: (val) => document.body.classList.toggle('authorColors', !!val), - showsuserselections: (val) => document.body.classList.toggle('userSelections', !!val), + showsauthorcolors: (val) => targetBody.classList.toggle('authorColors', !!val), + showsuserselections: (val) => targetBody.classList.toggle('userSelections', !!val), showslinenumbers: (value) => { hasLineNumbers = !!value; sideDiv.parentNode.classList.toggle('line-numbers-hidden', !hasLineNumbers); @@ -654,8 +662,8 @@ function Ace2Inner(editorInfo, cssManagers) { styled: setStyled, textface: setTextFace, rtlistrue: (value) => { - document.body.classList.toggle('rtl', value); - document.body.classList.toggle('ltr', !value); + targetBody.classList.toggle('rtl', value); + targetBody.classList.toggle('ltr', !value); document.documentElement.dir = value ? 'rtl' : 'ltr'; }, }; @@ -894,11 +902,11 @@ function Ace2Inner(editorInfo, cssManagers) { clearObservedChanges(); const getCleanNodeByKey = (key) => { - let n = document.getElementById(key); + let n = targetDoc.getElementById(key); // copying and pasting can lead to duplicate ids while (n && isNodeDirty(n)) { n.id = ''; - n = document.getElementById(key); + n = targetDoc.getElementById(key); } return n; }; @@ -980,11 +988,11 @@ function Ace2Inner(editorInfo, cssManagers) { const observeSuspiciousNodes = () => { // inspired by Firefox bug #473255, where pasting formatted text // causes the cursor to jump away, making the new HTML never found. - if (document.body.getElementsByTagName) { - const elts = document.body.getElementsByTagName('style'); + if (targetBody.getElementsByTagName) { + const elts = targetBody.getElementsByTagName('style'); for (const elt of elts) { const n = topLevel(elt); - if (n && n.parentNode === document.body) { + if (n && n.parentNode === targetBody) { observeChangesAroundNode(n); } } @@ -999,8 +1007,8 @@ function Ace2Inner(editorInfo, cssManagers) { if (DEBUG && window.DONT_INCORP || window.DEBUG_DONT_INCORP) return false; // returns true if dom changes were made - if (!document.body.firstChild) { - document.body.innerHTML = '<div><!-- --></div>'; + if (!targetBody.firstChild) { + targetBody.innerHTML = '<div><!-- --></div>'; } observeChangesAroundSelection(); @@ -1022,7 +1030,7 @@ function Ace2Inner(editorInfo, cssManagers) { j++; } if (!dirtyRangesCheckOut) { - for (const bodyNode of document.body.childNodes) { + for (const bodyNode of targetBody.childNodes) { if ((bodyNode.tagName) && ((!bodyNode.id) || (!rep.lines.containsKey(bodyNode.id)))) { observeChangesAroundNode(bodyNode); } @@ -1044,11 +1052,11 @@ function Ace2Inner(editorInfo, cssManagers) { const range = dirtyRanges[i]; a = range[0]; b = range[1]; - let firstDirtyNode = (((a === 0) && document.body.firstChild) || + let firstDirtyNode = (((a === 0) && targetBody.firstChild) || getCleanNodeByKey(rep.lines.atIndex(a - 1).key).nextSibling); firstDirtyNode = (firstDirtyNode && isNodeDirty(firstDirtyNode) && firstDirtyNode); - let lastDirtyNode = (((b === rep.lines.length()) && document.body.lastChild) || + let lastDirtyNode = (((b === rep.lines.length()) && targetBody.lastChild) || getCleanNodeByKey(rep.lines.atIndex(b).key).previousSibling); lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode); @@ -1135,7 +1143,7 @@ function Ace2Inner(editorInfo, cssManagers) { callstack: currentCallStack, editorInfo, rep, - root: document.body, + root: targetBody, point: selection.startPoint, documentAttributeManager, }); @@ -1147,7 +1155,7 @@ function Ace2Inner(editorInfo, cssManagers) { callstack: currentCallStack, editorInfo, rep, - root: document.body, + root: targetBody, point: selection.endPoint, documentAttributeManager, }); @@ -1227,9 +1235,9 @@ function Ace2Inner(editorInfo, cssManagers) { info.prepareForAdd(); entry.lineMarker = info.lineMarker; if (!nodeToAddAfter) { - document.body.insertBefore(node, document.body.firstChild); + targetBody.insertBefore(node, targetBody.firstChild); } else { - document.body.insertBefore(node, nodeToAddAfter.nextSibling); + targetBody.insertBefore(node, nodeToAddAfter.nextSibling); } nodeToAddAfter = node; info.notifyAdded(); @@ -1266,7 +1274,7 @@ function Ace2Inner(editorInfo, cssManagers) { if (shouldIndent && /[[(:{]\s*$/.exec(prevLineText)) { theIndent += THE_TAB; } - const cs = Changeset.builder(rep.lines.totalWidth()).keep( + const cs = new Builder(rep.lines.totalWidth()).keep( rep.lines.offsetOfIndex(lineNum), lineNum).insert( theIndent, [ ['author', thisAuthor], @@ -1326,7 +1334,7 @@ function Ace2Inner(editorInfo, cssManagers) { // Turn DOM node selection into [line,char] selection. // This method has to work when the DOM is not pristine, // assuming the point is not in a dirty node. - if (point.node === document.body) { + if (point.node === targetBody) { if (point.index === 0) { return [0, 0]; } else { @@ -1345,7 +1353,7 @@ function Ace2Inner(editorInfo, cssManagers) { col = nodeText(n).length; } let parNode, prevSib; - while ((parNode = n.parentNode) !== document.body) { + while ((parNode = n.parentNode) !== targetBody) { if ((prevSib = n.previousSibling)) { n = prevSib; col += nodeText(n).length; @@ -1398,7 +1406,7 @@ function Ace2Inner(editorInfo, cssManagers) { insertDomLines(nodeToAddAfter, lineEntries.map((entry) => entry.domInfo)); for (const k of keysToDelete) { - const n = document.getElementById(k); + const n = targetDoc.getElementById(k); n.parentNode.removeChild(n); } @@ -1418,7 +1426,7 @@ function Ace2Inner(editorInfo, cssManagers) { const selStartChar = rep.lines.offsetOfIndex(rep.selStart[0]) + rep.selStart[1]; const selEndChar = rep.lines.offsetOfIndex(rep.selEnd[0]) + rep.selEnd[1]; const result = - Changeset.characterRangeFollow(changes, selStartChar, selEndChar, insertsAfterSelection); + characterRangeFollow(changes, selStartChar, selEndChar, insertsAfterSelection); requiredSelectionSetting = [result[0], result[1], rep.selFocusAtStart]; } @@ -1430,7 +1438,7 @@ function Ace2Inner(editorInfo, cssManagers) { length: () => rep.lines.length(), }; - Changeset.mutateTextLines(changes, linesMutatee); + mutateTextLines(changes, linesMutatee); if (requiredSelectionSetting) { performSelectionChange( @@ -1441,10 +1449,10 @@ function Ace2Inner(editorInfo, cssManagers) { }; const doRepApplyChangeset = (changes, insertsAfterSelection) => { - Changeset.checkRep(changes); + checkRep(changes); - if (Changeset.oldLen(changes) !== rep.alltext.length) { - const errMsg = `${Changeset.oldLen(changes)}/${rep.alltext.length}`; + if (oldLen(changes) !== rep.alltext.length) { + const errMsg = `${oldLen(changes)}/${rep.alltext.length}`; throw new Error(`doRepApplyChangeset length mismatch: ${errMsg}`); } @@ -1453,10 +1461,10 @@ function Ace2Inner(editorInfo, cssManagers) { if (!editEvent.changeset) { editEvent.changeset = changes; } else { - editEvent.changeset = Changeset.compose(editEvent.changeset, changes, rep.apool); + editEvent.changeset = compose(editEvent.changeset, changes, rep.apool); } } else { - const inverseChangeset = Changeset.inverse(changes, { + const inverseChangeset = inverse(changes, { get: (i) => `${rep.lines.atIndex(i).text}\n`, length: () => rep.lines.length(), }, rep.alines, rep.apool); @@ -1464,11 +1472,11 @@ function Ace2Inner(editorInfo, cssManagers) { if (!editEvent.backset) { editEvent.backset = inverseChangeset; } else { - editEvent.backset = Changeset.compose(inverseChangeset, editEvent.backset, rep.apool); + editEvent.backset = compose(inverseChangeset, editEvent.backset, rep.apool); } } - Changeset.mutateAttributionLines(changes, rep.alines, rep.apool); + mutateAttributionLines(changes, rep.alines, rep.apool); if (changesetTracker.isTracking()) { changesetTracker.composeUserChangeset(changes); @@ -1577,7 +1585,7 @@ function Ace2Inner(editorInfo, cssManagers) { let hasAttrib = true; let indexIntoLine = 0; - for (const op of Changeset.deserializeOps(rep.alines[lineNum])) { + for (const op of deserializeOps(rep.alines[lineNum])) { const opStartInLine = indexIntoLine; const opEndInLine = opStartInLine + op.chars; if (!hasIt(op.attribs)) { @@ -1622,7 +1630,7 @@ function Ace2Inner(editorInfo, cssManagers) { if (n === selEndLine) { selectionEndInLine = rep.selEnd[1]; } - for (const op of Changeset.deserializeOps(rep.alines[n])) { + for (const op of deserializeOps(rep.alines[n])) { const opStartInLine = indexIntoLine; const opEndInLine = opStartInLine + op.chars; if (!hasIt(op.attribs)) { @@ -1740,7 +1748,7 @@ function Ace2Inner(editorInfo, cssManagers) { const spliceStartLineStart = rep.lines.offsetOfIndex(spliceStartLine); const startBuilder = () => { - const builder = Changeset.builder(oldLen); + const builder = new Builder(oldLen); builder.keep(spliceStartLineStart, spliceStartLine); builder.keep(spliceStart - spliceStartLineStart); return builder; @@ -1750,7 +1758,7 @@ function Ace2Inner(editorInfo, cssManagers) { let textIndex = 0; const newTextStart = commonStart; const newTextEnd = newText.length - commonEnd - (shiftFinalNewlineToBeforeNewText ? 1 : 0); - for (const op of Changeset.deserializeOps(attribs)) { + for (const op of deserializeOps(attribs)) { const nextIndex = textIndex + op.chars; if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) { func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); @@ -1768,7 +1776,7 @@ function Ace2Inner(editorInfo, cssManagers) { // changeset the applies the styles found in the DOM. // This allows us to incorporate, e.g., Safari's native "unbold". const incorpedAttribClearer = cachedStrFunc( - (oldAtts) => Changeset.mapAttribNumbers(oldAtts, (n) => { + (oldAtts) => mapAttribNumbers(oldAtts, (n) => { const k = rep.apool.getAttribKey(n); if (isStyleAttribute(k)) { return rep.apool.putAttrib([k, '']); @@ -1794,7 +1802,7 @@ function Ace2Inner(editorInfo, cssManagers) { }); const styler = builder2.toString(); - theChangeset = Changeset.compose(clearer, styler, rep.apool); + theChangeset = compose(clearer, styler, rep.apool); } else { const builder = startBuilder(); @@ -1864,7 +1872,7 @@ function Ace2Inner(editorInfo, cssManagers) { const attribRuns = (attribs) => { const lengs = []; const atts = []; - for (const op of Changeset.deserializeOps(attribs)) { + for (const op of deserializeOps(attribs)) { lengs.push(op.chars); atts.push(op.attribs); } @@ -1893,8 +1901,8 @@ function Ace2Inner(editorInfo, cssManagers) { const newLen = newText.length; const minLen = Math.min(oldLen, newLen); - const oldARuns = attribRuns(Changeset.filterAttribNumbers(oldAttribs, incorpedAttribFilter)); - const newARuns = attribRuns(Changeset.filterAttribNumbers(newAttribs, incorpedAttribFilter)); + const oldARuns = attribRuns(filterAttribNumbers(oldAttribs, incorpedAttribFilter)); + const newARuns = attribRuns(filterAttribNumbers(newAttribs, incorpedAttribFilter)); let commonStart = 0; const oldStartIter = attribIterator(oldARuns, false); @@ -2087,7 +2095,7 @@ function Ace2Inner(editorInfo, cssManagers) { const a = cleanNodeForIndex(i - 1); const b = cleanNodeForIndex(i); if ((!a) || (!b)) return false; // violates precondition - if ((a === true) && (b === true)) return !document.body.firstChild; + if ((a === true) && (b === true)) return !targetBody.firstChild; if ((a === true) && b.previousSibling) return false; if ((b === true) && a.nextSibling) return false; if ((a === true) || (b === true)) return true; @@ -2232,7 +2240,7 @@ function Ace2Inner(editorInfo, cssManagers) { }; const isNodeDirty = (n) => { - if (n.parentNode !== document.body) return true; + if (n.parentNode !== targetBody) return true; const data = getAssoc(n, 'dirtiness'); if (!data) return true; if (n.id !== data.nodeId) return true; @@ -2248,7 +2256,7 @@ function Ace2Inner(editorInfo, cssManagers) { const isLink = (n) => (n.tagName || '').toLowerCase() === 'a' && n.href; // only want to catch left-click - if ((!evt.ctrlKey) && (evt.button !== 2) && (evt.button !== 3)) { + if ((evt.button !== 2) && (evt.button !== 3)) { // find A tag with HREF let n = evt.target; while (n && n.parentNode && !isLink(n)) { @@ -2257,6 +2265,7 @@ function Ace2Inner(editorInfo, cssManagers) { if (n && isLink(n)) { try { window.open(n.href, '_blank', 'noopener,noreferrer'); + if (evt.ctrlKey) window.focus(); } catch (e) { // absorb "user canceled" error in IE for certain prompts } @@ -2291,7 +2300,7 @@ function Ace2Inner(editorInfo, cssManagers) { // 3-renumber every list item of the same level from the beginning, level 1 // IMPORTANT: never skip a level because there imbrication may be arbitrary - const builder = Changeset.builder(rep.lines.totalWidth()); + const builder = new Builder(rep.lines.totalWidth()); let loc = [0, 0]; const applyNumberList = (line, level) => { // init @@ -2306,8 +2315,8 @@ function Ace2Inner(editorInfo, cssManagers) { if (isNaN(curLevel) || listType[0] === 'indent') { return line; } else if (curLevel === level) { - ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 0])); - ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 1]), [ + buildKeepRange(rep, builder, loc, (loc = [line, 0])); + buildKeepRange(rep, builder, loc, (loc = [line, 1]), [ ['start', position], ], rep.apool); @@ -2324,7 +2333,7 @@ function Ace2Inner(editorInfo, cssManagers) { applyNumberList(lineNum, 1); const cs = builder.toString(); - if (!Changeset.isIdentity(cs)) { + if (!isIdentity(cs)) { performDocumentApplyChangeset(cs); } @@ -2612,7 +2621,7 @@ function Ace2Inner(editorInfo, cssManagers) { // TODO: There appears to be a race condition or so. const authorIds = new Set(); if (alineAttrs) { - for (const op of Changeset.deserializeOps(alineAttrs)) { + for (const op of deserializeOps(alineAttrs)) { const authorId = AttributeMap.fromString(op.attribs, apool).get('author'); if (authorId) authorIds.add(authorId); } @@ -2855,7 +2864,7 @@ function Ace2Inner(editorInfo, cssManagers) { updateBrowserSelectionFromRep(); // get the current caret selection, can't use rep. here because that only gives // us the start position not the current - const myselection = document.getSelection(); + const myselection = targetDoc.getSelection(); // get the carets selection offset in px IE 214 let caretOffsetTop = myselection.focusNode.parentNode.offsetTop || myselection.focusNode.offsetTop; @@ -2969,13 +2978,13 @@ function Ace2Inner(editorInfo, cssManagers) { // with background doesn't seem to show up... if (isNodeText(p.node) && p.index === p.maxIndex) { let n = p.node; - while (!n.nextSibling && n !== document.body && n.parentNode !== document.body) { + while (!n.nextSibling && n !== targetBody && n.parentNode !== targetBody) { n = n.parentNode; } if (n.nextSibling && !(typeof n.nextSibling.tagName === 'string' && n.nextSibling.tagName.toLowerCase() === 'br') && - n !== p.node && n !== document.body && n.parentNode !== document.body) { + n !== p.node && n !== targetBody && n.parentNode !== targetBody) { // found a parent, go to next node and dive in p.node = n.nextSibling; p.maxIndex = nodeMaxIndex(p.node); @@ -3002,7 +3011,7 @@ function Ace2Inner(editorInfo, cssManagers) { }; } }; - const browserSelection = window.getSelection(); + const browserSelection = targetDoc.getSelection(); if (browserSelection) { browserSelection.removeAllRanges(); if (selection) { @@ -3077,7 +3086,7 @@ function Ace2Inner(editorInfo, cssManagers) { // each of which has node (a magicdom node), index, and maxIndex. If the node // is a text node, maxIndex is the length of the text; else maxIndex is 1. // index is between 0 and maxIndex, inclusive. - const browserSelection = window.getSelection(); + const browserSelection = targetDoc.getSelection(); if (!browserSelection || browserSelection.type === 'None' || browserSelection.rangeCount === 0) { return null; @@ -3095,7 +3104,7 @@ function Ace2Inner(editorInfo, cssManagers) { if (!isInBody(container)) { // command-click in Firefox selects whole document, HEAD and BODY! return { - node: document.body, + node: targetBody, index: 0, maxIndex: 1, }; @@ -3145,7 +3154,7 @@ function Ace2Inner(editorInfo, cssManagers) { browserSelection.anchorOffset === range.endOffset, }; - if (selection.startPoint.node.ownerDocument !== window.document) { + if (selection.startPoint.node.ownerDocument !== targetDoc) { return null; } @@ -3180,17 +3189,17 @@ function Ace2Inner(editorInfo, cssManagers) { editorInfo.ace_getInInternationalComposition = () => inInternationalComposition; const bindTheEventHandlers = () => { - $(document).on('keydown', handleKeyEvent); - $(document).on('keypress', handleKeyEvent); - $(document).on('keyup', handleKeyEvent); - $(document).on('click', handleClick); + $(targetDoc).on('keydown', handleKeyEvent); + $(targetDoc).on('keypress', handleKeyEvent); + $(targetDoc).on('keyup', handleKeyEvent); + $(targetDoc).on('click', handleClick); // dropdowns on edit bar need to be closed on clicks on both pad inner and pad outer $(outerDoc).on('click', hideEditBarDropdowns); // If non-nullish, pasting on a link should be suppressed. let suppressPasteOnLink = null; - $(document.body).on('auxclick', (e) => { + $(targetBody).on('auxclick', (e) => { if (e.originalEvent.button === 1 && (e.target.a || e.target.localName === 'a')) { // The user middle-clicked on a link. Usually users do this to open a link in a new tab, but // in X11 (Linux) this will instead paste the contents of the primary selection at the mouse @@ -3212,7 +3221,7 @@ function Ace2Inner(editorInfo, cssManagers) { } }); - $(document.body).on('paste', (e) => { + $(targetBody).on('paste', (e) => { if (suppressPasteOnLink != null && (e.target.a || e.target.localName === 'a')) { scheduler.clearTimeout(suppressPasteOnLink); suppressPasteOnLink = null; @@ -3232,7 +3241,7 @@ function Ace2Inner(editorInfo, cssManagers) { // We reference document here, this is because if we don't this will expose a bug // in Google Chrome. This bug will cause the last character on the last line to // not fire an event when dropped into.. - $(document).on('drop', (e) => { + $(targetBody).on('drop', (e) => { if (e.target.a || e.target.localName === 'a') { e.preventDefault(); } @@ -3250,7 +3259,7 @@ function Ace2Inner(editorInfo, cssManagers) { const lineAfterSelection = lastLineSelected.nextSibling; const neighbor = lineBeforeSelection || lineAfterSelection; - neighbor.appendChild(document.createElement('style')); + neighbor.appendChild(targetDoc.createElement('style')); } // Call drop hook @@ -3262,10 +3271,10 @@ function Ace2Inner(editorInfo, cssManagers) { }); }); - $(document.documentElement).on('compositionstart', () => { + $(targetDoc.documentElement).on('compositionstart', () => { if (inInternationalComposition) return; inInternationalComposition = new Promise((resolve) => { - $(document.documentElement).one('compositionend', () => { + $(targetDoc.documentElement).one('compositionend', () => { inInternationalComposition = null; resolve(); }); @@ -3274,8 +3283,8 @@ function Ace2Inner(editorInfo, cssManagers) { }; const topLevel = (n) => { - if ((!n) || n === document.body) return null; - while (n.parentNode !== document.body) { + if ((!n) || n === targetBody) return null; + while (n.parentNode !== targetBody) { n = n.parentNode; } return n; @@ -3435,10 +3444,10 @@ function Ace2Inner(editorInfo, cssManagers) { // but as it's non-text type the line-height/margins might not be present and it // could be that this breaks a theme that has a different default line height.. // So instead of using an integer here we get the value from the Editor CSS. - const innerdocbodyStyles = getComputedStyle(document.body); + const innerdocbodyStyles = getComputedStyle(targetBody); const defaultLineHeight = parseInt(innerdocbodyStyles['line-height']); - for (const docLine of document.body.children) { + for (const docLine of targetBody.children) { let h; const nextDocLine = docLine.nextElementSibling; if (nextDocLine) { @@ -3449,7 +3458,7 @@ function Ace2Inner(editorInfo, cssManagers) { // included on the first line. The default stylesheet doesn't add // extra margins/padding, but plugins might. h = nextDocLine.offsetTop - parseInt( - window.getComputedStyle(document.body) + window.getComputedStyle(targetBody) .getPropertyValue('padding-top').split('px')[0]); } else { h = nextDocLine.offsetTop - docLine.offsetTop; @@ -3495,20 +3504,20 @@ function Ace2Inner(editorInfo, cssManagers) { this.init = async () => { await $.ready; inCallStack('setup', () => { - if (browser.firefox) $(document.body).addClass('mozilla'); - if (browser.safari) $(document.body).addClass('safari'); - document.body.classList.toggle('authorColors', true); - document.body.classList.toggle('doesWrap', doesWrap); + if (browser.firefox) $(targetBody).addClass('mozilla'); + if (browser.safari) $(targetBody).addClass('safari'); + targetBody.classList.toggle('authorColors', true); + targetBody.classList.toggle('doesWrap', doesWrap); enforceEditability(); // set up dom and rep - while (document.body.firstChild) document.body.removeChild(document.body.firstChild); + while (targetBody.firstChild) targetBody.removeChild(targetBody.firstChild); const oneEntry = createDomLineEntry(''); doRepLineSplice(0, rep.lines.length(), [oneEntry]); insertDomLines(null, [oneEntry.domInfo]); - rep.alines = Changeset.splitAttributionLines( - Changeset.makeAttribution('\n'), '\n'); + rep.alines = splitAttributionLines( + makeAttribution('\n'), '\n'); bindTheEventHandlers(); }); diff --git a/src/static/js/attributes.js b/src/static/js/attributes.ts similarity index 77% rename from src/static/js/attributes.js rename to src/static/js/attributes.ts index 4ab34701957..b164f8759c4 100644 --- a/src/static/js/attributes.js +++ b/src/static/js/attributes.ts @@ -17,6 +17,9 @@ * @typedef {string} AttributeString */ +import AttributePool from "./AttributePool"; +import {Attribute} from "./types/Attribute"; + /** * Converts an attribute string into a sequence of attribute identifier numbers. * @@ -28,7 +31,7 @@ * appear in `str`. * @returns {Generator<number>} */ -exports.decodeAttribString = function* (str) { +export const decodeAttribString = function* (str: string): Generator<number> { const re = /\*([0-9a-z]+)|./gy; let match; while ((match = re.exec(str)) != null) { @@ -38,7 +41,7 @@ exports.decodeAttribString = function* (str) { } }; -const checkAttribNum = (n) => { +const checkAttribNum = (n: number|object) => { if (typeof n !== 'number') throw new TypeError(`not a number: ${n}`); if (n < 0) throw new Error(`attribute number is negative: ${n}`); if (n !== Math.trunc(n)) throw new Error(`attribute number is not an integer: ${n}`); @@ -50,7 +53,7 @@ const checkAttribNum = (n) => { * @param {Iterable<number>} attribNums - Sequence of attribute numbers. * @returns {AttributeString} */ -exports.encodeAttribString = (attribNums) => { +export const encodeAttribString = (attribNums: Iterable<number>): string => { let str = ''; for (const n of attribNums) { checkAttribNum(n); @@ -67,7 +70,7 @@ exports.encodeAttribString = (attribNums) => { * @yields {Attribute} The identified attributes, in the same order as `attribNums`. * @returns {Generator<Attribute>} */ -exports.attribsFromNums = function* (attribNums, pool) { +export const attribsFromNums = function* (attribNums: Iterable<number>, pool: AttributePool): Generator<Attribute> { for (const n of attribNums) { checkAttribNum(n); const attrib = pool.getAttrib(n); @@ -87,7 +90,7 @@ exports.attribsFromNums = function* (attribNums, pool) { * @yields {number} The attribute number of each attribute in `attribs`, in order. * @returns {Generator<number>} */ -exports.attribsToNums = function* (attribs, pool) { +export const attribsToNums = function* (attribs: Iterable<Attribute>, pool: AttributePool) { for (const attrib of attribs) yield pool.putAttrib(attrib); }; @@ -102,8 +105,8 @@ exports.attribsToNums = function* (attribs, pool) { * @yields {Attribute} The attributes identified in `str`, in order. * @returns {Generator<Attribute>} */ -exports.attribsFromString = function* (str, pool) { - yield* exports.attribsFromNums(exports.decodeAttribString(str), pool); +export const attribsFromString = function* (str: string, pool: AttributePool): Generator<Attribute> { + yield* attribsFromNums(decodeAttribString(str), pool); }; /** @@ -116,8 +119,8 @@ exports.attribsFromString = function* (str, pool) { * @param {AttributePool} pool - Attribute pool. * @returns {AttributeString} */ -exports.attribsToString = - (attribs, pool) => exports.encodeAttribString(exports.attribsToNums(attribs, pool)); +export const attribsToString = + (attribs: Iterable<Attribute>, pool: AttributePool): string => encodeAttribString(attribsToNums(attribs, pool)); /** * Sorts the attributes in canonical order. The order of entries with the same attribute name is @@ -126,5 +129,14 @@ exports.attribsToString = * @param {Attribute[]} attribs - Attributes to sort in place. * @returns {Attribute[]} `attribs` (for chaining). */ -exports.sort = - (attribs) => attribs.sort(([keyA], [keyB]) => (keyA > keyB ? 1 : 0) - (keyA < keyB ? 1 : 0)); +export const sort = (attribs: Attribute[]): Attribute[] => attribs.sort(([keyA], [keyB]) => (keyA > keyB ? 1 : 0) - (keyA < keyB ? 1 : 0)); + +export default { + decodeAttribString, + encodeAttribString, + attribsFromNums, + attribsToNums, + attribsFromString, + attribsToString, + sort, +} diff --git a/src/static/js/basic_error_handler.js b/src/static/js/basic_error_handler.ts similarity index 99% rename from src/static/js/basic_error_handler.js rename to src/static/js/basic_error_handler.ts index ab400aa8a83..ce8c4735348 100644 --- a/src/static/js/basic_error_handler.js +++ b/src/static/js/basic_error_handler.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt Apache-2.0 /* Copyright 2021 Richard Hansen <rhansen@rhansen.org> */ diff --git a/src/static/js/broadcast.js b/src/static/js/broadcast.ts similarity index 94% rename from src/static/js/broadcast.js rename to src/static/js/broadcast.ts index cd2211ae1f8..85ccdb0ff6d 100644 --- a/src/static/js/broadcast.js +++ b/src/static/js/broadcast.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -24,14 +25,17 @@ const makeCSSManager = require('./cssmanager').makeCSSManager; const domline = require('./domline').domline; -const AttribPool = require('./AttributePool'); -const Changeset = require('./Changeset'); +import AttribPool from './AttributePool'; +import {compose, deserializeOps, inverse, moveOpsToNewPool, mutateAttributionLines, mutateTextLines, splitAttributionLines, splitTextLines, unpack} from './Changeset'; const attributes = require('./attributes'); const linestylefilter = require('./linestylefilter').linestylefilter; const colorutils = require('./colorutils').colorutils; const _ = require('./underscore'); const hooks = require('./pluginfw/hooks'); +import html10n from './vendors/html10n'; + + // These parameters were global, now they are injected. A reference to the // Timeslider controller would probably be more appropriate. const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, BroadcastSlider) => { @@ -50,11 +54,11 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro currentRevision: clientVars.collab_client_vars.rev, currentTime: clientVars.collab_client_vars.time, currentLines: - Changeset.splitTextLines(clientVars.collab_client_vars.initialAttributedText.text), + splitTextLines(clientVars.collab_client_vars.initialAttributedText.text), currentDivs: null, // to be filled in once the dom loads apool: (new AttribPool()).fromJsonable(clientVars.collab_client_vars.apool), - alines: Changeset.splitAttributionLines( + alines: splitAttributionLines( clientVars.collab_client_vars.initialAttributedText.attribs, clientVars.collab_client_vars.initialAttributedText.text), @@ -117,7 +121,7 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro getActiveAuthors() { const authorIds = new Set(); for (const aline of this.alines) { - for (const op of Changeset.deserializeOps(aline)) { + for (const op of deserializeOps(aline)) { for (const [k, v] of attributes.attribsFromString(op.attribs, this.apool)) { if (k !== 'author') continue; if (v) authorIds.add(v); @@ -138,7 +142,7 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro const oldAlines = padContents.alines.slice(); try { // must mutate attribution lines before text lines - Changeset.mutateAttributionLines(changeset, padContents.alines, padContents.apool); + mutateAttributionLines(changeset, padContents.alines, padContents.apool); } catch (e) { debugLog(e); } @@ -160,7 +164,7 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro // some chars are replaced (no attributes change and no length change) // test if there are keep ops at the start of the cs if (lineChanged === undefined) { - const [op] = Changeset.deserializeOps(Changeset.unpack(changeset).ops); + const [op] = deserializeOps(unpack(changeset).ops); lineChanged = op != null && op.opcode === '=' ? op.lines : 0; } @@ -180,7 +184,7 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro goToLineNumber(lineChanged); } - Changeset.mutateTextLines(changeset, padContents); + mutateTextLines(changeset, padContents); padContents.currentRevision = revision; padContents.currentTime += timeDelta * 1000; @@ -269,7 +273,7 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro let changeset = cs[0]; let timeDelta = path.times[0]; for (let i = 1; i < cs.length; i++) { - changeset = Changeset.compose(changeset, cs[i], padContents.apool); + changeset = compose(changeset, cs[i], padContents.apool); timeDelta += path.times[i]; } if (changeset) applyChangeset(changeset, path.rev, true, timeDelta); @@ -287,7 +291,7 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro let changeset = cs[0]; let timeDelta = path.times[0]; for (let i = 1; i < cs.length; i++) { - changeset = Changeset.compose(changeset, cs[i], padContents.apool); + changeset = compose(changeset, cs[i], padContents.apool); timeDelta += path.times[i]; } if (changeset) applyChangeset(changeset, path.rev, true, timeDelta); @@ -393,9 +397,9 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro if (aend > data.actualEndNum - 1) aend = data.actualEndNum - 1; // debugLog("adding changeset:", astart, aend); const forwardcs = - Changeset.moveOpsToNewPool(data.forwardsChangesets[i], pool, padContents.apool); + moveOpsToNewPool(data.forwardsChangesets[i], pool, padContents.apool); const backwardcs = - Changeset.moveOpsToNewPool(data.backwardsChangesets[i], pool, padContents.apool); + moveOpsToNewPool(data.backwardsChangesets[i], pool, padContents.apool); window.revisionInfo.addChangeset(astart, aend, forwardcs, backwardcs, data.timeDeltas[i]); } if (callback) callback(start - 1, start + data.forwardsChangesets.length * granularity - 1); @@ -405,13 +409,13 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro obj = obj.data; if (obj.type === 'NEW_CHANGES') { - const changeset = Changeset.moveOpsToNewPool( + const changeset = moveOpsToNewPool( obj.changeset, (new AttribPool()).fromJsonable(obj.apool), padContents.apool); - let changesetBack = Changeset.inverse( + let changesetBack = inverse( obj.changeset, padContents.currentLines, padContents.alines, padContents.apool); - changesetBack = Changeset.moveOpsToNewPool( + changesetBack = moveOpsToNewPool( changesetBack, (new AttribPool()).fromJsonable(obj.apool), padContents.apool); loadedNewChangeset(changeset, changesetBack, obj.newRev - 1, obj.timeDelta); diff --git a/src/static/js/broadcast_revisions.js b/src/static/js/broadcast_revisions.ts similarity index 99% rename from src/static/js/broadcast_revisions.js rename to src/static/js/broadcast_revisions.ts index 8a9eb2e6c16..37272d86078 100644 --- a/src/static/js/broadcast_revisions.js +++ b/src/static/js/broadcast_revisions.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.ts similarity index 99% rename from src/static/js/broadcast_slider.js rename to src/static/js/broadcast_slider.ts index 80afffe288f..ccd896c4c0b 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. @@ -26,6 +27,7 @@ const _ = require('./underscore'); const padmodals = require('./pad_modals').padmodals; const colorutils = require('./colorutils').colorutils; +import html10n from './vendors/html10n'; const loadBroadcastSliderJS = (fireWhenAllScriptsAreLoaded) => { let BroadcastSlider; diff --git a/src/static/js/caretPosition.js b/src/static/js/caretPosition.ts similarity index 84% rename from src/static/js/caretPosition.js rename to src/static/js/caretPosition.ts index 03af77f33fc..5134a0ed072 100644 --- a/src/static/js/caretPosition.js +++ b/src/static/js/caretPosition.ts @@ -3,8 +3,11 @@ // One rep.line(div) can be broken in more than one line in the browser. // This function is useful to get the caret position of the line as // is represented by the browser -exports.getPosition = () => { +import {Position, RepModel, RepNode} from "./types/RepModel"; + +export const getPosition = () => { const range = getSelectionRange(); + // @ts-ignore if (!range || $(range.endContainer).closest('body')[0].id !== 'innerdocbody') return null; // When there's a <br> or any element that has no height, we can't get the dimension of the // element where the caret is. As we can't get the element height, we create a text node to get @@ -18,7 +21,7 @@ exports.getPosition = () => { return line; }; -const createSelectionRange = (range) => { +const createSelectionRange = (range: Range) => { const clonedRange = range.cloneRange(); // we set the selection start and end to avoid error when user selects a text bigger than @@ -30,14 +33,14 @@ const createSelectionRange = (range) => { return clonedRange; }; -const getPositionOfRepLineAtOffset = (node, offset) => { +const getPositionOfRepLineAtOffset = (node: any, offset: number) => { // it is not a text node, so we cannot make a selection if (node.tagName === 'BR' || node.tagName === 'EMPTY') { return getPositionOfElementOrSelection(node); } while (node.length === 0 && node.nextSibling) { - node = node.nextSibling; + node = node.nextSibling as any; } const newRange = new Range(); @@ -48,14 +51,13 @@ const getPositionOfRepLineAtOffset = (node, offset) => { return linePosition; }; -const getPositionOfElementOrSelection = (element) => { +const getPositionOfElementOrSelection = (element: Range):Position => { const rect = element.getBoundingClientRect(); - const linePosition = { + return { bottom: rect.bottom, height: rect.height, top: rect.top, - }; - return linePosition; + } satisfies Position; }; // here we have two possibilities: @@ -64,7 +66,7 @@ const getPositionOfElementOrSelection = (element) => { // where is the top of the previous line // [2] the line before is part of another rep line. It's possible this line has different margins // height. So we have to get the exactly position of the line -exports.getPositionTopOfPreviousBrowserLine = (caretLinePosition, rep) => { +export const getPositionTopOfPreviousBrowserLine = (caretLinePosition: Position, rep: RepModel) => { let previousLineTop = caretLinePosition.top - caretLinePosition.height; // [1] const isCaretLineFirstBrowserLine = caretLineIsFirstBrowserLine(caretLinePosition.top, rep); @@ -80,7 +82,7 @@ exports.getPositionTopOfPreviousBrowserLine = (caretLinePosition, rep) => { return previousLineTop; }; -const caretLineIsFirstBrowserLine = (caretLineTop, rep) => { +const caretLineIsFirstBrowserLine = (caretLineTop: number, rep: RepModel) => { const caretRepLine = rep.selStart[0]; const lineNode = rep.lines.atIndex(caretRepLine).lineNode; const firstRootNode = getFirstRootChildNode(lineNode); @@ -91,7 +93,7 @@ const caretLineIsFirstBrowserLine = (caretLineTop, rep) => { }; // find the first root node, usually it is a text node -const getFirstRootChildNode = (node) => { +const getFirstRootChildNode = (node: RepNode) => { if (!node.firstChild) { return node; } else { @@ -99,7 +101,7 @@ const getFirstRootChildNode = (node) => { } }; -const getDimensionOfLastBrowserLineOfRepLine = (line, rep) => { +const getDimensionOfLastBrowserLineOfRepLine = (line: number, rep: RepModel) => { const lineNode = rep.lines.atIndex(line).lineNode; const lastRootChildNode = getLastRootChildNode(lineNode); @@ -109,7 +111,7 @@ const getDimensionOfLastBrowserLineOfRepLine = (line, rep) => { return lastRootChildNodePosition; }; -const getLastRootChildNode = (node) => { +const getLastRootChildNode = (node: RepNode) => { if (!node.lastChild) { return { node, @@ -125,7 +127,7 @@ const getLastRootChildNode = (node) => { // So, we can use the caret line to calculate the bottom of the line. // [2] the next line is part of another rep line. // It's possible this line has different dimensions, so we have to get the exactly dimension of it -exports.getBottomOfNextBrowserLine = (caretLinePosition, rep) => { +export const getBottomOfNextBrowserLine = (caretLinePosition: Position, rep: RepModel) => { let nextLineBottom = caretLinePosition.bottom + caretLinePosition.height; // [1] const isCaretLineLastBrowserLine = caretLineIsLastBrowserLineOfRepLine(caretLinePosition.top, rep); @@ -142,7 +144,7 @@ exports.getBottomOfNextBrowserLine = (caretLinePosition, rep) => { return nextLineBottom; }; -const caretLineIsLastBrowserLineOfRepLine = (caretLineTop, rep) => { +const caretLineIsLastBrowserLineOfRepLine = (caretLineTop: number, rep: RepModel) => { const caretRepLine = rep.selStart[0]; const lineNode = rep.lines.atIndex(caretRepLine).lineNode; const lastRootChildNode = getLastRootChildNode(lineNode); @@ -153,7 +155,7 @@ const caretLineIsLastBrowserLineOfRepLine = (caretLineTop, rep) => { return lastRootChildNodePosition.top === caretLineTop; }; -const getPreviousVisibleLine = (line, rep) => { +export const getPreviousVisibleLine = (line: number, rep: RepModel): number => { const firstLineOfPad = 0; if (line <= firstLineOfPad) { return firstLineOfPad; @@ -165,9 +167,8 @@ const getPreviousVisibleLine = (line, rep) => { }; -exports.getPreviousVisibleLine = getPreviousVisibleLine; -const getNextVisibleLine = (line, rep) => { +export const getNextVisibleLine = (line: number, rep: RepModel): number => { const lastLineOfThePad = rep.lines.length() - 1; if (line >= lastLineOfThePad) { return lastLineOfThePad; @@ -177,11 +178,10 @@ const getNextVisibleLine = (line, rep) => { return getNextVisibleLine(line + 1, rep); } }; -exports.getNextVisibleLine = getNextVisibleLine; -const isLineVisible = (line, rep) => rep.lines.atIndex(line).lineNode.offsetHeight > 0; +const isLineVisible = (line: number, rep: RepModel) => rep.lines.atIndex(line).lineNode.offsetHeight > 0; -const getDimensionOfFirstBrowserLineOfRepLine = (line, rep) => { +const getDimensionOfFirstBrowserLineOfRepLine = (line: number, rep: RepModel) => { const lineNode = rep.lines.atIndex(line).lineNode; const firstRootChildNode = getFirstRootChildNode(lineNode); diff --git a/src/static/js/changesettracker.js b/src/static/js/changesettracker.ts similarity index 76% rename from src/static/js/changesettracker.js rename to src/static/js/changesettracker.ts index 30c70aa748f..41d5c266891 100644 --- a/src/static/js/changesettracker.js +++ b/src/static/js/changesettracker.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -22,17 +23,18 @@ * limitations under the License. */ -const AttributeMap = require('./AttributeMap'); -const AttributePool = require('./AttributePool'); -const Changeset = require('./Changeset'); +import AttributeMap from './AttributeMap'; +import AttributePool from './AttributePool'; +import {applyToAText, checkRep, cloneAText, compose, deserializeOps, follow, identity, isIdentity, makeAText, moveOpsToNewPool, newLen, pack, prepareForWire, unpack} from './Changeset'; +import {MergingOpAssembler} from "./MergingOpAssembler"; const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => { // latest official text from server - let baseAText = Changeset.makeAText('\n'); + let baseAText = makeAText('\n'); // changes applied to baseText that have been submitted let submittedChangeset = null; // changes applied to submittedChangeset since it was prepared - let userChangeset = Changeset.identity(1); + let userChangeset = identity(1); // is the changesetTracker enabled let tracking = false; // stack state flag so that when we change the rep we don't @@ -66,18 +68,18 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => { return self = { isTracking: () => tracking, setBaseText: (text) => { - self.setBaseAttributedText(Changeset.makeAText(text), null); + self.setBaseAttributedText(makeAText(text), null); }, setBaseAttributedText: (atext, apoolJsonObj) => { aceCallbacksProvider.withCallbacks('setBaseText', (callbacks) => { tracking = true; - baseAText = Changeset.cloneAText(atext); + baseAText = cloneAText(atext); if (apoolJsonObj) { const wireApool = (new AttributePool()).fromJsonable(apoolJsonObj); - baseAText.attribs = Changeset.moveOpsToNewPool(baseAText.attribs, wireApool, apool); + baseAText.attribs = moveOpsToNewPool(baseAText.attribs, wireApool, apool); } submittedChangeset = null; - userChangeset = Changeset.identity(atext.text.length); + userChangeset = identity(atext.text.length); applyingNonUserChanges = true; try { callbacks.setDocumentAttributedText(atext); @@ -89,8 +91,8 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => { composeUserChangeset: (c) => { if (!tracking) return; if (applyingNonUserChanges) return; - if (Changeset.isIdentity(c)) return; - userChangeset = Changeset.compose(userChangeset, c, apool); + if (isIdentity(c)) return; + userChangeset = compose(userChangeset, c, apool); setChangeCallbackTimeout(); }, @@ -100,23 +102,23 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => { aceCallbacksProvider.withCallbacks('applyChangesToBase', (callbacks) => { if (apoolJsonObj) { const wireApool = (new AttributePool()).fromJsonable(apoolJsonObj); - c = Changeset.moveOpsToNewPool(c, wireApool, apool); + c = moveOpsToNewPool(c, wireApool, apool); } - baseAText = Changeset.applyToAText(c, baseAText, apool); + baseAText = applyToAText(c, baseAText, apool); let c2 = c; if (submittedChangeset) { const oldSubmittedChangeset = submittedChangeset; - submittedChangeset = Changeset.follow(c, oldSubmittedChangeset, false, apool); - c2 = Changeset.follow(oldSubmittedChangeset, c, true, apool); + submittedChangeset = follow(c, oldSubmittedChangeset, false, apool); + c2 = follow(oldSubmittedChangeset, c, true, apool); } const preferInsertingAfterUserChanges = true; const oldUserChangeset = userChangeset; - userChangeset = Changeset.follow( + userChangeset = follow( c2, oldUserChangeset, preferInsertingAfterUserChanges, apool); - const postChange = Changeset.follow( + const postChange = follow( oldUserChangeset, c2, !preferInsertingAfterUserChanges, apool); const preferInsertionAfterCaret = true; // (optAuthor && optAuthor > thisAuthor); @@ -135,17 +137,17 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => { if (submittedChangeset) { // submission must have been canceled, prepare new changeset // that includes old submittedChangeset - toSubmit = Changeset.compose(submittedChangeset, userChangeset, apool); + toSubmit = compose(submittedChangeset, userChangeset, apool); } else { // Get my authorID const authorId = parent.parent.pad.myUserInfo.userId; // Sanitize authorship: Replace all author attributes with this user's author ID in case the // text was copied from another author. - const cs = Changeset.unpack(userChangeset); - const assem = Changeset.mergingOpAssembler(); + const cs = unpack(userChangeset); + const assem = new MergingOpAssembler(); - for (const op of Changeset.deserializeOps(cs.ops)) { + for (const op of deserializeOps(cs.ops)) { if (op.opcode === '+') { const attribs = AttributeMap.fromString(op.attribs, apool); const oldAuthorId = attribs.get('author'); @@ -157,23 +159,23 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => { assem.append(op); } assem.endDocument(); - userChangeset = Changeset.pack(cs.oldLen, cs.newLen, assem.toString(), cs.charBank); - Changeset.checkRep(userChangeset); + userChangeset = pack(cs.oldLen, cs.newLen, assem.toString(), cs.charBank); + checkRep(userChangeset); - if (Changeset.isIdentity(userChangeset)) toSubmit = null; + if (isIdentity(userChangeset)) toSubmit = null; else toSubmit = userChangeset; } let cs = null; if (toSubmit) { submittedChangeset = toSubmit; - userChangeset = Changeset.identity(Changeset.newLen(toSubmit)); + userChangeset = identity(newLen(toSubmit)); cs = toSubmit; } let wireApool = null; if (cs) { - const forWire = Changeset.prepareForWire(cs, apool); + const forWire = prepareForWire(cs, apool); wireApool = forWire.pool.toJsonable(); cs = forWire.translated; } @@ -190,13 +192,13 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => { throw new Error('applySubmittedChangesToBase: no submitted changes to apply'); } // bumpDebug("applying committed changeset: "+submittedChangeset.encodeToString(false)); - baseAText = Changeset.applyToAText(submittedChangeset, baseAText, apool); + baseAText = applyToAText(submittedChangeset, baseAText, apool); submittedChangeset = null; }, setUserChangeNotificationCallback: (callback) => { changeCallback = callback; }, - hasUncommittedChanges: () => !!(submittedChangeset || (!Changeset.isIdentity(userChangeset))), + hasUncommittedChanges: () => !!(submittedChangeset || (!isIdentity(userChangeset))), }; }; diff --git a/src/static/js/chat.js b/src/static/js/chat.ts old mode 100755 new mode 100644 similarity index 98% rename from src/static/js/chat.js rename to src/static/js/chat.ts index 9dee3be4f27..6357663c4f6 --- a/src/static/js/chat.js +++ b/src/static/js/chat.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd) @@ -15,16 +16,18 @@ * limitations under the License. */ -const ChatMessage = require('./ChatMessage'); -const padutils = require('./pad_utils').padutils; +import ChatMessage from './ChatMessage'; +import padutils from './pad_utils' const padcookie = require('./pad_cookie').padcookie; const Tinycon = require('tinycon/tinycon'); const hooks = require('./pluginfw/hooks'); const padeditor = require('./pad_editor').padeditor; +import html10n from './vendors/html10n'; // Removes diacritics and lower-cases letters. https://stackoverflow.com/a/37511463 const normalize = (s) => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); + exports.chat = (() => { let isStuck = false; let userAndChat = false; diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.ts similarity index 99% rename from src/static/js/collab_client.js rename to src/static/js/collab_client.ts index 69c8e41f302..7bc1c37df51 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/colorutils.js b/src/static/js/colorutils.ts similarity index 99% rename from src/static/js/colorutils.js rename to src/static/js/colorutils.ts index 9688b8e59ce..b60b32aa97d 100644 --- a/src/static/js/colorutils.js +++ b/src/static/js/colorutils.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.ts similarity index 97% rename from src/static/js/contentcollector.js rename to src/static/js/contentcollector.ts index 7dd70e51287..5b9d7f3625d 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.ts @@ -1,3 +1,5 @@ +// @ts-nocheck + 'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. @@ -8,6 +10,8 @@ // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector // %APPJET%: import("etherpad.collab.ace.easysync2.Changeset"); // %APPJET%: import("etherpad.admin.plugins"); +import Op from "./Op"; + /** * Copyright 2009 Google Inc. * @@ -26,9 +30,10 @@ const _MAX_LIST_LEVEL = 16; -const AttributeMap = require('./AttributeMap'); -const UNorm = require('unorm'); -const Changeset = require('./Changeset'); +import AttributeMap from './AttributeMap'; +import UNorm from 'unorm'; +import {subattribution} from './Changeset'; +import {SmartOpAssembler} from "./SmartOpAssembler"; const hooks = require('./pluginfw/hooks'); const sanitizeUnicode = (s) => UNorm.nfc(s); @@ -83,14 +88,14 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const textArray = []; const attribsArray = []; let attribsBuilder = null; - const op = new Changeset.Op('+'); + const op = new Op('+'); const self = { length: () => textArray.length, atColumnZero: () => textArray[textArray.length - 1] === '', startNew: () => { textArray.push(''); self.flush(true); - attribsBuilder = Changeset.smartOpAssembler(); + attribsBuilder = new SmartOpAssembler(); }, textOfLine: (i) => textArray[i], appendText: (txt, attrString = '') => { @@ -534,7 +539,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) // As the concept of parent's doesn't exist when processing each domline... } } else { - // Below needs more testin if it's neccesary as _exitList should take care of this. + // Below needs more testin if it's necessary as _exitList should take care of this. // delete state.start; // delete state.listNesting; // _recalcAttribString(state); @@ -653,8 +658,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const lengthToTake = lineLimit; newStrings.push(oldString.substring(0, lengthToTake)); oldString = oldString.substring(lengthToTake); - newAttribStrings.push(Changeset.subattribution(oldAttribString, 0, lengthToTake)); - oldAttribString = Changeset.subattribution(oldAttribString, lengthToTake); + newAttribStrings.push(subattribution(oldAttribString, 0, lengthToTake)); + oldAttribString = subattribution(oldAttribString, lengthToTake); } if (oldString.length > 0) { newStrings.push(oldString); diff --git a/src/static/js/cssmanager.js b/src/static/js/cssmanager.ts similarity index 99% rename from src/static/js/cssmanager.js rename to src/static/js/cssmanager.ts index 5bf2adb303b..89036df6723 100644 --- a/src/static/js/cssmanager.js +++ b/src/static/js/cssmanager.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/domline.js b/src/static/js/domline.ts similarity index 99% rename from src/static/js/domline.js rename to src/static/js/domline.ts index af786b2dc40..900f60176f4 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline diff --git a/src/static/js/index.js b/src/static/js/index.ts similarity index 90% rename from src/static/js/index.js rename to src/static/js/index.ts index 944e0bb8e52..19499c977a1 100644 --- a/src/static/js/index.js +++ b/src/static/js/index.ts @@ -19,6 +19,7 @@ * limitations under the License. */ + const randomPadName = () => { // the number of distinct chars (64) is chosen to ensure that the selection will be uniform when // using the PRNG below @@ -28,8 +29,7 @@ const randomPadName = () => { // make room for 8-bit integer values that span from 0 to 255. const randomarray = new Uint8Array(stringLength); // use browser's PRNG to generate a "unique" sequence - const cryptoObj = window.crypto || window.msCrypto; // for IE 11 - cryptoObj.getRandomValues(randomarray); + crypto.getRandomValues(randomarray); let randomstring = ''; for (let i = 0; i < stringLength; i++) { // instead of writing "Math.floor(randomarray[i]/256*64)" @@ -42,10 +42,9 @@ const randomPadName = () => { $(() => { $('#go2Name').on('submit', () => { - const padname = $('#padname').val(); + const padname = $('#padname').val() as string; if (padname.length > 0) { - const padName = encodeURIComponent(padname.trim()); - window.location = `p/${padName}`; + window.location.href = `p/${encodeURIComponent(padname.trim())}`; } else { alert('Please enter a name'); } @@ -63,6 +62,7 @@ $(() => { }); // start the custom js + // @ts-ignore if (typeof window.customStart === 'function') window.customStart(); }); diff --git a/src/static/js/l10n.js b/src/static/js/l10n.js deleted file mode 100644 index 7206f913b6e..00000000000 --- a/src/static/js/l10n.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -((document) => { - // Set language for l10n - let language = document.cookie.match(/language=((\w{2,3})(-\w+)?)/); - if (language) language = language[1]; - - html10n.bind('indexed', () => { - html10n.localize([language, navigator.language, navigator.userLanguage, 'en']); - }); - - html10n.bind('localized', () => { - document.documentElement.lang = html10n.getLanguage(); - document.documentElement.dir = html10n.getDirection(); - }); -})(document); diff --git a/src/static/js/l10n.ts b/src/static/js/l10n.ts new file mode 100644 index 00000000000..2211318c012 --- /dev/null +++ b/src/static/js/l10n.ts @@ -0,0 +1,18 @@ +import html10n from '../js/vendors/html10n'; + + +// Set language for l10n +let regexpLang: string | undefined; +let language = document.cookie.match(/language=((\w{2,3})(-\w+)?)/); +if (language) regexpLang = language[1]; + +html10n.mt.bind('indexed', () => { + console.log('Navigator language', navigator.language) + console.log('Localizing things', [regexpLang, navigator.language, 'en']) + html10n.localize([regexpLang, navigator.language, 'en']); +}); + +html10n.mt.bind('localized', () => { + document.documentElement.lang = html10n.getLanguage()!; + document.documentElement.dir = html10n.getDirection()!; +}); diff --git a/src/static/js/linestylefilter.js b/src/static/js/linestylefilter.ts similarity index 97% rename from src/static/js/linestylefilter.js rename to src/static/js/linestylefilter.ts index 632e6b3ccb5..4080a7c52b3 100644 --- a/src/static/js/linestylefilter.js +++ b/src/static/js/linestylefilter.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -30,12 +31,13 @@ // requires: plugins // requires: undefined -const Changeset = require('./Changeset'); -const attributes = require('./attributes'); +import {deserializeOps} from './Changeset'; +import attributes from './attributes'; const hooks = require('./pluginfw/hooks'); const linestylefilter = {}; const AttributeManager = require('./AttributeManager'); -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' +import Op from "./Op"; linestylefilter.ATTRIB_CLASSES = { bold: 'tag:b', @@ -98,12 +100,12 @@ linestylefilter.getLineStyleFilter = (lineLength, aline, textAndClassFunc, apool return classes.substring(1); }; - const attrOps = Changeset.deserializeOps(aline); + const attrOps = deserializeOps(aline); let attrOpsNext = attrOps.next(); let nextOp, nextOpClasses; const goNextOp = () => { - nextOp = attrOpsNext.done ? new Changeset.Op() : attrOpsNext.value; + nextOp = attrOpsNext.done ? new Op() : attrOpsNext.value; if (!attrOpsNext.done) attrOpsNext = attrOps.next(); nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs)); }; diff --git a/src/static/js/pad.js b/src/static/js/pad.ts similarity index 94% rename from src/static/js/pad.js rename to src/static/js/pad.ts index df409372a39..e94611fcd3b 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -24,13 +25,17 @@ let socket; + // These jQuery things should create local references, but for now `require()` // assigns to the global `$` and augments it with plugins. require('./vendors/jquery'); require('./vendors/farbtastic'); require('./vendors/gritter'); -const Cookies = require('./pad_utils').Cookies; +import html10n from './vendors/html10n' + +import {Cookies} from "./pad_utils"; + const chat = require('./chat').chat; const getCollabClient = require('./collab_client').getCollabClient; const padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus; @@ -41,9 +46,9 @@ const padimpexp = require('./pad_impexp').padimpexp; const padmodals = require('./pad_modals').padmodals; const padsavedrevs = require('./pad_savedrevs'); const paduserlist = require('./pad_userlist').paduserlist; -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' const colorutils = require('./colorutils').colorutils; -const randomString = require('./pad_utils').randomString; +import {randomString} from "./pad_utils"; const socketio = require('./socketio'); const hooks = require('./pluginfw/hooks'); @@ -136,7 +141,8 @@ const getParameters = [ name: 'lang', checkVal: null, callback: (val) => { - window.html10n.localize([val, 'en']); + console.log('Val is', val) + html10n.localize([val, 'en']); Cookies.set('language', val); }, }, @@ -167,7 +173,7 @@ const getUrlVars = () => new URL(window.location.href).searchParams; const sendClientReady = (isReconnect) => { let padId = document.location.pathname.substring(document.location.pathname.lastIndexOf('/') + 1); - // unescape neccesary due to Safari and Opera interpretation of spaces + // unescape necessary due to Safari and Opera interpretation of spaces padId = decodeURIComponent(padId); if (!isReconnect) { @@ -213,7 +219,7 @@ const sendClientReady = (isReconnect) => { const handshake = async () => { let receivedClientVars = false; let padId = document.location.pathname.substring(document.location.pathname.lastIndexOf('/') + 1); - // unescape neccesary due to Safari and Opera interpretation of spaces + // unescape necessary due to Safari and Opera interpretation of spaces padId = decodeURIComponent(padId); // padId is used here for sharding / scaling. We prefix the padId with padId: so it's clear @@ -230,7 +236,7 @@ const handshake = async () => { sendClientReady(false); }); - socket.on('reconnect', () => { + socket.io.on('reconnect', () => { // pad.collabClient might be null if the hanshake failed (or it never got that far). if (pad.collabClient != null) { pad.collabClient.setChannelState('CONNECTED'); @@ -250,14 +256,29 @@ const handshake = async () => { socket.on('disconnect', (reason) => { // The socket.io client will automatically try to reconnect for all reasons other than "io // server disconnect". - if (reason !== 'io server disconnect') return; + console.log(`Socket disconnected: ${reason}`) + //if (reason !== 'io server disconnect' || reason !== 'ping timeout') return; socketReconnecting(); - socket.connect(); }); - socket.on('reconnecting', socketReconnecting); - socket.on('reconnect_failed', (error) => { + socket.on('shout', (obj) => { + if(obj.type === "COLLABROOM") { + let date = new Date(obj.data.payload.timestamp); + $.gritter.add({ + // (string | mandatory) the heading of the notification + title: 'Admin message', + // (string | mandatory) the text inside the notification + text: '[' + date.toLocaleTimeString() + ']: ' + obj.data.payload.message.message, + // (bool | optional) if you want it to fade out on its own or just sit there + sticky: obj.data.payload.message.sticky + }); + } + }) + + socket.io.on('reconnect_attempt', socketReconnecting); + + socket.io.on('reconnect_failed', (error) => { // pad.collabClient might be null if the hanshake failed (or it never got that far). if (pad.collabClient != null) { pad.collabClient.setChannelState('DISCONNECTED', 'reconnect_timeout'); @@ -266,6 +287,7 @@ const handshake = async () => { } }); + socket.on('error', (error) => { // pad.collabClient might be null if the error occurred before the hanshake completed. if (pad.collabClient != null) { @@ -298,6 +320,15 @@ const handshake = async () => { () => $.ajax('../_extendExpressSessionLifetime', {method: 'PUT'}).catch(() => {}); setInterval(ping, window.clientVars.sessionRefreshInterval); } + if(window.clientVars.mode === "development") { + console.warn('Enabling development mode with live update') + socket.on('liveupdate', ()=>{ + + console.log('Live reload update received') + location.reload() + }) + } + } else if (obj.disconnect) { padconnectionstatus.disconnected(obj.disconnect); socket.disconnect(); @@ -698,7 +729,7 @@ const pad = { $.ajax( { type: 'post', - url: 'ep/pad/connection-diagnostic-info', + url: '../ep/pad/connection-diagnostic-info', data: { diagnosticInfo: JSON.stringify(pad.diagnosticInfo), }, diff --git a/src/static/js/pad_automatic_reconnect.js b/src/static/js/pad_automatic_reconnect.ts similarity index 99% rename from src/static/js/pad_automatic_reconnect.js rename to src/static/js/pad_automatic_reconnect.ts index 576e0d3505f..8172d5be789 100644 --- a/src/static/js/pad_automatic_reconnect.js +++ b/src/static/js/pad_automatic_reconnect.ts @@ -1,4 +1,6 @@ +// @ts-nocheck 'use strict'; +import html10n from './vendors/html10n'; exports.showCountDownTimerToReconnectOnModal = ($modal, pad) => { if (clientVars.automaticReconnectionTimeout && $modal.is('.with_reconnect_timer')) { diff --git a/src/static/js/pad_connectionstatus.js b/src/static/js/pad_connectionstatus.ts similarity index 99% rename from src/static/js/pad_connectionstatus.js rename to src/static/js/pad_connectionstatus.ts index 7b0497d96d8..600defa8dd0 100644 --- a/src/static/js/pad_connectionstatus.js +++ b/src/static/js/pad_connectionstatus.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/pad_cookie.js b/src/static/js/pad_cookie.ts similarity index 97% rename from src/static/js/pad_cookie.js rename to src/static/js/pad_cookie.ts index 0e946ea5c89..bc624e96250 100644 --- a/src/static/js/pad_cookie.js +++ b/src/static/js/pad_cookie.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -16,7 +17,7 @@ * limitations under the License. */ -const Cookies = require('./pad_utils').Cookies; +import {Cookies} from "./pad_utils"; exports.padcookie = new class { constructor() { diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.ts similarity index 99% rename from src/static/js/pad_editbar.js rename to src/static/js/pad_editbar.ts index af8d59f1fc5..d98174fe568 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -24,7 +25,7 @@ const browser = require('./vendors/browser'); const hooks = require('./pluginfw/hooks'); -const padutils = require('./pad_utils').padutils; +import padutils from "./pad_utils"; const padeditor = require('./pad_editor').padeditor; const padsavedrevs = require('./pad_savedrevs'); const _ = require('underscore'); diff --git a/src/static/js/pad_editor.js b/src/static/js/pad_editor.ts similarity index 96% rename from src/static/js/pad_editor.js rename to src/static/js/pad_editor.ts index 585cccbb5f7..362412f2fdd 100644 --- a/src/static/js/pad_editor.js +++ b/src/static/js/pad_editor.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. @@ -21,12 +22,12 @@ * limitations under the License. */ -const Cookies = require('./pad_utils').Cookies; +import padutils,{Cookies} from "./pad_utils"; const padcookie = require('./pad_cookie').padcookie; -const padutils = require('./pad_utils').padutils; +const Ace2Editor = require('./ace').Ace2Editor; +import html10n from '../js/vendors/html10n' const padeditor = (() => { - let Ace2Editor = undefined; let pad = undefined; let settings = undefined; @@ -35,7 +36,6 @@ const padeditor = (() => { // this is accessed directly from other files viewZoom: 100, init: async (initialViewOptions, _pad) => { - Ace2Editor = require('./ace').Ace2Editor; pad = _pad; settings = pad.settings; self.ace = new Ace2Editor(); @@ -99,7 +99,7 @@ const padeditor = (() => { $('#languagemenu').val(html10n.getLanguage()); $('#languagemenu').on('change', () => { Cookies.set('language', $('#languagemenu').val()); - window.html10n.localize([$('#languagemenu').val(), 'en']); + html10n.localize([$('#languagemenu').val(), 'en']); if ($('select').niceSelect) { $('select').niceSelect('update'); } diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.ts similarity index 99% rename from src/static/js/pad_impexp.js rename to src/static/js/pad_impexp.ts index 3dab8a69511..0c3f9d423e1 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -22,6 +23,8 @@ * limitations under the License. */ +import html10n from './vendors/html10n'; + const { jsPDF } = require("jspdf/dist/jspdf.umd.min.js"); window.DOMPurify = require("dompurify/dist/purify.min.js"); window.html2canvas = require("html2canvas/dist/html2canvas.min.js"); diff --git a/src/static/js/pad_modals.js b/src/static/js/pad_modals.ts similarity index 99% rename from src/static/js/pad_modals.js rename to src/static/js/pad_modals.ts index 54bd838772d..3e2c2459b9b 100644 --- a/src/static/js/pad_modals.js +++ b/src/static/js/pad_modals.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/pad_savedrevs.js b/src/static/js/pad_savedrevs.ts similarity index 96% rename from src/static/js/pad_savedrevs.js rename to src/static/js/pad_savedrevs.ts index b5868f699c5..6722a03a21d 100644 --- a/src/static/js/pad_savedrevs.js +++ b/src/static/js/pad_savedrevs.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -20,7 +21,7 @@ let pad; exports.saveNow = () => { pad.collabClient.sendMessage({type: 'SAVE_REVISION'}); - $.gritter.add({ + window.$.gritter.add({ // (string | mandatory) the heading of the notification title: html10n.get('pad.savedrevs.marked'), // (string | mandatory) the text inside the notification diff --git a/src/static/js/pad_userlist.js b/src/static/js/pad_userlist.ts similarity index 99% rename from src/static/js/pad_userlist.js rename to src/static/js/pad_userlist.ts index b689ae1adbb..227e6b0630a 100644 --- a/src/static/js/pad_userlist.js +++ b/src/static/js/pad_userlist.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -16,9 +17,9 @@ * limitations under the License. */ -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' const hooks = require('./pluginfw/hooks'); - +import html10n from './vendors/html10n'; let myUserInfo = {}; let colorPickerOpen = false; diff --git a/src/static/js/pad_utils.js b/src/static/js/pad_utils.ts similarity index 58% rename from src/static/js/pad_utils.js rename to src/static/js/pad_utils.ts index 58105d23c1d..0a66abf0003 100644 --- a/src/static/js/pad_utils.js +++ b/src/static/js/pad_utils.ts @@ -6,6 +6,8 @@ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED */ +import {binarySearch} from "./ace2_common"; + /** * Copyright 2009 Google Inc. * @@ -22,13 +24,14 @@ * limitations under the License. */ -const Security = require('./security'); +const Security = require('security'); +import jsCookie, {CookiesStatic} from 'js-cookie' /** * Generates a random String with the given length. Is needed to generate the Author, Group, * readonly, session Ids */ -const randomString = (len) => { +export const randomString = (len?: number) => { const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; let randomstring = ''; len = len || 20; @@ -85,13 +88,41 @@ const urlRegex = (() => { 'tel', ].join('|')}):`; return new RegExp( - `(?:${withAuth}|${withoutAuth}|www\\.)${urlChar}*(?!${postUrlPunct})${urlChar}`, 'g'); + `(?:${withAuth}|${withoutAuth}|www\\.)${urlChar}*(?!${postUrlPunct})${urlChar}`, 'g'); })(); // https://stackoverflow.com/a/68957976 const base64url = /^(?=(?:.{4})*$)[A-Za-z0-9_-]*(?:[AQgw]==|[AEIMQUYcgkosw048]=)?$/; -const padutils = { +type PadEvent = { + which: number +} + +type JQueryNode = JQuery<HTMLElement> + +class PadUtils { + public urlRegex: RegExp + public wordCharRegex: RegExp + public warnDeprecatedFlags: { + disabledForTestingOnly: boolean, + _rl?: { + prevs: Map<string, number>, + now: () => number, + period: number + } + logger?: any + } + public globalExceptionHandler: null | any = null; + + + constructor() { + this.warnDeprecatedFlags = { + disabledForTestingOnly: false + } + this.wordCharRegex = wordCharRegex + this.urlRegex = urlRegex + } + /** * Prints a warning message followed by a stack trace (to make it easier to figure out what code * is using the deprecated function). @@ -107,41 +138,41 @@ const padutils = { * @param {...*} args - Passed to `padutils.warnDeprecated.logger.warn` (or `console.warn` if no * logger is set), with a stack trace appended if available. */ - warnDeprecated: (...args) => { - if (padutils.warnDeprecated.disabledForTestingOnly) return; + warnDeprecated = (...args: any[]) => { + if (this.warnDeprecatedFlags.disabledForTestingOnly) return; const err = new Error(); - if (Error.captureStackTrace) Error.captureStackTrace(err, padutils.warnDeprecated); + if (Error.captureStackTrace) Error.captureStackTrace(err, this.warnDeprecated); err.name = ''; // Rate limit identical deprecation warnings (as determined by the stack) to avoid log spam. if (typeof err.stack === 'string') { - if (padutils.warnDeprecated._rl == null) { - padutils.warnDeprecated._rl = - {prevs: new Map(), now: () => Date.now(), period: 10 * 60 * 1000}; + if (this.warnDeprecatedFlags._rl == null) { + this.warnDeprecatedFlags._rl = + {prevs: new Map(), now: () => Date.now(), period: 10 * 60 * 1000}; } - const rl = padutils.warnDeprecated._rl; + const rl = this.warnDeprecatedFlags._rl; const now = rl.now(); const prev = rl.prevs.get(err.stack); if (prev != null && now - prev < rl.period) return; rl.prevs.set(err.stack, now); } if (err.stack) args.push(err.stack); - (padutils.warnDeprecated.logger || console).warn(...args); - }, - - escapeHtml: (x) => Security.escapeHTML(String(x)), - uniqueId: () => { + (this.warnDeprecatedFlags.logger || console).warn(...args); + } + escapeHtml = (x: string) => Security.escapeHTML(String(x)) + uniqueId = () => { const pad = require('./pad').pad; // Sidestep circular dependency // returns string that is exactly 'width' chars, padding with zeros and taking rightmost digits const encodeNum = - (n, width) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width); + (n: number, width: number) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width); return [ pad.getClientIp(), encodeNum(+new Date(), 7), encodeNum(Math.floor(Math.random() * 1e9), 4), ].join('.'); - }, + } + // e.g. "Thu Jun 18 2009 13:09" - simpleDateTime: (date) => { + simpleDateTime = (date: string) => { const d = new Date(+date); // accept either number or date const dayOfWeek = (['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])[d.getDay()]; const month = ([ @@ -162,16 +193,14 @@ const padutils = { const year = d.getFullYear(); const hourmin = `${d.getHours()}:${(`0${d.getMinutes()}`).slice(-2)}`; return `${dayOfWeek} ${month} ${dayOfMonth} ${year} ${hourmin}`; - }, - wordCharRegex, - urlRegex, + } // returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...] - findURLs: (text) => { + findURLs = (text: string) => { // Copy padutils.urlRegex so that the use of .exec() below (which mutates the RegExp object) // does not break other concurrent uses of padutils.urlRegex. - const urlRegex = new RegExp(padutils.urlRegex, 'g'); + const urlRegex = new RegExp(this.urlRegex, 'g'); urlRegex.lastIndex = 0; - let urls = null; + let urls: [number, string][] | null = null; let execResult; // TODO: Switch to String.prototype.matchAll() after support for Node.js < 12.0.0 is dropped. while ((execResult = urlRegex.exec(text))) { @@ -181,18 +210,19 @@ const padutils = { urls.push([startIndex, url]); } return urls; - }, - escapeHtmlWithClickableLinks: (text, target) => { + } + escapeHtmlWithClickableLinks = (text: string, target: string) => { let idx = 0; const pieces = []; - const urls = padutils.findURLs(text); + const urls = this.findURLs(text); - const advanceTo = (i) => { - if (i > idx) { - pieces.push(Security.escapeHTML(text.substring(idx, i))); - idx = i; + const advanceTo = (i: number) => { + if (i > idx) { + pieces.push(Security.escapeHTML(text.substring(idx, i))); + idx = i; + } } - }; + ; if (urls) { for (let j = 0; j < urls.length; j++) { const startIndex = urls[j][0]; @@ -206,25 +236,25 @@ const padutils = { // https://mathiasbynens.github.io/rel-noopener/ // https://github.com/ether/etherpad-lite/pull/3636 pieces.push( - '<a ', - (target ? `target="${Security.escapeHTMLAttribute(target)}" ` : ''), - 'href="', - Security.escapeHTMLAttribute(href), - '" rel="noreferrer noopener">'); + '<a ', + (target ? `target="${Security.escapeHTMLAttribute(target)}" ` : ''), + 'href="', + Security.escapeHTMLAttribute(href), + '" rel="noreferrer noopener">'); advanceTo(startIndex + href.length); pieces.push('</a>'); } } advanceTo(text.length); return pieces.join(''); - }, - bindEnterAndEscape: (node, onEnter, onEscape) => { + } + bindEnterAndEscape = (node: JQueryNode, onEnter: Function, onEscape: Function) => { // Use keypress instead of keyup in bindEnterAndEscape. Keyup event is fired on enter in IME // (Input Method Editor), But keypress is not. So, I changed to use keypress instead of keyup. // It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox // 3.6.10, Chrome 6.0.472, Safari 5.0). if (onEnter) { - node.on('keypress', (evt) => { + node.on('keypress', (evt: { which: number; }) => { if (evt.which === 13) { onEnter(evt); } @@ -238,13 +268,15 @@ const padutils = { } }); } - }, - timediff: (d) => { + } + + timediff = (d: number) => { const pad = require('./pad').pad; // Sidestep circular dependency - const format = (n, word) => { - n = Math.round(n); - return (`${n} ${word}${n !== 1 ? 's' : ''} ago`); - }; + const format = (n: number, word: string) => { + n = Math.round(n); + return (`${n} ${word}${n !== 1 ? 's' : ''} ago`); + } + ; d = Math.max(0, (+(new Date()) - (+d) - pad.clientTimeOffset) / 1000); if (d < 60) { return format(d, 'second'); @@ -259,78 +291,89 @@ const padutils = { } d /= 24; return format(d, 'day'); - }, - makeAnimationScheduler: (funcToAnimateOneStep, stepTime, stepsAtOnce) => { - if (stepsAtOnce === undefined) { - stepsAtOnce = 1; - } + } + makeAnimationScheduler = + (funcToAnimateOneStep: any, stepTime: number, stepsAtOnce?: number) => { + if (stepsAtOnce === undefined) { + stepsAtOnce = 1; + } - let animationTimer = null; + let animationTimer: any = null; - const scheduleAnimation = () => { - if (!animationTimer) { - animationTimer = window.setTimeout(() => { - animationTimer = null; - let n = stepsAtOnce; - let moreToDo = true; - while (moreToDo && n > 0) { - moreToDo = funcToAnimateOneStep(); - n--; - } - if (moreToDo) { - // more to do - scheduleAnimation(); - } - }, stepTime * stepsAtOnce); - } - }; - return {scheduleAnimation}; - }, - makeFieldLabeledWhenEmpty: (field, labelText) => { - field = $(field); + const scheduleAnimation = () => { + if (!animationTimer) { + animationTimer = window.setTimeout(() => { + animationTimer = null; + let n = stepsAtOnce; + let moreToDo = true; + while (moreToDo && n > 0) { + moreToDo = funcToAnimateOneStep(); + n--; + } + if (moreToDo) { + // more to do + scheduleAnimation(); + } + }, stepTime * stepsAtOnce); + } + }; + return {scheduleAnimation}; + } - const clear = () => { - field.addClass('editempty'); - field.val(labelText); - }; - field.focus(() => { - if (field.hasClass('editempty')) { - field.val(''); - } - field.removeClass('editempty'); - }); - field.on('blur', () => { - if (!field.val()) { - clear(); + makeFieldLabeledWhenEmpty + = + (field: JQueryNode, labelText: string) => { + field = $(field); + + const clear = () => { + field.addClass('editempty'); + field.val(labelText); + } + ; + field.focus(() => { + if (field.hasClass('editempty')) { + field.val(''); + } + field.removeClass('editempty'); + }); + field.on('blur', () => { + if (!field.val()) { + clear(); + } + }); + return { + clear, + }; + } + getCheckbox = (node: string) => $(node).is(':checked') + setCheckbox = + (node: JQueryNode, value: boolean) => { + if (value) { + $(node).attr('checked', 'checked'); + } else { + $(node).prop('checked', false); } - }); - return { - clear, - }; - }, - getCheckbox: (node) => $(node).is(':checked'), - setCheckbox: (node, value) => { - if (value) { - $(node).attr('checked', 'checked'); - } else { - $(node).prop('checked', false); } - }, - bindCheckboxChange: (node, func) => { - $(node).on('change', func); - }, - encodeUserId: (userId) => userId.replace(/[^a-y0-9]/g, (c) => { - if (c === '.') return '-'; - return `z${c.charCodeAt(0)}z`; - }), - decodeUserId: (encodedUserId) => encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => { - if (cc === '-') { return '.'; } else if (cc.charAt(0) === 'z') { - return String.fromCharCode(Number(cc.slice(1, -1))); - } else { - return cc; + bindCheckboxChange = + (node: JQueryNode, func: Function) => { + // @ts-ignore + $(node).on("change", func); } - }), - + encodeUserId = + (userId: string) => userId.replace(/[^a-y0-9]/g, (c) => { + if (c === '.') return '-'; + return `z${c.charCodeAt(0)}z`; + }) + decodeUserId = + (encodedUserId: string) => encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => { + if (cc === '-') { + return '.'; + } else if (cc.charAt(0) === 'z') { + return String.fromCharCode(Number(cc.slice(1, -1))); + } else { + return cc; + } + }) /** * Returns whether a string has the expected format to be used as a secret token identifying an * author. The format is defined as: 't.' followed by a non-empty base64url string (RFC 4648 @@ -340,110 +383,109 @@ const padutils = { * conditional transformation of a token to a database key in a way that does not allow a * malicious user to impersonate another user). */ - isValidAuthorToken: (t) => { + isValidAuthorToken = (t: string | object) => { if (typeof t !== 'string' || !t.startsWith('t.')) return false; const v = t.slice(2); return v.length > 0 && base64url.test(v); - }, + } + /** * Returns a string that can be used in the `token` cookie as a secret that authenticates a * particular author. */ - generateAuthorToken: () => `t.${randomString()}`, -}; - -let globalExceptionHandler = null; -padutils.setupGlobalExceptionHandler = () => { - if (globalExceptionHandler == null) { - require('./vendors/gritter'); - globalExceptionHandler = (e) => { - let type; - let err; - let msg, url, linenumber; - if (e instanceof ErrorEvent) { - type = 'Uncaught exception'; - err = e.error || {}; - ({message: msg, filename: url, lineno: linenumber} = e); - } else if (e instanceof PromiseRejectionEvent) { - type = 'Unhandled Promise rejection'; - err = e.reason || {}; - ({message: msg = 'unknown', fileName: url = 'unknown', lineNumber: linenumber = -1} = err); - } else { - throw new Error(`unknown event: ${e.toString()}`); - } - if (err.name != null && msg !== err.name && !msg.startsWith(`${err.name}: `)) { - msg = `${err.name}: ${msg}`; - } - const errorId = randomString(20); - - let msgAlreadyVisible = false; - $('.gritter-item .error-msg').each(function () { - if ($(this).text() === msg) { - msgAlreadyVisible = true; + generateAuthorToken = () => `t.${randomString()}` + setupGlobalExceptionHandler = () => { + if (this.globalExceptionHandler == null) { + this.globalExceptionHandler = (e: any) => { + let type; + let err; + let msg, url, linenumber; + if (e instanceof ErrorEvent) { + type = 'Uncaught exception'; + err = e.error || {}; + ({message: msg, filename: url, lineno: linenumber} = e); + } else if (e instanceof PromiseRejectionEvent) { + type = 'Unhandled Promise rejection'; + err = e.reason || {}; + ({message: msg = 'unknown', fileName: url = 'unknown', lineNumber: linenumber = -1} = err); + } else { + throw new Error(`unknown event: ${e.toString()}`); } - }); + if (err.name != null && msg !== err.name && !msg.startsWith(`${err.name}: `)) { + msg = `${err.name}: ${msg}`; + } + const errorId = randomString(20); + + let msgAlreadyVisible = false; + $('.gritter-item .error-msg').each(function () { + if ($(this).text() === msg) { + msgAlreadyVisible = true; + } + }); - if (!msgAlreadyVisible) { - const txt = document.createTextNode.bind(document); // Convenience shorthand. - const errorMsg = [ - $('<p>') + if (!msgAlreadyVisible) { + const txt = document.createTextNode.bind(document); // Convenience shorthand. + const errorMsg = [ + $('<p>') .append($('<b>').text('Please press and hold Ctrl and press F5 to reload this page')), - $('<p>') + $('<p>') .text('If the problem persists, please send this error message to your webmaster:'), - $('<div>').css('text-align', 'left').css('font-size', '.8em').css('margin-top', '1em') + $('<div>').css('text-align', 'left').css('font-size', '.8em').css('margin-top', '1em') .append($('<b>').addClass('error-msg').text(msg)).append($('<br>')) .append(txt(`at ${url} at line ${linenumber}`)).append($('<br>')) .append(txt(`ErrorId: ${errorId}`)).append($('<br>')) .append(txt(type)).append($('<br>')) .append(txt(`URL: ${window.location.href}`)).append($('<br>')) .append(txt(`UserAgent: ${navigator.userAgent}`)).append($('<br>')), - ]; + ]; - $.gritter.add({ - title: 'An error occurred', - text: errorMsg, - class_name: 'error', - position: 'bottom', - sticky: true, - }); - } + // @ts-ignore + $.gritter.add({ + title: 'An error occurred', + text: errorMsg, + class_name: 'error', + position: 'bottom', + sticky: true, + }); + } - // send javascript errors to the server - $.post('../jserror', { - errorInfo: JSON.stringify({ - errorId, - type, - msg, - url: window.location.href, - source: url, - linenumber, - userAgent: navigator.userAgent, - stack: err.stack, - }), - }); - }; - window.onerror = null; // Clear any pre-existing global error handler. - window.addEventListener('error', globalExceptionHandler); - window.addEventListener('unhandledrejection', globalExceptionHandler); + // send javascript errors to the server + $.post('../jserror', { + errorInfo: JSON.stringify({ + errorId, + type, + msg, + url: window.location.href, + source: url, + linenumber, + userAgent: navigator.userAgent, + stack: err.stack, + }), + }); + }; + window.onerror = null; // Clear any pre-existing global error handler. + window.addEventListener('error', this.globalExceptionHandler); + window.addEventListener('unhandledrejection', this.globalExceptionHandler); + } } -}; - -padutils.binarySearch = require('./ace2_common').binarySearch; + binarySearch = binarySearch +} // https://stackoverflow.com/a/42660748 const inThirdPartyIframe = () => { try { - return (!window.top.location.hostname); + return (!window.top!.location.hostname); } catch (e) { return true; } }; +export let Cookies: CookiesStatic<string> // This file is included from Node so that it can reuse randomString, but Node doesn't have a global // window object. if (typeof window !== 'undefined') { - exports.Cookies = require('js-cookie/dist/js.cookie').withAttributes({ + Cookies = jsCookie.withAttributes({ // Use `SameSite=Lax`, unless Etherpad is embedded in an iframe from another site in which case // use `SameSite=None`. For iframes from another site, only `None` has a chance of working // because the cookies are third-party (not same-site). Many browsers/users block third-party @@ -456,5 +498,5 @@ if (typeof window !== 'undefined') { secure: window.location.protocol === 'https:', }); } -exports.randomString = randomString; -exports.padutils = padutils; + +export default new PadUtils() diff --git a/src/static/js/pluginfw/LinkInstaller.ts b/src/static/js/pluginfw/LinkInstaller.ts index 7a4721e57ae..e5eaf0b5c89 100644 --- a/src/static/js/pluginfw/LinkInstaller.ts +++ b/src/static/js/pluginfw/LinkInstaller.ts @@ -1,4 +1,4 @@ -import {IPluginInfo, PluginManager} from "live-plugin-manager-pnpm"; +import {IPluginInfo, PluginManager} from "live-plugin-manager"; import path from "path"; import {node_modules, pluginInstallPath} from "./installer"; import {accessSync, constants, rmSync, symlinkSync, unlinkSync} from "node:fs"; @@ -19,8 +19,8 @@ export class LinkInstaller { constructor() { this.livePluginManager = new PluginManager({ pluginsPath: pluginInstallPath, - cwd: path.join(settings.root, 'src'), - hostRequire: undefined + hostRequire: undefined, + cwd: path.join(settings.root, 'src') }); this.dependenciesMap = new Map(); @@ -44,6 +44,12 @@ export class LinkInstaller { await this.checkLinkedDependencies(installedPlugin) } + public async installFromGitHub(repository: string) { + const installedPlugin = await this.livePluginManager.installFromGithub(repository) + this.linkDependency(installedPlugin.name) + await this.checkLinkedDependencies(installedPlugin) + } + public async installPlugin(pluginName: string, version?: string) { if (version) { const installedPlugin = await this.livePluginManager.install(pluginName, version); diff --git a/src/static/js/pluginfw/client_plugins.js b/src/static/js/pluginfw/client_plugins.ts similarity index 64% rename from src/static/js/pluginfw/client_plugins.js rename to src/static/js/pluginfw/client_plugins.ts index 221e786f89b..0688d12ca7a 100644 --- a/src/static/js/pluginfw/client_plugins.js +++ b/src/static/js/pluginfw/client_plugins.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; const pluginUtils = require('./shared'); @@ -7,24 +8,13 @@ exports.baseURL = ''; exports.ensure = (cb) => !defs.loaded ? exports.update(cb) : cb(); -exports.update = (cb) => { - // It appears that this response (see #620) may interrupt the current thread - // of execution on Firefox. This schedules the response in the run-loop, - // which appears to fix the issue. - const callback = () => setTimeout(cb, 0); - - jQuery.getJSON( - `${exports.baseURL}pluginfw/plugin-definitions.json?v=${clientVars.randomVersionString}` - ).done((data) => { - defs.plugins = data.plugins; - defs.parts = data.parts; - defs.hooks = pluginUtils.extractHooks(defs.parts, 'client_hooks'); - defs.loaded = true; - callback(); - }).fail((err) => { - console.error(`Failed to load plugin-definitions: ${err}`); - callback(); - }); +exports.update = async (modules) => { + const data = await jQuery.getJSON( + `${exports.baseURL}pluginfw/plugin-definitions.json?v=${clientVars.randomVersionString}`); + defs.plugins = data.plugins; + defs.parts = data.parts; + defs.hooks = pluginUtils.extractHooks(defs.parts, 'client_hooks', null, modules); + defs.loaded = true; }; const adoptPluginsFromAncestorsOf = (frame) => { diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.ts similarity index 99% rename from src/static/js/pluginfw/hooks.js rename to src/static/js/pluginfw/hooks.ts index 89f4267bba9..a480ecf46ee 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; const pluginDefs = require('./plugin_defs'); @@ -69,7 +70,7 @@ const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); // A hook function settles when it provides a value (via callback or return) or throws. If a hook // function attempts to settle again (e.g., call the callback again, or call the callback and also // return a value) then the second attempt has no effect except either an error message is logged or -// there will be an unhandled promise rejection depending on whether the the subsequent attempt is a +// there will be an unhandled promise rejection depending on whether the subsequent attempt is a // duplicate (same value or error) or different, respectively. // // See the tests in src/tests/backend/specs/hooks.js for examples of supported and prohibited @@ -225,7 +226,7 @@ exports.callAll = (hookName, context) => { // provides settles (resolves or rejects). If a hook function attempts to settle again (e.g., call // the callback again, or return a value and also call the callback) then the second attempt has no // effect except either an error message is logged or an Error object is thrown depending on whether -// the the subsequent attempt is a duplicate (same value or error) or different, respectively. +// the subsequent attempt is a duplicate (same value or error) or different, respectively. // // See the tests in src/tests/backend/specs/hooks.js for examples of supported and prohibited // behaviors. diff --git a/src/static/js/pluginfw/installer.ts b/src/static/js/pluginfw/installer.ts index 78cc3d081a4..effed768a80 100644 --- a/src/static/js/pluginfw/installer.ts +++ b/src/static/js/pluginfw/installer.ts @@ -60,11 +60,12 @@ const migratePluginsFromNodeModules = async () => { const cmd = ['pnpm', 'ls', '--long', '--json', '--depth=0', '--no-production']; const [{dependencies = {}}] = JSON.parse(await runCmd(cmd, {stdio: [null, 'string']})); + await Promise.all(Object.entries(dependencies) .filter(([pkg, info]) => pkg.startsWith(plugins.prefix) && pkg !== 'ep_etherpad-lite') .map(async ([pkg, info]) => { const _info = info as PackageInfo - if (!_info.resolved || _info.resolved.includes('github')) { + if (!_info.resolved) { // Install from node_modules directory await linkInstaller.installFromPath(`${findEtherpadRoot()}/node_modules/${pkg}`); } else { @@ -99,6 +100,10 @@ export const checkForMigration = async () => { for (let file of files){ const moduleName = path.basename(file); + if (moduleName === '.versions') { + // Skip the directory using live-plugin-manager + continue; + } try { await fs.access(path.join(node_modules, moduleName), fs.constants.F_OK); logger.debug(`plugin ${moduleName} already exists in node_modules`); diff --git a/src/static/js/pluginfw/plugin_defs.js b/src/static/js/pluginfw/plugin_defs.ts similarity index 100% rename from src/static/js/pluginfw/plugin_defs.js rename to src/static/js/pluginfw/plugin_defs.ts diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.ts similarity index 98% rename from src/static/js/pluginfw/plugins.js rename to src/static/js/pluginfw/plugins.ts index 61c28ae7f30..97c1694e217 100644 --- a/src/static/js/pluginfw/plugins.js +++ b/src/static/js/pluginfw/plugins.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; const fs = require('fs').promises; @@ -129,8 +130,7 @@ exports.getPackages = async () => { if (!plugin.name.startsWith(exports.prefix)) { continue; } - plugin.realPath = await fs.realpath(plugin.location); - plugin.path = plugin.realPath; + plugin.path = plugin.realPath = plugin.location; newDependencies[plugin.name] = plugin; } diff --git a/src/static/js/pluginfw/shared.js b/src/static/js/pluginfw/shared.ts similarity index 89% rename from src/static/js/pluginfw/shared.js rename to src/static/js/pluginfw/shared.ts index 2c81ccd8198..a7c76178619 100644 --- a/src/static/js/pluginfw/shared.js +++ b/src/static/js/pluginfw/shared.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; const defs = require('./plugin_defs'); @@ -9,7 +10,7 @@ const disabledHookReasons = { }, }; -const loadFn = (path, hookName) => { +const loadFn = (path, hookName, modules) => { let functionName; const parts = path.split(':'); @@ -24,7 +25,13 @@ const loadFn = (path, hookName) => { functionName = parts[1]; } - let fn = require(path); + let fn + if (modules === undefined || !("get" in modules)) { + fn = require(/* webpackIgnore: true */ path); + } else { + fn = modules.get(path); + } + functionName = functionName ? functionName : hookName; for (const name of functionName.split('.')) { @@ -33,7 +40,7 @@ const loadFn = (path, hookName) => { return fn; }; -const extractHooks = (parts, hookSetName, normalizer) => { +const extractHooks = (parts, hookSetName, normalizer, modules) => { const hooks = {}; for (const part of parts) { for (const [hookName, regHookFnName] of Object.entries(part[hookSetName] || {})) { @@ -53,7 +60,7 @@ const extractHooks = (parts, hookSetName, normalizer) => { } let hookFn; try { - hookFn = loadFn(hookFnName, hookName); + hookFn = loadFn(hookFnName, hookName, modules); if (!hookFn) throw new Error('Not a function'); } catch (err) { console.error(`Failed to load hook function "${hookFnName}" for plugin "${part.plugin}" ` + diff --git a/src/static/js/pluginfw/tsort.js b/src/static/js/pluginfw/tsort.ts similarity index 99% rename from src/static/js/pluginfw/tsort.js rename to src/static/js/pluginfw/tsort.ts index 117f8555cd8..a067d29de8d 100644 --- a/src/static/js/pluginfw/tsort.js +++ b/src/static/js/pluginfw/tsort.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -70,7 +71,6 @@ const tsortTest = () => { ]; let sorted = tsort(edges); - console.log(sorted); // example 2: failure ( A > B > C > A ) edges = [ diff --git a/src/static/js/qrcode_download.js b/src/static/js/qrcode_download.js index a3a6fc380e5..324623179e6 100644 --- a/src/static/js/qrcode_download.js +++ b/src/static/js/qrcode_download.js @@ -1,4 +1,5 @@ function initialize_qrcode() { + console.log('initialize_qrcode') document.getElementById("embedreadonly").addEventListener('change', (changeEvent) => { generate_qrcode(); return true; @@ -8,8 +9,8 @@ function initialize_qrcode() { } function generate_qrcode() { - const url = document.getElementById("linkinput").value; - const qrCodeDiv = document.getElementById('qrcode') + const url = document.getElementById("linkinput").value || window.location.href; + const qrCodeDiv = document.getElementById("qrcode"); const width = 200; const height = 200; @@ -48,13 +49,13 @@ function qrCodeOptions(url, width, height) { function createQrCode(qrCodeDiv, url, width, height) { const canvas = document.createElement('div'); - qrCodeDiv.appendChild(canvas) + qrCodeDiv.appendChild(canvas); let qrCode = new QRCodeStyling(qrCodeOptions(url, width, height)); qrCode.append(canvas); const downloadButton = addDownloadButton(qrCode); - qrCodeDiv.appendChild(downloadButton) + qrCodeDiv.appendChild(downloadButton); return qrCode } diff --git a/src/static/js/rjquery.js b/src/static/js/rjquery.ts similarity index 93% rename from src/static/js/rjquery.js rename to src/static/js/rjquery.ts index a80e1f8d30a..167e960907b 100644 --- a/src/static/js/rjquery.js +++ b/src/static/js/rjquery.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; // Provides a require'able version of jQuery without leaking $ and jQuery; window.$ = require('./vendors/jquery'); diff --git a/src/static/js/scroll.js b/src/static/js/scroll.js deleted file mode 100644 index 86d6a33445f..00000000000 --- a/src/static/js/scroll.js +++ /dev/null @@ -1,351 +0,0 @@ -'use strict'; - -/* - This file handles scroll on edition or when user presses arrow keys. - In this file we have two representations of line (browser and rep line). - Rep Line = a line in the way is represented by Etherpad(rep) (each <div> is a line) - Browser Line = each vertical line. A <div> can be break into more than one - browser line. -*/ -const caretPosition = require('./caretPosition'); - -function Scroll(outerWin) { - // scroll settings - this.scrollSettings = parent.parent.clientVars.scrollWhenFocusLineIsOutOfViewport; - - // DOM reference - this.outerWin = outerWin; - this.doc = this.outerWin.document; - this.rootDocument = parent.parent.document; -} - -Scroll.prototype.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary = - function (rep, isScrollableEvent, innerHeight) { - // are we placing the caret on the line at the bottom of viewport? - // And if so, do we need to scroll the editor, as defined on the settings.json? - const shouldScrollWhenCaretIsAtBottomOfViewport = - this.scrollSettings.scrollWhenCaretIsInTheLastLineOfViewport; - if (shouldScrollWhenCaretIsAtBottomOfViewport) { - // avoid scrolling when selection includes multiple lines -- - // user can potentially be selecting more lines - // than it fits on viewport - const multipleLinesSelected = rep.selStart[0] !== rep.selEnd[0]; - - // avoid scrolling when pad loads - if (isScrollableEvent && !multipleLinesSelected && this._isCaretAtTheBottomOfViewport(rep)) { - // when scrollWhenFocusLineIsOutOfViewport.percentage is 0, pixelsToScroll is 0 - const pixelsToScroll = this._getPixelsRelativeToPercentageOfViewport(innerHeight); - this._scrollYPage(pixelsToScroll); - } - } - }; - -Scroll.prototype.scrollWhenPressArrowKeys = function (arrowUp, rep, innerHeight) { - // if percentageScrollArrowUp is 0, let the scroll to be handled as default, put the previous - // rep line on the top of the viewport - if (this._arrowUpWasPressedInTheFirstLineOfTheViewport(arrowUp, rep)) { - const pixelsToScroll = this._getPixelsToScrollWhenUserPressesArrowUp(innerHeight); - - // by default, the browser scrolls to the middle of the viewport. To avoid the twist made - // when we apply a second scroll, we made it immediately (without animation) - this._scrollYPageWithoutAnimation(-pixelsToScroll); - } else { - this.scrollNodeVerticallyIntoView(rep, innerHeight); - } -}; - -// Some plugins might set a minimum height to the editor (ex: ep_page_view), so checking -// if (caretLine() === rep.lines.length() - 1) is not enough. We need to check if there are -// other lines after caretLine(), and all of them are out of viewport. -Scroll.prototype._isCaretAtTheBottomOfViewport = function (rep) { - // computing a line position using getBoundingClientRect() is expensive. - // (obs: getBoundingClientRect() is called on caretPosition.getPosition()) - // To avoid that, we only call this function when it is possible that the - // caret is in the bottom of viewport - const caretLine = rep.selStart[0]; - const lineAfterCaretLine = caretLine + 1; - const firstLineVisibleAfterCaretLine = caretPosition.getNextVisibleLine(lineAfterCaretLine, rep); - const caretLineIsPartiallyVisibleOnViewport = - this._isLinePartiallyVisibleOnViewport(caretLine, rep); - const lineAfterCaretLineIsPartiallyVisibleOnViewport = - this._isLinePartiallyVisibleOnViewport(firstLineVisibleAfterCaretLine, rep); - if (caretLineIsPartiallyVisibleOnViewport || lineAfterCaretLineIsPartiallyVisibleOnViewport) { - // check if the caret is in the bottom of the viewport - const caretLinePosition = caretPosition.getPosition(); - const viewportBottom = this._getViewPortTopBottom().bottom; - const nextLineBottom = caretPosition.getBottomOfNextBrowserLine(caretLinePosition, rep); - const nextLineIsBelowViewportBottom = nextLineBottom > viewportBottom; - return nextLineIsBelowViewportBottom; - } - return false; -}; - -Scroll.prototype._isLinePartiallyVisibleOnViewport = function (lineNumber, rep) { - const lineNode = rep.lines.atIndex(lineNumber); - const linePosition = this._getLineEntryTopBottom(lineNode); - const lineTop = linePosition.top; - const lineBottom = linePosition.bottom; - const viewport = this._getViewPortTopBottom(); - const viewportBottom = viewport.bottom; - const viewportTop = viewport.top; - - const topOfLineIsAboveOfViewportBottom = lineTop < viewportBottom; - const bottomOfLineIsOnOrBelowOfViewportBottom = lineBottom >= viewportBottom; - const topOfLineIsBelowViewportTop = lineTop >= viewportTop; - const topOfLineIsAboveViewportBottom = lineTop <= viewportBottom; - const bottomOfLineIsAboveViewportBottom = lineBottom <= viewportBottom; - const bottomOfLineIsBelowViewportTop = lineBottom >= viewportTop; - - return (topOfLineIsAboveOfViewportBottom && bottomOfLineIsOnOrBelowOfViewportBottom) || - (topOfLineIsBelowViewportTop && topOfLineIsAboveViewportBottom) || - (bottomOfLineIsAboveViewportBottom && bottomOfLineIsBelowViewportTop); -}; - -Scroll.prototype._getViewPortTopBottom = function () { - const theTop = this.getScrollY(); - const doc = this.doc; - const height = doc.documentElement.clientHeight; // includes padding - - // we have to get the exactly height of the viewport. - // So it has to subtract all the values which changes - // the viewport height (E.g. padding, position top) - const viewportExtraSpacesAndPosition = - this._getEditorPositionTop() + this._getPaddingTopAddedWhenPageViewIsEnable(); - return { - top: theTop, - bottom: (theTop + height - viewportExtraSpacesAndPosition), - }; -}; - -Scroll.prototype._getEditorPositionTop = function () { - const editor = parent.document.getElementsByTagName('iframe'); - const editorPositionTop = editor[0].offsetTop; - return editorPositionTop; -}; - -// ep_page_view adds padding-top, which makes the viewport smaller -Scroll.prototype._getPaddingTopAddedWhenPageViewIsEnable = function () { - const aceOuter = this.rootDocument.getElementsByName('ace_outer'); - const aceOuterPaddingTop = parseInt($(aceOuter).css('padding-top')); - return aceOuterPaddingTop; -}; - -Scroll.prototype._getScrollXY = function () { - const win = this.outerWin; - const odoc = this.doc; - if (typeof (win.pageYOffset) === 'number') { - return { - x: win.pageXOffset, - y: win.pageYOffset, - }; - } - const docel = odoc.documentElement; - if (docel && typeof (docel.scrollTop) === 'number') { - return { - x: docel.scrollLeft, - y: docel.scrollTop, - }; - } -}; - -Scroll.prototype.getScrollX = function () { - return this._getScrollXY().x; -}; - -Scroll.prototype.getScrollY = function () { - return this._getScrollXY().y; -}; - -Scroll.prototype.setScrollX = function (x) { - this.outerWin.scrollTo(x, this.getScrollY()); -}; - -Scroll.prototype.setScrollY = function (y) { - this.outerWin.scrollTo(this.getScrollX(), y); -}; - -Scroll.prototype.setScrollXY = function (x, y) { - this.outerWin.scrollTo(x, y); -}; - -Scroll.prototype._isCaretAtTheTopOfViewport = function (rep) { - const caretLine = rep.selStart[0]; - const linePrevCaretLine = caretLine - 1; - const firstLineVisibleBeforeCaretLine = - caretPosition.getPreviousVisibleLine(linePrevCaretLine, rep); - const caretLineIsPartiallyVisibleOnViewport = - this._isLinePartiallyVisibleOnViewport(caretLine, rep); - const lineBeforeCaretLineIsPartiallyVisibleOnViewport = - this._isLinePartiallyVisibleOnViewport(firstLineVisibleBeforeCaretLine, rep); - if (caretLineIsPartiallyVisibleOnViewport || lineBeforeCaretLineIsPartiallyVisibleOnViewport) { - const caretLinePosition = caretPosition.getPosition(); // get the position of the browser line - const viewportPosition = this._getViewPortTopBottom(); - const viewportTop = viewportPosition.top; - const viewportBottom = viewportPosition.bottom; - const caretLineIsBelowViewportTop = caretLinePosition.bottom >= viewportTop; - const caretLineIsAboveViewportBottom = caretLinePosition.top < viewportBottom; - const caretLineIsInsideOfViewport = - caretLineIsBelowViewportTop && caretLineIsAboveViewportBottom; - if (caretLineIsInsideOfViewport) { - const prevLineTop = caretPosition.getPositionTopOfPreviousBrowserLine(caretLinePosition, rep); - const previousLineIsAboveViewportTop = prevLineTop < viewportTop; - return previousLineIsAboveViewportTop; - } - } - return false; -}; - -// By default, when user makes an edition in a line out of viewport, this line goes -// to the edge of viewport. This function gets the extra pixels necessary to get the -// caret line in a position X relative to Y% viewport. -Scroll.prototype._getPixelsRelativeToPercentageOfViewport = - function (innerHeight, aboveOfViewport) { - let pixels = 0; - const scrollPercentageRelativeToViewport = this._getPercentageToScroll(aboveOfViewport); - if (scrollPercentageRelativeToViewport > 0 && scrollPercentageRelativeToViewport <= 1) { - pixels = parseInt(innerHeight * scrollPercentageRelativeToViewport); - } - return pixels; - }; - -// we use different percentages when change selection. It depends on if it is -// either above the top or below the bottom of the page -Scroll.prototype._getPercentageToScroll = function (aboveOfViewport) { - let percentageToScroll = this.scrollSettings.percentage.editionBelowViewport; - if (aboveOfViewport) { - percentageToScroll = this.scrollSettings.percentage.editionAboveViewport; - } - return percentageToScroll; -}; - -Scroll.prototype._getPixelsToScrollWhenUserPressesArrowUp = function (innerHeight) { - let pixels = 0; - const percentageToScrollUp = this.scrollSettings.percentageToScrollWhenUserPressesArrowUp; - if (percentageToScrollUp > 0 && percentageToScrollUp <= 1) { - pixels = parseInt(innerHeight * percentageToScrollUp); - } - return pixels; -}; - -Scroll.prototype._scrollYPage = function (pixelsToScroll) { - const durationOfAnimationToShowFocusline = this.scrollSettings.duration; - if (durationOfAnimationToShowFocusline) { - this._scrollYPageWithAnimation(pixelsToScroll, durationOfAnimationToShowFocusline); - } else { - this._scrollYPageWithoutAnimation(pixelsToScroll); - } -}; - -Scroll.prototype._scrollYPageWithoutAnimation = function (pixelsToScroll) { - this.outerWin.scrollBy(0, pixelsToScroll); -}; - -Scroll.prototype._scrollYPageWithAnimation = - function (pixelsToScroll, durationOfAnimationToShowFocusline) { - const outerDocBody = this.doc.getElementById('outerdocbody'); - - // it works on later versions of Chrome - const $outerDocBody = $(outerDocBody); - this._triggerScrollWithAnimation( - $outerDocBody, pixelsToScroll, durationOfAnimationToShowFocusline); - - // it works on Firefox and earlier versions of Chrome - const $outerDocBodyParent = $outerDocBody.parent(); - this._triggerScrollWithAnimation( - $outerDocBodyParent, pixelsToScroll, durationOfAnimationToShowFocusline); - }; - -// using a custom queue and clearing it, we avoid creating a queue of scroll animations. -// So if this function is called twice quickly, only the last one runs. -Scroll.prototype._triggerScrollWithAnimation = - function ($elem, pixelsToScroll, durationOfAnimationToShowFocusline) { - // clear the queue of animation - $elem.stop('scrollanimation'); - $elem.animate({ - scrollTop: `+=${pixelsToScroll}`, - }, { - duration: durationOfAnimationToShowFocusline, - queue: 'scrollanimation', - }).dequeue('scrollanimation'); - }; - -// scrollAmountWhenFocusLineIsOutOfViewport is set to 0 (default), scroll it the minimum distance -// needed to be completely in view. If the value is greater than 0 and less than or equal to 1, -// besides of scrolling the minimum needed to be visible, it scrolls additionally -// (viewport height * scrollAmountWhenFocusLineIsOutOfViewport) pixels -Scroll.prototype.scrollNodeVerticallyIntoView = function (rep, innerHeight) { - const viewport = this._getViewPortTopBottom(); - - // when the selection changes outside of the viewport the browser automatically scrolls the line - // to inside of the viewport. Tested on IE, Firefox, Chrome in releases from 2015 until now - // So, when the line scrolled gets outside of the viewport we let the browser handle it. - const linePosition = caretPosition.getPosition(); - if (linePosition) { - const distanceOfTopOfViewport = linePosition.top - viewport.top; - const distanceOfBottomOfViewport = viewport.bottom - linePosition.bottom - linePosition.height; - const caretIsAboveOfViewport = distanceOfTopOfViewport < 0; - const caretIsBelowOfViewport = distanceOfBottomOfViewport < 0; - if (caretIsAboveOfViewport) { - const pixelsToScroll = - distanceOfTopOfViewport - this._getPixelsRelativeToPercentageOfViewport(innerHeight, true); - this._scrollYPage(pixelsToScroll); - } else if (caretIsBelowOfViewport) { - // setTimeout is required here as line might not be fully rendered onto the pad - setTimeout(() => { - const outer = window.parent; - // scroll to the very end of the pad outer - outer.scrollTo(0, outer[0].innerHeight); - }, 150); - // if the above setTimeout and functionality is removed then hitting an enter - // key while on the last line wont be an optimal user experience - // Details at: https://github.com/ether/etherpad-lite/pull/4639/files - } - } -}; - -Scroll.prototype._partOfRepLineIsOutOfViewport = function (viewportPosition, rep) { - const focusLine = (rep.selFocusAtStart ? rep.selStart[0] : rep.selEnd[0]); - const line = rep.lines.atIndex(focusLine); - const linePosition = this._getLineEntryTopBottom(line); - const lineIsAboveOfViewport = linePosition.top < viewportPosition.top; - const lineIsBelowOfViewport = linePosition.bottom > viewportPosition.bottom; - - return lineIsBelowOfViewport || lineIsAboveOfViewport; -}; - -Scroll.prototype._getLineEntryTopBottom = function (entry, destObj) { - const dom = entry.lineNode; - const top = dom.offsetTop; - const height = dom.offsetHeight; - const obj = (destObj || {}); - obj.top = top; - obj.bottom = (top + height); - return obj; -}; - -Scroll.prototype._arrowUpWasPressedInTheFirstLineOfTheViewport = function (arrowUp, rep) { - const percentageScrollArrowUp = this.scrollSettings.percentageToScrollWhenUserPressesArrowUp; - return percentageScrollArrowUp && arrowUp && this._isCaretAtTheTopOfViewport(rep); -}; - -Scroll.prototype.getVisibleLineRange = function (rep) { - const viewport = this._getViewPortTopBottom(); - // console.log("viewport top/bottom: %o", viewport); - const obj = {}; - const self = this; - const start = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).bottom > viewport.top); - // return the first line that the top position is greater or equal than - // the viewport. That is the first line that is below the viewport bottom. - // So the line that is in the bottom of the viewport is the very previous one. - let end = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).top >= viewport.bottom); - if (end < start) end = start; // unlikely - // top.console.log(start+","+(end -1)); - return [start, end - 1]; -}; - -Scroll.prototype.getVisibleCharRange = function (rep) { - const lineRange = this.getVisibleLineRange(rep); - return [rep.lines.offsetOfIndex(lineRange[0]), rep.lines.offsetOfIndex(lineRange[1])]; -}; - -exports.init = (outerWin) => new Scroll(outerWin); diff --git a/src/static/js/scroll.ts b/src/static/js/scroll.ts new file mode 100644 index 00000000000..95075d8078d --- /dev/null +++ b/src/static/js/scroll.ts @@ -0,0 +1,338 @@ +import {getBottomOfNextBrowserLine, getNextVisibleLine, getPosition, getPositionTopOfPreviousBrowserLine, getPreviousVisibleLine} from './caretPosition'; +import {Position, RepModel, RepNode, WindowElementWithScrolling} from "./types/RepModel"; + + +class Scroll { + private readonly outerWin: HTMLIFrameElement; + private readonly doc: Document; + private rootDocument: Document; + private scrollSettings: any; + + constructor(outerWin: HTMLIFrameElement) { + // @ts-ignore + this.scrollSettings = window.clientVars.scrollWhenFocusLineIsOutOfViewport; + + // DOM reference + this.outerWin = outerWin; + this.doc = this.outerWin.contentDocument!; + this.rootDocument = parent.parent.document; + } + + scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep: RepModel, isScrollableEvent: boolean, innerHeight: number) { + // are we placing the caret on the line at the bottom of viewport? + // And if so, do we need to scroll the editor, as defined on the settings.json? + const shouldScrollWhenCaretIsAtBottomOfViewport = + this.scrollSettings.scrollWhenCaretIsInTheLastLineOfViewport; + if (shouldScrollWhenCaretIsAtBottomOfViewport) { + // avoid scrolling when selection includes multiple lines -- + // user can potentially be selecting more lines + // than it fits on viewport + const multipleLinesSelected = rep.selStart[0] !== rep.selEnd[0]; + + // avoid scrolling when pad loads + if (isScrollableEvent && !multipleLinesSelected && this._isCaretAtTheBottomOfViewport(rep)) { + // when scrollWhenFocusLineIsOutOfViewport.percentage is 0, pixelsToScroll is 0 + const pixelsToScroll = this._getPixelsRelativeToPercentageOfViewport(innerHeight); + this._scrollYPage(pixelsToScroll); + } + } + } + + scrollWhenPressArrowKeys(arrowUp: boolean, rep: RepModel, innerHeight: number) { + // if percentageScrollArrowUp is 0, let the scroll to be handled as default, put the previous + // rep line on the top of the viewport + if (this._arrowUpWasPressedInTheFirstLineOfTheViewport(arrowUp, rep)) { + const pixelsToScroll = this._getPixelsToScrollWhenUserPressesArrowUp(innerHeight); + + // by default, the browser scrolls to the middle of the viewport. To avoid the twist made + // when we apply a second scroll, we made it immediately (without animation) + this._scrollYPageWithoutAnimation(-pixelsToScroll); + } else { + this.scrollNodeVerticallyIntoView(rep, innerHeight); + } + } + + _isCaretAtTheBottomOfViewport(rep: RepModel) { + // computing a line position using getBoundingClientRect() is expensive. + // (obs: getBoundingClientRect() is called on caretPosition.getPosition()) + // To avoid that, we only call this function when it is possible that the + // caret is in the bottom of viewport + const caretLine = rep.selStart[0]; + const lineAfterCaretLine = caretLine + 1; + const firstLineVisibleAfterCaretLine = getNextVisibleLine(lineAfterCaretLine, rep); + const caretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(caretLine, rep); + const lineAfterCaretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(firstLineVisibleAfterCaretLine, rep); + if (caretLineIsPartiallyVisibleOnViewport || lineAfterCaretLineIsPartiallyVisibleOnViewport) { + // check if the caret is in the bottom of the viewport + const caretLinePosition = getPosition()!; + const viewportBottom = this._getViewPortTopBottom().bottom; + const nextLineBottom = getBottomOfNextBrowserLine(caretLinePosition, rep); + return nextLineBottom > viewportBottom; + } + return false; + }; + + _isLinePartiallyVisibleOnViewport(lineNumber: number, rep: RepModel){ + const lineNode = rep.lines.atIndex(lineNumber); + const linePosition = this._getLineEntryTopBottom(lineNode); + const lineTop = linePosition.top; + const lineBottom = linePosition.bottom; + const viewport = this._getViewPortTopBottom(); + const viewportBottom = viewport.bottom; + const viewportTop = viewport.top; + + const topOfLineIsAboveOfViewportBottom = lineTop < viewportBottom; + const bottomOfLineIsOnOrBelowOfViewportBottom = lineBottom >= viewportBottom; + const topOfLineIsBelowViewportTop = lineTop >= viewportTop; + const topOfLineIsAboveViewportBottom = lineTop <= viewportBottom; + const bottomOfLineIsAboveViewportBottom = lineBottom <= viewportBottom; + const bottomOfLineIsBelowViewportTop = lineBottom >= viewportTop; + + return (topOfLineIsAboveOfViewportBottom && bottomOfLineIsOnOrBelowOfViewportBottom) || + (topOfLineIsBelowViewportTop && topOfLineIsAboveViewportBottom) || + (bottomOfLineIsAboveViewportBottom && bottomOfLineIsBelowViewportTop); + }; + + _getViewPortTopBottom() { + const theTop = this.getScrollY(); + const doc = this.doc; + const height = doc.documentElement.clientHeight; // includes padding + + // we have to get the exactly height of the viewport. + // So it has to subtract all the values which changes + // the viewport height (E.g. padding, position top) + const viewportExtraSpacesAndPosition = + this._getEditorPositionTop() + this._getPaddingTopAddedWhenPageViewIsEnable(); + return { + top: theTop, + bottom: (theTop + height - viewportExtraSpacesAndPosition), + }; + }; + + _getEditorPositionTop() { + const editor = parent.document.getElementsByTagName('iframe'); + const editorPositionTop = editor[0].offsetTop; + return editorPositionTop; + }; + + _getPaddingTopAddedWhenPageViewIsEnable() { + const aceOuter = this.rootDocument.getElementsByName('ace_outer'); + const aceOuterPaddingTop = parseInt($(aceOuter).css('padding-top')); + return aceOuterPaddingTop; + }; + + _getScrollXY() { + const win = this.outerWin as WindowElementWithScrolling; + const odoc = this.doc; + if (typeof (win.pageYOffset) === 'number') { + return { + x: win.pageXOffset, + y: win.pageYOffset, + }; + } + const docel = odoc.documentElement; + if (docel && typeof (docel.scrollTop) === 'number') { + return { + x: docel.scrollLeft, + y: docel.scrollTop, + }; + } + }; + + getScrollX() { + return this._getScrollXY()!.x; + }; + + getScrollY () { + return this._getScrollXY()!.y; + }; + + setScrollX(x: number) { + this.outerWin.scrollTo(x, this.getScrollY()); + }; + + setScrollY(y: number) { + this.outerWin.scrollTo(this.getScrollX(), y); + }; + + setScrollXY(x: number, y: number) { + this.outerWin.scrollTo(x, y); + }; + + _isCaretAtTheTopOfViewport(rep: RepModel) { + const caretLine = rep.selStart[0]; + const linePrevCaretLine = caretLine - 1; + const firstLineVisibleBeforeCaretLine = + getPreviousVisibleLine(linePrevCaretLine, rep); + const caretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(caretLine, rep); + const lineBeforeCaretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(firstLineVisibleBeforeCaretLine, rep); + if (caretLineIsPartiallyVisibleOnViewport || lineBeforeCaretLineIsPartiallyVisibleOnViewport) { + const caretLinePosition = getPosition(); // get the position of the browser line + const viewportPosition = this._getViewPortTopBottom(); + const viewportTop = viewportPosition.top; + const viewportBottom = viewportPosition.bottom; + const caretLineIsBelowViewportTop = caretLinePosition!.bottom >= viewportTop; + const caretLineIsAboveViewportBottom = caretLinePosition!.top < viewportBottom; + const caretLineIsInsideOfViewport = + caretLineIsBelowViewportTop && caretLineIsAboveViewportBottom; + if (caretLineIsInsideOfViewport) { + const prevLineTop = getPositionTopOfPreviousBrowserLine(caretLinePosition!, rep); + const previousLineIsAboveViewportTop = prevLineTop < viewportTop; + return previousLineIsAboveViewportTop; + } + } + return false; + }; + + // By default, when user makes an edition in a line out of viewport, this line goes +// to the edge of viewport. This function gets the extra pixels necessary to get the +// caret line in a position X relative to Y% viewport. + _getPixelsRelativeToPercentageOfViewport(innerHeight: number, aboveOfViewport?: boolean) { + let pixels = 0; + const scrollPercentageRelativeToViewport = this._getPercentageToScroll(aboveOfViewport); + if (scrollPercentageRelativeToViewport > 0 && scrollPercentageRelativeToViewport <= 1) { + pixels = parseInt(String(innerHeight * scrollPercentageRelativeToViewport)); + } + return pixels; + }; + + // we use different percentages when change selection. It depends on if it is +// either above the top or below the bottom of the page + _getPercentageToScroll(aboveOfViewport: boolean|undefined) { + let percentageToScroll = this.scrollSettings.percentage.editionBelowViewport; + if (aboveOfViewport) { + percentageToScroll = this.scrollSettings.percentage.editionAboveViewport; + } + return percentageToScroll; + }; + + _getPixelsToScrollWhenUserPressesArrowUp(innerHeight: number) { + let pixels = 0; + const percentageToScrollUp = this.scrollSettings.percentageToScrollWhenUserPressesArrowUp; + if (percentageToScrollUp > 0 && percentageToScrollUp <= 1) { + pixels = parseInt(String(innerHeight * percentageToScrollUp)); + } + return pixels; + }; + + _scrollYPage(pixelsToScroll: number) { + const durationOfAnimationToShowFocusline = this.scrollSettings.duration; + if (durationOfAnimationToShowFocusline) { + this._scrollYPageWithAnimation(pixelsToScroll, durationOfAnimationToShowFocusline); + } else { + this._scrollYPageWithoutAnimation(pixelsToScroll); + } + }; + + _scrollYPageWithoutAnimation(pixelsToScroll: number) { + this.outerWin.scrollBy(0, pixelsToScroll); + }; + + _scrollYPageWithAnimation(pixelsToScroll: number, durationOfAnimationToShowFocusline: number) { + const outerDocBody = this.doc.getElementById('outerdocbody'); + + // it works on later versions of Chrome + const $outerDocBody = $(outerDocBody!); + this._triggerScrollWithAnimation( + $outerDocBody, pixelsToScroll, durationOfAnimationToShowFocusline); + + // it works on Firefox and earlier versions of Chrome + const $outerDocBodyParent = $outerDocBody.parent(); + this._triggerScrollWithAnimation( + $outerDocBodyParent, pixelsToScroll, durationOfAnimationToShowFocusline); + }; + + _triggerScrollWithAnimation($elem:any, pixelsToScroll: number, durationOfAnimationToShowFocusline: number) { + // clear the queue of animation + $elem.stop('scrollanimation'); + $elem.animate({ + scrollTop: `+=${pixelsToScroll}`, + }, { + duration: durationOfAnimationToShowFocusline, + queue: 'scrollanimation', + }).dequeue('scrollanimation'); + }; + + + + scrollNodeVerticallyIntoView(rep: RepModel, innerHeight: number) { + const viewport = this._getViewPortTopBottom(); + + // when the selection changes outside of the viewport the browser automatically scrolls the line + // to inside of the viewport. Tested on IE, Firefox, Chrome in releases from 2015 until now + // So, when the line scrolled gets outside of the viewport we let the browser handle it. + const linePosition = getPosition(); + if (linePosition) { + const distanceOfTopOfViewport = linePosition.top - viewport.top; + const distanceOfBottomOfViewport = viewport.bottom - linePosition.bottom - linePosition.height; + const caretIsAboveOfViewport = distanceOfTopOfViewport < 0; + const caretIsBelowOfViewport = distanceOfBottomOfViewport < 0; + if (caretIsAboveOfViewport) { + const pixelsToScroll = + distanceOfTopOfViewport - this._getPixelsRelativeToPercentageOfViewport(innerHeight, true); + this._scrollYPage(pixelsToScroll); + } else if (caretIsBelowOfViewport) { + // setTimeout is required here as line might not be fully rendered onto the pad + setTimeout(() => { + const outer = window.parent; + // scroll to the very end of the pad outer + outer.scrollTo(0, outer[0].innerHeight); + }, 150); + // if the above setTimeout and functionality is removed then hitting an enter + // key while on the last line wont be an optimal user experience + // Details at: https://github.com/ether/etherpad-lite/pull/4639/files + } + } + }; + + _partOfRepLineIsOutOfViewport(viewportPosition: Position, rep: RepModel) { + const focusLine = (rep.selFocusAtStart ? rep.selStart[0] : rep.selEnd[0]); + const line = rep.lines.atIndex(focusLine); + const linePosition = this._getLineEntryTopBottom(line); + const lineIsAboveOfViewport = linePosition.top < viewportPosition.top; + const lineIsBelowOfViewport = linePosition.bottom > viewportPosition.bottom; + + return lineIsBelowOfViewport || lineIsAboveOfViewport; + }; + + _getLineEntryTopBottom(entry: RepNode, destObj?: Position) { + const dom = entry.lineNode; + const top = dom.offsetTop; + const height = dom.offsetHeight; + const obj = (destObj || {}) as Position; + obj.top = top; + obj.bottom = (top + height); + return obj; + }; + + _arrowUpWasPressedInTheFirstLineOfTheViewport(arrowUp: boolean, rep: RepModel) { + const percentageScrollArrowUp = this.scrollSettings.percentageToScrollWhenUserPressesArrowUp; + return percentageScrollArrowUp && arrowUp && this._isCaretAtTheTopOfViewport(rep); + }; + + getVisibleLineRange(rep: RepModel) { + const viewport = this._getViewPortTopBottom(); + // console.log("viewport top/bottom: %o", viewport); + const obj = {} as Position; + const self = this; + const start = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).bottom > viewport.top); + // return the first line that the top position is greater or equal than + // the viewport. That is the first line that is below the viewport bottom. + // So the line that is in the bottom of the viewport is the very previous one. + let end = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).top >= viewport.bottom); + if (end < start) end = start; // unlikely + // top.console.log(start+","+(end -1)); + return [start, end - 1]; + }; + + getVisibleCharRange(rep: RepModel) { + const lineRange = this.getVisibleLineRange(rep); + return [rep.lines.offsetOfIndex(lineRange[0]), rep.lines.offsetOfIndex(lineRange[1])]; + }; +} + +export default Scroll diff --git a/src/static/js/security.js b/src/static/js/security.ts similarity index 97% rename from src/static/js/security.js rename to src/static/js/security.ts index d92425cb789..d5f9b726622 100644 --- a/src/static/js/security.js +++ b/src/static/js/security.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/skin_variants.js b/src/static/js/skin_variants.ts similarity index 99% rename from src/static/js/skin_variants.js rename to src/static/js/skin_variants.ts index 9a0427ac908..c9783aaa3d8 100644 --- a/src/static/js/skin_variants.js +++ b/src/static/js/skin_variants.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; // Specific hash to display the skin variants builder popup diff --git a/src/static/js/skiplist.js b/src/static/js/skiplist.ts similarity index 61% rename from src/static/js/skiplist.js rename to src/static/js/skiplist.ts index f10a4e7a8ce..0e7724b2329 100644 --- a/src/static/js/skiplist.js +++ b/src/static/js/skiplist.ts @@ -22,10 +22,24 @@ * limitations under the License. */ -const _entryWidth = (e) => (e && e.width) || 0; +const _entryWidth = (e: Entry) => (e && e.width) || 0; + +type Entry = { + key: string, + value?: string + width?: number +} class Node { - constructor(entry, levels = 0, downSkips = 1, downSkipWidths = 0) { + public key: string|null + readonly entry: Entry|null + levels: number + upPtrs: Node[] + downPtrs: Node[] + downSkips: number[] + readonly downSkipWidths: number[] + + constructor(entry: Entry|null, levels = 0, downSkips: number|null = 1, downSkipWidths:number|null = 0) { this.key = entry != null ? entry.key : null; this.entry = entry; this.levels = levels; @@ -37,9 +51,9 @@ class Node { propagateWidthChange() { const oldWidth = this.downSkipWidths[0]; - const newWidth = _entryWidth(this.entry); + const newWidth = _entryWidth(this.entry!); const widthChange = newWidth - oldWidth; - let n = this; + let n: Node = this; let lvl = 0; while (lvl < n.levels) { n.downSkipWidths[lvl] += widthChange; @@ -57,17 +71,23 @@ class Node { // is still valid and points to the same index in the skiplist. Other operations with other points // invalidate this point. class Point { - constructor(skipList, loc) { - this._skipList = skipList; + private skipList: SkipList + private readonly loc: number + private readonly idxs: number[] + private readonly nodes: Node[] + private widthSkips: number[] + + constructor(skipList: SkipList, loc: number) { + this.skipList = skipList; this.loc = loc; - const numLevels = this._skipList._start.levels; + const numLevels = this.skipList.start.levels; let lvl = numLevels - 1; let i = -1; let ws = 0; - const nodes = new Array(numLevels); - const idxs = new Array(numLevels); - const widthSkips = new Array(numLevels); - nodes[lvl] = this._skipList._start; + const nodes: Node[] = new Array(numLevels); + const idxs: number[] = new Array(numLevels); + const widthSkips: number[] = new Array(numLevels); + nodes[lvl] = this.skipList.start; idxs[lvl] = -1; widthSkips[lvl] = 0; while (lvl >= 0) { @@ -94,9 +114,9 @@ class Point { return `Point(${this.loc})`; } - insert(entry) { + insert(entry: Entry) { if (entry.key == null) throw new Error('entry.key must not be null'); - if (this._skipList.containsKey(entry.key)) { + if (this.skipList.containsKey(entry.key)) { throw new Error(`an entry with key ${entry.key} already exists`); } @@ -115,14 +135,14 @@ class Point { if (lvl === pNodes.length) { // assume we have just passed the end of this.nodes, and reached one level greater // than the skiplist currently supports - pNodes[lvl] = this._skipList._start; + pNodes[lvl] = this.skipList.start; pIdxs[lvl] = -1; - this._skipList._start.levels++; - this._skipList._end.levels++; - this._skipList._start.downPtrs[lvl] = this._skipList._end; - this._skipList._end.upPtrs[lvl] = this._skipList._start; - this._skipList._start.downSkips[lvl] = this._skipList._keyToNodeMap.size + 1; - this._skipList._start.downSkipWidths[lvl] = this._skipList._totalWidth; + this.skipList.start.levels++; + this.skipList.end.levels++; + this.skipList.start.downPtrs[lvl] = this.skipList.end; + this.skipList.end.upPtrs[lvl] = this.skipList.start; + this.skipList.start.downSkips[lvl] = this.skipList.keyToNodeMap.size + 1; + this.skipList.start.downSkipWidths[lvl] = this.skipList._totalWidth; this.widthSkips[lvl] = 0; } const me = newNode; @@ -146,13 +166,13 @@ class Point { up.downSkips[lvl]++; up.downSkipWidths[lvl] += newWidth; } - this._skipList._keyToNodeMap.set(newNode.key, newNode); - this._skipList._totalWidth += newWidth; + this.skipList.keyToNodeMap.set(newNode.key as string, newNode); + this.skipList._totalWidth += newWidth; } delete() { const elem = this.nodes[0].downPtrs[0]; - const elemWidth = _entryWidth(elem.entry); + const elemWidth = _entryWidth(elem.entry!); for (let i = 0; i < this.nodes.length; i++) { if (i < elem.levels) { const up = elem.upPtrs[i]; @@ -169,8 +189,8 @@ class Point { up.downSkipWidths[i] -= elemWidth; } } - this._skipList._keyToNodeMap.delete(elem.key); - this._skipList._totalWidth -= elemWidth; + this.skipList.keyToNodeMap.delete(elem.key as string); + this.skipList._totalWidth -= elemWidth; } getNode() { @@ -183,20 +203,26 @@ class Point { * property that is a string. */ class SkipList { + start: Node + end: Node + _totalWidth: number + keyToNodeMap: Map<string, Node> + + constructor() { // if there are N elements in the skiplist, "start" is element -1 and "end" is element N - this._start = new Node(null, 1); - this._end = new Node(null, 1, null, null); + this.start = new Node(null, 1); + this.end = new Node(null, 1, null, null); this._totalWidth = 0; - this._keyToNodeMap = new Map(); - this._start.downPtrs[0] = this._end; - this._end.upPtrs[0] = this._start; + this.keyToNodeMap = new Map(); + this.start.downPtrs[0] = this.end; + this.end.upPtrs[0] = this.start; } - _getNodeAtOffset(targetOffset) { + _getNodeAtOffset(targetOffset: number) { let i = 0; - let n = this._start; - let lvl = this._start.levels - 1; + let n = this.start; + let lvl = this.start.levels - 1; while (lvl >= 0 && n.downPtrs[lvl]) { while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset)) { i += n.downSkipWidths[lvl]; @@ -204,17 +230,17 @@ class SkipList { } lvl--; } - if (n === this._start) return (this._start.downPtrs[0] || null); - if (n === this._end) { - return targetOffset === this._totalWidth ? (this._end.upPtrs[0] || null) : null; + if (n === this.start) return (this.start.downPtrs[0] || null); + if (n === this.end) { + return targetOffset === this._totalWidth ? (this.end.upPtrs[0] || null) : null; } return n; } - _getNodeIndex(node, byWidth) { + _getNodeIndex(node: Node, byWidth?: boolean) { let dist = (byWidth ? 0 : -1); let n = node; - while (n !== this._start) { + while (n !== this.start) { const lvl = n.levels - 1; n = n.upPtrs[lvl]; if (byWidth) dist += n.downSkipWidths[lvl]; @@ -223,17 +249,19 @@ class SkipList { return dist; } + totalWidth() { return this._totalWidth; } + // Returns index of first entry such that entryFunc(entry) is truthy, // or length() if no such entry. Assumes all falsy entries come before // all truthy entries. - search(entryFunc) { - let low = this._start; - let lvl = this._start.levels - 1; + search(entryFunc: Function) { + let low = this.start; + let lvl = this.start.levels - 1; let lowIndex = -1; - const f = (node) => { - if (node === this._start) return false; - else if (node === this._end) return true; + const f = (node: Node) => { + if (node === this.start) return false; + else if (node === this.end) return true; else return entryFunc(node.entry); }; @@ -249,20 +277,20 @@ class SkipList { return lowIndex + 1; } - length() { return this._keyToNodeMap.size; } + length() { return this.keyToNodeMap.size; } - atIndex(i) { + atIndex(i: number) { if (i < 0) console.warn(`atIndex(${i})`); - if (i >= this._keyToNodeMap.size) console.warn(`atIndex(${i}>=${this._keyToNodeMap.size})`); + if (i >= this.keyToNodeMap.size) console.warn(`atIndex(${i}>=${this.keyToNodeMap.size})`); return (new Point(this, i)).getNode().entry; } // differs from Array.splice() in that new elements are in an array, not varargs - splice(start, deleteCount, newEntryArray) { + splice(start: number, deleteCount: number, newEntryArray: Entry[]) { if (start < 0) console.warn(`splice(${start}, ...)`); - if (start + deleteCount > this._keyToNodeMap.size) { - console.warn(`splice(${start}, ${deleteCount}, ...), N=${this._keyToNodeMap.size}`); - console.warn('%s %s %s', typeof start, typeof deleteCount, typeof this._keyToNodeMap.size); + if (start + deleteCount > this.keyToNodeMap.size) { + console.warn(`splice(${start}, ${deleteCount}, ...), N=${this.keyToNodeMap.size}`); + console.warn('%s %s %s', typeof start, typeof deleteCount, typeof this.keyToNodeMap.size); console.trace(); } @@ -275,56 +303,55 @@ class SkipList { } } - next(entry) { return this._keyToNodeMap.get(entry.key).downPtrs[0].entry || null; } - prev(entry) { return this._keyToNodeMap.get(entry.key).upPtrs[0].entry || null; } - push(entry) { this.splice(this._keyToNodeMap.size, 0, [entry]); } + next(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.downPtrs[0].entry || null; } + prev(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.upPtrs[0].entry || null; } + push(entry: Entry) { this.splice(this.keyToNodeMap.size, 0, [entry]); } - slice(start, end) { + slice(start: number, end: number) { // act like Array.slice() if (start === undefined) start = 0; - else if (start < 0) start += this._keyToNodeMap.size; - if (end === undefined) end = this._keyToNodeMap.size; - else if (end < 0) end += this._keyToNodeMap.size; + else if (start < 0) start += this.keyToNodeMap.size; + if (end === undefined) end = this.keyToNodeMap.size; + else if (end < 0) end += this.keyToNodeMap.size; if (start < 0) start = 0; - if (start > this._keyToNodeMap.size) start = this._keyToNodeMap.size; + if (start > this.keyToNodeMap.size) start = this.keyToNodeMap.size; if (end < 0) end = 0; - if (end > this._keyToNodeMap.size) end = this._keyToNodeMap.size; + if (end > this.keyToNodeMap.size) end = this.keyToNodeMap.size; if (end <= start) return []; let n = this.atIndex(start); const array = [n]; for (let i = 1; i < (end - start); i++) { - n = this.next(n); + n = this.next(n!); array.push(n); } return array; } - atKey(key) { return this._keyToNodeMap.get(key).entry; } - indexOfKey(key) { return this._getNodeIndex(this._keyToNodeMap.get(key)); } - indexOfEntry(entry) { return this.indexOfKey(entry.key); } - containsKey(key) { return this._keyToNodeMap.has(key); } + atKey(key: string) { return this.keyToNodeMap.get(key)!.entry; } + indexOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap.get(key)!); } + indexOfEntry(entry: Entry) { return this.indexOfKey(entry.key); } + containsKey(key: string) { return this.keyToNodeMap.has(key); } // gets the last entry starting at or before the offset - atOffset(offset) { return this._getNodeAtOffset(offset).entry; } - keyAtOffset(offset) { return this.atOffset(offset).key; } - offsetOfKey(key) { return this._getNodeIndex(this._keyToNodeMap.get(key), true); } - offsetOfEntry(entry) { return this.offsetOfKey(entry.key); } - setEntryWidth(entry, width) { + atOffset(offset: number) { return this._getNodeAtOffset(offset)!.entry; } + keyAtOffset(offset: number) { return this.atOffset(offset)!.key; } + offsetOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap.get(key)!, true); } + offsetOfEntry(entry: Entry) { return this.offsetOfKey(entry.key); } + setEntryWidth(entry: Entry, width: number) { entry.width = width; - this._totalWidth += this._keyToNodeMap.get(entry.key).propagateWidthChange(); + this._totalWidth += this.keyToNodeMap.get(entry.key)!.propagateWidthChange(); } - totalWidth() { return this._totalWidth; } - offsetOfIndex(i) { + offsetOfIndex(i: number) { if (i < 0) return 0; - if (i >= this._keyToNodeMap.size) return this._totalWidth; - return this.offsetOfEntry(this.atIndex(i)); + if (i >= this.keyToNodeMap.size) return this._totalWidth; + return this.offsetOfEntry(this.atIndex(i)!); } - indexOfOffset(offset) { + indexOfOffset(offset: number) { if (offset <= 0) return 0; - if (offset >= this._totalWidth) return this._keyToNodeMap.size; - return this.indexOfEntry(this.atOffset(offset)); + if (offset >= this._totalWidth) return this.keyToNodeMap.size; + return this.indexOfEntry(this.atOffset(offset)!); } } -module.exports = SkipList; +export default SkipList diff --git a/src/static/js/socketio.js b/src/static/js/socketio.ts similarity index 90% rename from src/static/js/socketio.js rename to src/static/js/socketio.ts index 4d9e0940f77..aedd68c20e8 100644 --- a/src/static/js/socketio.js +++ b/src/static/js/socketio.ts @@ -1,4 +1,5 @@ -'use strict'; +// @ts-nocheck +import io from 'socket.io-client'; /** * Creates a socket.io connection. @@ -31,11 +32,11 @@ const connect = (etherpadBaseUrl, namespace = '/', options = {}) => { socket.on('connect_error', (error) => { console.log('Error connecting to pad', error); - if (socket.io.engine.transports.indexOf('polling') === -1) { + /*if (socket.io.engine.transports.indexOf('polling') === -1) { console.warn('WebSocket connection failed. Falling back to long-polling.'); - socket.io.opts.transports = ['polling']; + socket.io.opts.transports = ['websocket','polling']; socket.io.engine.upgrade = false; - } + }*/ }); return socket; diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.ts similarity index 93% rename from src/static/js/timeslider.js rename to src/static/js/timeslider.ts index 4cc5d45a629..befc4f8b7b4 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -26,12 +27,11 @@ // assigns to the global `$` and augments it with plugins. require('./vendors/jquery'); -const Cookies = require('./pad_utils').Cookies; -const randomString = require('./pad_utils').randomString; +import {randomString, Cookies} from "./pad_utils"; const hooks = require('./pluginfw/hooks'); -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' const socketio = require('./socketio'); - +import html10n from '../js/vendors/html10n' let token, padId, exportLinks, socket, changesetLoader, BroadcastSlider; const init = () => { @@ -117,6 +117,14 @@ const handleClientVars = (message) => { setInterval(ping, window.clientVars.sessionRefreshInterval); } + if(window.clientVars.mode === "development") { + console.warn('Enabling development mode with live update') + socket.on('liveupdate', ()=>{ + console.log('Doing live reload') + location.reload() + }) + } + // load all script that doesn't work without the clientVars BroadcastSlider = require('./broadcast_slider') .loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded); diff --git a/src/static/js/types/AText.ts b/src/static/js/types/AText.ts new file mode 100644 index 00000000000..d58626de25f --- /dev/null +++ b/src/static/js/types/AText.ts @@ -0,0 +1,4 @@ +export type AText = { + text: string, + attribs: string, +} diff --git a/src/static/js/types/Attribute.ts b/src/static/js/types/Attribute.ts new file mode 100644 index 00000000000..f1c06b3cb48 --- /dev/null +++ b/src/static/js/types/Attribute.ts @@ -0,0 +1 @@ +export type Attribute = [string, string] diff --git a/src/static/js/types/ChangeSet.ts b/src/static/js/types/ChangeSet.ts new file mode 100644 index 00000000000..a319e0db38b --- /dev/null +++ b/src/static/js/types/ChangeSet.ts @@ -0,0 +1,6 @@ +export type ChangeSet = { + oldLen: number, + newLen: number, + ops: string + charBank: string +} diff --git a/src/static/js/types/ChangeSetBuilder.ts b/src/static/js/types/ChangeSetBuilder.ts new file mode 100644 index 00000000000..6f39193520b --- /dev/null +++ b/src/static/js/types/ChangeSetBuilder.ts @@ -0,0 +1,7 @@ +import {Attribute} from "./Attribute"; +import AttributePool from "../AttributePool"; + +export type ChangeSetBuilder = { + remove: (start: number, end?: number)=>void, + keep: (start: number, end?: number, attribs?: Attribute[], pool?: AttributePool)=>void +} diff --git a/src/static/js/types/PadRevision.ts b/src/static/js/types/PadRevision.ts new file mode 100644 index 00000000000..c54dc437932 --- /dev/null +++ b/src/static/js/types/PadRevision.ts @@ -0,0 +1,7 @@ +export type PadRevision = { + revNum: number; + savedById: string; + label: string; + timestamp: number; + id: string; +} diff --git a/src/static/js/types/RepModel.ts b/src/static/js/types/RepModel.ts new file mode 100644 index 00000000000..821549e1d05 --- /dev/null +++ b/src/static/js/types/RepModel.ts @@ -0,0 +1,31 @@ +export type RepModel = { + lines: { + atIndex: (num: number)=>RepNode, + offsetOfIndex: (range: number)=>number, + search: (filter: (e: RepNode)=>boolean)=>number, + length: ()=>number + } + selStart: number[], + selEnd: number[], + selFocusAtStart: boolean +} + +export type Position = { + bottom: number, + height: number, + top: number +} + +export type RepNode = { + firstChild: RepNode, + lineNode: RepNode + length: number, + lastChild: RepNode, + offsetHeight: number, + offsetTop: number +} + +export type WindowElementWithScrolling = HTMLIFrameElement & { + pageYOffset: number|string, + pageXOffset: number +} diff --git a/src/static/js/types/SocketIOMessage.ts b/src/static/js/types/SocketIOMessage.ts new file mode 100644 index 00000000000..9c9ffea7fc9 --- /dev/null +++ b/src/static/js/types/SocketIOMessage.ts @@ -0,0 +1,317 @@ +import {MapArrayType} from "../../../node/types/MapType"; +import {AText} from "./AText"; +import AttributePool from "../AttributePool"; +import attributePool from "../AttributePool"; +import ChatMessage from "../ChatMessage"; +import {PadRevision} from "./PadRevision"; + +export type Part = { + name: string, + client_hooks: MapArrayType<string>, + hooks: MapArrayType<string> + pre?: string[] + post?: string[] + plugin?: string +} + + +export type MappedPlugin = Part& { + plugin: string + full_name: string +} + +export type SocketIOMessage = { + type: string + accessStatus: string +} + +export type HistoricalAuthorData = MapArrayType<{ + name: string; + colorId: number; + userId?: string +}> + +export type ServerVar = { + rev: number + clientIp: string + padId: string + historicalAuthorData?: HistoricalAuthorData, + initialAttributedText: { + attribs: string + text: string + }, + apool: AttributePoolWire + time: number +} + +export type AttributePoolWire = {numToAttrib: {[p: number]: [string, string]}, nextNum: number} + + +export type UserInfo = { + userId: string + colorId: string, + name: string|null +} + +export type ClientVarPayload = { + readOnlyId: string + automaticReconnectionTimeout: number + sessionRefreshInterval: number, + atext?: AText, + apool?: AttributePool, + userName?: string, + userColor: number, + hideChat?: boolean, + padOptions: PadOption, + padId: string, + clientIp: string, + colorPalette: string[], + accountPrivs: { + maxRevisions: number, + }, + collab_client_vars: ServerVar, + chatHead: number, + readonly: boolean, + serverTimestamp: number, + initialOptions: MapArrayType<string>, + userId: string, + mode: string, + randomVersionString: string, + skinName: string + skinVariants: string, + exportAvailable: string + savedRevisions: PadRevision[], + initialRevisionList: number[], + padShortcutEnabled: MapArrayType<boolean>, + initialTitle: string, + opts: {} + numConnectedUsers: number + abiwordAvailable: string + sofficeAvailable: string + plugins: { + plugins: MapArrayType<any> + parts: MappedPlugin[] + } + indentationOnNewLine: boolean + scrollWhenFocusLineIsOutOfViewport : { + percentage: { + editionAboveViewport: number, + editionBelowViewport: number + } + duration: number + scrollWhenCaretIsInTheLastLineOfViewport: boolean + percentageToScrollWhenUserPressesArrowUp: number + } + initialChangesets: [] +} + +export type ClientVarData = { + type: "CLIENT_VARS" + data: ClientVarPayload +} + +export type ClientNewChanges = { + type : 'NEW_CHANGES' + apool: AttributePool, + author: string, + changeset: string, + newRev: number, + payload?: ClientNewChanges +} + +export type ClientAcceptCommitMessage = { + type: 'ACCEPT_COMMIT' + newRev: number +} + +export type ClientConnectMessage = { + type: 'CLIENT_RECONNECT', + noChanges: boolean, + headRev: number, + newRev: number, + changeset: string, + author: string + apool: AttributePool +} + + +export type UserNewInfoMessage = { + type: 'USER_NEWINFO', + data: { + userInfo: UserInfo + } +} + +export type UserLeaveMessage = { + type: 'USER_LEAVE' + userInfo: UserInfo +} + + + +export type ClientMessageMessage = { + type: 'CLIENT_MESSAGE', + payload: ClientSendMessages +} + +export type ChatMessageMessage = { + type: 'CHAT_MESSAGE' + data: { + message: ChatMessage + } +} + +export type ChatMessageMessages = { + type: 'CHAT_MESSAGES' + messages: string +} + +export type ClientUserChangesMessage = { + type: 'USER_CHANGES', + baseRev: number, + changeset: string, + apool: attributePool +} + + + +export type ClientSendMessages = ClientUserChangesMessage |ClientReadyMessage| ClientSendUserInfoUpdate|ChatMessageMessage| ClientMessageMessage | GetChatMessageMessage |ClientSuggestUserName | NewRevisionListMessage | RevisionLabel | PadOptionsMessage| ClientSaveRevisionMessage + +export type ClientReadyMessage = { + type: 'CLIENT_READY', + component: string, + padId: string, + sessionID: string, + token: string, + userInfo: UserInfo, + reconnect?: boolean + client_rev?: number +} + +export type ClientSaveRevisionMessage = { + type: 'SAVE_REVISION' +} + +export type GetChatMessageMessage = { + type: 'GET_CHAT_MESSAGES', + start: number, + end: number +} + +export type ClientSendUserInfoUpdate = { + type: 'USERINFO_UPDATE', + userInfo: UserInfo +} + +export type ClientSuggestUserName = { + type: 'suggestUserName', + data: { + payload: { + unnamedId: string, + newName: string + } + } +} + +export type NewRevisionListMessage = { + type: 'newRevisionList', + revisionList: number[] +} + +export type RevisionLabel = { + type: 'revisionLabel' + revisionList: number[] +} + +export type PadOptionsMessage = { + type: 'padoptions' + options: PadOption + changedBy: string +} + +export type PadOption = { + "noColors"?: boolean, + "showControls"?: boolean, + "showChat"?: boolean, + "showLineNumbers"?: boolean, + "useMonospaceFont"?: boolean, + "userName"?: null|string, + "userColor"?: null|string, + "rtl"?: boolean, + "alwaysShowChat"?: boolean, + "chatAndUsers"?: boolean, + "lang"?: null|string, + view? : MapArrayType<boolean> +} + + +type SharedMessageType = { + payload:{ + timestamp: number + } +} + +export type x = { + disconnect: boolean +} + +export type ClientDisconnectedMessage = { + type: "disconnected" + disconnected: boolean +} + +export type UserChanges = { + data: ClientUserChangesMessage +} + +export type UserSuggestUserName = { + data: { + payload: ClientSuggestUserName + } +} + +export type ChangesetRequestMessage = { + type: 'CHANGESET_REQ' + data: { + granularity: number + start: number + requestID: string + } +} + + + +export type CollabroomMessage = { + type: 'COLLABROOM' + data: ClientSendUserInfoUpdate | ClientUserChangesMessage | ChatMessageMessage | GetChatMessageMessage | ClientSaveRevisionMessage | ClientMessageMessage +} + +export type ClientVarMessage = | ClientVarData | ClientDisconnectedMessage | ClientReadyMessage| ChangesetRequestMessage | CollabroomMessage | CustomMessage + + +export type CustomMessage = { + type: 'CUSTOM' + data: any +} + +export type ClientCustomMessage = { + type: 'CUSTOM', + action: string, + payload: any + +} + +export type SocketClientReadyMessage = { + type: string + component: string + padId: string + sessionID: string + token: string + userInfo: { + colorId: string|null + name: string|null + }, + reconnect?: boolean + client_rev?: number +} + diff --git a/src/static/js/underscore.js b/src/static/js/underscore.ts similarity index 78% rename from src/static/js/underscore.js rename to src/static/js/underscore.ts index d30543cabc4..79a3e8e7f10 100644 --- a/src/static/js/underscore.js +++ b/src/static/js/underscore.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; module.exports = require('underscore'); diff --git a/src/static/js/undomodule.js b/src/static/js/undomodule.ts similarity index 91% rename from src/static/js/undomodule.js rename to src/static/js/undomodule.ts index d0b83419dd9..c3bdd2fab9d 100644 --- a/src/static/js/undomodule.js +++ b/src/static/js/undomodule.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -22,7 +23,7 @@ * limitations under the License. */ -const Changeset = require('./Changeset'); +import {characterRangeFollow, compose, follow, isIdentity, unpack} from './Changeset'; const _ = require('./underscore'); const undoModule = (() => { @@ -61,7 +62,7 @@ const undoModule = (() => { const idx = stackElements.length - 1; if (stackElements[idx].elementType === EXTERNAL_CHANGE) { stackElements[idx].changeset = - Changeset.compose(stackElements[idx].changeset, cs, getAPool()); + compose(stackElements[idx].changeset, cs, getAPool()); } else { stackElements.push( { @@ -82,10 +83,10 @@ const undoModule = (() => { if (un.backset) { const excs = ex.changeset; const unbs = un.backset; - un.backset = Changeset.follow(excs, un.backset, false, getAPool()); - ex.changeset = Changeset.follow(unbs, ex.changeset, true, getAPool()); + un.backset = follow(excs, un.backset, false, getAPool()); + ex.changeset = follow(unbs, ex.changeset, true, getAPool()); if ((typeof un.selStart) === 'number') { - const newSel = Changeset.characterRangeFollow(excs, un.selStart, un.selEnd); + const newSel = characterRangeFollow(excs, un.selStart, un.selEnd); un.selStart = newSel[0]; un.selEnd = newSel[1]; if (un.selStart === un.selEnd) { @@ -97,7 +98,7 @@ const undoModule = (() => { stackElements[idx] = un; if (idx >= 2 && stackElements[idx - 2].elementType === EXTERNAL_CHANGE) { ex.changeset = - Changeset.compose(stackElements[idx - 2].changeset, ex.changeset, getAPool()); + compose(stackElements[idx - 2].changeset, ex.changeset, getAPool()); stackElements.splice(idx - 2, 1); idx--; } @@ -153,7 +154,7 @@ const undoModule = (() => { return count; }; - const _opcodeOccurrences = (cs, opcode) => _charOccurrences(Changeset.unpack(cs).ops, opcode); + const _opcodeOccurrences = (cs, opcode) => _charOccurrences(unpack(cs).ops, opcode); const _mergeChangesets = (cs1, cs2) => { if (!cs1) return cs2; @@ -170,14 +171,14 @@ const undoModule = (() => { const minusCount1 = _opcodeOccurrences(cs1, '-'); const minusCount2 = _opcodeOccurrences(cs2, '-'); if (plusCount1 === 1 && plusCount2 === 1 && minusCount1 === 0 && minusCount2 === 0) { - const merge = Changeset.compose(cs1, cs2, getAPool()); + const merge = compose(cs1, cs2, getAPool()!); const plusCount3 = _opcodeOccurrences(merge, '+'); const minusCount3 = _opcodeOccurrences(merge, '-'); if (plusCount3 === 1 && minusCount3 === 0) { return merge; } } else if (plusCount1 === 0 && plusCount2 === 0 && minusCount1 === 1 && minusCount2 === 1) { - const merge = Changeset.compose(cs1, cs2, getAPool()); + const merge = compose(cs1, cs2, getAPool()!); const plusCount3 = _opcodeOccurrences(merge, '+'); const minusCount3 = _opcodeOccurrences(merge, '-'); if (plusCount3 === 0 && minusCount3 === 1) { @@ -198,7 +199,7 @@ const undoModule = (() => { } }; - if ((!event.backset) || Changeset.isIdentity(event.backset)) { + if ((!event.backset) || isIdentity(event.backset)) { applySelectionToTop(); } else { let merged = false; @@ -226,7 +227,7 @@ const undoModule = (() => { }; const reportExternalChange = (changeset) => { - if (changeset && !Changeset.isIdentity(changeset)) { + if (changeset && !isIdentity(changeset)) { stack.pushExternalChange(changeset); } }; diff --git a/src/static/js/vendors/browser.js b/src/static/js/vendors/browser.ts similarity index 99% rename from src/static/js/vendors/browser.js rename to src/static/js/vendors/browser.ts index 2516a325b0b..a785d8a8ef9 100644 --- a/src/static/js/vendors/browser.js +++ b/src/static/js/vendors/browser.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // WARNING: This file may have been modified from original. // TODO: Check requirement of this file, this afaik was to cover weird edge cases // that have probably been fixed in browsers. diff --git a/src/static/js/vendors/farbtastic.js b/src/static/js/vendors/farbtastic.ts similarity index 97% rename from src/static/js/vendors/farbtastic.js rename to src/static/js/vendors/farbtastic.ts index ad832dc72d9..d2a286a44c8 100644 --- a/src/static/js/vendors/farbtastic.js +++ b/src/static/js/vendors/farbtastic.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // WARNING: This file has been modified from original. // TODO: Replace with https://github.com/Simonwep/pickr @@ -7,6 +8,7 @@ // Licensed under the terms of the GNU General Public License v2.0: // https://github.com/mattfarina/farbtastic/blob/71ca15f4a09c8e5a08a1b0d1cf37ef028adf22f0/LICENSE.txt // edited by Sebastian Castro <sebastian.castro@protonmail.com> on 2020-04-06 + (function ($) { var __debug = false; @@ -33,7 +35,7 @@ $._farbtastic = function (container, options) { fb.linkTo = function (callback) { // Unbind previous nodes if (typeof fb.callback == 'object') { - $(fb.callback).off('keyup').on('keyup', fb.updateValue); + $(document.body).find(fb.callback).off('keyup').on('keyup', fb.updateValue); } // Reset color @@ -44,7 +46,7 @@ $._farbtastic = function (container, options) { fb.callback = callback; } else if (typeof callback == 'object' || typeof callback == 'string') { - fb.callback = $(callback); + fb.callback = $(document.body).find(callback); fb.callback.on('keyup', fb.updateValue); if (fb.callback[0].value) { fb.setColor(fb.callback[0].value); @@ -172,7 +174,7 @@ $._farbtastic = function (container, options) { angle2 = d2 * Math.PI * 2, // Endpoints x1 = Math.sin(angle1), y1 = -Math.cos(angle1); - x2 = Math.sin(angle2), y2 = -Math.cos(angle2), + let x2 = Math.sin(angle2), y2 = -Math.cos(angle2), // Midpoint chosen so that the endpoints are tangent to the circle. am = (angle1 + angle2) / 2, tan = 1 / Math.cos((angle2 - angle1) / 2), @@ -329,8 +331,8 @@ $._farbtastic = function (container, options) { // Update the overlay canvas. fb.ctxOverlay.clearRect(-fb.mid, -fb.mid, sz, sz); - for (i in circles) { - var c = circles[i]; + for (let i in circles) { + const c = circles[i]; fb.ctxOverlay.lineWidth = c.lw; fb.ctxOverlay.strokeStyle = c.c; fb.ctxOverlay.beginPath(); @@ -355,13 +357,14 @@ $._farbtastic = function (container, options) { // Linked elements or callback if (typeof fb.callback == 'object') { // Set background/foreground color - $(fb.callback).css({ + $(document.body).find(fb.callback).css({ backgroundColor: fb.color, color: fb.invert ? '#fff' : '#000' }); + // Change linked value - $(fb.callback).each(function() { + $(document.body).find(fb.callback).each(function() { if ((typeof this.value == 'string') && this.value != fb.color) { this.value = fb.color; } diff --git a/src/static/js/vendors/gritter.js b/src/static/js/vendors/gritter.ts similarity index 98% rename from src/static/js/vendors/gritter.js rename to src/static/js/vendors/gritter.ts index a20cb4de93c..993514c74e7 100644 --- a/src/static/js/vendors/gritter.js +++ b/src/static/js/vendors/gritter.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // WARNING: This file has been modified from the Original /* @@ -42,8 +43,8 @@ return Gritter.add(params || {}); } catch(e) { - var err = 'Gritter Error: ' + e; - (typeof(console) != 'undefined' && console.error) ? + const err = 'Gritter Error: ' + e; + (typeof(console) != 'undefined' && console.error) ? console.error(err, params) : alert(err); @@ -289,7 +290,7 @@ */ _runSetup: function(){ - for(opt in $.gritter.options){ + for(let opt in $.gritter.options){ this[opt] = $.gritter.options[opt]; } this._is_setup = 1; diff --git a/src/static/js/vendors/html10n.js b/src/static/js/vendors/html10n.js deleted file mode 100644 index 50b6d227944..00000000000 --- a/src/static/js/vendors/html10n.js +++ /dev/null @@ -1,1056 +0,0 @@ -// WARNING: This file has been modified from the Original - -/** - * Copyright (c) 2012 Marcel Klehr - * Copyright (c) 2011-2012 Fabien Cazenave, Mozilla - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -window.html10n = (function(window, document, undefined) { - - // fix console - (function() { - const noop = function () { - }; - const names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; - const console = (window.console = window.console || {}); - for (let i = 0; i < names.length; ++i) { - if (!console[names[i]]) { - console[names[i]] = noop; - } - } - }()); - - // fix Array#forEach in IE - // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach - if (!Array.prototype.forEach) { - Array.prototype.forEach = function(fn, scope) { - let i = 0, len = this.length; - for(; i < len; ++i) { - if (i in this) { - fn.call(scope, this[i], i, this); - } - } - }; - } - - // fix Array#indexOf in, guess what, IE! <3 - // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { - "use strict"; - if (this == null) { - throw new TypeError(); - } - const t = Object(this); - const len = t.length >>> 0; - if (len === 0) { - return -1; - } - let n = 0; - if (arguments.length > 1) { - n = Number(arguments[1]); - if (n != n) { // shortcut for verifying if it's NaN - n = 0; - } else if (n != 0 && n != Infinity && n != -Infinity) { - n = (n > 0 || -1) * Math.floor(Math.abs(n)); - } - } - if (n >= len) { - return -1; - } - var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); - for (; k < len; k++) { - if (k in t && t[k] === searchElement) { - return k; - } - } - return -1; - } - } - - /** - * MicroEvent - to make any js object an event emitter (server or browser) - */ - const MicroEvent = function () { - }; - MicroEvent.prototype = { - bind: function(event, fct){ - this._events = this._events || {}; - this._events[event] = this._events[event] || []; - this._events[event].push(fct); - }, - unbind: function(event, fct){ - this._events = this._events || {}; - if( event in this._events === false ) return; - this._events[event].splice(this._events[event].indexOf(fct), 1); - }, - trigger: function(event /* , args... */){ - this._events = this._events || {}; - if( event in this._events === false ) return; - for(var i = 0; i < this._events[event].length; i++){ - this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)) - } - } - }; - /** - * mixin will delegate all MicroEvent.js function in the destination object - * @param {Object} the object which will support MicroEvent - */ - MicroEvent.mixin = function(destObject){ - var props = ['bind', 'unbind', 'trigger']; - if(!destObject) return; - for(var i = 0; i < props.length; i ++){ - destObject[props[i]] = MicroEvent.prototype[props[i]]; - } - } - - /** - * Loader - * The loader is responsible for loading - * and caching all necessary resources - */ - function Loader(resources) { - this.resources = resources - this.cache = {} // file => contents - this.langs = {} // lang => strings - } - - Loader.prototype.load = function(lang, cb) { - if(this.langs[lang]) return cb() - - if (this.resources.length > 0) { - var reqs = 0; - for (var i=0, n=this.resources.length; i < n; i++) { - this.fetch(this.resources[i], lang, function(e) { - reqs++; - if(e) console.warn(e) - - if (reqs < n) return;// Call back once all reqs are completed - cb && cb() - }) - } - } - } - - Loader.prototype.fetch = function(href, lang, cb) { - var that = this - - if (this.cache[href]) { - this.parse(lang, href, this.cache[href], cb) - return; - } - - var xhr = new XMLHttpRequest() - xhr.open('GET', href, /*async: */true) - if (xhr.overrideMimeType) { - xhr.overrideMimeType('application/json; charset=utf-8'); - } - xhr.onreadystatechange = function() { - if (xhr.readyState == 4) { - if (xhr.status == 200 || xhr.status === 0) { - var data = JSON.parse(xhr.responseText) - that.cache[href] = data - // Pass on the contents for parsing - that.parse(lang, href, data, cb) - } else { - cb(new Error('Failed to load '+href)) - } - } - }; - xhr.send(null); - } - - Loader.prototype.parse = function(lang, currHref, data, cb) { - if ('object' != typeof data) { - cb(new Error('A file couldn\'t be parsed as json.')) - return - } - - // Issue #6129: Fix exceptions caused by browsers - // Also for fallback, see BCP 47 RFC 4647 section 3.4 - // NOTE: this output the all lowercase form - function getBcp47LangCode(browserLang) { - const bcp47Lang = browserLang.toLowerCase(); - // Browser => BCP 47 - const langCodeMap = { - 'zh-cn': 'zh-hans-cn', - 'zh-hk': 'zh-hant-hk', - 'zh-mo': 'zh-hant-mo', - 'zh-my': 'zh-hans-my', - 'zh-sg': 'zh-hans-sg', - 'zh-tw': 'zh-hant-tw', - }; - - return langCodeMap[bcp47Lang] ?? bcp47Lang; - } - - // Issue #6129: Fix exceptions - // NOTE: translatewiki.net use all lowercase form by default ('en-gb' insted of 'en-GB') - function getJsonLangCode(bcp47Lang) { - const jsonLang = bcp47Lang.toLowerCase(); - // BCP 47 => JSON - const langCodeMap = { - 'sr-cyrl': 'sr-ec', - 'sr-latn': 'sr-el', - 'zh-hant-hk': 'zh-hk', - }; - - return langCodeMap[jsonLang] ?? jsonLang; - } - - let bcp47LangCode = getBcp47LangCode(lang); - let jsonLangCode = getJsonLangCode(bcp47LangCode); - - // Check if lang exists - if (!data[jsonLangCode]) { - // lang not found - // This may be due to formatting (expected 'ru' but browser sent 'ru-RU') - // Set err msg before mutating lang (we may need this later) - const msg = 'Couldn\'t find translations for ' + lang + - '(lowercase BCP 47 lang tag ' + bcp47LangCode + - ', JSON lang code ' + jsonLangCode + ')'; - - // Check for '-' (BCP 47 'ROOT-SCRIPT-REGION-VARIANT') and fallback until found data or ROOT - // - 'ROOT-SCRIPT-REGION': 'zh-Hans-CN' - // - 'ROOT-SCRIPT': 'zh-Hans' - // - 'ROOT-REGION': 'en-GB' - // - 'ROOT-VARIANT': 'be-tarask' - while (!data[jsonLangCode] && bcp47LangCode.lastIndexOf('-') > -1) { - // ROOT-SCRIPT-REGION-VARIANT formatting detected - bcp47LangCode = bcp47LangCode.substring(0, bcp47LangCode.lastIndexOf('-')); // set lang to ROOT lang - jsonLangCode = getJsonLangCode(bcp47LangCode); - } - - // Check if already found data or ROOT lang exists (e.g 'ru') - if (!data[jsonLangCode]) { - // ROOT lang not found. (e.g 'zh') - // Loop through langs data. Maybe we have a variant? e.g (zh-hans) - let l; // langs item. Declare outside of loop - - for (l in data) { - // Is not ROOT? - // And is variant of ROOT? - // (NOTE: index of ROOT equals 0 would cause unexpected ISO 639-1 vs. 639-3 issues, - // so append dash into query string) - // And is known lang? - if (bcp47LangCode != l && l.indexOf(lang + '-') === 0 && data[l]) { - bcp47LangCode = l; // set lang to ROOT-SCRIPT (e.g 'zh-hans') - jsonLangCode = getJsonLangCode(bcp47LangCode); - break; - } - } - - // Did we find a variant? If not, return err. - if (bcp47LangCode != l) { - return cb(new Error(msg)); - } - } - } - - lang = jsonLangCode; - - if ('string' == typeof data[lang]) { - // Import rule - - // absolute path - let importUrl = data[lang]; - - // relative path - if(data[lang].indexOf("http") != 0 && data[lang].indexOf("/") != 0) { - importUrl = currHref+"/../"+data[lang] - } - - this.fetch(importUrl, lang, cb) - return - } - - if ('object' != typeof data[lang]) { - cb(new Error('Translations should be specified as JSON objects!')) - return - } - - this.langs[lang] = data[lang] - // TODO: Also store accompanying langs - cb() - } - - - /** - * The html10n object - */ - const html10n = - { - language: null - }; - MicroEvent.mixin(html10n) - - html10n.macros = {} - - html10n.rtl = ["ar","dv","fa","ha","he","ks","ku","ps","ur","yi"] - - /** - * Get rules for plural forms (shared with JetPack), see: - * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html - * https://github.com/mozilla/addon-sdk/blob/master/python-lib/plural-rules-generator.p - * - * @param {string} lang - * locale (language) used. - * - * @return {Function} - * returns a function that gives the plural form name for a given integer: - * var fun = getPluralRules('en'); - * fun(1) -> 'one' - * fun(0) -> 'other' - * fun(1000) -> 'other'. - */ - function getPluralRules(lang) { - var locales2rules = { - 'af': 3, - 'ak': 4, - 'am': 4, - 'ar': 1, - 'asa': 3, - 'az': 0, - 'be': 11, - 'bem': 3, - 'bez': 3, - 'bg': 3, - 'bh': 4, - 'bm': 0, - 'bn': 3, - 'bo': 0, - 'br': 20, - 'brx': 3, - 'bs': 11, - 'ca': 3, - 'cgg': 3, - 'chr': 3, - 'cs': 12, - 'cy': 17, - 'da': 3, - 'de': 3, - 'dv': 3, - 'dz': 0, - 'ee': 3, - 'el': 3, - 'en': 3, - 'eo': 3, - 'es': 3, - 'et': 3, - 'eu': 3, - 'fa': 0, - 'ff': 5, - 'fi': 3, - 'fil': 4, - 'fo': 3, - 'fr': 5, - 'fur': 3, - 'fy': 3, - 'ga': 8, - 'gd': 24, - 'gl': 3, - 'gsw': 3, - 'gu': 3, - 'guw': 4, - 'gv': 23, - 'ha': 3, - 'haw': 3, - 'he': 2, - 'hi': 4, - 'hr': 11, - 'hu': 0, - 'id': 0, - 'ig': 0, - 'ii': 0, - 'is': 3, - 'it': 3, - 'iu': 7, - 'ja': 0, - 'jmc': 3, - 'jv': 0, - 'ka': 0, - 'kab': 5, - 'kaj': 3, - 'kcg': 3, - 'kde': 0, - 'kea': 0, - 'kk': 3, - 'kl': 3, - 'km': 0, - 'kn': 0, - 'ko': 0, - 'ksb': 3, - 'ksh': 21, - 'ku': 3, - 'kw': 7, - 'lag': 18, - 'lb': 3, - 'lg': 3, - 'ln': 4, - 'lo': 0, - 'lt': 10, - 'lv': 6, - 'mas': 3, - 'mg': 4, - 'mk': 16, - 'ml': 3, - 'mn': 3, - 'mo': 9, - 'mr': 3, - 'ms': 0, - 'mt': 15, - 'my': 0, - 'nah': 3, - 'naq': 7, - 'nb': 3, - 'nd': 3, - 'ne': 3, - 'nl': 3, - 'nn': 3, - 'no': 3, - 'nr': 3, - 'nso': 4, - 'ny': 3, - 'nyn': 3, - 'om': 3, - 'or': 3, - 'pa': 3, - 'pap': 3, - 'pl': 13, - 'ps': 3, - 'pt': 3, - 'rm': 3, - 'ro': 9, - 'rof': 3, - 'ru': 11, - 'rwk': 3, - 'sah': 0, - 'saq': 3, - 'se': 7, - 'seh': 3, - 'ses': 0, - 'sg': 0, - 'sh': 11, - 'shi': 19, - 'sk': 12, - 'sl': 14, - 'sma': 7, - 'smi': 7, - 'smj': 7, - 'smn': 7, - 'sms': 7, - 'sn': 3, - 'so': 3, - 'sq': 3, - 'sr': 11, - 'ss': 3, - 'ssy': 3, - 'st': 3, - 'sv': 3, - 'sw': 3, - 'syr': 3, - 'ta': 3, - 'te': 3, - 'teo': 3, - 'th': 0, - 'ti': 4, - 'tig': 3, - 'tk': 3, - 'tl': 4, - 'tn': 3, - 'to': 0, - 'tr': 0, - 'ts': 3, - 'tzm': 22, - 'uk': 11, - 'ur': 3, - 've': 3, - 'vi': 0, - 'vun': 3, - 'wa': 4, - 'wae': 3, - 'wo': 0, - 'xh': 3, - 'xog': 3, - 'yo': 0, - 'zh': 0, - 'zu': 3 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - var pluralRules = { - '0': function(n) { - return 'other'; - }, - '1': function(n) { - if ((isBetween((n % 100), 3, 10))) - return 'few'; - if (n === 0) - return 'zero'; - if ((isBetween((n % 100), 11, 99))) - return 'many'; - if (n == 2) - return 'two'; - if (n == 1) - return 'one'; - return 'other'; - }, - '2': function(n) { - if (n !== 0 && (n % 10) === 0) - return 'many'; - if (n == 2) - return 'two'; - if (n == 1) - return 'one'; - return 'other'; - }, - '3': function(n) { - if (n == 1) - return 'one'; - return 'other'; - }, - '4': function(n) { - if ((isBetween(n, 0, 1))) - return 'one'; - return 'other'; - }, - '5': function(n) { - if ((isBetween(n, 0, 2)) && n != 2) - return 'one'; - return 'other'; - }, - '6': function(n) { - if (n === 0) - return 'zero'; - if ((n % 10) == 1 && (n % 100) != 11) - return 'one'; - return 'other'; - }, - '7': function(n) { - if (n == 2) - return 'two'; - if (n == 1) - return 'one'; - return 'other'; - }, - '8': function(n) { - if ((isBetween(n, 3, 6))) - return 'few'; - if ((isBetween(n, 7, 10))) - return 'many'; - if (n == 2) - return 'two'; - if (n == 1) - return 'one'; - return 'other'; - }, - '9': function(n) { - if (n === 0 || n != 1 && (isBetween((n % 100), 1, 19))) - return 'few'; - if (n == 1) - return 'one'; - return 'other'; - }, - '10': function(n) { - if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19))) - return 'few'; - if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19))) - return 'one'; - return 'other'; - }, - '11': function(n) { - if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) - return 'few'; - if ((n % 10) === 0 || - (isBetween((n % 10), 5, 9)) || - (isBetween((n % 100), 11, 14))) - return 'many'; - if ((n % 10) == 1 && (n % 100) != 11) - return 'one'; - return 'other'; - }, - '12': function(n) { - if ((isBetween(n, 2, 4))) - return 'few'; - if (n == 1) - return 'one'; - return 'other'; - }, - '13': function(n) { - if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) - return 'few'; - if (n != 1 && (isBetween((n % 10), 0, 1)) || - (isBetween((n % 10), 5, 9)) || - (isBetween((n % 100), 12, 14))) - return 'many'; - if (n == 1) - return 'one'; - return 'other'; - }, - '14': function(n) { - if ((isBetween((n % 100), 3, 4))) - return 'few'; - if ((n % 100) == 2) - return 'two'; - if ((n % 100) == 1) - return 'one'; - return 'other'; - }, - '15': function(n) { - if (n === 0 || (isBetween((n % 100), 2, 10))) - return 'few'; - if ((isBetween((n % 100), 11, 19))) - return 'many'; - if (n == 1) - return 'one'; - return 'other'; - }, - '16': function(n) { - if ((n % 10) == 1 && n != 11) - return 'one'; - return 'other'; - }, - '17': function(n) { - if (n == 3) - return 'few'; - if (n === 0) - return 'zero'; - if (n == 6) - return 'many'; - if (n == 2) - return 'two'; - if (n == 1) - return 'one'; - return 'other'; - }, - '18': function(n) { - if (n === 0) - return 'zero'; - if ((isBetween(n, 0, 2)) && n !== 0 && n != 2) - return 'one'; - return 'other'; - }, - '19': function(n) { - if ((isBetween(n, 2, 10))) - return 'few'; - if ((isBetween(n, 0, 1))) - return 'one'; - return 'other'; - }, - '20': function(n) { - if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !( - isBetween((n % 100), 10, 19) || - isBetween((n % 100), 70, 79) || - isBetween((n % 100), 90, 99) - )) - return 'few'; - if ((n % 1000000) === 0 && n !== 0) - return 'many'; - if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92])) - return 'two'; - if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91])) - return 'one'; - return 'other'; - }, - '21': function(n) { - if (n === 0) - return 'zero'; - if (n == 1) - return 'one'; - return 'other'; - }, - '22': function(n) { - if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99))) - return 'one'; - return 'other'; - }, - '23': function(n) { - if ((isBetween((n % 10), 1, 2)) || (n % 20) === 0) - return 'one'; - return 'other'; - }, - '24': function(n) { - if ((isBetween(n, 3, 10) || isBetween(n, 13, 19))) - return 'few'; - if (isIn(n, [2, 12])) - return 'two'; - if (isIn(n, [1, 11])) - return 'one'; - return 'other'; - } - }; - - // return a function that gives the plural form name for a given integer - var index = locales2rules[lang.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - console.warn('plural form unknown for [' + lang + ']'); - return function() { return 'other'; }; - } - return pluralRules[index]; - } - - /** - * pre-defined 'plural' macro - */ - html10n.macros.plural = function(key, param, opts) { - var str - , n = parseFloat(param); - if (isNaN(n)) - return; - - // initialize _pluralRules - if (!this._pluralRules) - this._pluralRules = getPluralRules(html10n.language); - var index = this._pluralRules(n); - - // try to find a [zero|one|two] key if it's defined - if (n === 0 && ('zero') in opts) { - str = opts['zero']; - } else if (n == 1 && ('one') in opts) { - str = opts['one']; - } else if (n == 2 && ('two') in opts) { - str = opts['two']; - } else if (index in opts) { - str = opts[index]; - } - - return str; - }; - - /** - * Localize a document - * @param langs An array of lang codes defining fallbacks - */ - html10n.localize = function(langs) { - var that = this - // if only one string => create an array - if ('string' == typeof langs) langs = [langs] - - // Expand two-part locale specs - var i=0 - langs.forEach(function(lang) { - if(!lang) return; - langs[i++] = lang; - if(~lang.indexOf('-')) langs[i++] = lang.substr(0, lang.indexOf('-')); - }) - - this.build(langs, function(er, translations) { - html10n.translations = translations - html10n.translateElement(translations) - that.trigger('localized') - }) - } - - /** - * Triggers the translation process - * for an element - * @param translations A hash of all translation strings - * @param element A DOM element, if omitted, the document element will be used - */ - html10n.translateElement = function(translations, element) { - element = element || document.documentElement - - var children = element? getTranslatableChildren(element) : document.childNodes; - for (var i=0, n=children.length; i < n; i++) { - this.translateNode(translations, children[i]) - } - - // translate element itself if necessary - this.translateNode(translations, element) - } - - function asyncForEach(list, iterator, cb) { - var i = 0 - , n = list.length - iterator(list[i], i, function each(err) { - if(err) console.error(err) - i++ - if (i < n) return iterator(list[i],i, each); - cb() - }) - } - - function getTranslatableChildren(element) { - if(!document.querySelectorAll) { - if (!element) return [] - var nodes = element.getElementsByTagName('*') - , l10nElements = [] - for (var i=0, n=nodes.length; i < n; i++) { - if (nodes[i].getAttribute('data-l10n-id')) - l10nElements.push(nodes[i]); - } - return l10nElements - } - return element.querySelectorAll('*[data-l10n-id]') - } - - html10n.get = function(id, args) { - var translations = html10n.translations - if(!translations) return console.warn('No translations available (yet)') - if(!translations[id]) return console.warn('Could not find string '+id) - - // apply macros - var str = translations[id] - - str = substMacros(id, str, args) - - // apply args - str = substArguments(str, args) - - return str - } - - // replace {{arguments}} with their values or the - // associated translation string (based on its key) - function substArguments(str, args) { - var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/ - , match - var translations = html10n.translations; - while (match = reArgs.exec(str)) { - if (!match || match.length < 2) - return str // argument key not found - - var arg = match[1] - , sub = '' - if (args && arg in args) { - sub = args[arg] - } else if (translations && arg in translations) { - sub = translations[arg] - } else { - console.warn('Could not find argument {{' + arg + '}}') - return str - } - - str = str.substring(0, match.index) + sub + str.substr(match.index + match[0].length) - } - - return str - } - - // replace {[macros]} with their values - function substMacros(key, str, args) { - var regex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}/ //.exec('{[ plural(n) other: are {{n}}, one: is ]}') - , match - - while(match = regex.exec(str)) { - // a macro has been found - // Note: at the moment, only one parameter is supported - var macroName = match[1] - , paramName = match[2] - , optv = match[3] - , opts = {} - - if (!(macroName in html10n.macros)) continue - - if(optv) { - optv.match(/(?=\s*)([a-zA-Z]+)\: ?([ a-zA-Z{}]+)(?=,?)/g).forEach(function(arg) { - var parts = arg.split(':') - , name = parts[0] - , value = parts[1].trim() - opts[name] = value - }) - } - - var param - if (args && paramName in args) { - param = args[paramName] - } else if (paramName in html10n.translations) { - param = translations[paramName] - } - - // there's no macro parser: it has to be defined in html10n.macros - var macro = html10n.macros[macroName] - str = str.substr(0, match.index) + macro(key, param, opts) + str.substr(match.index+match[0].length) - } - - return str - } - - /** - * Applies translations to a DOM node (recursive) - */ - html10n.translateNode = function(translations, node) { - var str = {} - - // get id - str.id = node.getAttribute('data-l10n-id') - if (!str.id) return - - if(!translations[str.id]) return console.warn('Couldn\'t find translation key '+str.id) - - // get args - if(window.JSON) { - str.args = JSON.parse(node.getAttribute('data-l10n-args')) - }else{ - try{ - str.args = eval(node.getAttribute('data-l10n-args')) - }catch(e) { - console.warn('Couldn\'t parse args for '+str.id) - } - } - - str.str = html10n.get(str.id, str.args) - - // get attribute name to apply str to - var prop - , index = str.id.lastIndexOf('.') - , attrList = // allowed attributes - { "title": 1 - , "innerHTML": 1 - , "alt": 1 - , "textContent": 1 - , "value": 1 - , "placeholder": 1 - } - if (index > 0 && str.id.substr(index + 1) in attrList) { - // an attribute has been specified (example: "my_translation_key.placeholder") - prop = str.id.substr(index + 1) - } else { // no attribute: assuming text content by default - prop = document.body.textContent ? 'textContent' : 'innerText' - } - - // Apply translation - if (node.children.length === 0 || prop != 'textContent') { - node[prop] = str.str - node.setAttribute("aria-label", str.str); // Sets the aria-label - // The idea of the above is that we always have an aria value - // This might be a bit of an abrupt solution but let's see how it goes - } else { - var children = node.childNodes, - found = false - for (var i=0, n=children.length; i < n; i++) { - if (children[i].nodeType === 3 && /\S/.test(children[i].textContent)) { - if (!found) { - children[i].nodeValue = str.str - found = true - } else { - children[i].nodeValue = '' - } - } - } - if (!found) { - console.warn('Unexpected error: could not translate element content for key '+str.id, node) - } - } - } - - /** - * Builds a translation object from a list of langs (loads the necessary translations) - * @param langs Array - a list of langs sorted by priority (default langs should go last) - */ - html10n.build = function(langs, cb) { - var that = this - , build = {} - - asyncForEach(langs, function (lang, i, next) { - if(!lang) return next(); - that.loader.load(lang, next) - }, function() { - var lang - langs.reverse() - - // loop through the priority array... - for (var i=0, n=langs.length; i < n; i++) { - lang = langs[i] - - if(!lang) continue; - if(!(lang in that.loader.langs)) {// uh, we don't have this lang availbable.. - // then check for related langs - if(~lang.indexOf('-')) lang = lang.split('-')[0]; - for(var l in that.loader.langs) { - if(lang != l && l.indexOf(lang) === 0) { - lang = l - break; - } - } - if(lang != l) continue; - } - - // ... and apply all strings of the current lang in the list - // to our build object - for (var string in that.loader.langs[lang]) { - build[string] = that.loader.langs[lang][string] - } - - // the last applied lang will be exposed as the - // lang the page was translated to - that.language = lang - } - cb(null, build) - }) - } - - /** - * Returns the language that was last applied to the translations hash - * thus overriding most of the formerly applied langs - */ - html10n.getLanguage = function() { - return this.language; - } - - /** - * Returns the direction of the language returned be html10n#getLanguage - */ - html10n.getDirection = function() { - if(!this.language) return - var langCode = this.language.indexOf('-') == -1? this.language : this.language.substr(0, this.language.indexOf('-')) - return html10n.rtl.indexOf(langCode) == -1? 'ltr' : 'rtl' - } - - /** - * Index all <link>s - */ - html10n.index = function () { - // Find all <link>s - var links = document.getElementsByTagName('link') - , resources = [] - for (var i=0, n=links.length; i < n; i++) { - if (links[i].type != 'application/l10n+json') - continue; - resources.push(links[i].href) - } - this.loader = new Loader(resources) - this.trigger('indexed') - } - - if (document.addEventListener) // modern browsers and IE9+ - document.addEventListener('DOMContentLoaded', function() { - html10n.index() - }, false) - else if (window.attachEvent) - window.attachEvent('onload', function() { - html10n.index() - }, false) - - // gettext-like shortcut - if (window._ === undefined) - window._ = html10n.get; - - return html10n -})(window, document) diff --git a/src/static/js/vendors/html10n.ts b/src/static/js/vendors/html10n.ts new file mode 100644 index 00000000000..ea5a1cb24a0 --- /dev/null +++ b/src/static/js/vendors/html10n.ts @@ -0,0 +1,997 @@ +import {Func} from "mocha"; + + +type PluralFunc = (n: number) => string + +export class Html10n { + public language?: string + private rtl: string[] + private _pluralRules?: PluralFunc + public mt: MicroEvent + private loader: Loader | undefined + public translations: Map<string, any> + private macros: Map<string, Function> + + constructor() { + this.language = undefined + this.rtl = ["ar","dv","fa","ha","he","ks","ku","ps","ur","yi"] + this.mt = new MicroEvent() + this.translations = new Map() + this.macros = new Map() + + this.macros.set('plural', (_key: string, param:string, opts: any)=>{ + let str + , n = parseFloat(param); + if (isNaN(n)) + return; + + // initialize _pluralRules + if (this._pluralRules === undefined) { + this._pluralRules = this.getPluralRules(this.language!); + } + let index = this._pluralRules!(n); + + // try to find a [zero|one|two] key if it's defined + if (n === 0 && ('zero') in opts) { + str = opts['zero']; + } else if (n == 1 && ('one') in opts) { + str = opts['one']; + } else if (n == 2 && ('two') in opts) { + str = opts['two']; + } else if (index in opts) { + str = opts[index]; + } + + return str; + }) + + document.addEventListener('DOMContentLoaded', ()=> { + this.index() + }, false) + } + + bind(event: string, fct: Func) { + this.mt.bind(event, fct) + } + + /** + * Get rules for plural forms (shared with JetPack), see: + * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html + * https://github.com/mozilla/addon-sdk/blob/master/python-lib/plural-rules-generator.p + * + * @param {string} lang + * locale (language) used. + * + * @return {PluralFunc} + * returns a function that gives the plural form name for a given integer: + * var fun = getPluralRules('en'); + * fun(1) -> 'one' + * fun(0) -> 'other' + * fun(1000) -> 'other'. + */ + getPluralRules(lang: string): PluralFunc { + const locales2rules = new Map([ + ['af', 3], + ['ak', 4], + ['am', 4], + ['ar', 1], + ['asa', 3], + ['az', 0], + ['be', 11], + ['bem', 3], + ['bez', 3], + ['bg', 3], + ['bh', 4], + ['bm', 0], + ['bn', 3], + ['bo', 0], + ['br', 20], + ['brx', 3], + ['bs', 11], + ['ca', 3], + ['cgg', 3], + ['chr', 3], + ['cs', 12], + ['cy', 17], + ['da', 3], + ['de', 3], + ['dv', 3], + ['dz', 0], + ['ee', 3], + ['el', 3], + ['en', 3], + ['eo', 3], + ['es', 3], + ['et', 3], + ['eu', 3], + ['fa', 0], + ['ff', 5], + ['fi', 3], + ['fil', 4], + ['fo', 3], + ['fr', 5], + ['fur', 3], + ['fy', 3], + ['ga', 8], + ['gd', 24], + ['gl', 3], + ['gsw', 3], + ['gu', 3], + ['guw', 4], + ['gv', 23], + ['ha', 3], + ['haw', 3], + ['he', 2], + ['hi', 4], + ['hr', 11], + ['hu', 0], + ['id', 0], + ['ig', 0], + ['ii', 0], + ['is', 3], + ['it', 3], + ['iu', 7], + ['ja', 0], + ['jmc', 3], + ['jv', 0], + ['ka', 0], + ['kab', 5], + ['kaj', 3], + ['kcg', 3], + ['kde', 0], + ['kea', 0], + ['kk', 3], + ['kl', 3], + ['km', 0], + ['kn', 0], + ['ko', 0], + ['ksb', 3], + ['ksh', 21], + ['ku', 3], + ['kw', 7], + ['lag', 18], + ['lb', 3], + ['lg', 3], + ['ln', 4], + ['lo', 0], + ['lt', 10], + ['lv', 6], + ['mas', 3], + ['mg', 4], + ['mk', 16], + ['ml', 3], + ['mn', 3], + ['mo', 9], + ['mr', 3], + ['ms', 0], + ['mt', 15], + ['my', 0], + ['nah', 3], + ['naq', 7], + ['nb', 3], + ['nd', 3], + ['ne', 3], + ['nl', 3], + ['nn', 3], + ['no', 3], + ['nr', 3], + ['nso', 4], + ['ny', 3], + ['nyn', 3], + ['om', 3], + ['or', 3], + ['pa', 3], + ['pap', 3], + ['pl', 13], + ['ps', 3], + ['pt', 3], + ['rm', 3], + ['ro', 9], + ['rof', 3], + ['ru', 11], + ['rwk', 3], + ['sah', 0], + ['saq', 3], + ['se', 7], + ['seh', 3], + ['ses', 0], + ['sg', 0], + ['sh', 11], + ['shi', 19], + ['sk', 12], + ['sl', 14], + ['sma', 7], + ['smi', 7], + ['smj', 7], + ['smn', 7], + ['sms', 7], + ['sn', 3], + ['so', 3], + ['sq', 3], + ['sr', 11], + ['ss', 3], + ['ssy', 3], + ['st', 3], + ['sv', 3], + ['sw', 3], + ['syr', 3], + ['ta', 3], + ['te', 3], + ['teo', 3], + ['th', 0], + ['ti', 4], + ['tig', 3], + ['tk', 3], + ['tl', 4], + ['tn', 3], + ['to', 0], + ['tr', 0], + ['ts', 3], + ['tzm', 22], + ['uk', 11], + ['ur', 3], + ['ve', 3], + ['vi', 0], + ['vun', 3], + ['wa', 4], + ['wae', 3], + ['wo', 0], + ['xh', 3], + ['xog', 3], + ['yo', 0], + ['zh', 0], + ['zu', 3] + ]) + + function isIn(n: number, list: number[]) { + return list.indexOf(n) !== -1; + } + function isBetween(n: number, start: number, end: number) { + return start <= n && n <= end; + } + + type PluralFunc = (n: number) => string + + + const pluralRules: { + [key: string]: PluralFunc + } = { + '0': function() { + return 'other'; + }, + '1': function(n: number) { + if ((isBetween((n % 100), 3, 10))) + return 'few'; + if (n === 0) + return 'zero'; + if ((isBetween((n % 100), 11, 99))) + return 'many'; + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '2': function(n: number) { + if (n !== 0 && (n % 10) === 0) + return 'many'; + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '3': function(n: number) { + if (n == 1) + return 'one'; + return 'other'; + }, + '4': function(n: number) { + if ((isBetween(n, 0, 1))) + return 'one'; + return 'other'; + }, + '5': function(n: number) { + if ((isBetween(n, 0, 2)) && n != 2) + return 'one'; + return 'other'; + }, + '6': function(n: number) { + if (n === 0) + return 'zero'; + if ((n % 10) == 1 && (n % 100) != 11) + return 'one'; + return 'other'; + }, + '7': function(n: number) { + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '8': function(n: number) { + if ((isBetween(n, 3, 6))) + return 'few'; + if ((isBetween(n, 7, 10))) + return 'many'; + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '9': function(n: number) { + if (n === 0 || n != 1 && (isBetween((n % 100), 1, 19))) + return 'few'; + if (n == 1) + return 'one'; + return 'other'; + }, + '10': function(n: number) { + if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19))) + return 'few'; + if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19))) + return 'one'; + return 'other'; + }, + '11': function(n: number) { + if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) + return 'few'; + if ((n % 10) === 0 || + (isBetween((n % 10), 5, 9)) || + (isBetween((n % 100), 11, 14))) + return 'many'; + if ((n % 10) == 1 && (n % 100) != 11) + return 'one'; + return 'other'; + }, + '12': function(n: number) { + if ((isBetween(n, 2, 4))) + return 'few'; + if (n == 1) + return 'one'; + return 'other'; + }, + '13': function(n: number) { + if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) + return 'few'; + if (n != 1 && (isBetween((n % 10), 0, 1)) || + (isBetween((n % 10), 5, 9)) || + (isBetween((n % 100), 12, 14))) + return 'many'; + if (n == 1) + return 'one'; + return 'other'; + }, + '14': function(n: number) { + if ((isBetween((n % 100), 3, 4))) + return 'few'; + if ((n % 100) == 2) + return 'two'; + if ((n % 100) == 1) + return 'one'; + return 'other'; + }, + '15': function(n: number) { + if (n === 0 || (isBetween((n % 100), 2, 10))) + return 'few'; + if ((isBetween((n % 100), 11, 19))) + return 'many'; + if (n == 1) + return 'one'; + return 'other'; + }, + '16': function(n: number) { + if ((n % 10) == 1 && n != 11) + return 'one'; + return 'other'; + }, + '17': function(n: number) { + if (n == 3) + return 'few'; + if (n === 0) + return 'zero'; + if (n == 6) + return 'many'; + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '18': function(n: number) { + if (n === 0) + return 'zero'; + if ((isBetween(n, 0, 2)) && n !== 0 && n != 2) + return 'one'; + return 'other'; + }, + '19': function(n: number) { + if ((isBetween(n, 2, 10))) + return 'few'; + if ((isBetween(n, 0, 1))) + return 'one'; + return 'other'; + }, + '20': function(n: number) { + if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !( + isBetween((n % 100), 10, 19) || + isBetween((n % 100), 70, 79) || + isBetween((n % 100), 90, 99) + )) + return 'few'; + if ((n % 1000000) === 0 && n !== 0) + return 'many'; + if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92])) + return 'two'; + if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91])) + return 'one'; + return 'other'; + }, + '21': function(n: number) { + if (n === 0) + return 'zero'; + if (n == 1) + return 'one'; + return 'other'; + }, + '22': function(n: number) { + if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99))) + return 'one'; + return 'other'; + }, + '23': function(n: number) { + if ((isBetween((n % 10), 1, 2)) || (n % 20) === 0) + return 'one'; + return 'other'; + }, + '24': function(n: number) { + if ((isBetween(n, 3, 10) || isBetween(n, 13, 19))) + return 'few'; + if (isIn(n, [2, 12])) + return 'two'; + if (isIn(n, [1, 11])) + return 'one'; + return 'other'; + } + }; + + const index = locales2rules.get(lang.replace(/-.*$/, '')); + // @ts-ignore + if (!(index in pluralRules)) { + console.warn('plural form unknown for [' + lang + ']'); + return function() { return 'other'; }; + } + // @ts-ignore + return pluralRules[index]; + } + + getTranslatableChildren(element: HTMLElement) { + return element.querySelectorAll('*[data-l10n-id]') + } + + localize(langs: (string|undefined)[]|string) { + console.log('Available langs ', langs) + if ('string' === typeof langs) { + langs = [langs]; + } + let i = 0 + langs.forEach((lang) => { + if(!lang) return; + langs[i++] = lang; + if(~lang.indexOf('-')) langs[i++] = lang.substring(0, lang.indexOf('-')); + }) + + this.build(langs, (er: null, translations: Map<string, any>) =>{ + this.translations = translations + this.translateElement(translations) + this.mt.trigger('localized') + }) + } + + /** + * Triggers the translation process + * for an element + * @param translations A hash of all translation strings + * @param element A DOM element, if omitted, the document element will be used + */ + translateElement(translations: Map<string, any>, element?: HTMLElement) { + element = element || document.documentElement + const children = element ? this.getTranslatableChildren(element): document.childNodes + + for (let child of children) { + this.translateNode(translations, child as HTMLElement) + } + + // translate element itself if necessary + this.translateNode(translations, element) + } + + asyncForEach(list: (string|undefined)[], iterator: any, cb: Function) { + let i = 0 + , n = list.length + iterator(list[i], i, function each(err?: string) { + if(err) console.error(err) + i++ + if (i < n) return iterator(list[i],i, each); + cb() + }) + } + + /** + * Builds a translation object from a list of langs (loads the necessary translations) + * @param langs Array - a list of langs sorted by priority (default langs should go last) + * @param cb Function - a callback that will be called once all langs have been loaded + */ + build(langs: (string|undefined)[], cb: Function) { + const build = new Map<string, any>() + + this.asyncForEach(langs, (lang: string, _i: number, next:LoaderFunc)=> { + if(!lang) return next(); + this.loader!.load(lang, next) + }, () =>{ + let lang; + langs.reverse() + + // loop through the priority array... + for (let i=0, n=langs.length; i < n; i++) { + lang = langs[i] + if(!lang) continue; + if(!(lang in langs)) {// uh, we don't have this lang availbable.. + // then check for related langs + if(~lang.indexOf('-') != -1) { + lang = lang.split('-')[0]; + } + let l: string|undefined = '' + for(l of langs) { + if(l && lang != l && l.indexOf(lang) === 0) { + lang = l + break; + } + } + + // @ts-ignore + if(lang != l) continue; + } + + + // ... and apply all strings of the current lang in the list + // to our build object + //lang = "de" + if (this.loader!.langs.has(lang)) { + for (let string in this.loader!.langs.get(lang)) { + build.set(string,this.loader!.langs.get(lang)[string]) + } + this.language = lang + } else { + const loaderLang = lang.split('-')[0] + for (let string in this.loader!.langs.get(loaderLang)) { + build.set(string,this.loader!.langs.get(loaderLang)[string]) + } + this.language = loaderLang + } + + // the last applied lang will be exposed as the + // lang the page was translated to + } + cb(null, build) + }) + } + + /** + * Returns the language that was last applied to the translations hash + * thus overriding most of the formerly applied langs + */ + getLanguage() { + return this.language + } + + /** + * Returns the direction of the language returned be html10n#getLanguage + */ + getDirection() { + if(!this.language) return + const langCode = this.language.indexOf('-') == -1? this.language : this.language.substring(0, this.language.indexOf('-')) + return this.rtl.indexOf(langCode) == -1? 'ltr' : 'rtl' + } + + + /** + * Index all <link>s + */ + index() { + // Find all <link>s + const links = document.getElementsByTagName('link') + , resources = [] + for (let i=0, n=links.length; i < n; i++) { + if (links[i].type != 'application/l10n+json') + continue; + resources.push(links[i].href) + } + this.loader = new Loader(resources) + this.mt.trigger('indexed') + } + + translateNode(translations: Map<string, any>, node: HTMLElement) { + const str: { + id?: string, + args?: any, + str?: string + + } = {} + + // get id + str.id = node.getAttribute('data-l10n-id') as string + if (!str.id) return + + if(!translations.get(str.id)) return console.warn('Couldn\'t find translation key '+str.id) + + // get args + if(window.JSON) { + str.args = JSON.parse(node.getAttribute('data-l10n-args') as string) + }else{ + try{ + //str.args = eval(node.getAttribute('data-l10n-args') as string) + console.error("Old eval method invoked!!") + }catch(e) { + console.warn('Couldn\'t parse args for '+str.id) + } + } + + str.str = this.get(str.id, str.args) + + // get attribute name to apply str to + let prop + , index = str.id.lastIndexOf('.') + , attrList = // allowed attributes + { "title": 1 + , "innerHTML": 1 + , "alt": 1 + , "textContent": 1 + , "value": 1 + , "placeholder": 1 + } + if (index > 0 && str.id.substring(index + 1) in attrList) { + // an attribute has been specified (example: "my_translation_key.placeholder") + prop = str.id.substring(index + 1) + } else { // no attribute: assuming text content by default + prop = document.body.textContent ? 'textContent' : 'innerText' + } + + // Apply translation + if (node.children.length === 0 || prop != 'textContent') { + // @ts-ignore + node[prop] = str.str! + node.setAttribute("aria-label", str.str!); // Sets the aria-label + // The idea of the above is that we always have an aria value + // This might be a bit of an abrupt solution but let's see how it goes + } else { + let children = node.childNodes, + found = false + let i = 0, n = children.length; + for (; i < n; i++) { + if (children[i].nodeType === 3 && /\S/.test(children[i].textContent!)) { + if (!found) { + children[i].nodeValue = str.str! + found = true + } else { + children[i].nodeValue = '' + } + } + } + if (!found) { + console.warn('Unexpected error: could not translate element content for key '+str.id, node) + } + } + } + + get(id: string, args?:any) { + let translations = this.translations + if(!translations) return console.warn('No translations available (yet)') + if(!translations.get(id)) return console.warn('Could not find string '+id) + + // apply macros + let str = translations.get(id) + + str = this.substMacros(id, str, args) + + // apply args + str = this.substArguments(str, args) + + return str + } + + substMacros(key: string, str:string, args:any) { + let regex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}/ //.exec('{[ plural(n) other: are {{n}}, one: is ]}') + , match + + while(match = regex.exec(str)) { + // a macro has been found + // Note: at the moment, only one parameter is supported + let macroName = match[1] + , paramName = match[2] + , optv = match[3] + , opts: {[key:string]:any} = {} + + if (!(this.macros.has(macroName))) continue + + if(optv) { + optv.match(/(?=\s*)([a-zA-Z]+)\: ?([ a-zA-Z{}]+)(?=,?)/g)!.forEach(function(arg) { + const parts = arg.split(':') + , name = parts[0]; + opts[name] = parts[1].trim() + }) + } + + let param + if (args && paramName in args) { + param = args[paramName] + } else if (paramName in this.translations) { + param = this.translations.get(paramName) + } + + // there's no macro parser: it has to be defined in html10n.macros + let macro = this.macros.get(macroName)! + str = str.substring(0, match.index) + macro(key, param, opts) + str.substring(match.index+match[0].length) + } + + return str + } + + substArguments(str: string, args:any) { + let reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/ + , match + let translations = this.translations; + while (match = reArgs.exec(str)) { + if (!match || match.length < 2) + return str // argument key not found + + let arg = match[1] + , sub = '' + if (args && arg in args) { + sub = args[arg] + } else if (translations && arg in translations) { + sub = translations.get(arg) + } else { + console.warn('Could not find argument {{' + arg + '}}') + return str + } + + str = str.substring(0, match.index) + sub + str.substring(match.index + match[0].length) + } + + return str + } + +} + + +class MicroEvent { + private events: Map<string, Function[]> + + constructor() { + this.events = new Map(); + } + + bind(event: string, fct: Func) { + if (this.events.get(event) === undefined) { + this.events.set(event, []); + } + + this.events.get(event)!.push(fct); + } + + unbind(event: string, fct: Func) { + if (this.events.get(event) === undefined) { + return; + } + + const index = this.events.get(event)!.indexOf(fct); + if (index !== -1) { + this.events.get(event)!.splice(index, 1); + } + } + + trigger(event: string, ...args: any[]) { + if (this.events.get(event) === undefined) { + return; + } + + for (const fct of this.events.get(event)!) { + fct(...args); + } + } + + mixin(destObject: any) { + const props = ['bind', 'unbind', 'trigger']; + if (destObject !== undefined) { + for (const prop of props) { + // @ts-ignore + destObject[prop] = this[prop]; + } + } + } +} + +type LoaderFunc = () => void + +type ErrorFunc = (data?:any)=>void + +class Loader { + private resources: any + private cache: Map<string, any> + langs: Map<string, any> + + constructor(resources: any) { + this.resources = resources; + this.cache = new Map(); + this.langs = new Map(); + } + + load(lang: string, callback: LoaderFunc) { + if (this.langs.get(lang) !== undefined) { + callback(); + return; + } + + if (this.resources.length > 0) { + let reqs = 0 + for (const resource of this.resources) { + this.fetch(resource, lang, (e)=> { + reqs++; + if (e) console.warn(e) + + if (reqs < this.resources.length) return;// Call back once all reqs are completed + callback && callback() + }) + } + } + } + + fetch(href: string, lang: string, callback: ErrorFunc) { + + if (this.cache.get(href)) { + this.parse(lang, href, this.cache.get(href), callback) + return; + } + + const xhr = new XMLHttpRequest(); + xhr.open('GET', href, /*async: */true) + if (xhr.overrideMimeType) { + xhr.overrideMimeType('application/json; charset=utf-8'); + } + xhr.onreadystatechange = ()=> { + if (xhr.readyState == 4) { + if (xhr.status == 200 || xhr.status === 0) { + const data = JSON.parse(xhr.responseText); + this.cache.set(href, data) + // Pass on the contents for parsing + this.parse(lang, href, data, callback) + } else { + callback(new Error('Failed to load '+href)) + } + } + }; + xhr.send(null); + } + + + parse(lang: string, href: string, data: { + [key: string]: string + }, callback: ErrorFunc) { + if ('object' !== typeof data) { + callback(new Error('A file couldn\'t be parsed as json.')) + return + } + + function getBcp47LangCode(browserLang: string) { + const bcp47Lang = browserLang.toLowerCase(); + + // Browser => BCP 47 + const langCodeMap = new Map([ + ['zh-cn', 'zh-hans-cn'], + ['zh-hk', 'zh-hant-hk'], + ['zh-mo', 'zh-hant-mo'], + ['zh-my', 'zh-hans-my'], + ['zh-sg', 'zh-hans-sg'], + ['zh-tw', 'zh-hant-tw'], + ]) + + return langCodeMap.get(bcp47Lang) ?? bcp47Lang; + } + + // Issue #6129: Fix exceptions + // NOTE: translatewiki.net use all lowercase form by default ('en-gb' insted of 'en-GB') + function getJsonLangCode(bcp47Lang: string) { + const jsonLang = bcp47Lang.toLowerCase(); + // BCP 47 => JSON + const langCodeMap = new Map([ + ['sr-ec', 'sr-cyrl'], + ['sr-el', 'sr-latn'], + ['zh-hk', 'zh-hant-hk'], + ]) + + return langCodeMap.get(jsonLang) ?? jsonLang; + } + + let bcp47LangCode = getBcp47LangCode(lang); + let jsonLangCode = getJsonLangCode(bcp47LangCode); + + if (!data[jsonLangCode]) { + // lang not found + // This may be due to formatting (expected 'ru' but browser sent 'ru-RU') + // Set err msg before mutating lang (we may need this later) + const msg = 'Couldn\'t find translations for ' + lang + + '(lowercase BCP 47 lang tag ' + bcp47LangCode + + ', JSON lang code ' + jsonLangCode + ')'; + // Check for '-' (BCP 47 'ROOT-SCRIPT-REGION-VARIANT') and fallback until found data or ROOT + // - 'ROOT-SCRIPT-REGION': 'zh-Hans-CN' + // - 'ROOT-SCRIPT': 'zh-Hans' + // - 'ROOT-REGION': 'en-GB' + // - 'ROOT-VARIANT': 'be-tarask' + while (!data[jsonLangCode] && bcp47LangCode.lastIndexOf('-') > -1) { + // ROOT-SCRIPT-REGION-VARIANT formatting detected + bcp47LangCode = bcp47LangCode.substring(0, bcp47LangCode.lastIndexOf('-')); // set lang to ROOT lang + jsonLangCode = getJsonLangCode(bcp47LangCode); + } + + if (!data[jsonLangCode]) { + // ROOT lang not found. (e.g 'zh') + // Loop through langs data. Maybe we have a variant? e.g (zh-hans) + let l; // langs item. Declare outside of loop + + for (l in data) { + // Is not ROOT? + // And is variant of ROOT? + // (NOTE: index of ROOT equals 0 would cause unexpected ISO 639-1 vs. 639-3 issues, + // so append dash into query string) + // And is known lang? + if (bcp47LangCode != l && l.indexOf(lang + '-') === 0 && data[l]) { + bcp47LangCode = l; // set lang to ROOT-SCRIPT (e.g 'zh-hans') + jsonLangCode = getJsonLangCode(bcp47LangCode); + break; + } + } + + // Did we find a variant? If not, return err. + if (bcp47LangCode != l) { + return callback(new Error(msg)); + } + } + } + + + lang = jsonLangCode + + if('string' === typeof data[lang]) { + // Import rule + + // absolute path + let importUrl = data[lang]; + + // relative path + if(data[lang].indexOf("http") != 0 && data[lang].indexOf("/") != 0) { + importUrl = href+"/../"+data[lang] + } + + this.fetch(importUrl, lang, callback) + return + } + + if ('object' != typeof data[lang]) { + callback(new Error('Translations should be specified as JSON objects!')) + return + } + + this.langs.set(lang,data[lang]) + // TODO: Also store accompanying langs + callback() + } +} + +const html10n = new Html10n() +export default html10n + +// @ts-ignore +window.html10n = html10n diff --git a/src/static/js/vendors/jquery.js b/src/static/js/vendors/jquery.js deleted file mode 100644 index 26f1a6735fb..00000000000 --- a/src/static/js/vendors/jquery.js +++ /dev/null @@ -1,10704 +0,0 @@ -/*! - * jQuery JavaScript Library v3.7.0 - * https://jquery.com/ - * - * Copyright OpenJS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2023-05-11T18:29Z - */ -( function( global, factory ) { - - "use strict"; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket trac-14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common -// enough that all such attempts are guarded in a try block. - "use strict"; - - var arr = []; - - var getProto = Object.getPrototypeOf; - - var slice = arr.slice; - - var flat = arr.flat ? function( array ) { - return arr.flat.call( array ); - } : function( array ) { - return arr.concat.apply( [], array ); - }; - - - var push = arr.push; - - var indexOf = arr.indexOf; - - var class2type = {}; - - var toString = class2type.toString; - - var hasOwn = class2type.hasOwnProperty; - - var fnToString = hasOwn.toString; - - var ObjectFunctionString = fnToString.call( Object ); - - var support = {}; - - var isFunction = function isFunction( obj ) { - - // Support: Chrome <=57, Firefox <=52 - // In some browsers, typeof returns "function" for HTML <object> elements - // (i.e., `typeof document.createElement( "object" ) === "function"`). - // We don't want to classify *any* DOM node as a function. - // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 - // Plus for old WebKit, typeof returns "function" for HTML collections - // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) - return typeof obj === "function" && typeof obj.nodeType !== "number" && - typeof obj.item !== "function"; - }; - - - var isWindow = function isWindow( obj ) { - return obj != null && obj === obj.window; - }; - - - var document = window.document; - - - - var preservedScriptAttributes = { - type: true, - src: true, - nonce: true, - noModule: true - }; - - function DOMEval( code, node, doc ) { - doc = doc || document; - - var i, val, - script = doc.createElement( "script" ); - - script.text = code; - if ( node ) { - for ( i in preservedScriptAttributes ) { - - // Support: Firefox 64+, Edge 18+ - // Some browsers don't support the "nonce" property on scripts. - // On the other hand, just using `getAttribute` is not enough as - // the `nonce` attribute is reset to an empty string whenever it - // becomes browsing-context connected. - // See https://github.com/whatwg/html/issues/2369 - // See https://html.spec.whatwg.org/#nonce-attributes - // The `node.getAttribute` check was added for the sake of - // `jQuery.globalEval` so that it can fake a nonce-containing node - // via an object. - val = node[ i ] || node.getAttribute && node.getAttribute( i ); - if ( val ) { - script.setAttribute( i, val ); - } - } - } - doc.head.appendChild( script ).parentNode.removeChild( script ); - } - - - function toType( obj ) { - if ( obj == null ) { - return obj + ""; - } - - // Support: Android <=2.3 only (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; - } - /* global Symbol */ -// Defining this global in .eslintrc.json would create a danger of using the global -// unguarded in another place, it seems safer to define global only for this module - - - - var version = "3.7.0", - - rhtmlSuffix = /HTML$/i, - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }; - - jQuery.fn = jQuery.prototype = { - - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - - // Return all the elements in a clean array - if ( num == null ) { - return slice.call( this ); - } - - // Return just the one element from the set - return num < 0 ? this[ num + this.length ] : this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - each: function( callback ) { - return jQuery.each( this, callback ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map( this, function( elem, i ) { - return callback.call( elem, i, elem ); - } ) ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - even: function() { - return this.pushStack( jQuery.grep( this, function( _elem, i ) { - return ( i + 1 ) % 2; - } ) ); - }, - - odd: function() { - return this.pushStack( jQuery.grep( this, function( _elem, i ) { - return i % 2; - } ) ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice - }; - - jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[ 0 ] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !isFunction( target ) ) { - target = {}; - } - - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - - // Only deal with non-null/undefined values - if ( ( options = arguments[ i ] ) != null ) { - - // Extend the base object - for ( name in options ) { - copy = options[ name ]; - - // Prevent Object.prototype pollution - // Prevent never-ending loop - if ( name === "__proto__" || target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject( copy ) || - ( copyIsArray = Array.isArray( copy ) ) ) ) { - src = target[ name ]; - - // Ensure proper type for the source value - if ( copyIsArray && !Array.isArray( src ) ) { - clone = []; - } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { - clone = {}; - } else { - clone = src; - } - copyIsArray = false; - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; - }; - - jQuery.extend( { - - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isPlainObject: function( obj ) { - var proto, Ctor; - - // Detect obvious negatives - // Use toString instead of jQuery.type to catch host objects - if ( !obj || toString.call( obj ) !== "[object Object]" ) { - return false; - } - - proto = getProto( obj ); - - // Objects with no prototype (e.g., `Object.create( null )`) are plain - if ( !proto ) { - return true; - } - - // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; - return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; - }, - - isEmptyObject: function( obj ) { - var name; - - for ( name in obj ) { - return false; - } - return true; - }, - - // Evaluates a script in a provided context; falls back to the global one - // if not specified. - globalEval: function( code, options, doc ) { - DOMEval( code, { nonce: options && options.nonce }, doc ); - }, - - each: function( obj, callback ) { - var length, i = 0; - - if ( isArrayLike( obj ) ) { - length = obj.length; - for ( ; i < length; i++ ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } else { - for ( i in obj ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } - - return obj; - }, - - - // Retrieve the text value of an array of DOM nodes - text: function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - - // If no nodeType, this is expected to be an array - while ( ( node = elem[ i++ ] ) ) { - - // Do not traverse comment nodes - ret += jQuery.text( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - return elem.textContent; - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - - // Do not include comment or processing instruction nodes - - return ret; - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArrayLike( Object( arr ) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - isXMLDoc: function( elem ) { - var namespace = elem && elem.namespaceURI, - docElem = elem && ( elem.ownerDocument || elem ).documentElement; - - // Assume HTML when documentElement doesn't yet exist, such as inside - // document fragments. - return !rhtmlSuffix.test( namespace || docElem && docElem.nodeName || "HTML" ); - }, - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var length, value, - i = 0, - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArrayLike( elems ) ) { - length = elems.length; - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return flat( ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support - } ); - - if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; - } - -// Populate the class2type map - jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), - function( _i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); - } ); - - function isArrayLike( obj ) { - - // Support: real iOS 8.2 only (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = !!obj && "length" in obj && obj.length, - type = toType( obj ); - - if ( isFunction( obj ) || isWindow( obj ) ) { - return false; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; - } - - - function nodeName( elem, name ) { - - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - - } - var pop = arr.pop; - - - var sort = arr.sort; - - - var splice = arr.splice; - - - var whitespace = "[\\x20\\t\\r\\n\\f]"; - - - var rtrimCSS = new RegExp( - "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", - "g" - ); - - - - -// Note: an element does not contain itself - jQuery.contains = function( a, b ) { - var bup = b && b.parentNode; - - return a === bup || !!( bup && bup.nodeType === 1 && ( - - // Support: IE 9 - 11+ - // IE doesn't have `contains` on SVG. - a.contains ? - a.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - ) ); - }; - - - - -// CSS string/identifier serialization -// https://drafts.csswg.org/cssom/#common-serializing-idioms - var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; - - function fcssescape( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; - } - - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; - } - - jQuery.escapeSelector = function( sel ) { - return ( sel + "" ).replace( rcssescape, fcssescape ); - }; - - - - - var preferredDoc = document, - pushNative = push; - - ( function() { - - var i, - Expr, - outermostContext, - sortInput, - hasDuplicate, - push = pushNative, - - // Local document vars - document, - documentElement, - documentIsHTML, - rbuggyQSA, - matches, - - // Instance-specific data - expando = jQuery.expando, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - nonnativeSelectorCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" + - "loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram - identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + - "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", - - // Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + - whitespace + "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + - whitespace + "*" ), - rdescend = new RegExp( whitespace + "|>" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - ID: new RegExp( "^#(" + identifier + ")" ), - CLASS: new RegExp( "^\\.(" + identifier + ")" ), - TAG: new RegExp( "^(" + identifier + "|[*])" ), - ATTR: new RegExp( "^" + attributes ), - PSEUDO: new RegExp( "^" + pseudos ), - CHILD: new RegExp( - "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + - whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + - whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - bool: new RegExp( "^(?:" + booleans + ")$", "i" ), - - // For use in libraries implementing .is() - // We use this for POS matching in `select` - needsContext: new RegExp( "^" + whitespace + - "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + - "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - - // CSS escapes - // https://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + - "?|\\\\([^\\r\\n\\f])", "g" ), - funescape = function( escape, nonHex ) { - var high = "0x" + escape.slice( 1 ) - 0x10000; - - if ( nonHex ) { - - // Strip the backslash prefix from a non-hex escape sequence - return nonHex; - } - - // Replace a hexadecimal escape sequence with the encoded Unicode code point - // Support: IE <=11+ - // For values outside the Basic Multilingual Plane (BMP), manually construct a - // surrogate pair - return high < 0 ? - String.fromCharCode( high + 0x10000 ) : - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // Used for iframes; see `setDocument`. - // Support: IE 9 - 11+, Edge 12 - 18+ - // Removing the function wrapper causes a "Permission Denied" - // error in IE/Edge. - unloadHandler = function() { - setDocument(); - }, - - inDisabledFieldset = addCombinator( - function( elem ) { - return elem.disabled === true && nodeName( elem, "fieldset" ); - }, - { dir: "parentNode", next: "legend" } - ); - -// Support: IE <=9 only -// Accessing document.activeElement can throw unexpectedly -// https://bugs.jquery.com/ticket/13393 - function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } - } - -// Optimize for push.apply( _, NodeList ) - try { - push.apply( - ( arr = slice.call( preferredDoc.childNodes ) ), - preferredDoc.childNodes - ); - - // Support: Android <=4.0 - // Detect silently failing push.apply - // eslint-disable-next-line no-unused-expressions - arr[ preferredDoc.childNodes.length ].nodeType; - } catch ( e ) { - push = { - apply: function( target, els ) { - pushNative.apply( target, slice.call( els ) ); - }, - call: function( target ) { - pushNative.apply( target, slice.call( arguments, 1 ) ); - } - }; - } - - function find( selector, context, results, seed ) { - var m, i, elem, nid, match, groups, newSelector, - newContext = context && context.ownerDocument, - - // nodeType defaults to 9, since context defaults to document - nodeType = context ? context.nodeType : 9; - - results = results || []; - - // Return early from calls with invalid selector or context - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - // Try to shortcut find operations (as opposed to filters) in HTML documents - if ( !seed ) { - setDocument( context ); - context = context || document; - - if ( documentIsHTML ) { - - // If the selector is sufficiently simple, try using a "get*By*" DOM method - // (excepting DocumentFragment context, where the methods don't exist) - if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { - - // ID selector - if ( ( m = match[ 1 ] ) ) { - - // Document context - if ( nodeType === 9 ) { - if ( ( elem = context.getElementById( m ) ) ) { - - // Support: IE 9 only - // getElementById can match elements by name instead of ID - if ( elem.id === m ) { - push.call( results, elem ); - return results; - } - } else { - return results; - } - - // Element context - } else { - - // Support: IE 9 only - // getElementById can match elements by name instead of ID - if ( newContext && ( elem = newContext.getElementById( m ) ) && - find.contains( context, elem ) && - elem.id === m ) { - - push.call( results, elem ); - return results; - } - } - - // Type selector - } else if ( match[ 2 ] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Class selector - } else if ( ( m = match[ 3 ] ) && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // Take advantage of querySelectorAll - if ( !nonnativeSelectorCache[ selector + " " ] && - ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) { - - newSelector = selector; - newContext = context; - - // qSA considers elements outside a scoping root when evaluating child or - // descendant combinators, which is not what we want. - // In such cases, we work around the behavior by prefixing every selector in the - // list with an ID selector referencing the scope context. - // The technique has to be used as well when a leading combinator is used - // as such selectors are not recognized by querySelectorAll. - // Thanks to Andrew Dupont for this technique. - if ( nodeType === 1 && - ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) { - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; - - // We can use :scope instead of the ID hack if the browser - // supports it & if we're not changing the context. - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when - // strict-comparing two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( newContext != context || !support.scope ) { - - // Capture the context ID, setting it first if necessary - if ( ( nid = context.getAttribute( "id" ) ) ) { - nid = jQuery.escapeSelector( nid ); - } else { - context.setAttribute( "id", ( nid = expando ) ); - } - } - - // Prefix every selector in the list - groups = tokenize( selector ); - i = groups.length; - while ( i-- ) { - groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + - toSelector( groups[ i ] ); - } - newSelector = groups.join( "," ); - } - - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - nonnativeSelectorCache( selector, true ); - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } - } - } - } - } - - // All others - return select( selector.replace( rtrimCSS, "$1" ), context, results, seed ); - } - - /** - * Create key-value caches of limited size - * @returns {function(string, object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ - function createCache() { - var keys = []; - - function cache( key, value ) { - - // Use (key + " ") to avoid collision with native prototype properties - // (see https://github.com/jquery/sizzle/issues/157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return ( cache[ key + " " ] = value ); - } - return cache; - } - - /** - * Mark a function for special use by jQuery selector module - * @param {Function} fn The function to mark - */ - function markFunction( fn ) { - fn[ expando ] = true; - return fn; - } - - /** - * Support testing using an element - * @param {Function} fn Passed the created element and returns a boolean result - */ - function assert( fn ) { - var el = document.createElement( "fieldset" ); - - try { - return !!fn( el ); - } catch ( e ) { - return false; - } finally { - - // Remove from its parent by default - if ( el.parentNode ) { - el.parentNode.removeChild( el ); - } - - // release memory in IE - el = null; - } - } - - /** - * Returns a function to use in pseudos for input types - * @param {String} type - */ - function createInputPseudo( type ) { - return function( elem ) { - return nodeName( elem, "input" ) && elem.type === type; - }; - } - - /** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ - function createButtonPseudo( type ) { - return function( elem ) { - return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) && - elem.type === type; - }; - } - - /** - * Returns a function to use in pseudos for :enabled/:disabled - * @param {Boolean} disabled true for :disabled; false for :enabled - */ - function createDisabledPseudo( disabled ) { - - // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable - return function( elem ) { - - // Only certain elements can match :enabled or :disabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled - if ( "form" in elem ) { - - // Check for inherited disabledness on relevant non-disabled elements: - // * listed form-associated elements in a disabled fieldset - // https://html.spec.whatwg.org/multipage/forms.html#category-listed - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled - // * option elements in a disabled optgroup - // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled - // All such elements have a "form" property. - if ( elem.parentNode && elem.disabled === false ) { - - // Option elements defer to a parent optgroup if present - if ( "label" in elem ) { - if ( "label" in elem.parentNode ) { - return elem.parentNode.disabled === disabled; - } else { - return elem.disabled === disabled; - } - } - - // Support: IE 6 - 11+ - // Use the isDisabled shortcut property to check for disabled fieldset ancestors - return elem.isDisabled === disabled || - - // Where there is no isDisabled, check manually - elem.isDisabled !== !disabled && - inDisabledFieldset( elem ) === disabled; - } - - return elem.disabled === disabled; - - // Try to winnow out elements that can't be disabled before trusting the disabled property. - // Some victims get caught in our net (label, legend, menu, track), but it shouldn't - // even exist on them, let alone have a boolean value. - } else if ( "label" in elem ) { - return elem.disabled === disabled; - } - - // Remaining elements are neither :enabled nor :disabled - return false; - }; - } - - /** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ - function createPositionalPseudo( fn ) { - return markFunction( function( argument ) { - argument = +argument; - return markFunction( function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ ( j = matchIndexes[ i ] ) ] ) { - seed[ j ] = !( matches[ j ] = seed[ j ] ); - } - } - } ); - } ); - } - - /** - * Checks a node for validity as a jQuery selector context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ - function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; - } - - /** - * Sets document-related variables once based on the current document - * @param {Element|Object} [node] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ - function setDocument( node ) { - var subWindow, - doc = node ? node.ownerDocument || node : preferredDoc; - - // Return early if doc is invalid or already selected - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Update global variables - document = doc; - documentElement = document.documentElement; - documentIsHTML = !jQuery.isXMLDoc( document ); - - // Support: iOS 7 only, IE 9 - 11+ - // Older browsers didn't support unprefixed `matches`. - matches = documentElement.matches || - documentElement.webkitMatchesSelector || - documentElement.msMatchesSelector; - - // Support: IE 9 - 11+, Edge 12 - 18+ - // Accessing iframe documents after unload throws "permission denied" errors (see trac-13936) - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( preferredDoc != document && - ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { - - // Support: IE 9 - 11+, Edge 12 - 18+ - subWindow.addEventListener( "unload", unloadHandler ); - } - - // Support: IE <10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert( function( el ) { - documentElement.appendChild( el ).id = jQuery.expando; - return !document.getElementsByName || - !document.getElementsByName( jQuery.expando ).length; - } ); - - // Support: IE 9 only - // Check to see if it's possible to do matchesSelector - // on a disconnected node. - support.disconnectedMatch = assert( function( el ) { - return matches.call( el, "*" ); - } ); - - // Support: IE 9 - 11+, Edge 12 - 18+ - // IE/Edge don't support the :scope pseudo-class. - support.scope = assert( function() { - return document.querySelectorAll( ":scope" ); - } ); - - // Support: Chrome 105 - 111 only, Safari 15.4 - 16.3 only - // Make sure the `:has()` argument is parsed unforgivingly. - // We include `*` in the test to detect buggy implementations that are - // _selectively_ forgiving (specifically when the list includes at least - // one valid selector). - // Note that we treat complete lack of support for `:has()` as if it were - // spec-compliant support, which is fine because use of `:has()` in such - // environments will fail in the qSA path and fall back to jQuery traversal - // anyway. - support.cssHas = assert( function() { - try { - document.querySelector( ":has(*,:jqfake)" ); - return false; - } catch ( e ) { - return true; - } - } ); - - // ID filter and find - if ( support.getById ) { - Expr.filter.ID = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute( "id" ) === attrId; - }; - }; - Expr.find.ID = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var elem = context.getElementById( id ); - return elem ? [ elem ] : []; - } - }; - } else { - Expr.filter.ID = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && - elem.getAttributeNode( "id" ); - return node && node.value === attrId; - }; - }; - - // Support: IE 6 - 7 only - // getElementById is not reliable as a find shortcut - Expr.find.ID = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var node, i, elems, - elem = context.getElementById( id ); - - if ( elem ) { - - // Verify the id attribute - node = elem.getAttributeNode( "id" ); - if ( node && node.value === id ) { - return [ elem ]; - } - - // Fall back on getElementsByName - elems = context.getElementsByName( id ); - i = 0; - while ( ( elem = elems[ i++ ] ) ) { - node = elem.getAttributeNode( "id" ); - if ( node && node.value === id ) { - return [ elem ]; - } - } - } - - return []; - } - }; - } - - // Tag - Expr.find.TAG = function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else { - return context.querySelectorAll( tag ); - } - }; - - // Class - Expr.find.CLASS = function( className, context ) { - if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - rbuggyQSA = []; - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert( function( el ) { - - var input; - - documentElement.appendChild( el ).innerHTML = - "<a id='" + expando + "' href='' disabled='disabled'></a>" + - "<select id='" + expando + "-\r\\' disabled='disabled'>" + - "<option selected=''></option></select>"; - - // Support: iOS <=7 - 8 only - // Boolean attributes and "value" are not treated correctly in some XML documents - if ( !el.querySelectorAll( "[selected]" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: iOS <=7 - 8 only - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push( "~=" ); - } - - // Support: iOS 8 only - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push( ".#.+[+~]" ); - } - - // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ - // In some of the document kinds, these selectors wouldn't work natively. - // This is probably OK but for backwards compatibility we want to maintain - // handling them through jQuery traversal in jQuery 3.x. - if ( !el.querySelectorAll( ":checked" ).length ) { - rbuggyQSA.push( ":checked" ); - } - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - input = document.createElement( "input" ); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE 9 - 11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ - // In some of the document kinds, these selectors wouldn't work natively. - // This is probably OK but for backwards compatibility we want to maintain - // handling them through jQuery traversal in jQuery 3.x. - documentElement.appendChild( el ).disabled = true; - if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE 11+, Edge 15 - 18+ - // IE 11/Edge don't find elements on a `[name='']` query in some cases. - // Adding a temporary attribute to the document before the selection works - // around the issue. - // Interestingly, IE 10 & older don't seem to have the issue. - input = document.createElement( "input" ); - input.setAttribute( "name", "" ); - el.appendChild( input ); - if ( !el.querySelectorAll( "[name='']" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + - whitespace + "*(?:''|\"\")" ); - } - } ); - - if ( !support.cssHas ) { - - // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ - // Our regular `try-catch` mechanism fails to detect natively-unsupported - // pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`) - // in browsers that parse the `:has()` argument as a forgiving selector list. - // https://drafts.csswg.org/selectors/#relational now requires the argument - // to be parsed unforgivingly, but browsers have not yet fully adjusted. - rbuggyQSA.push( ":has" ); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { - - // Choose the first element that is related to our preferred document - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( a === document || a.ownerDocument == preferredDoc && - find.contains( preferredDoc, a ) ) { - return -1; - } - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( b === document || b.ownerDocument == preferredDoc && - find.contains( preferredDoc, b ) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - }; - - return document; - } - - find.matches = function( expr, elements ) { - return find( expr, null, null, elements ); - }; - - find.matchesSelector = function( elem, expr ) { - setDocument( elem ); - - if ( documentIsHTML && - !nonnativeSelectorCache[ expr + " " ] && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch ( e ) { - nonnativeSelectorCache( expr, true ); - } - } - - return find( expr, document, null, [ elem ] ).length > 0; - }; - - find.contains = function( context, elem ) { - - // Set document vars if needed - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( ( context.ownerDocument || context ) != document ) { - setDocument( context ); - } - return jQuery.contains( context, elem ); - }; - - - find.attr = function( elem, name ) { - - // Set document vars if needed - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( ( elem.ownerDocument || elem ) != document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - - // Don't get fooled by Object.prototype properties (see trac-13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - if ( val !== undefined ) { - return val; - } - - return elem.getAttribute( name ); - }; - - find.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); - }; - - /** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ - jQuery.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - // - // Support: Android <=4.0+ - // Testing for detecting duplicates is unpredictable so instead assume we can't - // depend on duplicate detection in all browsers without a stable sort. - hasDuplicate = !support.sortStable; - sortInput = !support.sortStable && slice.call( results, 0 ); - sort.call( results, sortOrder ); - - if ( hasDuplicate ) { - while ( ( elem = results[ i++ ] ) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - splice.call( results, duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; - }; - - jQuery.fn.uniqueSort = function() { - return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) ); - }; - - Expr = jQuery.expr = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - ATTR: function( match ) { - match[ 1 ] = match[ 1 ].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" ) - .replace( runescape, funescape ); - - if ( match[ 2 ] === "~=" ) { - match[ 3 ] = " " + match[ 3 ] + " "; - } - - return match.slice( 0, 4 ); - }, - - CHILD: function( match ) { - - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[ 1 ] = match[ 1 ].toLowerCase(); - - if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { - - // nth-* requires argument - if ( !match[ 3 ] ) { - find.error( match[ 0 ] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[ 4 ] = +( match[ 4 ] ? - match[ 5 ] + ( match[ 6 ] || 1 ) : - 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) - ); - match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); - - // other types prohibit arguments - } else if ( match[ 3 ] ) { - find.error( match[ 0 ] ); - } - - return match; - }, - - PSEUDO: function( match ) { - var excess, - unquoted = !match[ 6 ] && match[ 2 ]; - - if ( matchExpr.CHILD.test( match[ 0 ] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[ 3 ] ) { - match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - - // Get excess from tokenize (recursively) - ( excess = tokenize( unquoted, true ) ) && - - // advance to the next closing parenthesis - ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { - - // excess is a negative index - match[ 0 ] = match[ 0 ].slice( 0, excess ); - match[ 2 ] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - TAG: function( nodeNameSelector ) { - var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { - return true; - } : - function( elem ) { - return nodeName( elem, expectedNodeName ); - }; - }, - - CLASS: function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - ( pattern = new RegExp( "(^|" + whitespace + ")" + className + - "(" + whitespace + "|$)" ) ) && - classCache( className, function( elem ) { - return pattern.test( - typeof elem.className === "string" && elem.className || - typeof elem.getAttribute !== "undefined" && - elem.getAttribute( "class" ) || - "" - ); - } ); - }, - - ATTR: function( name, operator, check ) { - return function( elem ) { - var result = find.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - if ( operator === "=" ) { - return result === check; - } - if ( operator === "!=" ) { - return result !== check; - } - if ( operator === "^=" ) { - return check && result.indexOf( check ) === 0; - } - if ( operator === "*=" ) { - return check && result.indexOf( check ) > -1; - } - if ( operator === "$=" ) { - return check && result.slice( -check.length ) === check; - } - if ( operator === "~=" ) { - return ( " " + result.replace( rwhitespace, " " ) + " " ) - .indexOf( check ) > -1; - } - if ( operator === "|=" ) { - return result === check || result.slice( 0, check.length + 1 ) === check + "-"; - } - - return false; - }; - }, - - CHILD: function( type, what, _argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, _context, xml ) { - var cache, outerCache, node, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType, - diff = false; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( ( node = node[ dir ] ) ) { - if ( ofType ? - nodeName( node, name ) : - node.nodeType === 1 ) { - - return false; - } - } - - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || ( parent[ expando ] = {} ); - cache = outerCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex && cache[ 2 ]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( ( node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - ( diff = nodeIndex = 0 ) || start.pop() ) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else { - - // Use previously-cached element index if available - if ( useCache ) { - outerCache = elem[ expando ] || ( elem[ expando ] = {} ); - cache = outerCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex; - } - - // xml :nth-child(...) - // or :nth-last-child(...) or :nth(-last)?-of-type(...) - if ( diff === false ) { - - // Use the same loop as above to seek `elem` from the start - while ( ( node = ++nodeIndex && node && node[ dir ] || - ( diff = nodeIndex = 0 ) || start.pop() ) ) { - - if ( ( ofType ? - nodeName( node, name ) : - node.nodeType === 1 ) && - ++diff ) { - - // Cache the index of each encountered element - if ( useCache ) { - outerCache = node[ expando ] || - ( node[ expando ] = {} ); - outerCache[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - PSEUDO: function( pseudo, argument ) { - - // pseudo-class names are case-insensitive - // https://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - find.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as jQuery does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction( function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[ i ] ); - seed[ idx ] = !( matches[ idx ] = matched[ i ] ); - } - } ) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - - // Potentially complex pseudos - not: markFunction( function( selector ) { - - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrimCSS, "$1" ) ); - - return matcher[ expando ] ? - markFunction( function( seed, matches, _context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( ( elem = unmatched[ i ] ) ) { - seed[ i ] = !( matches[ i ] = elem ); - } - } - } ) : - function( elem, _context, xml ) { - input[ 0 ] = elem; - matcher( input, null, xml, results ); - - // Don't keep the element - // (see https://github.com/jquery/sizzle/issues/299) - input[ 0 ] = null; - return !results.pop(); - }; - } ), - - has: markFunction( function( selector ) { - return function( elem ) { - return find( selector, elem ).length > 0; - }; - } ), - - contains: markFunction( function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1; - }; - } ), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // https://www.w3.org/TR/selectors/#lang-pseudo - lang: markFunction( function( lang ) { - - // lang value must be a valid identifier - if ( !ridentifier.test( lang || "" ) ) { - find.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( ( elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); - return false; - }; - } ), - - // Miscellaneous - target: function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - root: function( elem ) { - return elem === documentElement; - }, - - focus: function( elem ) { - return elem === safeActiveElement() && - document.hasFocus() && - !!( elem.type || elem.href || ~elem.tabIndex ); - }, - - // Boolean properties - enabled: createDisabledPseudo( false ), - disabled: createDisabledPseudo( true ), - - checked: function( elem ) { - - // In CSS3, :checked should return both checked and selected elements - // https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - return ( nodeName( elem, "input" ) && !!elem.checked ) || - ( nodeName( elem, "option" ) && !!elem.selected ); - }, - - selected: function( elem ) { - - // Support: IE <=11+ - // Accessing the selectedIndex property - // forces the browser to treat the default option as - // selected when in an optgroup. - if ( elem.parentNode ) { - // eslint-disable-next-line no-unused-expressions - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - empty: function( elem ) { - - // https://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - parent: function( elem ) { - return !Expr.pseudos.empty( elem ); - }, - - // Element/input types - header: function( elem ) { - return rheader.test( elem.nodeName ); - }, - - input: function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - button: function( elem ) { - return nodeName( elem, "input" ) && elem.type === "button" || - nodeName( elem, "button" ); - }, - - text: function( elem ) { - var attr; - return nodeName( elem, "input" ) && elem.type === "text" && - - // Support: IE <10 only - // New HTML5 attribute values (e.g., "search") appear - // with elem.type === "text" - ( ( attr = elem.getAttribute( "type" ) ) == null || - attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - first: createPositionalPseudo( function() { - return [ 0 ]; - } ), - - last: createPositionalPseudo( function( _matchIndexes, length ) { - return [ length - 1 ]; - } ), - - eq: createPositionalPseudo( function( _matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - } ), - - even: createPositionalPseudo( function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - odd: createPositionalPseudo( function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - lt: createPositionalPseudo( function( matchIndexes, length, argument ) { - var i; - - if ( argument < 0 ) { - i = argument + length; - } else if ( argument > length ) { - i = length; - } else { - i = argument; - } - - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - gt: createPositionalPseudo( function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ) - } - }; - - Expr.pseudos.nth = Expr.pseudos.eq; - -// Add button/input type pseudos - for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); - } - for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); - } - -// Easy API for creating new setFilters - function setFilters() {} - setFilters.prototype = Expr.filters = Expr.pseudos; - Expr.setFilters = new setFilters(); - - function tokenize( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || ( match = rcomma.exec( soFar ) ) ) { - if ( match ) { - - // Don't consume trailing commas as valid - soFar = soFar.slice( match[ 0 ].length ) || soFar; - } - groups.push( ( tokens = [] ) ); - } - - matched = false; - - // Combinators - if ( ( match = rleadingCombinator.exec( soFar ) ) ) { - matched = match.shift(); - tokens.push( { - value: matched, - - // Cast descendant combinators to space - type: match[ 0 ].replace( rtrimCSS, " " ) - } ); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || - ( match = preFilters[ type ]( match ) ) ) ) { - matched = match.shift(); - tokens.push( { - value: matched, - type: type, - matches: match - } ); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - if ( parseOnly ) { - return soFar.length; - } - - return soFar ? - find.error( selector ) : - - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); - } - - function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[ i ].value; - } - return selector; - } - - function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - skip = combinator.next, - key = skip || dir, - checkNonElements = base && key === "parentNode", - doneName = done++; - - return combinator.first ? - - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - return false; - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching - if ( xml ) { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || ( elem[ expando ] = {} ); - - if ( skip && nodeName( elem, skip ) ) { - elem = elem[ dir ] || elem; - } else if ( ( oldCache = outerCache[ key ] ) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return ( newCache[ 2 ] = oldCache[ 2 ] ); - } else { - - // Reuse newcache so results back-propagate to previous elements - outerCache[ key ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { - return true; - } - } - } - } - } - return false; - }; - } - - function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[ i ]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[ 0 ]; - } - - function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - find( selector, contexts[ i ], results ); - } - return results; - } - - function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( ( elem = unmatched[ i ] ) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; - } - - function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction( function( seed, results, context, xml ) { - var temp, i, elem, matcherOut, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || - multipleContexts( selector || "*", - context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems; - - if ( matcher ) { - - // If we have a postFinder, or filtered seed, or non-seed postFilter - // or preexisting results, - matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results; - - // Find primary matches - matcher( matcherIn, matcherOut, context, xml ); - } else { - matcherOut = matcherIn; - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( ( elem = temp[ i ] ) ) { - matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( ( elem = matcherOut[ i ] ) ) { - - // Restore matcherIn since elem is not yet a final match - temp.push( ( matcherIn[ i ] = elem ) ); - } - } - postFinder( null, ( matcherOut = [] ), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( ( elem = matcherOut[ i ] ) && - ( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) { - - seed[ temp ] = !( results[ temp ] = elem ); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - } ); - } - - function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[ 0 ].type ], - implicitRelative = leadingRelative || Expr.relative[ " " ], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || ( - ( checkContext = context ).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - - // Avoid hanging onto element - // (see https://github.com/jquery/sizzle/issues/299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { - matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; - } else { - matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[ j ].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ) - .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) - ).replace( rtrimCSS, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); - } - - function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find.TAG( "*", outermost ), - - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), - len = elems.length; - - if ( outermost ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - outermostContext = context == document || context || outermost; - } - - // Add elements passing elementMatchers directly to results - // Support: iOS <=7 - 9 only - // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching - // elements by id. (see trac-14142) - for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( !context && elem.ownerDocument != document ) { - setDocument( elem ); - xml = !documentIsHTML; - } - while ( ( matcher = elementMatchers[ j++ ] ) ) { - if ( matcher( elem, context || document, xml ) ) { - push.call( results, elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - - // They will have gone through all possible matchers - if ( ( elem = !matcher && elem ) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // `i` is now the count of elements visited above, and adding it to `matchedCount` - // makes the latter nonnegative. - matchedCount += i; - - // Apply set filters to unmatched elements - // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` - // equals `i`), unless we didn't visit _any_ elements in the above loop because we have - // no element matchers and no seed. - // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that - // case, which will result in a "00" `matchedCount` that differs from `i` but is also - // numerically zero. - if ( bySet && i !== matchedCount ) { - j = 0; - while ( ( matcher = setMatchers[ j++ ] ) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !( unmatched[ i ] || setMatched[ i ] ) ) { - setMatched[ i ] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - jQuery.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; - } - - function compile( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[ i ] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, - matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; - } - - /** - * A low-level selection function that works with jQuery's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with jQuery selector compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ - function select( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( ( selector = compiled.selector || selector ) ); - - results = results || []; - - // Try to minimize operations if there is only one selector in the list and no seed - // (the latter of which guarantees us context) - if ( match.length === 1 ) { - - // Reduce context if the leading compound selector is an ID - tokens = match[ 0 ] = match[ 0 ].slice( 0 ); - if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && - context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { - - context = ( Expr.find.ID( - token.matches[ 0 ].replace( runescape, funescape ), - context - ) || [] )[ 0 ]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[ i ]; - - // Abort if we hit a combinator - if ( Expr.relative[ ( type = token.type ) ] ) { - break; - } - if ( ( find = Expr.find[ type ] ) ) { - - // Search, expanding context for leading sibling combinators - if ( ( seed = find( - token.matches[ 0 ].replace( runescape, funescape ), - rsibling.test( tokens[ 0 ].type ) && - testContext( context.parentNode ) || context - ) ) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - !context || rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; - } - -// One-time assignments - -// Support: Android <=4.0 - 4.1+ -// Sort stability - support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; - -// Initialize against the default document - setDocument(); - -// Support: Android <=4.0 - 4.1+ -// Detached nodes confoundingly follow *each other* - support.sortDetached = assert( function( el ) { - - // Should return 1, but returns 4 (following) - return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; - } ); - - jQuery.find = find; - -// Deprecated - jQuery.expr[ ":" ] = jQuery.expr.pseudos; - jQuery.unique = jQuery.uniqueSort; - -// These have always been private, but they used to be documented -// as part of Sizzle so let's maintain them in the 3.x line -// for backwards compatibility purposes. - find.compile = compile; - find.select = select; - find.setDocument = setDocument; - - find.escape = jQuery.escapeSelector; - find.getText = jQuery.text; - find.isXML = jQuery.isXMLDoc; - find.selectors = jQuery.expr; - find.support = jQuery.support; - find.uniqueSort = jQuery.uniqueSort; - - /* eslint-enable */ - - } )(); - - - var dir = function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; - }; - - - var siblings = function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; - }; - - - var rneedsContext = jQuery.expr.match.needsContext; - - var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); - - - -// Implement the identical functionality for filter and not - function winnow( elements, qualifier, not ) { - if ( isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) !== not; - } ); - } - - // Single element - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - } ); - } - - // Arraylike of elements (jQuery, arguments, Array) - if ( typeof qualifier !== "string" ) { - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not; - } ); - } - - // Filtered directly for both simple and complex selectors - return jQuery.filter( qualifier, elements, not ); - } - - jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - if ( elems.length === 1 && elem.nodeType === 1 ) { - return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; - } - - return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - } ) ); - }; - - jQuery.fn.extend( { - find: function( selector ) { - var i, ret, - len = this.length, - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter( function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - } ) ); - } - - ret = this.pushStack( [] ); - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - return len > 1 ? jQuery.uniqueSort( ret ) : ret; - }, - filter: function( selector ) { - return this.pushStack( winnow( this, selector || [], false ) ); - }, - not: function( selector ) { - return this.pushStack( winnow( this, selector || [], true ) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } - } ); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) - var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over <tag> to avoid XSS via location.hash (trac-9521) - // Strict HTML recognition (trac-11290: must start with <) - // Shortcut simple #id case for speed - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - - init = jQuery.fn.init = function( selector, context, root ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Method init() accepts an alternate rootjQuery - // so migrate can support jQuery.sub (gh-2101) - root = root || rootjQuery; - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[ 0 ] === "<" && - selector[ selector.length - 1 ] === ">" && - selector.length >= 3 ) { - - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && ( match[ 1 ] || !context ) ) { - - // HANDLE: $(html) -> $(array) - if ( match[ 1 ] ) { - context = context instanceof jQuery ? context[ 0 ] : context; - - // Option to run scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[ 1 ], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - - // Properties of context are called as methods if possible - if ( isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[ 2 ] ); - - if ( elem ) { - - // Inject the element directly into the jQuery object - this[ 0 ] = elem; - this.length = 1; - } - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || root ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this[ 0 ] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( isFunction( selector ) ) { - return root.ready !== undefined ? - root.ready( selector ) : - - // Execute immediately if ready is not present - selector( jQuery ); - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation - init.prototype = jQuery.fn; - -// Initialize central reference - rootjQuery = jQuery( document ); - - - var rparentsprev = /^(?:parents|prev(?:Until|All))/, - - // Methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - - jQuery.fn.extend( { - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter( function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[ i ] ) ) { - return true; - } - } - } ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - targets = typeof selectors !== "string" && jQuery( selectors ); - - // Positional selectors never match, since there's no _selection_ context - if ( !rneedsContext.test( selectors ) ) { - for ( ; i < l; i++ ) { - for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - - // Always skip document fragments - if ( cur.nodeType < 11 && ( targets ? - targets.index( cur ) > -1 : - - // Don't pass non-elements to jQuery#find - cur.nodeType === 1 && - jQuery.find.matchesSelector( cur, selectors ) ) ) { - - matched.push( cur ); - break; - } - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); - }, - - // Determine the position of an element within the set - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // Index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.uniqueSort( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - } - } ); - - function sibling( cur, dir ) { - while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} - return cur; - } - - jQuery.each( { - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, _i, until ) { - return dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, _i, until ) { - return dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, _i, until ) { - return dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return siblings( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return siblings( elem.firstChild ); - }, - contents: function( elem ) { - if ( elem.contentDocument != null && - - // Support: IE 11+ - // <object> elements with no `data` attribute has an object - // `contentDocument` with a `null` prototype. - getProto( elem.contentDocument ) ) { - - return elem.contentDocument; - } - - // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only - // Treat the template element as a regular one in browsers that - // don't support it. - if ( nodeName( elem, "template" ) ) { - elem = elem.content || elem; - } - - return jQuery.merge( [], elem.childNodes ); - } - }, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.uniqueSort( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; - } ); - var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); - - - -// Convert String-formatted options into Object-formatted ones - function createOptions( options ) { - var object = {}; - jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { - object[ flag ] = true; - } ); - return object; - } - - /* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ - jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - createOptions( options ) : - jQuery.extend( {}, options ); - - var // Flag to know if list is currently firing - firing, - - // Last fire value for non-forgettable lists - memory, - - // Flag to know if list was already fired - fired, - - // Flag to prevent firing - locked, - - // Actual callback list - list = [], - - // Queue of execution data for repeatable lists - queue = [], - - // Index of currently firing callback (modified by add/remove as needed) - firingIndex = -1, - - // Fire callbacks - fire = function() { - - // Enforce single-firing - locked = locked || options.once; - - // Execute callbacks for all pending executions, - // respecting firingIndex overrides and runtime changes - fired = firing = true; - for ( ; queue.length; firingIndex = -1 ) { - memory = queue.shift(); - while ( ++firingIndex < list.length ) { - - // Run callback and check for early termination - if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && - options.stopOnFalse ) { - - // Jump to end and forget the data so .add doesn't re-fire - firingIndex = list.length; - memory = false; - } - } - } - - // Forget the data if we're done with it - if ( !options.memory ) { - memory = false; - } - - firing = false; - - // Clean up if we're done firing for good - if ( locked ) { - - // Keep an empty list if we have data for future add calls - if ( memory ) { - list = []; - - // Otherwise, this object is spent - } else { - list = ""; - } - } - }, - - // Actual Callbacks object - self = { - - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - - // If we have memory from a past run, we should fire after adding - if ( memory && !firing ) { - firingIndex = list.length - 1; - queue.push( memory ); - } - - ( function add( args ) { - jQuery.each( args, function( _, arg ) { - if ( isFunction( arg ) ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && toType( arg ) !== "string" ) { - - // Inspect recursively - add( arg ); - } - } ); - } )( arguments ); - - if ( memory && !firing ) { - fire(); - } - } - return this; - }, - - // Remove a callback from the list - remove: function() { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - - // Handle firing indexes - if ( index <= firingIndex ) { - firingIndex--; - } - } - } ); - return this; - }, - - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? - jQuery.inArray( fn, list ) > -1 : - list.length > 0; - }, - - // Remove all callbacks from the list - empty: function() { - if ( list ) { - list = []; - } - return this; - }, - - // Disable .fire and .add - // Abort any current/pending executions - // Clear all callbacks and values - disable: function() { - locked = queue = []; - list = memory = ""; - return this; - }, - disabled: function() { - return !list; - }, - - // Disable .fire - // Also disable .add unless we have memory (since it would have no effect) - // Abort any pending executions - lock: function() { - locked = queue = []; - if ( !memory && !firing ) { - list = memory = ""; - } - return this; - }, - locked: function() { - return !!locked; - }, - - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( !locked ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - queue.push( args ); - if ( !firing ) { - fire(); - } - } - return this; - }, - - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; - }; - - - function Identity( v ) { - return v; - } - function Thrower( ex ) { - throw ex; - } - - function adoptValue( value, resolve, reject, noValue ) { - var method; - - try { - - // Check for promise aspect first to privilege synchronous behavior - if ( value && isFunction( ( method = value.promise ) ) ) { - method.call( value ).done( resolve ).fail( reject ); - - // Other thenables - } else if ( value && isFunction( ( method = value.then ) ) ) { - method.call( value, resolve, reject ); - - // Other non-thenables - } else { - - // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: - // * false: [ value ].slice( 0 ) => resolve( value ) - // * true: [ value ].slice( 1 ) => resolve() - resolve.apply( undefined, [ value ].slice( noValue ) ); - } - - // For Promises/A+, convert exceptions into rejections - // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in - // Deferred#then to conditionally suppress rejection. - } catch ( value ) { - - // Support: Android 4.0 only - // Strict mode functions invoked without .call/.apply get global-object context - reject.apply( undefined, [ value ] ); - } - } - - jQuery.extend( { - - Deferred: function( func ) { - var tuples = [ - - // action, add listener, callbacks, - // ... .then handlers, argument index, [final state] - [ "notify", "progress", jQuery.Callbacks( "memory" ), - jQuery.Callbacks( "memory" ), 2 ], - [ "resolve", "done", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 0, "resolved" ], - [ "reject", "fail", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 1, "rejected" ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - "catch": function( fn ) { - return promise.then( null, fn ); - }, - - // Keep pipe for back-compat - pipe: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - - return jQuery.Deferred( function( newDefer ) { - jQuery.each( tuples, function( _i, tuple ) { - - // Map tuples (progress, done, fail) to arguments (done, fail, progress) - var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; - - // deferred.progress(function() { bind to newDefer or newDefer.notify }) - // deferred.done(function() { bind to newDefer or newDefer.resolve }) - // deferred.fail(function() { bind to newDefer or newDefer.reject }) - deferred[ tuple[ 1 ] ]( function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && isFunction( returned.promise ) ) { - returned.promise() - .progress( newDefer.notify ) - .done( newDefer.resolve ) - .fail( newDefer.reject ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( - this, - fn ? [ returned ] : arguments - ); - } - } ); - } ); - fns = null; - } ).promise(); - }, - then: function( onFulfilled, onRejected, onProgress ) { - var maxDepth = 0; - function resolve( depth, deferred, handler, special ) { - return function() { - var that = this, - args = arguments, - mightThrow = function() { - var returned, then; - - // Support: Promises/A+ section 2.3.3.3.3 - // https://promisesaplus.com/#point-59 - // Ignore double-resolution attempts - if ( depth < maxDepth ) { - return; - } - - returned = handler.apply( that, args ); - - // Support: Promises/A+ section 2.3.1 - // https://promisesaplus.com/#point-48 - if ( returned === deferred.promise() ) { - throw new TypeError( "Thenable self-resolution" ); - } - - // Support: Promises/A+ sections 2.3.3.1, 3.5 - // https://promisesaplus.com/#point-54 - // https://promisesaplus.com/#point-75 - // Retrieve `then` only once - then = returned && - - // Support: Promises/A+ section 2.3.4 - // https://promisesaplus.com/#point-64 - // Only check objects and functions for thenability - ( typeof returned === "object" || - typeof returned === "function" ) && - returned.then; - - // Handle a returned thenable - if ( isFunction( then ) ) { - - // Special processors (notify) just wait for resolution - if ( special ) { - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ) - ); - - // Normal processors (resolve) also hook into progress - } else { - - // ...and disregard older resolution values - maxDepth++; - - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ), - resolve( maxDepth, deferred, Identity, - deferred.notifyWith ) - ); - } - - // Handle all other returned values - } else { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Identity ) { - that = undefined; - args = [ returned ]; - } - - // Process the value(s) - // Default process is resolve - ( special || deferred.resolveWith )( that, args ); - } - }, - - // Only normal processors (resolve) catch and reject exceptions - process = special ? - mightThrow : - function() { - try { - mightThrow(); - } catch ( e ) { - - if ( jQuery.Deferred.exceptionHook ) { - jQuery.Deferred.exceptionHook( e, - process.error ); - } - - // Support: Promises/A+ section 2.3.3.3.4.1 - // https://promisesaplus.com/#point-61 - // Ignore post-resolution exceptions - if ( depth + 1 >= maxDepth ) { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Thrower ) { - that = undefined; - args = [ e ]; - } - - deferred.rejectWith( that, args ); - } - } - }; - - // Support: Promises/A+ section 2.3.3.3.1 - // https://promisesaplus.com/#point-57 - // Re-resolve promises immediately to dodge false rejection from - // subsequent errors - if ( depth ) { - process(); - } else { - - // Call an optional hook to record the error, in case of exception - // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getErrorHook ) { - process.error = jQuery.Deferred.getErrorHook(); - - // The deprecated alias of the above. While the name suggests - // returning the stack, not an error instance, jQuery just passes - // it directly to `console.warn` so both will work; an instance - // just better cooperates with source maps. - } else if ( jQuery.Deferred.getStackHook ) { - process.error = jQuery.Deferred.getStackHook(); - } - window.setTimeout( process ); - } - }; - } - - return jQuery.Deferred( function( newDefer ) { - - // progress_handlers.add( ... ) - tuples[ 0 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onProgress ) ? - onProgress : - Identity, - newDefer.notifyWith - ) - ); - - // fulfilled_handlers.add( ... ) - tuples[ 1 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onFulfilled ) ? - onFulfilled : - Identity - ) - ); - - // rejected_handlers.add( ... ) - tuples[ 2 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onRejected ) ? - onRejected : - Thrower - ) - ); - } ).promise(); - }, - - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 5 ]; - - // promise.progress = list.add - // promise.done = list.add - // promise.fail = list.add - promise[ tuple[ 1 ] ] = list.add; - - // Handle state - if ( stateString ) { - list.add( - function() { - - // state = "resolved" (i.e., fulfilled) - // state = "rejected" - state = stateString; - }, - - // rejected_callbacks.disable - // fulfilled_callbacks.disable - tuples[ 3 - i ][ 2 ].disable, - - // rejected_handlers.disable - // fulfilled_handlers.disable - tuples[ 3 - i ][ 3 ].disable, - - // progress_callbacks.lock - tuples[ 0 ][ 2 ].lock, - - // progress_handlers.lock - tuples[ 0 ][ 3 ].lock - ); - } - - // progress_handlers.fire - // fulfilled_handlers.fire - // rejected_handlers.fire - list.add( tuple[ 3 ].fire ); - - // deferred.notify = function() { deferred.notifyWith(...) } - // deferred.resolve = function() { deferred.resolveWith(...) } - // deferred.reject = function() { deferred.rejectWith(...) } - deferred[ tuple[ 0 ] ] = function() { - deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); - return this; - }; - - // deferred.notifyWith = list.fireWith - // deferred.resolveWith = list.fireWith - // deferred.rejectWith = list.fireWith - deferred[ tuple[ 0 ] + "With" ] = list.fireWith; - } ); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( singleValue ) { - var - - // count of uncompleted subordinates - remaining = arguments.length, - - // count of unprocessed arguments - i = remaining, - - // subordinate fulfillment data - resolveContexts = Array( i ), - resolveValues = slice.call( arguments ), - - // the primary Deferred - primary = jQuery.Deferred(), - - // subordinate callback factory - updateFunc = function( i ) { - return function( value ) { - resolveContexts[ i ] = this; - resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( !( --remaining ) ) { - primary.resolveWith( resolveContexts, resolveValues ); - } - }; - }; - - // Single- and empty arguments are adopted like Promise.resolve - if ( remaining <= 1 ) { - adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, - !remaining ); - - // Use .then() to unwrap secondary thenables (cf. gh-3000) - if ( primary.state() === "pending" || - isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - - return primary.then(); - } - } - - // Multiple arguments are aggregated like Promise.all array elements - while ( i-- ) { - adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); - } - - return primary.promise(); - } - } ); - - -// These usually indicate a programmer mistake during development, -// warn about them ASAP rather than swallowing them by default. - var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - -// If `jQuery.Deferred.getErrorHook` is defined, `asyncError` is an error -// captured before the async barrier to get the original error cause -// which may otherwise be hidden. - jQuery.Deferred.exceptionHook = function( error, asyncError ) { - - // Support: IE 8 - 9 only - // Console exists when dev tools are open, which can happen at any time - if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { - window.console.warn( "jQuery.Deferred exception: " + error.message, - error.stack, asyncError ); - } - }; - - - - - jQuery.readyException = function( error ) { - window.setTimeout( function() { - throw error; - } ); - }; - - - - -// The deferred used on DOM ready - var readyList = jQuery.Deferred(); - - jQuery.fn.ready = function( fn ) { - - readyList - .then( fn ) - - // Wrap jQuery.readyException in a function so that the lookup - // happens at the time of error handling instead of callback - // registration. - .catch( function( error ) { - jQuery.readyException( error ); - } ); - - return this; - }; - - jQuery.extend( { - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See trac-6781 - readyWait: 1, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - } - } ); - - jQuery.ready.then = readyList.then; - -// The ready event handler and self cleanup method - function completed() { - document.removeEventListener( "DOMContentLoaded", completed ); - window.removeEventListener( "load", completed ); - jQuery.ready(); - } - -// Catch cases where $(document).ready() is called -// after the browser event has already occurred. -// Support: IE <=9 - 10 only -// Older IE sometimes signals "interactive" too soon - if ( document.readyState === "complete" || - ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - - // Handle it asynchronously to allow scripts the opportunity to delay ready - window.setTimeout( jQuery.ready ); - - } else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed ); - } - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function - var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( toType( key ) === "object" ) { - chainable = true; - for ( i in key ) { - access( elems, fn, i, key[ i ], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, _key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( - elems[ i ], key, raw ? - value : - value.call( elems[ i ], i, fn( elems[ i ], key ) ) - ); - } - } - } - - if ( chainable ) { - return elems; - } - - // Gets - if ( bulk ) { - return fn.call( elems ); - } - - return len ? fn( elems[ 0 ], key ) : emptyGet; - }; - - -// Matches dashed string for camelizing - var rmsPrefix = /^-ms-/, - rdashAlpha = /-([a-z])/g; - -// Used by camelCase as callback to replace() - function fcamelCase( _all, letter ) { - return letter.toUpperCase(); - } - -// Convert dashed to camelCase; used by the css and data modules -// Support: IE <=9 - 11, Edge 12 - 15 -// Microsoft forgot to hump their vendor prefix (trac-9572) - function camelCase( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - } - var acceptData = function( owner ) { - - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); - }; - - - - - function Data() { - this.expando = jQuery.expando + Data.uid++; - } - - Data.uid = 1; - - Data.prototype = { - - cache: function( owner ) { - - // Check if the owner object already has a cache - var value = owner[ this.expando ]; - - // If not, create one - if ( !value ) { - value = {}; - - // We can accept data for non-element nodes in modern browsers, - // but we should not, see trac-8335. - // Always return an empty object. - if ( acceptData( owner ) ) { - - // If it is a node unlikely to be stringify-ed or looped over - // use plain assignment - if ( owner.nodeType ) { - owner[ this.expando ] = value; - - // Otherwise secure it in a non-enumerable property - // configurable must be true to allow the property to be - // deleted when data is removed - } else { - Object.defineProperty( owner, this.expando, { - value: value, - configurable: true - } ); - } - } - } - - return value; - }, - set: function( owner, data, value ) { - var prop, - cache = this.cache( owner ); - - // Handle: [ owner, key, value ] args - // Always use camelCase key (gh-2257) - if ( typeof data === "string" ) { - cache[ camelCase( data ) ] = value; - - // Handle: [ owner, { properties } ] args - } else { - - // Copy the properties one-by-one to the cache object - for ( prop in data ) { - cache[ camelCase( prop ) ] = data[ prop ]; - } - } - return cache; - }, - get: function( owner, key ) { - return key === undefined ? - this.cache( owner ) : - - // Always use camelCase key (gh-2257) - owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; - }, - access: function( owner, key, value ) { - - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ( ( key && typeof key === "string" ) && value === undefined ) ) { - - return this.get( owner, key ); - } - - // When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, - cache = owner[ this.expando ]; - - if ( cache === undefined ) { - return; - } - - if ( key !== undefined ) { - - // Support array or space separated string of keys - if ( Array.isArray( key ) ) { - - // If key is an array of keys... - // We always set camelCase keys, so remove that. - key = key.map( camelCase ); - } else { - key = camelCase( key ); - - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - key = key in cache ? - [ key ] : - ( key.match( rnothtmlwhite ) || [] ); - } - - i = key.length; - - while ( i-- ) { - delete cache[ key[ i ] ]; - } - } - - // Remove the expando if there's no more data - if ( key === undefined || jQuery.isEmptyObject( cache ) ) { - - // Support: Chrome <=35 - 45 - // Webkit & Blink performance suffers when deleting properties - // from DOM nodes, so set to undefined instead - // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) - if ( owner.nodeType ) { - owner[ this.expando ] = undefined; - } else { - delete owner[ this.expando ]; - } - } - }, - hasData: function( owner ) { - var cache = owner[ this.expando ]; - return cache !== undefined && !jQuery.isEmptyObject( cache ); - } - }; - var dataPriv = new Data(); - - var dataUser = new Data(); - - - -// Implementation Summary -// -// 1. Enforce API surface and semantic compatibility with 1.9.x branch -// 2. Improve the module's maintainability by reducing the storage -// paths to a single mechanism. -// 3. Use the same single mechanism to support "private" and "user" data. -// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) -// 5. Avoid exposing implementation details on user objects (eg. expando properties) -// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - - var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /[A-Z]/g; - - function getData( data ) { - if ( data === "true" ) { - return true; - } - - if ( data === "false" ) { - return false; - } - - if ( data === "null" ) { - return null; - } - - // Only convert to a number if it doesn't change the string - if ( data === +data + "" ) { - return +data; - } - - if ( rbrace.test( data ) ) { - return JSON.parse( data ); - } - - return data; - } - - function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = getData( data ); - } catch ( e ) {} - - // Make sure we set the data so it isn't changed later - dataUser.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; - } - - jQuery.extend( { - hasData: function( elem ) { - return dataUser.hasData( elem ) || dataPriv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return dataUser.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - dataUser.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to dataPriv methods, these can be deprecated. - _data: function( elem, name, data ) { - return dataPriv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - dataPriv.remove( elem, name ); - } - } ); - - jQuery.fn.extend( { - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = dataUser.get( elem ); - - if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE 11 only - // The attrs elements can be null (trac-14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = camelCase( name.slice( 5 ) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - dataPriv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each( function() { - dataUser.set( this, key ); - } ); - } - - return access( this, function( value ) { - var data; - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - - // Attempt to get data from the cache - // The key will always be camelCased in Data - data = dataUser.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, key ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each( function() { - - // We always store the camelCased key - dataUser.set( this, key, value ); - } ); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each( function() { - dataUser.remove( this, key ); - } ); - } - } ); - - - jQuery.extend( { - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = dataPriv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || Array.isArray( data ) ) { - queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // Clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // Not public - generate a queueHooks object, or return the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { - empty: jQuery.Callbacks( "once memory" ).add( function() { - dataPriv.remove( elem, [ type + "queue", key ] ); - } ) - } ); - } - } ); - - jQuery.fn.extend( { - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[ 0 ], type ); - } - - return data === undefined ? - this : - this.each( function() { - var queue = jQuery.queue( this, type, data ); - - // Ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - } ); - }, - dequeue: function( type ) { - return this.each( function() { - jQuery.dequeue( this, type ); - } ); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } - } ); - var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; - - var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); - - - var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - - var documentElement = document.documentElement; - - - - var isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ); - }, - composed = { composed: true }; - - // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only - // Check attachment across shadow DOM boundaries when possible (gh-3504) - // Support: iOS 10.0-10.2 only - // Early iOS 10 versions support `attachShadow` but not `getRootNode`, - // leading to errors. We need to check for `getRootNode`. - if ( documentElement.getRootNode ) { - isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ) || - elem.getRootNode( composed ) === elem.ownerDocument; - }; - } - var isHiddenWithinTree = function( elem, el ) { - - // isHiddenWithinTree might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - - // Inline style trumps all - return elem.style.display === "none" || - elem.style.display === "" && - - // Otherwise, check computed style - // Support: Firefox <=43 - 45 - // Disconnected elements can have computed display: none, so first confirm that elem is - // in the document. - isAttached( elem ) && - - jQuery.css( elem, "display" ) === "none"; - }; - - - - function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, scale, - maxIterations = 20, - currentValue = tween ? - function() { - return tween.cur(); - } : - function() { - return jQuery.css( elem, prop, "" ); - }, - initial = currentValue(), - unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), - - // Starting value computation is required for potential unit mismatches - initialInUnit = elem.nodeType && - ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && - rcssNum.exec( jQuery.css( elem, prop ) ); - - if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { - - // Support: Firefox <=54 - // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) - initial = initial / 2; - - // Trust units reported by jQuery.css - unit = unit || initialInUnit[ 3 ]; - - // Iteratively approximate from a nonzero starting point - initialInUnit = +initial || 1; - - while ( maxIterations-- ) { - - // Evaluate and update our best guess (doubling guesses that zero out). - // Finish if the scale equals or crosses 1 (making the old*new product non-positive). - jQuery.style( elem, prop, initialInUnit + unit ); - if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { - maxIterations = 0; - } - initialInUnit = initialInUnit / scale; - - } - - initialInUnit = initialInUnit * 2; - jQuery.style( elem, prop, initialInUnit + unit ); - - // Make sure we update the tween properties later on - valueParts = valueParts || []; - } - - if ( valueParts ) { - initialInUnit = +initialInUnit || +initial || 0; - - // Apply relative offset (+=/-=) if specified - adjusted = valueParts[ 1 ] ? - initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : - +valueParts[ 2 ]; - if ( tween ) { - tween.unit = unit; - tween.start = initialInUnit; - tween.end = adjusted; - } - } - return adjusted; - } - - - var defaultDisplayMap = {}; - - function getDefaultDisplay( elem ) { - var temp, - doc = elem.ownerDocument, - nodeName = elem.nodeName, - display = defaultDisplayMap[ nodeName ]; - - if ( display ) { - return display; - } - - temp = doc.body.appendChild( doc.createElement( nodeName ) ); - display = jQuery.css( temp, "display" ); - - temp.parentNode.removeChild( temp ); - - if ( display === "none" ) { - display = "block"; - } - defaultDisplayMap[ nodeName ] = display; - - return display; - } - - function showHide( elements, show ) { - var display, elem, - values = [], - index = 0, - length = elements.length; - - // Determine new display value for elements that need to change - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - display = elem.style.display; - if ( show ) { - - // Since we force visibility upon cascade-hidden elements, an immediate (and slow) - // check is required in this first loop unless we have a nonempty display value (either - // inline or about-to-be-restored) - if ( display === "none" ) { - values[ index ] = dataPriv.get( elem, "display" ) || null; - if ( !values[ index ] ) { - elem.style.display = ""; - } - } - if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { - values[ index ] = getDefaultDisplay( elem ); - } - } else { - if ( display !== "none" ) { - values[ index ] = "none"; - - // Remember what we're overwriting - dataPriv.set( elem, "display", display ); - } - } - } - - // Set the display of the elements in a second loop to avoid constant reflow - for ( index = 0; index < length; index++ ) { - if ( values[ index ] != null ) { - elements[ index ].style.display = values[ index ]; - } - } - - return elements; - } - - jQuery.fn.extend( { - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - if ( typeof state === "boolean" ) { - return state ? this.show() : this.hide(); - } - - return this.each( function() { - if ( isHiddenWithinTree( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - } ); - } - } ); - var rcheckableType = ( /^(?:checkbox|radio)$/i ); - - var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); - - var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); - - - - ( function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // Support: Android 4.0 - 4.3 only - // Check state lost if the name is set (trac-11217) - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (trac-14901) - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Android <=4.1 only - // Older WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE <=11 only - // Make sure textarea (and checkbox) defaultValue is properly cloned - div.innerHTML = "<textarea>x</textarea>"; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; - - // Support: IE <=9 only - // IE <=9 replaces <option> tags with their contents when inserted outside of - // the select element. - div.innerHTML = "<option></option>"; - support.option = !!div.lastChild; - } )(); - - -// We have to close these tags to support XHTML (trac-13200) - var wrapMap = { - - // XHTML parsers do not magically insert elements in the - // same way that tag soup parsers do. So we cannot shorten - // this by omitting <tbody> or other required elements. - thead: [ 1, "<table>", "</table>" ], - col: [ 2, "<table><colgroup>", "</colgroup></table>" ], - tr: [ 2, "<table><tbody>", "</tbody></table>" ], - td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], - - _default: [ 0, "", "" ] - }; - - wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; - wrapMap.th = wrapMap.td; - -// Support: IE <=9 only - if ( !support.option ) { - wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ]; - } - - - function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (trac-15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; - } - - -// Mark scripts as having already been evaluated - function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } - } - - - var rhtml = /<|&#?\w+;/; - - function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, attached, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( toType( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (trac-12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - attached = isAttached( elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( attached ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; - } - - - var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - - function returnTrue() { - return true; - } - - function returnFalse() { - return false; - } - - function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); - } - - /* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ - jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Only attach events to objects that accept data - if ( !acceptData( elem ) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = Object.create( null ); - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( nativeEvent ), - - handlers = ( - dataPriv.get( this, "events" ) || Object.create( null ) - )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // If the event is namespaced, then each handler is only invoked if it is - // specially universal or its namespaces are a superset of the event's. - if ( !event.rnamespace || handleObj.namespace === false || - event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG <use> instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (trac-13208) - // Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (trac-13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - click: { - - // Utilize native event to ensure correct state for checkable inputs - setup: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Claim the first handler - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", true ); - } - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Force setup before triggering a click - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - leverageNative( el, "click" ); - } - - // Return non-false to allow normal event-path propagation - return true; - }, - - // For cross-browser consistency, suppress native .click() on links - // Also prevent it if we're currently inside a leveraged native-event stack - _default: function( event ) { - var target = event.target; - return rcheckableType.test( target.type ) && - target.click && nodeName( target, "input" ) && - dataPriv.get( target, "click" ) || - nodeName( target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } - }; - -// Ensure the presence of an event listener that handles manually-triggered -// synthetic events by interrupting progress until reinvoked in response to -// *native* events that it fires directly, ensuring that state changes have -// already occurred before other listeners are invoked. - function leverageNative( el, type, isSetup ) { - - // Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add - if ( !isSetup ) { - if ( dataPriv.get( el, type ) === undefined ) { - jQuery.event.add( el, type, returnTrue ); - } - return; - } - - // Register the controller as a special universal handler for all event namespaces - dataPriv.set( el, type, false ); - jQuery.event.add( el, type, { - namespace: false, - handler: function( event ) { - var result, - saved = dataPriv.get( this, type ); - - if ( ( event.isTrigger & 1 ) && this[ type ] ) { - - // Interrupt processing of the outer synthetic .trigger()ed event - if ( !saved ) { - - // Store arguments for use when handling the inner native event - // There will always be at least one argument (an event object), so this array - // will not be confused with a leftover capture object. - saved = slice.call( arguments ); - dataPriv.set( this, type, saved ); - - // Trigger the native event and capture its result - this[ type ](); - result = dataPriv.get( this, type ); - dataPriv.set( this, type, false ); - - if ( saved !== result ) { - - // Cancel the outer synthetic event - event.stopImmediatePropagation(); - event.preventDefault(); - - return result; - } - - // If this is an inner synthetic event for an event with a bubbling surrogate - // (focus or blur), assume that the surrogate already propagated from triggering - // the native event and prevent that from happening again here. - // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the - // bubbling surrogate propagates *after* the non-bubbling base), but that seems - // less bad than duplication. - } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { - event.stopPropagation(); - } - - // If this is a native event triggered above, everything is now in order - // Fire an inner synthetic event with the original arguments - } else if ( saved ) { - - // ...and capture the result - dataPriv.set( this, type, jQuery.event.trigger( - saved[ 0 ], - saved.slice( 1 ), - this - ) ); - - // Abort handling of the native event by all jQuery handlers while allowing - // native handlers on the same element to run. On target, this is achieved - // by stopping immediate propagation just on the jQuery event. However, - // the native event is re-wrapped by a jQuery one on each level of the - // propagation so the only way to stop it for jQuery is to stop it for - // everyone via native `stopPropagation()`. This is not a problem for - // focus/blur which don't bubble, but it does also stop click on checkboxes - // and radios. We accept this limitation. - event.stopPropagation(); - event.isImmediatePropagationStopped = returnTrue; - } - } - } ); - } - - jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } - }; - - jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (trac-504, trac-13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; - }; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html - jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } - }; - -// Includes all common event props including KeyEvent and MouseEvent specific props - jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - code: true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - which: true - }, jQuery.event.addProp ); - - jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { - - function focusMappedHandler( nativeEvent ) { - if ( document.documentMode ) { - - // Support: IE 11+ - // Attach a single focusin/focusout handler on the document while someone wants - // focus/blur. This is because the former are synchronous in IE while the latter - // are async. In other browsers, all those handlers are invoked synchronously. - - // `handle` from private data would already wrap the event, but we need - // to change the `type` here. - var handle = dataPriv.get( this, "handle" ), - event = jQuery.event.fix( nativeEvent ); - event.type = nativeEvent.type === "focusin" ? "focus" : "blur"; - event.isSimulated = true; - - // First, handle focusin/focusout - handle( nativeEvent ); - - // ...then, handle focus/blur - // - // focus/blur don't bubble while focusin/focusout do; simulate the former by only - // invoking the handler at the lower level. - if ( event.target === event.currentTarget ) { - - // The setup part calls `leverageNative`, which, in turn, calls - // `jQuery.event.add`, so event handle will already have been set - // by this point. - handle( event ); - } - } else { - - // For non-IE browsers, attach a single capturing handler on the document - // while someone wants focusin/focusout. - jQuery.event.simulate( delegateType, nativeEvent.target, - jQuery.event.fix( nativeEvent ) ); - } - } - - jQuery.event.special[ type ] = { - - // Utilize native event if possible so blur/focus sequence is correct - setup: function() { - - var attaches; - - // Claim the first handler - // dataPriv.set( this, "focus", ... ) - // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, true ); - - if ( document.documentMode ) { - - // Support: IE 9 - 11+ - // We use the same native handler for focusin & focus (and focusout & blur) - // so we need to coordinate setup & teardown parts between those events. - // Use `delegateType` as the key as `type` is already used by `leverageNative`. - attaches = dataPriv.get( this, delegateType ); - if ( !attaches ) { - this.addEventListener( delegateType, focusMappedHandler ); - } - dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 ); - } else { - - // Return false to allow normal processing in the caller - return false; - } - }, - trigger: function() { - - // Force setup before trigger - leverageNative( this, type ); - - // Return non-false to allow normal event-path propagation - return true; - }, - - teardown: function() { - var attaches; - - if ( document.documentMode ) { - attaches = dataPriv.get( this, delegateType ) - 1; - if ( !attaches ) { - this.removeEventListener( delegateType, focusMappedHandler ); - dataPriv.remove( this, delegateType ); - } else { - dataPriv.set( this, delegateType, attaches ); - } - } else { - - // Return false to indicate standard teardown should be applied - return false; - } - }, - - // Suppress native focus or blur if we're currently inside - // a leveraged native-event stack - _default: function( event ) { - return dataPriv.get( event.target, type ); - }, - - delegateType: delegateType - }; - - // Support: Firefox <=44 - // Firefox doesn't have focus(in | out) events - // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 - // - // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 - // focus(in | out) events fire after focus & blur events, - // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order - // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 - // - // Support: IE 9 - 11+ - // To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch, - // attach a single handler for both events in IE. - jQuery.event.special[ delegateType ] = { - setup: function() { - - // Handle: regular nodes (via `this.ownerDocument`), window - // (via `this.document`) & document (via `this`). - var doc = this.ownerDocument || this.document || this, - dataHolder = document.documentMode ? this : doc, - attaches = dataPriv.get( dataHolder, delegateType ); - - // Support: IE 9 - 11+ - // We use the same native handler for focusin & focus (and focusout & blur) - // so we need to coordinate setup & teardown parts between those events. - // Use `delegateType` as the key as `type` is already used by `leverageNative`. - if ( !attaches ) { - if ( document.documentMode ) { - this.addEventListener( delegateType, focusMappedHandler ); - } else { - doc.addEventListener( type, focusMappedHandler, true ); - } - } - dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this.document || this, - dataHolder = document.documentMode ? this : doc, - attaches = dataPriv.get( dataHolder, delegateType ) - 1; - - if ( !attaches ) { - if ( document.documentMode ) { - this.removeEventListener( delegateType, focusMappedHandler ); - } else { - doc.removeEventListener( type, focusMappedHandler, true ); - } - dataPriv.remove( dataHolder, delegateType ); - } else { - dataPriv.set( dataHolder, delegateType, attaches ); - } - } - }; - } ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). - jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" - }, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; - } ); - - jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } - } ); - - - var - - // Support: IE <=10 - 11, Edge 12 - 13 only - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /<script|<style|<link/i, - - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - - rcleanScript = /^\s*<!\[CDATA\[|\]\]>\s*$/g; - -// Prefer a tbody over its parent table for containing new rows - function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( elem ).children( "tbody" )[ 0 ] || elem; - } - - return elem; - } - -// Replace/restore the type attribute of script elements for safe DOM manipulation - function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; - } - function restoreScript( elem ) { - if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { - elem.type = elem.type.slice( 5 ); - } else { - elem.removeAttribute( "type" ); - } - - return elem; - } - - function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.get( src ); - events = pdataOld.events; - - if ( events ) { - dataPriv.remove( dest, "handle events" ); - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } - } - -// Fix IE bugs, see support tests - function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } - } - - function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = flat( args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - valueIsFunction = isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( valueIsFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( valueIsFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (trac-8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl && !node.noModule ) { - jQuery._evalUrl( node.src, { - nonce: node.nonce || node.getAttribute( "nonce" ) - }, doc ); - } - } else { - - // Unwrap a CDATA section containing script contents. This shouldn't be - // needed as in XML documents they're already not visible when - // inspecting element contents and in HTML documents they have no - // meaning but we're preserving that logic for backwards compatibility. - // This will be removed completely in 4.0. See gh-4904. - DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); - } - } - } - } - } - } - - return collection; - } - - function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && isAttached( node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; - } - - jQuery.extend( { - htmlPrefilter: function( html ) { - return html; - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = isAttached( elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew jQuery#find here for performance reasons: - // https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } - } ); - - jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } - } ); - - jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" - }, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; - } ); - var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - - var rcustomProp = /^--/; - - - var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - - var swap = function( elem, options, callback ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; - }; - - - var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); - - - - ( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - container.style.cssText = "position:absolute;left:-11111px;width:60px;" + - "margin-top:1px;padding:0;border:0"; - div.style.cssText = - "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + - "margin:auto;border:1px;padding:1px;" + - "width:60%;top:1%"; - documentElement.appendChild( container ).appendChild( div ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; - - // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 - // Some styles come back with percentage values, even though they shouldn't - div.style.right = "60%"; - pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; - - // Support: IE 9 - 11 only - // Detect misreporting of content dimensions for box-sizing:border-box elements - boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; - - // Support: IE 9 only - // Detect overflow:scroll screwiness (gh-3699) - // Support: Chrome <=64 - // Don't get tricked when zoom affects offsetWidth (gh-4029) - div.style.position = "absolute"; - scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - function roundPixelMeasures( measure ) { - return Math.round( parseFloat( measure ) ); - } - - var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, - reliableTrDimensionsVal, reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (trac-8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - jQuery.extend( support, { - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelBoxStyles: function() { - computeStyleTests(); - return pixelBoxStylesVal; - }, - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - }, - scrollboxSize: function() { - computeStyleTests(); - return scrollboxSizeVal; - }, - - // Support: IE 9 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Behavior in IE 9 is more subtle than in newer versions & it passes - // some versions of this test; make sure not to make it pass there! - // - // Support: Firefox 70+ - // Only Firefox includes border widths - // in computed dimensions. (gh-4529) - reliableTrDimensions: function() { - var table, tr, trChild, trStyle; - if ( reliableTrDimensionsVal == null ) { - table = document.createElement( "table" ); - tr = document.createElement( "tr" ); - trChild = document.createElement( "div" ); - - table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; - tr.style.cssText = "border:1px solid"; - - // Support: Chrome 86+ - // Height set through cssText does not get applied. - // Computed height then comes back as 0. - tr.style.height = "1px"; - trChild.style.height = "9px"; - - // Support: Android 8 Chrome 86+ - // In our bodyBackground.html iframe, - // display for all div elements is set to "inline", - // which causes a problem only in Android 8 Chrome 86. - // Ensuring the div is display: block - // gets around this issue. - trChild.style.display = "block"; - - documentElement - .appendChild( table ) - .appendChild( tr ) - .appendChild( trChild ); - - trStyle = window.getComputedStyle( tr ); - reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + - parseInt( trStyle.borderTopWidth, 10 ) + - parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; - - documentElement.removeChild( table ); - } - return reliableTrDimensionsVal; - } - } ); - } )(); - - - function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - isCustomProp = rcustomProp.test( name ), - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, trac-12537) - // .css('--customProperty) (gh-3144) - if ( computed ) { - - // Support: IE <=9 - 11+ - // IE only supports `"float"` in `getPropertyValue`; in computed styles - // it's only available as `"cssFloat"`. We no longer modify properties - // sent to `.css()` apart from camelCasing, so we need to check both. - // Normally, this would create difference in behavior: if - // `getPropertyValue` returns an empty string, the value returned - // by `.css()` would be `undefined`. This is usually the case for - // disconnected elements. However, in IE even disconnected elements - // with no styles return `"none"` for `getPropertyValue( "float" )` - ret = computed.getPropertyValue( name ) || computed[ name ]; - - if ( isCustomProp && ret ) { - - // Support: Firefox 105+, Chrome <=105+ - // Spec requires trimming whitespace for custom properties (gh-4926). - // Firefox only trims leading whitespace. Chrome just collapses - // both leading & trailing whitespace to a single space. - // - // Fall back to `undefined` if empty string returned. - // This collapses a missing definition with property defined - // and set to an empty string but there's no standard API - // allowing us to differentiate them without a performance penalty - // and returning `undefined` aligns with older jQuery. - // - // rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED - // as whitespace while CSS does not, but this is not a problem - // because CSS preprocessing replaces them with U+000A LINE FEED - // (which *is* CSS whitespace) - // https://www.w3.org/TR/css-syntax-3/#input-preprocessing - ret = ret.replace( rtrimCSS, "$1" ) || undefined; - } - - if ( ret === "" && !isAttached( elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; - } - - - function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; - } - - - var cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style, - vendorProps = {}; - -// Return a vendor-prefixed property or undefined - function vendorPropName( name ) { - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } - } - -// Return a potentially-mapped jQuery.cssProps or vendor prefixed property - function finalPropName( name ) { - var final = jQuery.cssProps[ name ] || vendorProps[ name ]; - - if ( final ) { - return final; - } - if ( name in emptyStyle ) { - return name; - } - return vendorProps[ name ] = vendorPropName( name ) || name; - } - - - var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }; - - function setPositiveNumber( _elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; - } - - function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { - var i = dimension === "width" ? 1 : 0, - extra = 0, - delta = 0, - marginDelta = 0; - - // Adjustment may not be necessary - if ( box === ( isBorderBox ? "border" : "content" ) ) { - return 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin - // Count margin delta separately to only add it after scroll gutter adjustment. - // This is needed to make negative margins work with `outerHeight( true )` (gh-3982). - if ( box === "margin" ) { - marginDelta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); - } - - // If we get here with a content-box, we're seeking "padding" or "border" or "margin" - if ( !isBorderBox ) { - - // Add padding - delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // For "border" or "margin", add border - if ( box !== "padding" ) { - delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - - // But still keep track of it otherwise - } else { - extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - - // If we get here with a border-box (content + padding + border), we're seeking "content" or - // "padding" or "margin" - } else { - - // For "content", subtract padding - if ( box === "content" ) { - delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // For "content" or "padding", subtract border - if ( box !== "margin" ) { - delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - // Account for positive content-box scroll gutter when requested by providing computedVal - if ( !isBorderBox && computedVal >= 0 ) { - - // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border - // Assuming integer scroll gutter, subtract the rest and round down - delta += Math.max( 0, Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - computedVal - - delta - - extra - - 0.5 - - // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter - // Use an explicit zero to avoid NaN (gh-3964) - ) ) || 0; - } - - return delta + marginDelta; - } - - function getWidthOrHeight( elem, dimension, extra ) { - - // Start with computed style - var styles = getStyles( elem ), - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). - // Fake content-box until we know it's needed to know the true value. - boxSizingNeeded = !support.boxSizingReliable() || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - valueIsBorderBox = isBorderBox, - - val = curCSS( elem, dimension, styles ), - offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); - - // Support: Firefox <=54 - // Return a confounding non-pixel value or feign ignorance, as appropriate. - if ( rnumnonpx.test( val ) ) { - if ( !extra ) { - return val; - } - val = "auto"; - } - - - // Support: IE 9 - 11 only - // Use offsetWidth/offsetHeight for when box sizing is unreliable. - // In those cases, the computed value can be trusted to be border-box. - if ( ( !support.boxSizingReliable() && isBorderBox || - - // Support: IE 10 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Interestingly, in some cases IE 9 doesn't suffer from this issue. - !support.reliableTrDimensions() && nodeName( elem, "tr" ) || - - // Fall back to offsetWidth/offsetHeight when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - val === "auto" || - - // Support: Android <=4.1 - 4.3 only - // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) - !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && - - // Make sure the element is visible & connected - elem.getClientRects().length ) { - - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Where available, offsetWidth/offsetHeight approximate border box dimensions. - // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the - // retrieved value as a content box dimension. - valueIsBorderBox = offsetProp in elem; - if ( valueIsBorderBox ) { - val = elem[ offsetProp ]; - } - } - - // Normalize "" and auto - val = parseFloat( val ) || 0; - - // Adjust for the element's box model - return ( val + - boxModelAdjustment( - elem, - dimension, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles, - - // Provide the current computed size to request scroll gutter calculation (gh-3589) - val - ) - ) + "px"; - } - - jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - animationIterationCount: true, - aspectRatio: true, - borderImageSlice: true, - columnCount: true, - flexGrow: true, - flexShrink: true, - fontWeight: true, - gridArea: true, - gridColumn: true, - gridColumnEnd: true, - gridColumnStart: true, - gridRow: true, - gridRowEnd: true, - gridRowStart: true, - lineHeight: true, - opacity: true, - order: true, - orphans: true, - scale: true, - widows: true, - zIndex: true, - zoom: true, - - // SVG-related - fillOpacity: true, - floodOpacity: true, - stopOpacity: true, - strokeMiterlimit: true, - strokeOpacity: true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: {}, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (trac-7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug trac-9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (trac-7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append - // "px" to a few hardcoded values. - if ( type === "number" && !isCustomProp ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } - } ); - - jQuery.each( [ "height", "width" ], function( _i, dimension ) { - jQuery.cssHooks[ dimension ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, dimension, extra ); - } ) : - getWidthOrHeight( elem, dimension, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = getStyles( elem ), - - // Only read styles.position if the test has a chance to fail - // to avoid forcing a reflow. - scrollboxSizeBuggy = !support.scrollboxSize() && - styles.position === "absolute", - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) - boxSizingNeeded = scrollboxSizeBuggy || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - subtract = extra ? - boxModelAdjustment( - elem, - dimension, - extra, - isBorderBox, - styles - ) : - 0; - - // Account for unreliable border-box dimensions by comparing offset* to computed and - // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && scrollboxSizeBuggy ) { - subtract -= Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - parseFloat( styles[ dimension ] ) - - boxModelAdjustment( elem, dimension, "border", false, styles ) - - 0.5 - ); - } - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ dimension ] = value; - value = jQuery.css( elem, dimension ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; - } ); - - jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } - ); - -// These hooks are used by animate to expand properties - jQuery.each( { - margin: "", - padding: "", - border: "Width" - }, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( prefix !== "margin" ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } - } ); - - jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } - } ); - - - function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); - } - jQuery.Tween = Tween; - - Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } - }; - - Tween.prototype.init.prototype = Tween.prototype; - - Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && ( - jQuery.cssHooks[ tween.prop ] || - tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } - }; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes - Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } - }; - - jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" - }; - - jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point - jQuery.fx.step = {}; - - - - - var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - - function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } - } - -// Animations created synchronously will run synchronously - function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = Date.now() ); - } - -// Generate parameters to create a standard animation - function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; - } - - function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } - } - - function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 15 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY and Edge just mirrors - // the overflowX value there. - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } - } - - function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } - } - - function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (trac-12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - result.stop.bind( result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; - } - - jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } - } ); - - jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; - }; - - jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } - } ); - - jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; - } ); - -// Generate shortcuts for custom animations - jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } - }, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; - } ); - - jQuery.timers = []; - jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = Date.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; - }; - - jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); - }; - - jQuery.fx.interval = 13; - jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); - }; - - jQuery.fx.stop = function() { - inProgress = null; - }; - - jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 - }; - - -// Based off of the plugin by Clint Helfers, with permission. - jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); - }; - - - ( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; - } )(); - - - var boolHook, - attrHandle = jQuery.expr.attrHandle; - - jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } - } ); - - jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } - } ); - -// Hooks for boolean attributes - boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } - }; - - jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; - } ); - - - - - var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - - jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } - } ); - - jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // Use proper attribute retrieval (trac-12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } - } ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop - if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; - } - - jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" - ], function() { - jQuery.propFix[ this.toLowerCase() ] = this; - } ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - - function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; - } - - function classesToArray( value ) { - if ( Array.isArray( value ) ) { - return value; - } - if ( typeof value === "string" ) { - return value.match( rnothtmlwhite ) || []; - } - return []; - } - - jQuery.fn.extend( { - addClass: function( value ) { - var classNames, cur, curValue, className, i, finalValue; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - classNames = classesToArray( value ); - - if ( classNames.length ) { - return this.each( function() { - curValue = getClass( this ); - cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - if ( cur.indexOf( " " + className + " " ) < 0 ) { - cur += className + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - this.setAttribute( "class", finalValue ); - } - } - } ); - } - - return this; - }, - - removeClass: function( value ) { - var classNames, cur, curValue, className, i, finalValue; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - classNames = classesToArray( value ); - - if ( classNames.length ) { - return this.each( function() { - curValue = getClass( this ); - - // This expression is here for better compressibility (see addClass) - cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - - // Remove *all* instances - while ( cur.indexOf( " " + className + " " ) > -1 ) { - cur = cur.replace( " " + className + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - this.setAttribute( "class", finalValue ); - } - } - } ); - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var classNames, className, i, self, - type = typeof value, - isValidValue = type === "string" || Array.isArray( value ); - - if ( isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - if ( typeof stateVal === "boolean" && isValidValue ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - classNames = classesToArray( value ); - - return this.each( function() { - if ( isValidValue ) { - - // Toggle individual class names - self = jQuery( this ); - - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } - } ); - - - - - var rreturn = /\r/g; - - jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, valueIsFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - valueIsFunction = isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( valueIsFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } - } ); - - jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (trac-14686, trac-14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (trac-2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } - } ); - -// Radios and checkboxes getter/setter - jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } - } ); - - - - -// Return jQuery for attributes-only inclusion - var location = window.location; - - var nonce = { guid: Date.now() }; - - var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing - jQuery.parseXML = function( data ) { - var xml, parserErrorElem; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) {} - - parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; - if ( !xml || parserErrorElem ) { - jQuery.error( "Invalid XML: " + ( - parserErrorElem ? - jQuery.map( parserErrorElem.childNodes, function( el ) { - return el.textContent; - } ).join( "\n" ) : - data - ) ); - } - return xml; - }; - - - var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - stopPropagationCallback = function( e ) { - e.stopPropagation(); - }; - - jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = lastElement = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (trac-9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724) - if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - lastElement = cur; - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (trac-6170) - if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - - if ( event.isPropagationStopped() ) { - lastElement.addEventListener( type, stopPropagationCallback ); - } - - elem[ type ](); - - if ( event.isPropagationStopped() ) { - lastElement.removeEventListener( type, stopPropagationCallback ); - } - - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - - } ); - - jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } - } ); - - - var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - - function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && toType( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } - } - -// Serialize an array of form elements or a set of -// key/values into a query string - jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - if ( a == null ) { - return ""; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); - }; - - jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ).filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ).map( function( _i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } - } ); - - - var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // trac-7653, trac-8125, trac-8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (trac-10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - - originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport - function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; - } - -// Base inspection function for prefilters and transports - function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); - } - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes trac-9887 - function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; - } - - /* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ - function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } - } - - /* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ - function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; - } - - jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() + " " ] = - ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) - .concat( match[ 2 ] ); - } - } - match = responseHeaders[ key.toLowerCase() + " " ]; - } - return match == null ? null : match.join( ", " ); - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (trac-10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket trac-12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 15 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (trac-15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available and should be processed, append data to url - if ( s.data && ( s.processData || typeof s.data === "string" ) ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // trac-9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + - uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Use a noop converter for missing script but not if jsonp - if ( !isSuccess && - jQuery.inArray( "script", s.dataTypes ) > -1 && - jQuery.inArray( "json", s.dataTypes ) < 0 ) { - s.converters[ "text script" ] = function() {}; - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } - } ); - - jQuery.each( [ "get", "post" ], function( _i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; - } ); - - jQuery.ajaxPrefilter( function( s ) { - var i; - for ( i in s.headers ) { - if ( i.toLowerCase() === "content-type" ) { - s.contentType = s.headers[ i ] || ""; - } - } - } ); - - - jQuery._evalUrl = function( url, options, doc ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (trac-11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - - // Only evaluate the response if it is successful (gh-4126) - // dataFilter is not invoked for failure responses, so using it instead - // of the default converter is kludgy but it works. - converters: { - "text script": function() {} - }, - dataFilter: function( response ) { - jQuery.globalEval( response, options, doc ); - } - } ); - }; - - - jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var htmlIsFunction = isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } - } ); - - - jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); - }; - jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); - }; - - - - - jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} - }; - - var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // trac-1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - - support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); - support.ajax = xhrSupported = !!xhrSupported; - - jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.ontimeout = - xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see trac-8605, trac-14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // trac-14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } - } ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) - jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } - } ); - -// Install script dataType - jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } - } ); - -// Handle cache's special case and crossDomain - jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } - } ); - -// Bind script tag hack transport - jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain or forced-by-attrs requests - if ( s.crossDomain || s.scriptAttrs ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( "<script>" ) - .attr( s.scriptAttrs || {} ) - .prop( { charset: s.scriptCharset, src: s.url } ) - .on( "load error", callback = function( evt ) { - script.remove(); - callback = null; - if ( evt ) { - complete( evt.type === "error" ? 404 : 200, evt.type ); - } - } ); - - // Use native DOM manipulation to avoid our domManip AJAX trickery - document.head.appendChild( script[ 0 ] ); - }, - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } - } ); - - - - - var oldCallbacks = [], - rjsonp = /(=)\?(?=&|$)|\?\?/; - -// Default jsonp settings - jQuery.ajaxSetup( { - jsonp: "callback", - jsonpCallback: function() { - var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce.guid++ ) ); - this[ callback ] = true; - return callback; - } - } ); - -// Detect, normalize options and install callbacks for jsonp requests - jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { - - var callbackName, overwritten, responseContainer, - jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? - "url" : - typeof s.data === "string" && - ( s.contentType || "" ) - .indexOf( "application/x-www-form-urlencoded" ) === 0 && - rjsonp.test( s.data ) && "data" - ); - - // Handle iff the expected data type is "jsonp" or we have a parameter to set - if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { - - // Get callback name, remembering preexisting value associated with it - callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ? - s.jsonpCallback() : - s.jsonpCallback; - - // Insert callback into url or form data - if ( jsonProp ) { - s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); - } else if ( s.jsonp !== false ) { - s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; - } - - // Use data converter to retrieve json after script execution - s.converters[ "script json" ] = function() { - if ( !responseContainer ) { - jQuery.error( callbackName + " was not called" ); - } - return responseContainer[ 0 ]; - }; - - // Force json dataType - s.dataTypes[ 0 ] = "json"; - - // Install callback - overwritten = window[ callbackName ]; - window[ callbackName ] = function() { - responseContainer = arguments; - }; - - // Clean-up function (fires after converters) - jqXHR.always( function() { - - // If previous value didn't exist - remove it - if ( overwritten === undefined ) { - jQuery( window ).removeProp( callbackName ); - - // Otherwise restore preexisting value - } else { - window[ callbackName ] = overwritten; - } - - // Save back as free - if ( s[ callbackName ] ) { - - // Make sure that re-using the options doesn't screw things around - s.jsonpCallback = originalSettings.jsonpCallback; - - // Save the callback name for future use - oldCallbacks.push( callbackName ); - } - - // Call if it was a function and we have a response - if ( responseContainer && isFunction( overwritten ) ) { - overwritten( responseContainer[ 0 ] ); - } - - responseContainer = overwritten = undefined; - } ); - - // Delegate to script - return "script"; - } - } ); - - - - -// Support: Safari 8 only -// In Safari 8 documents created via document.implementation.createHTMLDocument -// collapse sibling forms: the second one becomes a child of the first one. -// Because of that, this security measure has to be disabled in Safari 8. -// https://bugs.webkit.org/show_bug.cgi?id=137337 - support.createHTMLDocument = ( function() { - var body = document.implementation.createHTMLDocument( "" ).body; - body.innerHTML = "<form></form><form></form>"; - return body.childNodes.length === 2; - } )(); - - -// Argument "data" should be string of html -// context (optional): If specified, the fragment will be created in this context, -// defaults to document -// keepScripts (optional): If true, will include scripts passed in the html string - jQuery.parseHTML = function( data, context, keepScripts ) { - if ( typeof data !== "string" ) { - return []; - } - if ( typeof context === "boolean" ) { - keepScripts = context; - context = false; - } - - var base, parsed, scripts; - - if ( !context ) { - - // Stop scripts or inline event handlers from being executed immediately - // by using document.implementation - if ( support.createHTMLDocument ) { - context = document.implementation.createHTMLDocument( "" ); - - // Set the base href for the created document - // so any parsed elements with URLs - // are based on the document's URL (gh-2965) - base = context.createElement( "base" ); - base.href = document.location.href; - context.head.appendChild( base ); - } else { - context = document; - } - } - - parsed = rsingleTag.exec( data ); - scripts = !keepScripts && []; - - // Single tag - if ( parsed ) { - return [ context.createElement( parsed[ 1 ] ) ]; - } - - parsed = buildFragment( [ data ], context, scripts ); - - if ( scripts && scripts.length ) { - jQuery( scripts ).remove(); - } - - return jQuery.merge( [], parsed.childNodes ); - }; - - - /** - * Load a url into a page - */ - jQuery.fn.load = function( url, params, callback ) { - var selector, type, response, - self = this, - off = url.indexOf( " " ); - - if ( off > -1 ) { - selector = stripAndCollapse( url.slice( off ) ); - url = url.slice( 0, off ); - } - - // If it's a function - if ( isFunction( params ) ) { - - // We assume that it's the callback - callback = params; - params = undefined; - - // Otherwise, build a param string - } else if ( params && typeof params === "object" ) { - type = "POST"; - } - - // If we have elements to modify, make the request - if ( self.length > 0 ) { - jQuery.ajax( { - url: url, - - // If "type" variable is undefined, then "GET" method will be used. - // Make value of this field explicit since - // user can override it through ajaxSetup method - type: type || "GET", - dataType: "html", - data: params - } ).done( function( responseText ) { - - // Save response for use in complete callback - response = arguments; - - self.html( selector ? - - // If a selector was specified, locate the right elements in a dummy div - // Exclude scripts to avoid IE 'Permission Denied' errors - jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) : - - // Otherwise use the full result - responseText ); - - // If the request succeeds, this function gets "data", "status", "jqXHR" - // but they are ignored because response was set above. - // If it fails, this function gets "jqXHR", "status", "error" - } ).always( callback && function( jqXHR, status ) { - self.each( function() { - callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] ); - } ); - } ); - } - - return this; - }; - - - - - jQuery.expr.pseudos.animated = function( elem ) { - return jQuery.grep( jQuery.timers, function( fn ) { - return elem === fn.elem; - } ).length; - }; - - - - - jQuery.offset = { - setOffset: function( elem, options, i ) { - var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, - position = jQuery.css( elem, "position" ), - curElem = jQuery( elem ), - props = {}; - - // Set position first, in-case top/left are set even on static elem - if ( position === "static" ) { - elem.style.position = "relative"; - } - - curOffset = curElem.offset(); - curCSSTop = jQuery.css( elem, "top" ); - curCSSLeft = jQuery.css( elem, "left" ); - calculatePosition = ( position === "absolute" || position === "fixed" ) && - ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; - - // Need to be able to calculate position if either - // top or left is auto and position is either absolute or fixed - if ( calculatePosition ) { - curPosition = curElem.position(); - curTop = curPosition.top; - curLeft = curPosition.left; - - } else { - curTop = parseFloat( curCSSTop ) || 0; - curLeft = parseFloat( curCSSLeft ) || 0; - } - - if ( isFunction( options ) ) { - - // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) - options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); - } - - if ( options.top != null ) { - props.top = ( options.top - curOffset.top ) + curTop; - } - if ( options.left != null ) { - props.left = ( options.left - curOffset.left ) + curLeft; - } - - if ( "using" in options ) { - options.using.call( elem, props ); - - } else { - curElem.css( props ); - } - } - }; - - jQuery.fn.extend( { - - // offset() relates an element's border box to the document origin - offset: function( options ) { - - // Preserve chaining for setter - if ( arguments.length ) { - return options === undefined ? - this : - this.each( function( i ) { - jQuery.offset.setOffset( this, options, i ); - } ); - } - - var rect, win, - elem = this[ 0 ]; - - if ( !elem ) { - return; - } - - // Return zeros for disconnected and hidden (display: none) elements (gh-2310) - // Support: IE <=11 only - // Running getBoundingClientRect on a - // disconnected node in IE throws an error - if ( !elem.getClientRects().length ) { - return { top: 0, left: 0 }; - } - - // Get document-relative position by adding viewport scroll to viewport-relative gBCR - rect = elem.getBoundingClientRect(); - win = elem.ownerDocument.defaultView; - return { - top: rect.top + win.pageYOffset, - left: rect.left + win.pageXOffset - }; - }, - - // position() relates an element's margin box to its offset parent's padding box - // This corresponds to the behavior of CSS absolute positioning - position: function() { - if ( !this[ 0 ] ) { - return; - } - - var offsetParent, offset, doc, - elem = this[ 0 ], - parentOffset = { top: 0, left: 0 }; - - // position:fixed elements are offset from the viewport, which itself always has zero offset - if ( jQuery.css( elem, "position" ) === "fixed" ) { - - // Assume position:fixed implies availability of getBoundingClientRect - offset = elem.getBoundingClientRect(); - - } else { - offset = this.offset(); - - // Account for the *real* offset parent, which can be the document or its root element - // when a statically positioned element is identified - doc = elem.ownerDocument; - offsetParent = elem.offsetParent || doc.documentElement; - while ( offsetParent && - ( offsetParent === doc.body || offsetParent === doc.documentElement ) && - jQuery.css( offsetParent, "position" ) === "static" ) { - - offsetParent = offsetParent.parentNode; - } - if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) { - - // Incorporate borders into its offset, since they are outside its content origin - parentOffset = jQuery( offsetParent ).offset(); - parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true ); - parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true ); - } - } - - // Subtract parent offsets and element margins - return { - top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), - left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) - }; - }, - - // This method will return documentElement in the following cases: - // 1) For the element inside the iframe without offsetParent, this method will return - // documentElement of the parent window - // 2) For the hidden or detached element - // 3) For body or html element, i.e. in case of the html node - it will return itself - // - // but those exceptions were never presented as a real life use-cases - // and might be considered as more preferable results. - // - // This logic, however, is not guaranteed and can change at any point in the future - offsetParent: function() { - return this.map( function() { - var offsetParent = this.offsetParent; - - while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) { - offsetParent = offsetParent.offsetParent; - } - - return offsetParent || documentElement; - } ); - } - } ); - -// Create scrollLeft and scrollTop methods - jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { - var top = "pageYOffset" === prop; - - jQuery.fn[ method ] = function( val ) { - return access( this, function( elem, method, val ) { - - // Coalesce documents and windows - var win; - if ( isWindow( elem ) ) { - win = elem; - } else if ( elem.nodeType === 9 ) { - win = elem.defaultView; - } - - if ( val === undefined ) { - return win ? win[ prop ] : elem[ method ]; - } - - if ( win ) { - win.scrollTo( - !top ? val : win.pageXOffset, - top ? val : win.pageYOffset - ); - - } else { - elem[ method ] = val; - } - }, method, val, arguments.length ); - }; - } ); - -// Support: Safari <=7 - 9.1, Chrome <=37 - 49 -// Add the top/left cssHooks using jQuery.fn.position -// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 -// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347 -// getComputedStyle returns percent when specified for top/left/bottom/right; -// rather than make the css module depend on the offset module, just check for it here - jQuery.each( [ "top", "left" ], function( _i, prop ) { - jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, - function( elem, computed ) { - if ( computed ) { - computed = curCSS( elem, prop ); - - // If curCSS returns percentage, fallback to offset - return rnumnonpx.test( computed ) ? - jQuery( elem ).position()[ prop ] + "px" : - computed; - } - } - ); - } ); - - -// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods - jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { - jQuery.each( { - padding: "inner" + name, - content: type, - "": "outer" + name - }, function( defaultExtra, funcName ) { - - // Margin is only for outerHeight, outerWidth - jQuery.fn[ funcName ] = function( margin, value ) { - var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), - extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); - - return access( this, function( elem, type, value ) { - var doc; - - if ( isWindow( elem ) ) { - - // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) - return funcName.indexOf( "outer" ) === 0 ? - elem[ "inner" + name ] : - elem.document.documentElement[ "client" + name ]; - } - - // Get document width or height - if ( elem.nodeType === 9 ) { - doc = elem.documentElement; - - // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], - // whichever is greatest - return Math.max( - elem.body[ "scroll" + name ], doc[ "scroll" + name ], - elem.body[ "offset" + name ], doc[ "offset" + name ], - doc[ "client" + name ] - ); - } - - return value === undefined ? - - // Get width or height on the element, requesting but not forcing parseFloat - jQuery.css( elem, type, extra ) : - - // Set width or height on the element - jQuery.style( elem, type, value, extra ); - }, type, chainable ? margin : undefined, chainable ); - }; - } ); - } ); - - - jQuery.each( [ - "ajaxStart", - "ajaxStop", - "ajaxComplete", - "ajaxError", - "ajaxSuccess", - "ajaxSend" - ], function( _i, type ) { - jQuery.fn[ type ] = function( fn ) { - return this.on( type, fn ); - }; - } ); - - - - - jQuery.fn.extend( { - - bind: function( types, data, fn ) { - return this.on( types, null, data, fn ); - }, - unbind: function( types, fn ) { - return this.off( types, null, fn ); - }, - - delegate: function( selector, types, data, fn ) { - return this.on( types, selector, data, fn ); - }, - undelegate: function( selector, types, fn ) { - - // ( namespace ) or ( selector, types [, fn] ) - return arguments.length === 1 ? - this.off( selector, "**" ) : - this.off( types, selector || "**", fn ); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } - } ); - - jQuery.each( - ( "blur focus focusin focusout resize scroll click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup contextmenu" ).split( " " ), - function( _i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; - } - ); - - - - -// Support: Android <=4.0 only -// Make sure we trim BOM and NBSP -// Require that the "whitespace run" starts from a non-whitespace -// to avoid O(N^2) behavior when the engine would try matching "\s+$" at each space position. - var rtrim = /^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g; - -// Bind a function to a context, optionally partially applying any -// arguments. -// jQuery.proxy is deprecated to promote standards (specifically Function#bind) -// However, it is not slated for removal any time soon - jQuery.proxy = function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }; - - jQuery.holdReady = function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }; - jQuery.isArray = Array.isArray; - jQuery.parseJSON = JSON.parse; - jQuery.nodeName = nodeName; - jQuery.isFunction = isFunction; - jQuery.isWindow = isWindow; - jQuery.camelCase = camelCase; - jQuery.type = toType; - - jQuery.now = Date.now; - - jQuery.isNumeric = function( obj ) { - - // As of jQuery 3.0, isNumeric is limited to - // strings and numbers (primitives or objects) - // that can be coerced to finite numbers (gh-2662) - var type = jQuery.type( obj ); - return ( type === "number" || type === "string" ) && - - // parseFloat NaNs numeric-cast false positives ("") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - !isNaN( obj - parseFloat( obj ) ); - }; - - jQuery.trim = function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "$1" ); - }; - - - -// Register as a named AMD module, since jQuery can be concatenated with other -// files that may use define, but not via a proper concatenation script that -// understands anonymous AMD modules. A named AMD is safest and most robust -// way to register. Lowercase jquery is used because AMD module names are -// derived from file names, and jQuery is normally delivered in a lowercase -// file name. Do this after creating the global so that if an AMD module wants -// to call noConflict to hide this version of jQuery, it will work. - -// Note that for maximum portability, libraries that are not jQuery should -// declare themselves as anonymous modules, and avoid setting a global if an -// AMD loader is present. jQuery is a special case. For more information, see -// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon - - if ( typeof define === "function" && define.amd ) { - define( "jquery", [], function() { - return jQuery; - } ); - } - - - - - var - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$; - - jQuery.noConflict = function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }; - -// Expose jQuery and $ identifiers, even in AMD -// (trac-7102#comment:10, https://github.com/jquery/jquery/pull/557) -// and CommonJS for browser emulators (trac-13566) - if ( typeof noGlobal === "undefined" ) { - window.jQuery = window.$ = jQuery; - } - - - - - return jQuery; -} ); \ No newline at end of file diff --git a/src/static/js/vendors/jquery.ts b/src/static/js/vendors/jquery.ts new file mode 100644 index 00000000000..1b9923a6204 --- /dev/null +++ b/src/static/js/vendors/jquery.ts @@ -0,0 +1,10713 @@ +// @ts-nocheck +/*! + * jQuery JavaScript Library v3.7.1 + * https://jquery.com/ + * + * Copyright OpenJS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2023-08-28T13:37Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket trac-14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. + "use strict"; + + var arr = []; + + var getProto = Object.getPrototypeOf; + + var slice = arr.slice; + + var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); + } : function( array ) { + return arr.concat.apply( [], array ); + }; + + + var push = arr.push; + + var indexOf = arr.indexOf; + + var class2type = {}; + + var toString = class2type.toString; + + var hasOwn = class2type.hasOwnProperty; + + var fnToString = hasOwn.toString; + + var ObjectFunctionString = fnToString.call( Object ); + + var support = {}; + + var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML <object> elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 + // Plus for old WebKit, typeof returns "function" for HTML collections + // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) + return typeof obj === "function" && typeof obj.nodeType !== "number" && + typeof obj.item !== "function"; + }; + + + var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + + function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + } + /* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + + var version = "3.7.1", + + rhtmlSuffix = /HTML$/i, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + + jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice + }; + + jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; + }; + + jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + + // Retrieve the text value of an array of DOM nodes + text: function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += jQuery.text( node ); + } + } + if ( nodeType === 1 || nodeType === 11 ) { + return elem.textContent; + } + if ( nodeType === 9 ) { + return elem.documentElement.textContent; + } + if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + isXMLDoc: function( elem ) { + var namespace = elem && elem.namespaceURI, + docElem = elem && ( elem.ownerDocument || elem ).documentElement; + + // Assume HTML when documentElement doesn't yet exist, such as inside + // document fragments. + return !rhtmlSuffix.test( namespace || docElem && docElem.nodeName || "HTML" ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support + } ); + + if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; + } + +// Populate the class2type map + jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), + function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); + } ); + + function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; + } + + + function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + + } + var pop = arr.pop; + + + var sort = arr.sort; + + + var splice = arr.splice; + + + var whitespace = "[\\x20\\t\\r\\n\\f]"; + + + var rtrimCSS = new RegExp( + "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", + "g" + ); + + + + +// Note: an element does not contain itself + jQuery.contains = function( a, b ) { + var bup = b && b.parentNode; + + return a === bup || !!( bup && bup.nodeType === 1 && ( + + // Support: IE 9 - 11+ + // IE doesn't have `contains` on SVG. + a.contains ? + a.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + }; + + + + +// CSS string/identifier serialization +// https://drafts.csswg.org/cssom/#common-serializing-idioms + var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; + + function fcssescape( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + } + + jQuery.escapeSelector = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); + }; + + + + + var preferredDoc = document, + pushNative = push; + + ( function() { + + var i, + Expr, + outermostContext, + sortInput, + hasDuplicate, + push = pushNative, + + // Local document vars + document, + documentElement, + documentIsHTML, + rbuggyQSA, + matches, + + // Instance-specific data + expando = jQuery.expando, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" + + "loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + + whitespace + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + ID: new RegExp( "^#(" + identifier + ")" ), + CLASS: new RegExp( "^\\.(" + identifier + ")" ), + TAG: new RegExp( "^(" + identifier + "|[*])" ), + ATTR: new RegExp( "^" + attributes ), + PSEUDO: new RegExp( "^" + pseudos ), + CHILD: new RegExp( + "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + bool: new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + needsContext: new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // https://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + if ( nonHex ) { + + // Strip the backslash prefix from a non-hex escape sequence + return nonHex; + } + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + return high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes; see `setDocument`. + // Support: IE 9 - 11+, Edge 12 - 18+ + // Removing the function wrapper causes a "Permission Denied" + // error in IE/Edge. + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && nodeName( elem, "fieldset" ); + }, + { dir: "parentNode", next: "legend" } + ); + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 + function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } + } + +// Optimize for push.apply( _, NodeList ) + try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android <=4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; + } catch ( e ) { + push = { + apply: function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + }, + call: function( target ) { + pushNative.apply( target, slice.call( arguments, 1 ) ); + } + }; + } + + function find( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE 9 only + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + push.call( results, elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE 9 only + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + find.contains( context, elem ) && + elem.id === m ) { + + push.call( results, elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when + // strict-comparing two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( newContext != context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = jQuery.escapeSelector( nid ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrimCSS, "$1" ), context, results, seed ); + } + + /** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ + function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties + // (see https://github.com/jquery/sizzle/issues/157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; + } + + /** + * Mark a function for special use by jQuery selector module + * @param {Function} fn The function to mark + */ + function markFunction( fn ) { + fn[ expando ] = true; + return fn; + } + + /** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ + function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } + } + + /** + * Returns a function to use in pseudos for input types + * @param {String} type + */ + function createInputPseudo( type ) { + return function( elem ) { + return nodeName( elem, "input" ) && elem.type === type; + }; + } + + /** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ + function createButtonPseudo( type ) { + return function( elem ) { + return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) && + elem.type === type; + }; + } + + /** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ + function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11+ + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; + } + + /** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ + function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); + } + + /** + * Checks a node for validity as a jQuery selector context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ + function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; + } + + /** + * Sets document-related variables once based on the current document + * @param {Element|Object} [node] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ + function setDocument( node ) { + var subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + documentElement = document.documentElement; + documentIsHTML = !jQuery.isXMLDoc( document ); + + // Support: iOS 7 only, IE 9 - 11+ + // Older browsers didn't support unprefixed `matches`. + matches = documentElement.matches || + documentElement.webkitMatchesSelector || + documentElement.msMatchesSelector; + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors + // (see trac-13936). + // Limit the fix to IE & Edge Legacy; despite Edge 15+ implementing `matches`, + // all IE 9+ and Edge Legacy versions implement `msMatchesSelector` as well. + if ( documentElement.msMatchesSelector && + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 9 - 11+, Edge 12 - 18+ + subWindow.addEventListener( "unload", unloadHandler ); + } + + // Support: IE <10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + documentElement.appendChild( el ).id = jQuery.expando; + return !document.getElementsByName || + !document.getElementsByName( jQuery.expando ).length; + } ); + + // Support: IE 9 only + // Check to see if it's possible to do matchesSelector + // on a disconnected node. + support.disconnectedMatch = assert( function( el ) { + return matches.call( el, "*" ); + } ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // IE/Edge don't support the :scope pseudo-class. + support.scope = assert( function() { + return document.querySelectorAll( ":scope" ); + } ); + + // Support: Chrome 105 - 111 only, Safari 15.4 - 16.3 only + // Make sure the `:has()` argument is parsed unforgivingly. + // We include `*` in the test to detect buggy implementations that are + // _selectively_ forgiving (specifically when the list includes at least + // one valid selector). + // Note that we treat complete lack of support for `:has()` as if it were + // spec-compliant support, which is fine because use of `:has()` in such + // environments will fail in the qSA path and fall back to jQuery traversal + // anyway. + support.cssHas = assert( function() { + try { + document.querySelector( ":has(*,:jqfake)" ); + return false; + } catch ( e ) { + return true; + } + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter.ID = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find.ID = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter.ID = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find.ID = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find.TAG = function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else { + return context.querySelectorAll( tag ); + } + }; + + // Class + Expr.find.CLASS = function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + rbuggyQSA = []; + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + documentElement.appendChild( el ).innerHTML = + "<a id='" + expando + "' href='' disabled='disabled'></a>" + + "<select id='" + expando + "-\r\\' disabled='disabled'>" + + "<option selected=''></option></select>"; + + // Support: iOS <=7 - 8 only + // Boolean attributes and "value" are not treated correctly in some XML documents + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: iOS <=7 - 8 only + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: iOS 8 only + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ + // In some of the document kinds, these selectors wouldn't work natively. + // This is probably OK but for backwards compatibility we want to maintain + // handling them through jQuery traversal in jQuery 3.x. + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE 9 - 11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ + // In some of the document kinds, these selectors wouldn't work natively. + // This is probably OK but for backwards compatibility we want to maintain + // handling them through jQuery traversal in jQuery 3.x. + documentElement.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + } ); + + if ( !support.cssHas ) { + + // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ + // Our regular `try-catch` mechanism fails to detect natively-unsupported + // pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`) + // in browsers that parse the `:has()` argument as a forgiving selector list. + // https://drafts.csswg.org/selectors/#relational now requires the argument + // to be parsed unforgivingly, but browsers have not yet fully adjusted. + rbuggyQSA.push( ":has" ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a === document || a.ownerDocument == preferredDoc && + find.contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b === document || b.ownerDocument == preferredDoc && + find.contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + }; + + return document; + } + + find.matches = function( expr, elements ) { + return find( expr, null, null, elements ); + }; + + find.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return find( expr, document, null, [ elem ] ).length > 0; + }; + + find.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return jQuery.contains( context, elem ); + }; + + + find.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (see trac-13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + if ( val !== undefined ) { + return val; + } + + return elem.getAttribute( name ); + }; + + find.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); + }; + + /** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ + jQuery.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + // + // Support: Android <=4.0+ + // Testing for detecting duplicates is unpredictable so instead assume we can't + // depend on duplicate detection in all browsers without a stable sort. + hasDuplicate = !support.sortStable; + sortInput = !support.sortStable && slice.call( results, 0 ); + sort.call( results, sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + splice.call( results, duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; + }; + + jQuery.fn.uniqueSort = function() { + return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) ); + }; + + Expr = jQuery.expr = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + ATTR: function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" ) + .replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + CHILD: function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + find.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) + ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + find.error( match[ 0 ] ); + } + + return match; + }, + + PSEUDO: function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr.CHILD.test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + TAG: function( nodeNameSelector ) { + var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return nodeName( elem, expectedNodeName ); + }; + }, + + CLASS: function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + ")" + className + + "(" + whitespace + "|$)" ) ) && + classCache( className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + ATTR: function( name, operator, check ) { + return function( elem ) { + var result = find.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + if ( operator === "=" ) { + return result === check; + } + if ( operator === "!=" ) { + return result !== check; + } + if ( operator === "^=" ) { + return check && result.indexOf( check ) === 0; + } + if ( operator === "*=" ) { + return check && result.indexOf( check ) > -1; + } + if ( operator === "$=" ) { + return check && result.slice( -check.length ) === check; + } + if ( operator === "~=" ) { + return ( " " + result.replace( rwhitespace, " " ) + " " ) + .indexOf( check ) > -1; + } + if ( operator === "|=" ) { + return result === check || result.slice( 0, check.length + 1 ) === check + "-"; + } + + return false; + }; + }, + + CHILD: function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + nodeName( node, name ) : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || ( parent[ expando ] = {} ); + cache = outerCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + cache = outerCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + nodeName( node, name ) : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + outerCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + PSEUDO: function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // https://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + find.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as jQuery does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + not: markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrimCSS, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element + // (see https://github.com/jquery/sizzle/issues/299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + has: markFunction( function( selector ) { + return function( elem ) { + return find( selector, elem ).length > 0; + }; + } ), + + contains: markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // https://www.w3.org/TR/selectors/#lang-pseudo + lang: markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + find.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + target: function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + root: function( elem ) { + return elem === documentElement; + }, + + focus: function( elem ) { + return elem === safeActiveElement() && + document.hasFocus() && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + enabled: createDisabledPseudo( false ), + disabled: createDisabledPseudo( true ), + + checked: function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + return ( nodeName( elem, "input" ) && !!elem.checked ) || + ( nodeName( elem, "option" ) && !!elem.selected ); + }, + + selected: function( elem ) { + + // Support: IE <=11+ + // Accessing the selectedIndex property + // forces the browser to treat the default option as + // selected when in an optgroup. + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + empty: function( elem ) { + + // https://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + parent: function( elem ) { + return !Expr.pseudos.empty( elem ); + }, + + // Element/input types + header: function( elem ) { + return rheader.test( elem.nodeName ); + }, + + input: function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + button: function( elem ) { + return nodeName( elem, "input" ) && elem.type === "button" || + nodeName( elem, "button" ); + }, + + text: function( elem ) { + var attr; + return nodeName( elem, "input" ) && elem.type === "text" && + + // Support: IE <10 only + // New HTML5 attribute values (e.g., "search") appear + // with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + first: createPositionalPseudo( function() { + return [ 0 ]; + } ), + + last: createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + eq: createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + even: createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + odd: createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + lt: createPositionalPseudo( function( matchIndexes, length, argument ) { + var i; + + if ( argument < 0 ) { + i = argument + length; + } else if ( argument > length ) { + i = length; + } else { + i = argument; + } + + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + gt: createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } + }; + + Expr.pseudos.nth = Expr.pseudos.eq; + +// Add button/input type pseudos + for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); + } + for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); + } + +// Easy API for creating new setFilters + function setFilters() {} + setFilters.prototype = Expr.filters = Expr.pseudos; + Expr.setFilters = new setFilters(); + + function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rleadingCombinator.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrimCSS, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + if ( parseOnly ) { + return soFar.length; + } + + return soFar ? + find.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); + } + + function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; + } + + function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + if ( skip && nodeName( elem, skip ) ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = outerCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + outerCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; + } + + function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; + } + + function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + find( selector, contexts[ i ], results ); + } + return results; + } + + function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; + } + + function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, matcherOut, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || + multipleContexts( selector || "*", + context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems; + + if ( matcher ) { + + // If we have a postFinder, or filtered seed, or non-seed postFilter + // or preexisting results, + matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results; + + // Find primary matches + matcher( matcherIn, matcherOut, context, xml ); + } else { + matcherOut = matcherIn; + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); + } + + function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element + // (see https://github.com/jquery/sizzle/issues/299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrimCSS, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); + } + + function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find.TAG( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: iOS <=7 - 9 only + // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching + // elements by id. (see trac-14142) + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + push.call( results, elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + jQuery.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; + } + + function compile( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; + } + + /** + * A low-level selection function that works with jQuery's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with jQuery selector compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ + function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find.ID( + token.matches[ 0 ].replace( runescape, funescape ), + context + ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && + testContext( context.parentNode ) || context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; + } + +// One-time assignments + +// Support: Android <=4.0 - 4.1+ +// Sort stability + support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Initialize against the default document + setDocument(); + +// Support: Android <=4.0 - 4.1+ +// Detached nodes confoundingly follow *each other* + support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; + } ); + + jQuery.find = find; + +// Deprecated + jQuery.expr[ ":" ] = jQuery.expr.pseudos; + jQuery.unique = jQuery.uniqueSort; + +// These have always been private, but they used to be documented as part of +// Sizzle so let's maintain them for now for backwards compatibility purposes. + find.compile = compile; + find.select = select; + find.setDocument = setDocument; + find.tokenize = tokenize; + + find.escape = jQuery.escapeSelector; + find.getText = jQuery.text; + find.isXML = jQuery.isXMLDoc; + find.selectors = jQuery.expr; + find.support = jQuery.support; + find.uniqueSort = jQuery.uniqueSort; + + /* eslint-enable */ + + } )(); + + + var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; + }; + + + var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + }; + + + var rneedsContext = jQuery.expr.match.needsContext; + + var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not + function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); + } + + jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); + }; + + jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } + } ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) + var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over <tag> to avoid XSS via location.hash (trac-9521) + // Strict HTML recognition (trac-11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation + init.prototype = jQuery.fn; + +// Initialize central reference + rootjQuery = jQuery( document ); + + + var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + + jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to jQuery#find + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } + } ); + + function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; + } + + jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // <object> elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } + }, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; + } ); + var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones + function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; + } + + /* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ + jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; + }; + + + function Identity( v ) { + return v; + } + function Thrower( ex ) { + throw ex; + } + + function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } + } + + jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.error ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the error, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getErrorHook ) { + process.error = jQuery.Deferred.getErrorHook(); + + // The deprecated alias of the above. While the name suggests + // returning the stack, not an error instance, jQuery just passes + // it directly to `console.warn` so both will work; an instance + // just better cooperates with source maps. + } else if ( jQuery.Deferred.getStackHook ) { + process.error = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the primary Deferred + primary = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + primary.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( primary.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return primary.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); + } + + return primary.promise(); + } + } ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. + var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +// If `jQuery.Deferred.getErrorHook` is defined, `asyncError` is an error +// captured before the async barrier to get the original error cause +// which may otherwise be hidden. + jQuery.Deferred.exceptionHook = function( error, asyncError ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, + error.stack, asyncError ); + } + }; + + + + + jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); + }; + + + + +// The deferred used on DOM ready + var readyList = jQuery.Deferred(); + + jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; + }; + + jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See trac-6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } + } ); + + jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method + function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); + } + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon + if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); + } + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function + var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; + }; + + +// Matches dashed string for camelizing + var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() + function fcamelCase( _all, letter ) { + return letter.toUpperCase(); + } + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (trac-9572) + function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + } + var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); + }; + + + + + function Data() { + this.expando = jQuery.expando + Data.uid++; + } + + Data.uid = 1; + + Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see trac-8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } + }; + var dataPriv = new Data(); + + var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + + var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + + function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; + } + + function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; + } + + jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } + } ); + + jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (trac-14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } + } ); + + + jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } + } ); + + jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } + } ); + var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + + var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + + var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + + var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } + var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + + function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; + } + + + var defaultDisplayMap = {}; + + function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; + } + + function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; + } + + jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } + } ); + var rcheckableType = ( /^(?:checkbox|radio)$/i ); + + var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + + var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + + ( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (trac-11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (trac-14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = "<textarea>x</textarea>"; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces <option> tags with their contents when inserted outside of + // the select element. + div.innerHTML = "<option></option>"; + support.option = !!div.lastChild; + } )(); + + +// We have to close these tags to support XHTML (trac-13200) + var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting <tbody> or other required elements. + thead: [ 1, "<table>", "</table>" ], + col: [ 2, "<table><colgroup>", "</colgroup></table>" ], + tr: [ 2, "<table><tbody>", "</tbody></table>" ], + td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + + _default: [ 0, "", "" ] + }; + + wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; + wrapMap.th = wrapMap.td; + +// Support: IE <=9 only + if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ]; + } + + + function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (trac-15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; + } + + +// Mark scripts as having already been evaluated + function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } + } + + + var rhtml = /<|&#?\w+;/; + + function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (trac-12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; + } + + + var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + + function returnTrue() { + return true; + } + + function returnFalse() { + return false; + } + + function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); + } + + /* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ + jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG <use> instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (trac-13208) + // Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (trac-13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", true ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } + }; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. + function leverageNative( el, type, isSetup ) { + + // Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add + if ( !isSetup ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + if ( !saved ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + this[ type ](); + result = dataPriv.get( this, type ); + dataPriv.set( this, type, false ); + + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + + return result; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering + // the native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved ) { + + // ...and capture the result + dataPriv.set( this, type, jQuery.event.trigger( + saved[ 0 ], + saved.slice( 1 ), + this + ) ); + + // Abort handling of the native event by all jQuery handlers while allowing + // native handlers on the same element to run. On target, this is achieved + // by stopping immediate propagation just on the jQuery event. However, + // the native event is re-wrapped by a jQuery one on each level of the + // propagation so the only way to stop it for jQuery is to stop it for + // everyone via native `stopPropagation()`. This is not a problem for + // focus/blur which don't bubble, but it does also stop click on checkboxes + // and radios. We accept this limitation. + event.stopPropagation(); + event.isImmediatePropagationStopped = returnTrue; + } + } + } ); + } + + jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } + }; + + jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (trac-504, trac-13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; + }; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html + jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } + }; + +// Includes all common event props including KeyEvent and MouseEvent specific props + jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + which: true + }, jQuery.event.addProp ); + + jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + + function focusMappedHandler( nativeEvent ) { + if ( document.documentMode ) { + + // Support: IE 11+ + // Attach a single focusin/focusout handler on the document while someone wants + // focus/blur. This is because the former are synchronous in IE while the latter + // are async. In other browsers, all those handlers are invoked synchronously. + + // `handle` from private data would already wrap the event, but we need + // to change the `type` here. + var handle = dataPriv.get( this, "handle" ), + event = jQuery.event.fix( nativeEvent ); + event.type = nativeEvent.type === "focusin" ? "focus" : "blur"; + event.isSimulated = true; + + // First, handle focusin/focusout + handle( nativeEvent ); + + // ...then, handle focus/blur + // + // focus/blur don't bubble while focusin/focusout do; simulate the former by only + // invoking the handler at the lower level. + if ( event.target === event.currentTarget ) { + + // The setup part calls `leverageNative`, which, in turn, calls + // `jQuery.event.add`, so event handle will already have been set + // by this point. + handle( event ); + } + } else { + + // For non-IE browsers, attach a single capturing handler on the document + // while someone wants focusin/focusout. + jQuery.event.simulate( delegateType, nativeEvent.target, + jQuery.event.fix( nativeEvent ) ); + } + } + + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + var attaches; + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, true ); + + if ( document.documentMode ) { + + // Support: IE 9 - 11+ + // We use the same native handler for focusin & focus (and focusout & blur) + // so we need to coordinate setup & teardown parts between those events. + // Use `delegateType` as the key as `type` is already used by `leverageNative`. + attaches = dataPriv.get( this, delegateType ); + if ( !attaches ) { + this.addEventListener( delegateType, focusMappedHandler ); + } + dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 ); + } else { + + // Return false to allow normal processing in the caller + return false; + } + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + teardown: function() { + var attaches; + + if ( document.documentMode ) { + attaches = dataPriv.get( this, delegateType ) - 1; + if ( !attaches ) { + this.removeEventListener( delegateType, focusMappedHandler ); + dataPriv.remove( this, delegateType ); + } else { + dataPriv.set( this, delegateType, attaches ); + } + } else { + + // Return false to indicate standard teardown should be applied + return false; + } + }, + + // Suppress native focus or blur if we're currently inside + // a leveraged native-event stack + _default: function( event ) { + return dataPriv.get( event.target, type ); + }, + + delegateType: delegateType + }; + + // Support: Firefox <=44 + // Firefox doesn't have focus(in | out) events + // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 + // + // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 + // focus(in | out) events fire after focus & blur events, + // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order + // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 + // + // Support: IE 9 - 11+ + // To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch, + // attach a single handler for both events in IE. + jQuery.event.special[ delegateType ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + dataHolder = document.documentMode ? this : doc, + attaches = dataPriv.get( dataHolder, delegateType ); + + // Support: IE 9 - 11+ + // We use the same native handler for focusin & focus (and focusout & blur) + // so we need to coordinate setup & teardown parts between those events. + // Use `delegateType` as the key as `type` is already used by `leverageNative`. + if ( !attaches ) { + if ( document.documentMode ) { + this.addEventListener( delegateType, focusMappedHandler ); + } else { + doc.addEventListener( type, focusMappedHandler, true ); + } + } + dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + dataHolder = document.documentMode ? this : doc, + attaches = dataPriv.get( dataHolder, delegateType ) - 1; + + if ( !attaches ) { + if ( document.documentMode ) { + this.removeEventListener( delegateType, focusMappedHandler ); + } else { + doc.removeEventListener( type, focusMappedHandler, true ); + } + dataPriv.remove( dataHolder, delegateType ); + } else { + dataPriv.set( dataHolder, delegateType, attaches ); + } + } + }; + } ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). + jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" + }, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; + } ); + + jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } + } ); + + + var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /<script|<style|<link/i, + + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + + rcleanScript = /^\s*<!\[CDATA\[|\]\]>\s*$/g; + +// Prefer a tbody over its parent table for containing new rows + function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; + } + +// Replace/restore the type attribute of script elements for safe DOM manipulation + function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; + } + function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; + } + + function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } + } + +// Fix IE bugs, see support tests + function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } + } + + function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (trac-8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Re-enable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + + // Unwrap a CDATA section containing script contents. This shouldn't be + // needed as in XML documents they're already not visible when + // inspecting element contents and in HTML documents they have no + // meaning but we're preserving that logic for backwards compatibility. + // This will be removed completely in 4.0. See gh-4904. + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; + } + + function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; + } + + jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew jQuery#find here for performance reasons: + // https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } + } ); + + jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } + } ); + + jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" + }, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; + } ); + var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + + var rcustomProp = /^--/; + + + var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + + var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + }; + + + var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + + ( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (trac-8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + // + // Support: Firefox 70+ + // Only Firefox includes border widths + // in computed dimensions. (gh-4529) + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; + tr.style.cssText = "box-sizing:content-box;border:1px solid"; + + // Support: Chrome 86+ + // Height set through cssText does not get applied. + // Computed height then comes back as 0. + tr.style.height = "1px"; + trChild.style.height = "9px"; + + // Support: Android 8 Chrome 86+ + // In our bodyBackground.html iframe, + // display for all div elements is set to "inline", + // which causes a problem only in Android 8 Chrome 86. + // Ensuring the div is `display: block` + // gets around this issue. + trChild.style.display = "block"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + + parseInt( trStyle.borderTopWidth, 10 ) + + parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); + } )(); + + + function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + isCustomProp = rcustomProp.test( name ), + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, trac-12537) + // .css('--customProperty) (gh-3144) + if ( computed ) { + + // Support: IE <=9 - 11+ + // IE only supports `"float"` in `getPropertyValue`; in computed styles + // it's only available as `"cssFloat"`. We no longer modify properties + // sent to `.css()` apart from camelCasing, so we need to check both. + // Normally, this would create difference in behavior: if + // `getPropertyValue` returns an empty string, the value returned + // by `.css()` would be `undefined`. This is usually the case for + // disconnected elements. However, in IE even disconnected elements + // with no styles return `"none"` for `getPropertyValue( "float" )` + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( isCustomProp && ret ) { + + // Support: Firefox 105+, Chrome <=105+ + // Spec requires trimming whitespace for custom properties (gh-4926). + // Firefox only trims leading whitespace. Chrome just collapses + // both leading & trailing whitespace to a single space. + // + // Fall back to `undefined` if empty string returned. + // This collapses a missing definition with property defined + // and set to an empty string but there's no standard API + // allowing us to differentiate them without a performance penalty + // and returning `undefined` aligns with older jQuery. + // + // rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED + // as whitespace while CSS does not, but this is not a problem + // because CSS preprocessing replaces them with U+000A LINE FEED + // (which *is* CSS whitespace) + // https://www.w3.org/TR/css-syntax-3/#input-preprocessing + ret = ret.replace( rtrimCSS, "$1" ) || undefined; + } + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; + } + + + function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; + } + + + var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined + function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } + } + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property + function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; + } + + + var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + + function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; + } + + function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0, + marginDelta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + // Count margin delta separately to only add it after scroll gutter adjustment. + // This is needed to make negative margins work with `outerHeight( true )` (gh-3982). + if ( box === "margin" ) { + marginDelta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta + marginDelta; + } + + function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; + } + + jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + animationIterationCount: true, + aspectRatio: true, + borderImageSlice: true, + columnCount: true, + flexGrow: true, + flexShrink: true, + fontWeight: true, + gridArea: true, + gridColumn: true, + gridColumnEnd: true, + gridColumnStart: true, + gridRow: true, + gridRowEnd: true, + gridRowStart: true, + lineHeight: true, + opacity: true, + order: true, + orphans: true, + scale: true, + widows: true, + zIndex: true, + zoom: true, + + // SVG-related + fillOpacity: true, + floodOpacity: true, + stopOpacity: true, + strokeMiterlimit: true, + strokeOpacity: true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (trac-7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug trac-9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (trac-7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } + } ); + + jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; + } ); + + jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } + ); + +// These hooks are used by animate to expand properties + jQuery.each( { + margin: "", + padding: "", + border: "Width" + }, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } + } ); + + jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } + } ); + + + function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); + } + jQuery.Tween = Tween; + + Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } + }; + + Tween.prototype.init.prototype = Tween.prototype; + + Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } + }; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes + Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } + }; + + jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" + }; + + jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point + jQuery.fx.step = {}; + + + + + var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + + function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } + } + +// Animations created synchronously will run synchronously + function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); + } + +// Generate parameters to create a standard animation + function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; + } + + function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } + } + + function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } + } + + function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } + } + + function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (trac-12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; + } + + jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } + } ); + + jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; + }; + + jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } + } ); + + jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; + } ); + +// Generate shortcuts for custom animations + jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } + }, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; + } ); + + jQuery.timers = []; + jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; + }; + + jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); + }; + + jQuery.fx.interval = 13; + jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); + }; + + jQuery.fx.stop = function() { + inProgress = null; + }; + + jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 + }; + + +// Based off of the plugin by Clint Helfers, with permission. + jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); + }; + + + ( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; + } )(); + + + var boolHook, + attrHandle = jQuery.expr.attrHandle; + + jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } + } ); + + jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } + } ); + +// Hooks for boolean attributes + boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } + }; + + jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; + } ); + + + + + var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + + jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } + } ); + + jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // Use proper attribute retrieval (trac-12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } + } ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop + if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; + } + + jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" + ], function() { + jQuery.propFix[ this.toLowerCase() ] = this; + } ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + + function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; + } + + function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; + } + + jQuery.fn.extend( { + addClass: function( value ) { + var classNames, cur, curValue, className, i, finalValue; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classNames = classesToArray( value ); + + if ( classNames.length ) { + return this.each( function() { + curValue = getClass( this ); + cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + for ( i = 0; i < classNames.length; i++ ) { + className = classNames[ i ]; + if ( cur.indexOf( " " + className + " " ) < 0 ) { + cur += className + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + this.setAttribute( "class", finalValue ); + } + } + } ); + } + + return this; + }, + + removeClass: function( value ) { + var classNames, cur, curValue, className, i, finalValue; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classNames = classesToArray( value ); + + if ( classNames.length ) { + return this.each( function() { + curValue = getClass( this ); + + // This expression is here for better compressibility (see addClass) + cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + for ( i = 0; i < classNames.length; i++ ) { + className = classNames[ i ]; + + // Remove *all* instances + while ( cur.indexOf( " " + className + " " ) > -1 ) { + cur = cur.replace( " " + className + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + this.setAttribute( "class", finalValue ); + } + } + } ); + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var classNames, className, i, self, + type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + classNames = classesToArray( value ); + + return this.each( function() { + if ( isValidValue ) { + + // Toggle individual class names + self = jQuery( this ); + + for ( i = 0; i < classNames.length; i++ ) { + className = classNames[ i ]; + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } + } ); + + + + + var rreturn = /\r/g; + + jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } + } ); + + jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (trac-14686, trac-14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (trac-2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } + } ); + +// Radios and checkboxes getter/setter + jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } + } ); + + + + +// Return jQuery for attributes-only inclusion + var location = window.location; + + var nonce = { guid: Date.now() }; + + var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing + jQuery.parseXML = function( data ) { + var xml, parserErrorElem; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) {} + + parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; + if ( !xml || parserErrorElem ) { + jQuery.error( "Invalid XML: " + ( + parserErrorElem ? + jQuery.map( parserErrorElem.childNodes, function( el ) { + return el.textContent; + } ).join( "\n" ) : + data + ) ); + } + return xml; + }; + + + var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + + jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (trac-9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (trac-6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + + } ); + + jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } + } ); + + + var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + + function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } + } + +// Serialize an array of form elements or a set of +// key/values into a query string + jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); + }; + + jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ).filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ).map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } + } ); + + + var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // trac-7653, trac-8125, trac-8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (trac-10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport + function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; + } + +// Base inspection function for prefilters and transports + function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); + } + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes trac-9887 + function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; + } + + /* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ + function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } + } + + /* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ + function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; + } + + jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (trac-10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket trac-12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (trac-15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // trac-9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script but not if jsonp + if ( !isSuccess && + jQuery.inArray( "script", s.dataTypes ) > -1 && + jQuery.inArray( "json", s.dataTypes ) < 0 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } + } ); + + jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; + } ); + + jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } + } ); + + + jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (trac-11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); + }; + + + jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } + } ); + + + jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); + }; + jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); + }; + + + + + jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} + }; + + var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // trac-1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + + support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); + support.ajax = xhrSupported = !!xhrSupported; + + jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see trac-8605, trac-14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // trac-14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } + } ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) + jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } + } ); + +// Install script dataType + jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } + } ); + +// Handle cache's special case and crossDomain + jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } + } ); + +// Bind script tag hack transport + jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( "<script>" ) + .attr( s.scriptAttrs || {} ) + .prop( { charset: s.scriptCharset, src: s.url } ) + .on( "load error", callback = function( evt ) { + script.remove(); + callback = null; + if ( evt ) { + complete( evt.type === "error" ? 404 : 200, evt.type ); + } + } ); + + // Use native DOM manipulation to avoid our domManip AJAX trickery + document.head.appendChild( script[ 0 ] ); + }, + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } + } ); + + + + + var oldCallbacks = [], + rjsonp = /(=)\?(?=&|$)|\?\?/; + +// Default jsonp settings + jQuery.ajaxSetup( { + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce.guid++ ) ); + this[ callback ] = true; + return callback; + } + } ); + +// Detect, normalize options and install callbacks for jsonp requests + jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? + "url" : + typeof s.data === "string" && + ( s.contentType || "" ) + .indexOf( "application/x-www-form-urlencoded" ) === 0 && + rjsonp.test( s.data ) && "data" + ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + + // Insert callback into url or form data + if ( jsonProp ) { + s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); + } else if ( s.jsonp !== false ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters[ "script json" ] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // Force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + overwritten = window[ callbackName ]; + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always( function() { + + // If previous value didn't exist - remove it + if ( overwritten === undefined ) { + jQuery( window ).removeProp( callbackName ); + + // Otherwise restore preexisting value + } else { + window[ callbackName ] = overwritten; + } + + // Save back as free + if ( s[ callbackName ] ) { + + // Make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // Save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + } ); + + // Delegate to script + return "script"; + } + } ); + + + + +// Support: Safari 8 only +// In Safari 8 documents created via document.implementation.createHTMLDocument +// collapse sibling forms: the second one becomes a child of the first one. +// Because of that, this security measure has to be disabled in Safari 8. +// https://bugs.webkit.org/show_bug.cgi?id=137337 + support.createHTMLDocument = ( function() { + var body = document.implementation.createHTMLDocument( "" ).body; + body.innerHTML = "<form></form><form></form>"; + return body.childNodes.length === 2; + } )(); + + +// Argument "data" should be string of html +// context (optional): If specified, the fragment will be created in this context, +// defaults to document +// keepScripts (optional): If true, will include scripts passed in the html string + jQuery.parseHTML = function( data, context, keepScripts ) { + if ( typeof data !== "string" ) { + return []; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + + var base, parsed, scripts; + + if ( !context ) { + + // Stop scripts or inline event handlers from being executed immediately + // by using document.implementation + if ( support.createHTMLDocument ) { + context = document.implementation.createHTMLDocument( "" ); + + // Set the base href for the created document + // so any parsed elements with URLs + // are based on the document's URL (gh-2965) + base = context.createElement( "base" ); + base.href = document.location.href; + context.head.appendChild( base ); + } else { + context = document; + } + } + + parsed = rsingleTag.exec( data ); + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[ 1 ] ) ]; + } + + parsed = buildFragment( [ data ], context, scripts ); + + if ( scripts && scripts.length ) { + jQuery( scripts ).remove(); + } + + return jQuery.merge( [], parsed.childNodes ); + }; + + + /** + * Load a url into a page + */ + jQuery.fn.load = function( url, params, callback ) { + var selector, type, response, + self = this, + off = url.indexOf( " " ); + + if ( off > -1 ) { + selector = stripAndCollapse( url.slice( off ) ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // If we have elements to modify, make the request + if ( self.length > 0 ) { + jQuery.ajax( { + url: url, + + // If "type" variable is undefined, then "GET" method will be used. + // Make value of this field explicit since + // user can override it through ajaxSetup method + type: type || "GET", + dataType: "html", + data: params + } ).done( function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + self.html( selector ? + + // If a selector was specified, locate the right elements in a dummy div + // Exclude scripts to avoid IE 'Permission Denied' errors + jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) : + + // Otherwise use the full result + responseText ); + + // If the request succeeds, this function gets "data", "status", "jqXHR" + // but they are ignored because response was set above. + // If it fails, this function gets "jqXHR", "status", "error" + } ).always( callback && function( jqXHR, status ) { + self.each( function() { + callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] ); + } ); + } ); + } + + return this; + }; + + + + + jQuery.expr.pseudos.animated = function( elem ) { + return jQuery.grep( jQuery.timers, function( fn ) { + return elem === fn.elem; + } ).length; + }; + + + + + jQuery.offset = { + setOffset: function( elem, options, i ) { + var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, + position = jQuery.css( elem, "position" ), + curElem = jQuery( elem ), + props = {}; + + // Set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + curOffset = curElem.offset(); + curCSSTop = jQuery.css( elem, "top" ); + curCSSLeft = jQuery.css( elem, "left" ); + calculatePosition = ( position === "absolute" || position === "fixed" ) && + ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; + + // Need to be able to calculate position if either + // top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( isFunction( options ) ) { + + // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) + options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + + } else { + curElem.css( props ); + } + } + }; + + jQuery.fn.extend( { + + // offset() relates an element's border box to the document origin + offset: function( options ) { + + // Preserve chaining for setter + if ( arguments.length ) { + return options === undefined ? + this : + this.each( function( i ) { + jQuery.offset.setOffset( this, options, i ); + } ); + } + + var rect, win, + elem = this[ 0 ]; + + if ( !elem ) { + return; + } + + // Return zeros for disconnected and hidden (display: none) elements (gh-2310) + // Support: IE <=11 only + // Running getBoundingClientRect on a + // disconnected node in IE throws an error + if ( !elem.getClientRects().length ) { + return { top: 0, left: 0 }; + } + + // Get document-relative position by adding viewport scroll to viewport-relative gBCR + rect = elem.getBoundingClientRect(); + win = elem.ownerDocument.defaultView; + return { + top: rect.top + win.pageYOffset, + left: rect.left + win.pageXOffset + }; + }, + + // position() relates an element's margin box to its offset parent's padding box + // This corresponds to the behavior of CSS absolute positioning + position: function() { + if ( !this[ 0 ] ) { + return; + } + + var offsetParent, offset, doc, + elem = this[ 0 ], + parentOffset = { top: 0, left: 0 }; + + // position:fixed elements are offset from the viewport, which itself always has zero offset + if ( jQuery.css( elem, "position" ) === "fixed" ) { + + // Assume position:fixed implies availability of getBoundingClientRect + offset = elem.getBoundingClientRect(); + + } else { + offset = this.offset(); + + // Account for the *real* offset parent, which can be the document or its root element + // when a statically positioned element is identified + doc = elem.ownerDocument; + offsetParent = elem.offsetParent || doc.documentElement; + while ( offsetParent && + ( offsetParent === doc.body || offsetParent === doc.documentElement ) && + jQuery.css( offsetParent, "position" ) === "static" ) { + + offsetParent = offsetParent.parentNode; + } + if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) { + + // Incorporate borders into its offset, since they are outside its content origin + parentOffset = jQuery( offsetParent ).offset(); + parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true ); + parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true ); + } + } + + // Subtract parent offsets and element margins + return { + top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), + left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) + }; + }, + + // This method will return documentElement in the following cases: + // 1) For the element inside the iframe without offsetParent, this method will return + // documentElement of the parent window + // 2) For the hidden or detached element + // 3) For body or html element, i.e. in case of the html node - it will return itself + // + // but those exceptions were never presented as a real life use-cases + // and might be considered as more preferable results. + // + // This logic, however, is not guaranteed and can change at any point in the future + offsetParent: function() { + return this.map( function() { + var offsetParent = this.offsetParent; + + while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || documentElement; + } ); + } + } ); + +// Create scrollLeft and scrollTop methods + jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { + var top = "pageYOffset" === prop; + + jQuery.fn[ method ] = function( val ) { + return access( this, function( elem, method, val ) { + + // Coalesce documents and windows + var win; + if ( isWindow( elem ) ) { + win = elem; + } else if ( elem.nodeType === 9 ) { + win = elem.defaultView; + } + + if ( val === undefined ) { + return win ? win[ prop ] : elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : win.pageXOffset, + top ? val : win.pageYOffset + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length ); + }; + } ); + +// Support: Safari <=7 - 9.1, Chrome <=37 - 49 +// Add the top/left cssHooks using jQuery.fn.position +// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 +// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347 +// getComputedStyle returns percent when specified for top/left/bottom/right; +// rather than make the css module depend on the offset module, just check for it here + jQuery.each( [ "top", "left" ], function( _i, prop ) { + jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, + function( elem, computed ) { + if ( computed ) { + computed = curCSS( elem, prop ); + + // If curCSS returns percentage, fallback to offset + return rnumnonpx.test( computed ) ? + jQuery( elem ).position()[ prop ] + "px" : + computed; + } + } + ); + } ); + + +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods + jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { + padding: "inner" + name, + content: type, + "": "outer" + name + }, function( defaultExtra, funcName ) { + + // Margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return access( this, function( elem, type, value ) { + var doc; + + if ( isWindow( elem ) ) { + + // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) + return funcName.indexOf( "outer" ) === 0 ? + elem[ "inner" + name ] : + elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], + // whichever is greatest + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable ); + }; + } ); + } ); + + + jQuery.each( [ + "ajaxStart", + "ajaxStop", + "ajaxComplete", + "ajaxError", + "ajaxSuccess", + "ajaxSend" + ], function( _i, type ) { + jQuery.fn[ type ] = function( fn ) { + return this.on( type, fn ); + }; + } ); + + + + + jQuery.fn.extend( { + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? + this.off( selector, "**" ) : + this.off( types, selector || "**", fn ); + }, + + hover: function( fnOver, fnOut ) { + return this + .on( "mouseenter", fnOver ) + .on( "mouseleave", fnOut || fnOver ); + } + } ); + + jQuery.each( + ( "blur focus focusin focusout resize scroll click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup contextmenu" ).split( " " ), + function( _i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + } + ); + + + + +// Support: Android <=4.0 only +// Make sure we trim BOM and NBSP +// Require that the "whitespace run" starts from a non-whitespace +// to avoid O(N^2) behavior when the engine would try matching "\s+$" at each space position. + var rtrim = /^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g; + +// Bind a function to a context, optionally partially applying any +// arguments. +// jQuery.proxy is deprecated to promote standards (specifically Function#bind) +// However, it is not slated for removal any time soon + jQuery.proxy = function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }; + + jQuery.holdReady = function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }; + jQuery.isArray = Array.isArray; + jQuery.parseJSON = JSON.parse; + jQuery.nodeName = nodeName; + jQuery.isFunction = isFunction; + jQuery.isWindow = isWindow; + jQuery.camelCase = camelCase; + jQuery.type = toType; + + jQuery.now = Date.now; + + jQuery.isNumeric = function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); + }; + + jQuery.trim = function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "$1" ); + }; + + + +// Register as a named AMD module, since jQuery can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase jquery is used because AMD module names are +// derived from file names, and jQuery is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of jQuery, it will work. + +// Note that for maximum portability, libraries that are not jQuery should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. jQuery is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + + if ( typeof define === "function" && define.amd ) { + define( "jquery", [], function() { + return jQuery; + } ); + } + + + + + var + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$; + + jQuery.noConflict = function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }; + +// Expose jQuery and $ identifiers, even in AMD +// (trac-7102#comment:10, https://github.com/jquery/jquery/pull/557) +// and CommonJS for browser emulators (trac-13566) + if ( typeof noGlobal === "undefined" ) { + window.jQuery = window.$ = jQuery; + } + return jQuery; +} ); diff --git a/src/static/js/vendors/nice-select.js b/src/static/js/vendors/nice-select.ts similarity index 96% rename from src/static/js/vendors/nice-select.js rename to src/static/js/vendors/nice-select.ts index d19de5332d1..ec76b3cef15 100644 --- a/src/static/js/vendors/nice-select.js +++ b/src/static/js/vendors/nice-select.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // WARNING: This file has been modified from the Original // TODO: Nice Select seems relatively abandoned, we should consider other options. @@ -110,10 +111,10 @@ $dropdown.find('.list').css('min-width', $dropdown.outerWidth() + 'px'); } - $listHeight = $dropdown.find('.list').outerHeight(); - $top = $dropdown.parent().offset().top; - $bottom = $('body').height() - $top; - $maxListHeight = $bottom - $dropdown.outerHeight() - 20; + let $listHeight = $dropdown.find('.list').outerHeight(); + let $top = $dropdown.parent().offset().top; + let $bottom = $('body').height() - $top; + let $maxListHeight = $bottom - $dropdown.outerHeight() - 20; if ($maxListHeight < 200) { $dropdown.addClass('reverse'); $maxListHeight = 250; diff --git a/src/templates/export_html.html b/src/templates/export_html.html index f00f4d9535c..3ac27e9e895 100644 --- a/src/templates/export_html.html +++ b/src/templates/export_html.html @@ -2,7 +2,8 @@ <html lang="en"> <head> <title><%- padId %></title> -<meta name="generator" content="Etherpad"/> + <link rel="manifest" href="/manifest.json" /> + <meta name="generator" content="Etherpad"/> <meta name="author" content="Etherpad"/> <meta name="changedby" content="Etherpad"/> <meta charset="utf-8"/> diff --git a/src/templates/index.html b/src/templates/index.html index 6c72e7c0f51..386355fdbec 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -1,9 +1,9 @@ -<% var settings=require("ep_etherpad-lite/node/utils/Settings"); %> - <!doctype html> - <html> +<!doctype html> +<html> <title><%=settings.title%></title> <meta charset="utf-8"> + <link rel="manifest" href="/manifest.json" /> <meta name="referrer" content="no-referrer"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"> @@ -13,16 +13,7 @@ <link rel="manifest" href="static/site.webmanifest"> <link rel="mask-icon" href="static/safari-pinned-tab.svg" color="#5bbad5"> <link rel="shortcut icon" href="favicon.ico"> - <meta name="msapplication-TileColor" content="#da532c"> - <meta name="msapplication-config" content="static/browserconfig.xml"> - <meta name="theme-color" content="#ffffff"> - <link rel="localizations" type="application/l10n+json" href="locales.json"> - <script type="text/javascript" src="static/js/vendors/html10n.js?v=<%=settings.randomVersionString%>"></script> - <script type="text/javascript" src="static/js/l10n.js?v=<%=settings.randomVersionString%>"></script> - <script src="static/js/vendors/jquery.js"></script> - <script src="static/js/vendors/bootstrap.bundle.min.js"></script> - <script src="static/js/index.js"></script> <% e.begin_block("indexCustomStyles"); %> <link href="static/skins/kits/index.css?v=<%=settings.randomVersionString%>" rel="stylesheet"> @@ -100,15 +91,7 @@ <h5>Letzte Pads</h5> <!-- End Right Block --> </div> </div> - <!--[if IE - ]><div - class="w-100 text-center bg-dark text-white pt-3 px-4 pb-1 position-fixed" - style="z-index: 1000; bottom: 0"> - <p onClick="parentNode.remove()"> - Please use a modern browser for a better browsing experience. <span class="float-right">X</span> - </p> - </div><! - [endif]--> + <script src="<%=entrypoint%>"></script> <% e.begin_block("indexCustomScripts"); %> <script src="static/skins/<%=encodeURI(settings.skinName)%>/index.js?v=<%=settings.randomVersionString%>"></script> diff --git a/src/templates/indexBootstrap.js b/src/templates/indexBootstrap.js new file mode 100644 index 00000000000..faf6702e697 --- /dev/null +++ b/src/templates/indexBootstrap.js @@ -0,0 +1,6 @@ + +(async () => { + window.$ = window.jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery; + require('ep_etherpad-lite/static/js/l10n') + require('ep_etherpad-lite/static/js/index') +})() diff --git a/src/templates/javascript.html b/src/templates/javascript.html index c501af65c75..d93692b0952 100644 --- a/src/templates/javascript.html +++ b/src/templates/javascript.html @@ -2,7 +2,8 @@ <html> <head> <title>JavaScript license information</title> - <meta charset="utf-8"> + <link rel="manifest" href="/manifest.json" /> + <meta charset="utf-8"> <meta name="robots" content="noindex, nofollow"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> </head> diff --git a/src/templates/pad.html b/src/templates/pad.html index 89c90c9975d..62a36aab246 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -6,6 +6,7 @@ <% e.begin_block("htmlHead"); %> <% e.end_block(); %> <title><%=settings.title%></title> + <link rel="manifest" href="../../manifest.json" /> <script> /* |@licstart The following is the entire license notice for the @@ -29,9 +30,6 @@ for the JavaScript code in this page.| */ </script> - <script src="../static/js/basic_error_handler.js?v=<%=settings.randomVersionString%>"></script> - <script type="text/javascript" src="../static/js/vendors/qr-code-styling/qrcode-styling.js"></script> - <script type="text/javascript" src="../static/js/qrcode_download.js"></script> <meta charset="utf-8"> <meta name="robots" content="noindex, nofollow"> @@ -51,8 +49,8 @@ <% e.end_block(); %> <link rel="localizations" type="application/l10n+json" href="../locales.json" /> - <script type="text/javascript" src="../static/js/vendors/html10n.js?v=<%=settings.randomVersionString%>"></script> - <script type="text/javascript" src="../static/js/l10n.js?v=<%=settings.randomVersionString%>"></script> + <script type="text/javascript" src="../static/js/vendors/qr-code-styling/qrcode-styling.js"></script> + <script type="text/javascript" src="../static/js/qrcode_download.js"></script> </head> <body> <% e.begin_block("body"); %> @@ -327,7 +325,7 @@ <h2>QR-Code</h2> </div> </div> - + <script type="text/javascript">initialize_qrcode()</script> <% e.end_block(); %> </div></div> @@ -441,71 +439,11 @@ <h1>Skin Builder</h1> <% e.begin_block("scripts"); %> - <script type="text/javascript" src="../static/js/require-kernel.js?v=<%=settings.randomVersionString%>"></script> - <script type="text/javascript" src="../socket.io/socket.io.js"></script> - - <!-- Include base packages manually (this help with debugging) --> - <script type="text/javascript" src="../javascripts/lib/ep_etherpad-lite/static/js/pad.js?callback=require.define&v=<%=settings.randomVersionString%>"></script> - <script type="text/javascript" src="../javascripts/lib/ep_etherpad-lite/static/js/ace2_common.js?callback=require.define&v=<%=settings.randomVersionString%>"></script> + <script src="<%=entrypoint%>"></script> <% e.begin_block("customScripts"); %> <script type="text/javascript" src="../static/skins/<%=encodeURI(settings.skinName)%>/pad.js?v=<%=settings.randomVersionString%>"></script> <% e.end_block(); %> - - <!-- Bootstrap page --> - <script type="text/javascript"> - // @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt - var clientVars = { - // This is needed to fetch /pluginfw/plugin-definitions.json, which happens before the - // server sends the CLIENT_VARS message. - randomVersionString: <%-JSON.stringify(settings.randomVersionString)%>, - }; - (function () { - var pathComponents = location.pathname.split('/'); - - // Strip 'p' and the padname from the pathname and set as baseURL - var baseURL = pathComponents.slice(0,pathComponents.length-2).join('/') + '/'; - - require.setRootURI(baseURL + "javascripts/src"); - require.setLibraryURI(baseURL + "javascripts/lib"); - require.setGlobalKeyPath("require"); - - $ = jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery; // Expose jQuery #HACK - browser = require('ep_etherpad-lite/static/js/vendors/browser'); - - var plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); - var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); - - plugins.baseURL = baseURL; - plugins.update(function () { - // Mechanism for tests to register hook functions (install fake plugins). - window._postPluginUpdateForTestingDone = false; - if (window._postPluginUpdateForTesting != null) window._postPluginUpdateForTesting(); - window._postPluginUpdateForTestingDone = true; - // Call documentReady hook - $(function() { - hooks.aCallAll('documentReady'); - }); - - var pad = require('ep_etherpad-lite/static/js/pad'); - pad.baseURL = baseURL; - pad.init(); - }); - - /* TODO: These globals shouldn't exist. */ - pad = require('ep_etherpad-lite/static/js/pad').pad; - chat = require('ep_etherpad-lite/static/js/chat').chat; - padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; - padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp; - require('ep_etherpad-lite/static/js/skin_variants'); - - // load qrcode - we need to set the embed links first: - padeditbar.setEmbedLinks() - initialize_qrcode() - - }()); - // @license-end - </script> <div style="display:none"><a href="/javascript" data-jslicense="1">JavaScript license information</a></div> <% e.end_block(); %> </body> diff --git a/src/templates/padBootstrap.js b/src/templates/padBootstrap.js new file mode 100644 index 00000000000..caa3692a00a --- /dev/null +++ b/src/templates/padBootstrap.js @@ -0,0 +1,45 @@ + +(async () => { + + require('ep_etherpad-lite/static/js/l10n') + + window.clientVars = { + // This is needed to fetch /pluginfw/plugin-definitions.json, which happens before the server + // sends the CLIENT_VARS message. + randomVersionString: <%-JSON.stringify(settings.randomVersionString)%>, + }; + + // Allow other frames to access this frame's modules. + //window.require.resolveTmp = require.resolve('ep_etherpad-lite/static/js/pad_cookie'); + + const basePath = new URL('..', window.location.href).pathname; + window.$ = window.jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery; + window.browser = require('ep_etherpad-lite/static/js/vendors/browser'); + const pad = require('ep_etherpad-lite/static/js/pad'); + pad.baseURL = basePath; + window.plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); + const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); + + // TODO: These globals shouldn't exist. + window.pad = pad.pad; + window.chat = require('ep_etherpad-lite/static/js/chat').chat; + window.padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; + window.padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp; + require('ep_etherpad-lite/static/js/skin_variants'); + require('ep_etherpad-lite/static/js/basic_error_handler') + + window.plugins.baseURL = basePath; + await window.plugins.update(new Map([ + <% for (const module of pluginModules) { %> + [<%- JSON.stringify(module) %>, require("../../src/plugin_packages/"+<%- JSON.stringify(module) %>)], + <% } %> +])); + // Mechanism for tests to register hook functions (install fake plugins). + window._postPluginUpdateForTestingDone = false; + if (window._postPluginUpdateForTesting != null) window._postPluginUpdateForTesting(); + window._postPluginUpdateForTestingDone = true; + window.pluginDefs = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'); + pad.init(); + await new Promise((resolve) => $(resolve)); + await hooks.aCallAll('documentReady'); +})(); diff --git a/src/templates/padViteBootstrap.js b/src/templates/padViteBootstrap.js new file mode 100644 index 00000000000..05f759077de --- /dev/null +++ b/src/templates/padViteBootstrap.js @@ -0,0 +1,41 @@ +window.$ = window.jQuery = await import('../../src/static/js/rjquery').jQuery; +await import('../../src/static/js/l10n') + +window.clientVars = { + // This is needed to fetch /pluginfw/plugin-definitions.json, which happens before the server + // sends the CLIENT_VARS message. + randomVersionString: "7a7bdbad", +}; + +(async () => { + // Allow other frames to access this frame's modules. + //window.require.resolveTmp = require.resolve('ep_etherpad-lite/static/js/pad_cookie'); + + const basePath = new URL('..', window.location.href).pathname; + window.browser = require('../../src/static/js/vendors/browser'); + const pad = require('../../src/static/js/pad'); + pad.baseURL = basePath; + window.plugins = require('../../src/static/js/pluginfw/client_plugins'); + const hooks = require('../../src/static/js/pluginfw/hooks'); + + // TODO: These globals shouldn't exist. + window.pad = pad.pad; + window.chat = require('../../src/static/js/chat').chat; + window.padeditbar = require('../../src/static/js/pad_editbar').padeditbar; + window.padimpexp = require('../../src/static/js/pad_impexp').padimpexp; + require('../../src/static/js/skin_variants'); + require('../../src/static/js/basic_error_handler') + + window.plugins.baseURL = basePath; + await window.plugins.update(new Map([ + + ])); + // Mechanism for tests to register hook functions (install fake plugins). + window._postPluginUpdateForTestingDone = false; + if (window._postPluginUpdateForTesting != null) window._postPluginUpdateForTesting(); + window._postPluginUpdateForTestingDone = true; + window.pluginDefs = require('../../src/static/js/pluginfw/plugin_defs'); + pad.init(); + await new Promise((resolve) => $(resolve)); + await hooks.aCallAll('documentReady'); +})(); diff --git a/src/templates/timeSliderBootstrap.js b/src/templates/timeSliderBootstrap.js new file mode 100644 index 00000000000..e3138cfbdd6 --- /dev/null +++ b/src/templates/timeSliderBootstrap.js @@ -0,0 +1,37 @@ +// @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt +window.clientVars = { + // This is needed to fetch /pluginfw/plugin-definitions.json, which happens before the + // server sends the CLIENT_VARS message. + randomVersionString: <%-JSON.stringify(settings.randomVersionString)%>, +}; +let BroadcastSlider; + + +(function () { + const timeSlider = require('ep_etherpad-lite/static/js/timeslider') + const pathComponents = location.pathname.split('/'); + + // Strip 'p', the padname and 'timeslider' from the pathname and set as baseURL + const baseURL = pathComponents.slice(0,pathComponents.length-3).join('/') + '/'; + require('ep_etherpad-lite/static/js/l10n') + window.$ = window.jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery; // Expose jQuery #HACK + require('ep_etherpad-lite/static/js/vendors/gritter') + + window.browser = require('ep_etherpad-lite/static/js/vendors/browser'); + + window.plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); + const socket = timeSlider.socket; + BroadcastSlider = timeSlider.BroadcastSlider; + plugins.baseURL = baseURL; + plugins.update(function () { + + + /* TODO: These globals shouldn't exist. */ + + }); + const padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; + const padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp; + timeSlider.baseURL = baseURL; + timeSlider.init(); + padeditbar.init() +})(); diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index ee45f457556..02db406481d 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -29,8 +29,8 @@ for the JavaScript code in this page.| */ </script> - <script src="../../static/js/basic_error_handler.js?v=<%=settings.randomVersionString%>"></script> <meta charset="utf-8"> + <link rel="manifest" href="../../../manifest.json" /> <meta name="robots" content="noindex, nofollow"> <meta name="referrer" content="no-referrer"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> @@ -46,8 +46,6 @@ <link rel="localizations" type="application/l10n+json" href="../../locales.json" /> <% e.begin_block("timesliderScripts"); %> - <script type="text/javascript" src="../../static/js/vendors/html10n.js?v=<%=settings.randomVersionString%>"></script> - <script type="text/javascript" src="../../static/js/l10n.js?v=<%=settings.randomVersionString%>"></script> <% e.end_block(); %> </head> @@ -249,58 +247,14 @@ <h1 data-l10n-id="pad.settings.padSettings"></h1> <!-------- JAVASCRIPT ---------> <!-----------------------------> -<script type="text/javascript" src="../../static/js/require-kernel.js?v=<%=settings.randomVersionString%>"></script> <script type="text/javascript" src="../../socket.io/socket.io.js"></script> <!-- Include base packages manually (this help with debugging) --> -<script type="text/javascript" src="../../javascripts/lib/ep_etherpad-lite/static/js/timeslider.js?callback=require.define&v=<%=settings.randomVersionString%>"></script> -<script type="text/javascript" src="../../javascripts/lib/ep_etherpad-lite/static/js/ace2_common.js?callback=require.define&v=<%=settings.randomVersionString%>"></script> <script type="text/javascript" src="../../static/skins/<%=encodeURI(settings.skinName)%>/timeslider.js?v=<%=settings.randomVersionString%>"></script> <!-- Bootstrap --> -<script type="text/javascript" > - // @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt - var clientVars = { - // This is needed to fetch /pluginfw/plugin-definitions.json, which happens before the - // server sends the CLIENT_VARS message. - randomVersionString: <%-JSON.stringify(settings.randomVersionString)%>, - }; - let BroadcastSlider; - (function () { - const pathComponents = location.pathname.split('/'); - - // Strip 'p', the padname and 'timeslider' from the pathname and set as baseURL - const baseURL = pathComponents.slice(0,pathComponents.length-3).join('/') + '/'; - - - require.setRootURI(baseURL + "javascripts/src"); - require.setLibraryURI(baseURL + "javascripts/lib"); - require.setGlobalKeyPath("require"); - - $ = jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery; // Expose jQuery #HACK - browser = require('ep_etherpad-lite/static/js/vendors/browser'); - - const plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); - const socket = require('ep_etherpad-lite/static/js/timeslider').socket; - BroadcastSlider = require('ep_etherpad-lite/static/js/timeslider').BroadcastSlider; - plugins.baseURL = baseURL; - - plugins.update(function () { - const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); - const timeslider = require('ep_etherpad-lite/static/js/timeslider') - timeslider.baseURL = baseURL; - timeslider.init(); - - /* TODO: These globals shouldn't exist. */ - const padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; - const padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp; - - padeditbar.init() - }); - })(); - // @license-end -</script> +<script src="<%=entrypoint%>"></script> <% e.end_block(); %> <div style="display:none"><a href="/javascript" data-jslicense="1">JavaScript license information</a></div> </html> diff --git a/src/tests/backend-new/easysync-helper.ts b/src/tests/backend-new/easysync-helper.ts new file mode 100644 index 00000000000..1fc8dda95ae --- /dev/null +++ b/src/tests/backend-new/easysync-helper.ts @@ -0,0 +1,220 @@ +import AttributePool from "../../static/js/AttributePool"; +import { Attribute } from "../../static/js/types/Attribute"; +import {StringAssembler} from "../../static/js/StringAssembler"; +import {SmartOpAssembler} from "../../static/js/SmartOpAssembler"; +import Op from "../../static/js/Op"; +import {numToString} from "../../static/js/ChangesetUtils"; +import {checkRep, pack} from "../../static/js/Changeset"; + +export const poolOrArray = (attribs: any) => { + if (attribs.getAttrib) { + return attribs; // it's already an attrib pool + } else { + // assume it's an array of attrib strings to be split and added + const p = new AttributePool(); + attribs.forEach((kv: { split: (arg0: string) => Attribute; }) => { + p.putAttrib(kv.split(',')); + }); + return p; + } +}; +const randInt = (maxValue: number) => Math.floor(Math.random() * maxValue); +const randomInlineString = (len: number) => { + const assem = new StringAssembler(); + for (let i = 0; i < len; i++) { + assem.append(String.fromCharCode(randInt(26) + 97)); + } + return assem.toString(); +}; +export const randomMultiline = (approxMaxLines: number, approxMaxCols: number) => { + const numParts = randInt(approxMaxLines * 2) + 1; + const txt = new StringAssembler(); + txt.append(randInt(2) ? '\n' : ''); + for (let i = 0; i < numParts; i++) { + if ((i % 2) === 0) { + if (randInt(10)) { + txt.append(randomInlineString(randInt(approxMaxCols) + 1)); + } else { + txt.append('\n'); + } + } else { + txt.append('\n'); + } + } + return txt.toString(); +}; + +const randomTwoPropAttribs = (opcode: "" | "=" | "+" | "-") => { + // assumes attrib pool like ['apple,','apple,true','banana,','banana,true'] + if (opcode === '-' || randInt(3)) { + return ''; + } else if (randInt(3)) { // eslint-disable-line no-dupe-else-if + if (opcode === '+' || randInt(2)) { + return `*${numToString(randInt(2) * 2 + 1)}`; + } else { + return `*${numToString(randInt(2) * 2)}`; + } + } else if (opcode === '+' || randInt(4) === 0) { + return '*1*3'; + } else { + return ['*0*2', '*0*3', '*1*2'][randInt(3)]; + } +}; + + +const randomStringOperation = (numCharsLeft: number) => { + let result; + switch (randInt(11)) { + case 0: + { + // insert char + result = { + insert: randomInlineString(1), + }; + break; + } + case 1: + { + // delete char + result = { + remove: 1, + }; + break; + } + case 2: + { + // skip char + result = { + skip: 1, + }; + break; + } + case 3: + { + // insert small + result = { + insert: randomInlineString(randInt(4) + 1), + }; + break; + } + case 4: + { + // delete small + result = { + remove: randInt(4) + 1, + }; + break; + } + case 5: + { + // skip small + result = { + skip: randInt(4) + 1, + }; + break; + } + case 6: + { + // insert multiline; + result = { + insert: randomMultiline(5, 20), + }; + break; + } + case 7: + { + // delete multiline + result = { + remove: Math.round(numCharsLeft * Math.random() * Math.random()), + }; + break; + } + case 8: + { + // skip multiline + result = { + skip: Math.round(numCharsLeft * Math.random() * Math.random()), + }; + break; + } + case 9: + { + // delete to end + result = { + remove: numCharsLeft, + }; + break; + } + case 10: + { + // skip to end + result = { + skip: numCharsLeft, + }; + break; + } + } + const maxOrig = numCharsLeft - 1; + if ('remove' in result!) { + result.remove = Math.min(result.remove, maxOrig); + } else if ('skip' in result!) { + result.skip = Math.min(result.skip, maxOrig); + } + return result; +}; + +export const randomTestChangeset = (origText: string, withAttribs?: any) => { + const charBank = new StringAssembler(); + let textLeft = origText; // always keep final newline + const outTextAssem = new StringAssembler(); + const opAssem = new SmartOpAssembler(); + const oldLen = origText.length; + + const nextOp = new Op(); + + const appendMultilineOp = (opcode: "" | "=" | "+" | "-", txt: string) => { + nextOp.opcode = opcode; + if (withAttribs) { + nextOp.attribs = randomTwoPropAttribs(opcode); + } + txt.replace(/\n|[^\n]+/g, (t) => { + if (t === '\n') { + nextOp.chars = 1; + nextOp.lines = 1; + opAssem.append(nextOp); + } else { + nextOp.chars = t.length; + nextOp.lines = 0; + opAssem.append(nextOp); + } + return ''; + }); + }; + + const doOp = () => { + const o = randomStringOperation(textLeft.length); + if (o!.insert) { + const txt = o!.insert; + charBank.append(txt); + outTextAssem.append(txt); + appendMultilineOp('+', txt); + } else if (o!.skip) { + const txt = textLeft.substring(0, o!.skip); + textLeft = textLeft.substring(o!.skip); + outTextAssem.append(txt); + appendMultilineOp('=', txt); + } else if (o!.remove) { + const txt = textLeft.substring(0, o!.remove); + textLeft = textLeft.substring(o!.remove); + appendMultilineOp('-', txt); + } + }; + + while (textLeft.length > 1) doOp(); + for (let i = 0; i < 5; i++) doOp(); // do some more (only insertions will happen) + const outText = `${outTextAssem.toString()}\n`; + opAssem.endDocument(); + const cs = pack(oldLen, outText.length, opAssem.toString(), charBank.toString()); + checkRep(cs); + return [cs, outText]; +}; diff --git a/src/tests/frontend/specs/AttributeMap.js b/src/tests/backend-new/specs/AttributeMap.ts similarity index 91% rename from src/tests/frontend/specs/AttributeMap.js rename to src/tests/backend-new/specs/AttributeMap.ts index 92ca6833463..ce5e61f74af 100644 --- a/src/tests/frontend/specs/AttributeMap.js +++ b/src/tests/backend-new/specs/AttributeMap.ts @@ -1,16 +1,18 @@ 'use strict'; -const AttributeMap = require('../../../static/js/AttributeMap'); -const AttributePool = require('../../../static/js/AttributePool'); -const attributes = require('../../../static/js/attributes'); +import AttributeMap from '../../../static/js/AttributeMap'; +import AttributePool from '../../../static/js/AttributePool'; +import attributes from '../../../static/js/attributes'; +import {expect, describe, it, beforeEach} from 'vitest' +import {Attribute} from "../../../static/js/types/Attribute"; describe('AttributeMap', function () { - const attribs = [ + const attribs: Attribute[] = [ ['foo', 'bar'], ['baz', 'bif'], ['emptyValue', ''], ]; - let pool; + let pool: AttributePool; const getPoolSize = () => { let n = 0; @@ -66,15 +68,17 @@ describe('AttributeMap', function () { ['number', 1, '1'], ]; for (const [desc, input, want] of testCases) { - describe(desc, function () { + describe(desc as string, function () { it('key is coerced to string', async function () { const m = new AttributeMap(pool); + // @ts-ignore m.set(input, 'value'); expect(m.get(want)).to.equal('value'); }); it('value is coerced to string', async function () { const m = new AttributeMap(pool); + // @ts-ignore m.set('key', input); expect(m.get('key')).to.equal(want); }); @@ -116,10 +120,12 @@ describe('AttributeMap', function () { }); for (const funcName of ['update', 'updateFromString']) { - const callUpdateFn = (m, ...args) => { + const callUpdateFn = (m: any, ...args: (boolean | (string | null | undefined)[][])[]) => { if (funcName === 'updateFromString') { + // @ts-ignore args[0] = attributes.attribsToString(attributes.sort([...args[0]]), pool); } + // @ts-ignore return AttributeMap.prototype[funcName].call(m, ...args); }; diff --git a/src/tests/backend-new/specs/StringIteratorTest.ts b/src/tests/backend-new/specs/StringIteratorTest.ts new file mode 100644 index 00000000000..d88fa57aa08 --- /dev/null +++ b/src/tests/backend-new/specs/StringIteratorTest.ts @@ -0,0 +1,47 @@ +import {expect, describe, it} from 'vitest' +import {StringIterator} from "../../../static/js/StringIterator"; + + +describe('Test string iterator take', function () { + it('should iterate over a string', async function () { + const str = 'Hello, world!'; + const iter = new StringIterator(str); + let i = 0; + while (iter.remaining() > 0) { + expect(iter.remaining()).to.equal(str.length - i); + console.error(iter.remaining()); + expect(iter.take(1)).to.equal(str.charAt(i)); + i++; + } + }); +}) + + +describe('Test string iterator peek', function () { + it('should peek over a string', async function () { + const str = 'Hello, world!'; + const iter = new StringIterator(str); + let i = 0; + while (iter.remaining() > 0) { + expect(iter.remaining()).to.equal(str.length - i); + expect(iter.peek(1)).to.equal(str.charAt(i)); + i++; + iter.skip(1); + } + }); +}) + +describe('Test string iterator skip', function () { + it('should throw error when skip over a string too long', async function () { + const str = 'Hello, world!'; + const iter = new StringIterator(str); + expect(()=>iter.skip(1000)).toThrowError(); + }); + + it('should skip over a string', async function () { + const str = 'Hello, world!'; + const iter = new StringIterator(str); + iter.skip(7); + expect(iter.take(1)).to.equal('w'); + }); +}) diff --git a/src/tests/backend-new/specs/admin_utils.ts b/src/tests/backend-new/specs/admin_utils.ts new file mode 100644 index 00000000000..b115a38a637 --- /dev/null +++ b/src/tests/backend-new/specs/admin_utils.ts @@ -0,0 +1,38 @@ +'use strict'; + + +import {strict as assert} from "assert"; +import {cleanComments, minify} from "admin/src/utils/utils"; +import {describe, it, expect, beforeAll} from "vitest"; +import fs from 'fs'; +const fsp = fs.promises; +let template:string; + +describe(__filename, function () { + beforeAll(async function () { + template = await fsp.readFile('../settings.json.template', 'utf8') + }); + describe('adminUtils', function () { + it('cleanComments function empty', async function () { + expect(cleanComments("")).to.equal(""); + }); + it('cleanComments function HelloWorld no comment', async function () { + expect(cleanComments("HelloWorld")).to.equal("HelloWorld"); + }); + it('cleanComments function HelloWorld with comment', async function () { + expect(cleanComments("Hello/*abc*/World/*def*/")).to.equal("HelloWorld"); + }); + it('cleanComments function HelloWorld with comment and multiline', async function () { + expect(cleanComments("Hello \n/*abc\nxyz*/World/*def*/")).to.equal("Hello\nWorld"); + }); + it('cleanComments function HelloWorld with multiple line breaks', async function () { + expect(cleanComments(" \nHello \n \n \nWorld/*def*/")).to.equal("Hello\nWorld"); + }); + it('cleanComments function same after minified', async function () { + expect(minify(cleanComments(template)!)).to.equal(minify(template)); + }); + it('minified results are smaller', async function () { + expect(minify(template).length < template.length).to.equal(true); + }); + }); +}); diff --git a/src/tests/frontend/specs/attributes.js b/src/tests/backend-new/specs/attributes.ts similarity index 86% rename from src/tests/frontend/specs/attributes.js rename to src/tests/backend-new/specs/attributes.ts index 13058dbe38c..64a4464bd64 100644 --- a/src/tests/frontend/specs/attributes.js +++ b/src/tests/backend-new/specs/attributes.ts @@ -1,11 +1,16 @@ 'use strict'; -const AttributePool = require('../../../static/js/AttributePool'); -const attributes = require('../../../static/js/attributes'); +import {APool} from "../../../node/types/PadType"; + +import AttributePool from '../../../static/js/AttributePool'; +import attributes from '../../../static/js/attributes'; + +import {expect, describe, it, beforeEach} from 'vitest'; +import {Attribute} from "../../../static/js/types/Attribute"; describe('attributes', function () { - const attribs = [['foo', 'bar'], ['baz', 'bif']]; - let pool; + const attribs: Attribute[] = [['foo', 'bar'], ['baz', 'bif']]; + let pool: AttributePool; beforeEach(async function () { pool = new AttributePool(); @@ -14,7 +19,7 @@ describe('attributes', function () { describe('decodeAttribString', function () { it('is a generator function', async function () { - expect(attributes.decodeAttribString).to.be.a((function* () {}).constructor); + expect(attributes.decodeAttribString.constructor.name).to.equal('GeneratorFunction'); }); describe('rejects invalid attribute strings', function () { @@ -22,7 +27,7 @@ describe('attributes', function () { for (const tc of testCases) { it(JSON.stringify(tc), async function () { expect(() => [...attributes.decodeAttribString(tc)]) - .to.throwException(/invalid character/); + .toThrowError(/invalid character/); }); } }); @@ -41,6 +46,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = [...attributes.decodeAttribString(input)]; expect(JSON.stringify(got)).to.equal(JSON.stringify(want)); }); @@ -56,7 +62,8 @@ describe('attributes', function () { ['set', new Set([0, 1])], ]; for (const [desc, input] of testCases) { - it(desc, async function () { + it(desc as string, async function () { + // @ts-ignore expect(attributes.encodeAttribString(input)).to.equal('*0*1'); }); } @@ -74,7 +81,8 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { - expect(() => attributes.encodeAttribString(input)).to.throwException(wantErr); + // @ts-ignore + expect(() => attributes.encodeAttribString(input)).toThrowError(wantErr as RegExp); }); } }); @@ -93,6 +101,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore expect(attributes.encodeAttribString(input)).to.equal(want); }); } @@ -101,7 +110,7 @@ describe('attributes', function () { describe('attribsFromNums', function () { it('is a generator function', async function () { - expect(attributes.attribsFromNums).to.be.a((function* () {}).constructor); + expect(attributes.attribsFromNums.constructor.name).to.equal("GeneratorFunction"); }); describe('accepts any kind of iterable', function () { @@ -112,7 +121,8 @@ describe('attributes', function () { ]; for (const [desc, input] of testCases) { - it(desc, async function () { + it(desc as string, async function () { + // @ts-ignore const gotAttribs = [...attributes.attribsFromNums(input, pool)]; expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(attribs)); }); @@ -132,7 +142,8 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { - expect(() => [...attributes.attribsFromNums(input, pool)]).to.throwException(wantErr); + // @ts-ignore + expect(() => [...attributes.attribsFromNums(input, pool)]).toThrowError(wantErr as RegExp); }); } }); @@ -147,6 +158,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const gotAttribs = [...attributes.attribsFromNums(input, pool)]; expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(want)); }); @@ -156,7 +168,7 @@ describe('attributes', function () { describe('attribsToNums', function () { it('is a generator function', async function () { - expect(attributes.attribsToNums).to.be.a((function* () {}).constructor); + expect(attributes.attribsToNums.constructor.name).to.equal("GeneratorFunction") }); describe('accepts any kind of iterable', function () { @@ -167,7 +179,8 @@ describe('attributes', function () { ]; for (const [desc, input] of testCases) { - it(desc, async function () { + it(desc as string, async function () { + // @ts-ignore const gotNums = [...attributes.attribsToNums(input, pool)]; expect(JSON.stringify(gotNums)).to.equal(JSON.stringify([0, 1])); }); @@ -178,7 +191,8 @@ describe('attributes', function () { const testCases = [null, [null]]; for (const input of testCases) { it(JSON.stringify(input), async function () { - expect(() => [...attributes.attribsToNums(input, pool)]).to.throwException(); + // @ts-ignore + expect(() => [...attributes.attribsToNums(input, pool)]).toThrowError(); }); } }); @@ -193,6 +207,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = [...attributes.attribsToNums(input, pool)]; expect(JSON.stringify(got)).to.equal(JSON.stringify(want)); }); @@ -207,6 +222,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = [...attributes.attribsToNums(input, pool)]; expect(JSON.stringify(got)).to.equal(JSON.stringify(want)); expect(JSON.stringify(pool.getAttrib(attribs.length))) @@ -224,12 +240,13 @@ describe('attributes', function () { ['number', 1, '1'], ]; for (const [desc, inputVal, wantVal] of testCases) { - describe(desc, function () { + describe(desc as string, function () { for (const [desc, inputAttribs, wantAttribs] of [ ['key is coerced to string', [[inputVal, 'value']], [[wantVal, 'value']]], ['value is coerced to string', [['key', inputVal]], [['key', wantVal]]], ]) { - it(desc, async function () { + it(desc as string, async function () { + // @ts-ignore const gotNums = [...attributes.attribsToNums(inputAttribs, pool)]; // Each attrib in inputAttribs is expected to be new to the pool. const wantNums = [...Array(attribs.length + 1).keys()].slice(attribs.length); @@ -245,7 +262,7 @@ describe('attributes', function () { describe('attribsFromString', function () { it('is a generator function', async function () { - expect(attributes.attribsFromString).to.be.a((function* () {}).constructor); + expect(attributes.attribsFromString.constructor.name).to.equal('GeneratorFunction'); }); describe('rejects invalid attribute strings', function () { @@ -261,7 +278,8 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { - expect(() => [...attributes.attribsFromString(input, pool)]).to.throwException(wantErr); + // @ts-ignore + expect(() => [...attributes.attribsFromString(input, pool)]).toThrowError(wantErr); }); } }); @@ -276,6 +294,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const gotAttribs = [...attributes.attribsFromString(input, pool)]; expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(want)); }); @@ -292,7 +311,8 @@ describe('attributes', function () { ]; for (const [desc, input] of testCases) { - it(desc, async function () { + it(desc as string, async function () { + // @ts-ignore const got = attributes.attribsToString(input, pool); expect(got).to.equal('*0*1'); }); @@ -303,7 +323,8 @@ describe('attributes', function () { const testCases = [null, [null]]; for (const input of testCases) { it(JSON.stringify(input), async function () { - expect(() => attributes.attribsToString(input, pool)).to.throwException(); + // @ts-ignore + expect(() => attributes.attribsToString(input, pool)).toThrowError(); }); } }); @@ -318,6 +339,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = attributes.attribsToString(input, pool); expect(got).to.equal(want); }); @@ -332,6 +354,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = attributes.attribsToString(input, pool); expect(got).to.equal(want); expect(JSON.stringify(pool.getAttrib(attribs.length))) diff --git a/src/tests/frontend/specs/easysync-assembler.js b/src/tests/backend-new/specs/easysync-assembler.ts similarity index 64% rename from src/tests/frontend/specs/easysync-assembler.js rename to src/tests/backend-new/specs/easysync-assembler.ts index 80a6636d223..28cb2eb4601 100644 --- a/src/tests/frontend/specs/easysync-assembler.js +++ b/src/tests/backend-new/specs/easysync-assembler.ts @@ -1,99 +1,110 @@ 'use strict'; -const Changeset = require('../../../static/js/Changeset'); -const {padutils} = require('../../../static/js/pad_utils'); -const {poolOrArray} = require('../easysync-helper.js'); +import {deserializeOps, opsFromAText} from '../../../static/js/Changeset'; +import padutils from '../../../static/js/pad_utils'; +import {poolOrArray} from '../easysync-helper.js'; + +import {describe, it, expect} from 'vitest' +import {OpAssembler} from "../../../static/js/OpAssembler"; +import {SmartOpAssembler} from "../../../static/js/SmartOpAssembler"; +import Op from "../../../static/js/Op"; + describe('easysync-assembler', function () { it('opAssembler', async function () { const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; - const assem = Changeset.opAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new OpAssembler(); + var opLength = 0 + for (const op of deserializeOps(x)){ + console.log(op) + assem.append(op); + opLength++ + } expect(assem.toString()).to.equal(x); }); it('smartOpAssembler', async function () { const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal(x); }); it('smartOpAssembler ignore additional pure keeps (no attributes)', async function () { const x = '-c*3*4+6|1+1=5'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('-c*3*4+6|1+1'); }); it('smartOpAssembler merge consecutive + ops without multiline', async function () { const x = '-c*3*4+6*3*4+1*3*4+9=5'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('-c*3*4+g'); }); it('smartOpAssembler merge consecutive + ops with multiline', async function () { const x = '-c*3*4+6*3*4|1+1*3*4|9+f*3*4+k=5'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('-c*3*4|a+m*3*4+k'); }); it('smartOpAssembler merge consecutive - ops without multiline', async function () { const x = '-c-6-1-9=5'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('-s'); }); it('smartOpAssembler merge consecutive - ops with multiline', async function () { const x = '-c-6|1-1|9-f-k=5'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('|a-y-k'); }); it('smartOpAssembler merge consecutive = ops without multiline', async function () { const x = '-c*3*4=6*2*4=1*3*4=f*3*4=2*3*4=a=k=5'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('-c*3*4=6*2*4=1*3*4=r'); }); it('smartOpAssembler merge consecutive = ops with multiline', async function () { const x = '-c*3*4=6*2*4|1=1*3*4|9=f*3*4|2=2*3*4=a*3*4=1=k=5'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('-c*3*4=6*2*4|1=1*3*4|b=h*3*4=b'); }); it('smartOpAssembler ignore + ops with ops.chars === 0', async function () { const x = '-c*3*4+6*3*4+0*3*4+1+0*3*4+1'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('-c*3*4+8'); }); it('smartOpAssembler ignore - ops with ops.chars === 0', async function () { const x = '-c-6-0-1-0-1'; - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of deserializeOps(x)) assem.append(op); assem.endDocument(); expect(assem.toString()).to.equal('-k'); }); it('smartOpAssembler append + op with text', async function () { - const assem = Changeset.smartOpAssembler(); + const assem = new SmartOpAssembler(); const pool = poolOrArray([ 'attr1,1', 'attr2,2', @@ -102,20 +113,21 @@ describe('easysync-assembler', function () { 'attr5,5', ]); - padutils.warnDeprecated.disabledForTestingOnly = true; + padutils.warnDeprecatedFlags.disabledForTestingOnly = true; try { assem.appendOpWithText('+', 'test', '*3*4*5', pool); assem.appendOpWithText('+', 'test', '*3*4*5', pool); assem.appendOpWithText('+', 'test', '*1*4*5', pool); } finally { - delete padutils.warnDeprecated.disabledForTestingOnly; + // @ts-ignore + delete padutils.warnDeprecatedFlags.disabledForTestingOnly; } assem.endDocument(); expect(assem.toString()).to.equal('*3*4*5+8*1*4*5+4'); }); it('smartOpAssembler append + op with multiline text', async function () { - const assem = Changeset.smartOpAssembler(); + const assem = new SmartOpAssembler(); const pool = poolOrArray([ 'attr1,1', 'attr2,2', @@ -124,13 +136,14 @@ describe('easysync-assembler', function () { 'attr5,5', ]); - padutils.warnDeprecated.disabledForTestingOnly = true; + padutils.warnDeprecatedFlags.disabledForTestingOnly = true; try { assem.appendOpWithText('+', 'test\ntest', '*3*4*5', pool); assem.appendOpWithText('+', '\ntest\n', '*3*4*5', pool); assem.appendOpWithText('+', '\ntest', '*1*4*5', pool); } finally { - delete padutils.warnDeprecated.disabledForTestingOnly; + // @ts-ignore + delete padutils.warnDeprecatedFlags.disabledForTestingOnly; } assem.endDocument(); expect(assem.toString()).to.equal('*3*4*5|3+f*1*4*5|1+1*1*4*5+4'); @@ -138,30 +151,39 @@ describe('easysync-assembler', function () { it('smartOpAssembler clear should empty internal assemblers', async function () { const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; - const ops = Changeset.deserializeOps(x); + const ops = deserializeOps(x); const iter = { _n: ops.next(), hasNext() { return !this._n.done; }, - next() { const v = this._n.value; this._n = ops.next(); return v; }, + next() { const v = this._n.value; this._n = ops.next(); return v as Op; }, }; - const assem = Changeset.smartOpAssembler(); - assem.append(iter.next()); - assem.append(iter.next()); - assem.append(iter.next()); + const assem = new SmartOpAssembler(); + var iter1 = iter.next() + assem.append(iter1); + var iter2 = iter.next() + assem.append(iter2); + var iter3 = iter.next() + assem.append(iter3); + console.log(assem.toString()); assem.clear(); assem.append(iter.next()); assem.append(iter.next()); + console.log(assem.toString()); assem.clear(); - while (iter.hasNext()) assem.append(iter.next()); + let counter = 0; + while (iter.hasNext()) { + console.log(counter++) + assem.append(iter.next()); + } assem.endDocument(); expect(assem.toString()).to.equal('-1+1*0+1=1-1+1|c=c-1'); }); describe('append atext to assembler', function () { - const testAppendATextToAssembler = (testId, atext, correctOps) => { + const testAppendATextToAssembler = (testId: number, atext: { text: string; attribs: string; }, correctOps: string) => { it(`testAppendATextToAssembler#${testId}`, async function () { - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.opsFromAText(atext)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of opsFromAText(atext)) assem.append(op); expect(assem.toString()).to.equal(correctOps); }); }; diff --git a/src/tests/backend-new/specs/easysync-compose.ts b/src/tests/backend-new/specs/easysync-compose.ts new file mode 100644 index 00000000000..79369caad7b --- /dev/null +++ b/src/tests/backend-new/specs/easysync-compose.ts @@ -0,0 +1,54 @@ +'use strict'; + +import {applyToText, checkRep, compose} from '../../../static/js/Changeset'; +import AttributePool from '../../../static/js/AttributePool'; +import {randomMultiline, randomTestChangeset} from '../easysync-helper'; +import {expect, describe, it} from 'vitest'; + +describe('easysync-compose', function () { + describe('compose', function () { + const testCompose = (randomSeed: number) => { + it(`testCompose#${randomSeed}`, async function () { + const p = new AttributePool(); + + const startText = `${randomMultiline(10, 20)}\n`; + + const x1 = randomTestChangeset(startText); + const change1 = x1[0]; + const text1 = x1[1]; + + const x2 = randomTestChangeset(text1); + const change2 = x2[0]; + const text2 = x2[1]; + + const x3 = randomTestChangeset(text2); + const change3 = x3[0]; + const text3 = x3[1]; + + const change12 = checkRep(compose(change1, change2, p)); + const change23 = checkRep(compose(change2, change3, p)); + const change123 = checkRep(compose(change12, change3, p)); + const change123a = checkRep(compose(change1, change23, p)); + expect(change123a).to.equal(change123); + + expect(applyToText(change12, startText)).to.equal(text2); + expect(applyToText(change23, text1)).to.equal(text3); + expect(applyToText(change123, startText)).to.equal(text3); + }); + }; + + for (let i = 0; i < 30; i++) testCompose(i); + }); + + describe('compose attributes', function () { + it('simpleComposeAttributesTest', async function () { + const p = new AttributePool(); + p.putAttrib(['bold', '']); + p.putAttrib(['bold', 'true']); + const cs1 = checkRep('Z:2>1*1+1*1=1$x'); + const cs2 = checkRep('Z:3>0*0|1=3$'); + const cs12 = checkRep(compose(cs1, cs2, p)); + expect(cs12).to.equal('Z:2>1+1*0|1=2$x'); + }); + }); +}); diff --git a/src/tests/frontend/specs/easysync-inverseRandom.js b/src/tests/backend-new/specs/easysync-inverseRandom.ts similarity index 51% rename from src/tests/frontend/specs/easysync-inverseRandom.js rename to src/tests/backend-new/specs/easysync-inverseRandom.ts index 41ef86d5779..a9b743c7611 100644 --- a/src/tests/frontend/specs/easysync-inverseRandom.js +++ b/src/tests/backend-new/specs/easysync-inverseRandom.ts @@ -1,34 +1,36 @@ 'use strict'; -const Changeset = require('../../../static/js/Changeset'); -const {randomMultiline, randomTestChangeset, poolOrArray} = require('../easysync-helper.js'); +import AttributePool from '../../../static/js/AttributePool'; +import {checkRep, inverse, makeAttribution, mutateAttributionLines, mutateTextLines, splitAttributionLines} from '../../../static/js/Changeset'; +import {randomMultiline, randomTestChangeset, poolOrArray} from '../easysync-helper.js'; +import {expect, describe, it} from 'vitest' describe('easysync-inverseRandom', function () { describe('inverse random', function () { - const testInverseRandom = (randomSeed) => { + const testInverseRandom = (randomSeed: number) => { it(`testInverseRandom#${randomSeed}`, async function () { const p = poolOrArray(['apple,', 'apple,true', 'banana,', 'banana,true']); const startText = `${randomMultiline(10, 20)}\n`; const alines = - Changeset.splitAttributionLines(Changeset.makeAttribution(startText), startText); + splitAttributionLines(makeAttribution(startText), startText); const lines = startText.slice(0, -1).split('\n').map((s) => `${s}\n`); const stylifier = randomTestChangeset(startText, true)[0]; - Changeset.mutateAttributionLines(stylifier, alines, p); - Changeset.mutateTextLines(stylifier, lines); + mutateAttributionLines(stylifier, alines, p); + mutateTextLines(stylifier, lines); const changeset = randomTestChangeset(lines.join(''), true)[0]; - const inverseChangeset = Changeset.inverse(changeset, lines, alines, p); + const inverseChangeset = inverse(changeset, lines, alines, p); const origLines = lines.slice(); const origALines = alines.slice(); - Changeset.mutateTextLines(changeset, lines); - Changeset.mutateAttributionLines(changeset, alines, p); - Changeset.mutateTextLines(inverseChangeset, lines); - Changeset.mutateAttributionLines(inverseChangeset, alines, p); + mutateTextLines(changeset, lines); + mutateAttributionLines(changeset, alines, p); + mutateTextLines(inverseChangeset, lines); + mutateAttributionLines(inverseChangeset, alines, p); expect(lines).to.eql(origLines); expect(alines).to.eql(origALines); }); @@ -38,10 +40,10 @@ describe('easysync-inverseRandom', function () { }); describe('inverse', function () { - const testInverse = (testId, cs, lines, alines, pool, correctOutput) => { + const testInverse = (testId: number, cs: string, lines: string | RegExpMatchArray | null, alines: string[] | { get: (idx: number) => string; }, pool: string[] | AttributePool, correctOutput: string) => { it(`testInverse#${testId}`, async function () { pool = poolOrArray(pool); - const str = Changeset.inverse(Changeset.checkRep(cs), lines, alines, pool); + const str = inverse(checkRep(cs), lines, alines, pool as AttributePool); expect(str).to.equal(correctOutput); }); }; diff --git a/src/tests/frontend/specs/easysync-mutations.js b/src/tests/backend-new/specs/easysync-mutations.ts similarity index 75% rename from src/tests/frontend/specs/easysync-mutations.js rename to src/tests/backend-new/specs/easysync-mutations.ts index c10d34519f2..1cf2ec27655 100644 --- a/src/tests/frontend/specs/easysync-mutations.js +++ b/src/tests/backend-new/specs/easysync-mutations.ts @@ -1,12 +1,19 @@ 'use strict'; -const Changeset = require('../../../static/js/Changeset'); -const AttributePool = require('../../../static/js/AttributePool'); -const {poolOrArray} = require('../easysync-helper.js'); +import {applyToAttribution, applyToText, checkRep, joinAttributionLines, mutateAttributionLines, mutateTextLines, pack} from '../../../static/js/Changeset'; +import AttributePool from '../../../static/js/AttributePool'; +import {poolOrArray} from '../easysync-helper'; +import {expect, describe,it } from "vitest"; +import {SmartOpAssembler} from "../../../static/js/SmartOpAssembler"; +import Op from "../../../static/js/Op"; +import {StringAssembler} from "../../../static/js/StringAssembler"; +import TextLinesMutator from "../../../static/js/TextLinesMutator"; +import {numToString} from "../../../static/js/ChangesetUtils"; describe('easysync-mutations', function () { - const applyMutations = (mu, arrayOfArrays) => { + const applyMutations = (mu: TextLinesMutator, arrayOfArrays: any[]) => { arrayOfArrays.forEach((a) => { + // @ts-ignore const result = mu[a[0]](...a.slice(1)); if (a[0] === 'remove' && a[3]) { expect(result).to.equal(a[3]); @@ -14,13 +21,13 @@ describe('easysync-mutations', function () { }); }; - const mutationsToChangeset = (oldLen, arrayOfArrays) => { - const assem = Changeset.smartOpAssembler(); - const op = new Changeset.Op(); - const bank = Changeset.stringAssembler(); + const mutationsToChangeset = (oldLen: number, arrayOfArrays: string[][]) => { + const assem = new SmartOpAssembler(); + const op = new Op(); + const bank = new StringAssembler(); let oldPos = 0; let newLen = 0; - arrayOfArrays.forEach((a) => { + arrayOfArrays.forEach((a: any[]) => { if (a[0] === 'skip') { op.opcode = '='; op.chars = a[1]; @@ -45,13 +52,13 @@ describe('easysync-mutations', function () { }); newLen += oldLen - oldPos; assem.endDocument(); - return Changeset.pack(oldLen, newLen, assem.toString(), bank.toString()); + return pack(oldLen, newLen, assem.toString(), bank.toString()); }; - const runMutationTest = (testId, origLines, muts, correct) => { + const runMutationTest = (testId: number, origLines: string[], muts:any, correct: string[]) => { it(`runMutationTest#${testId}`, async function () { let lines = origLines.slice(); - const mu = new Changeset.exportedForTestingOnly.TextLinesMutator(lines); + const mu = new TextLinesMutator(lines); applyMutations(mu, muts); mu.close(); expect(lines).to.eql(correct); @@ -59,11 +66,11 @@ describe('easysync-mutations', function () { const inText = origLines.join(''); const cs = mutationsToChangeset(inText.length, muts); lines = origLines.slice(); - Changeset.mutateTextLines(cs, lines); + mutateTextLines(cs, lines); expect(lines).to.eql(correct); const correctText = correct.join(''); - const outText = Changeset.applyToText(cs, inText); + const outText = applyToText(cs, inText); expect(outText).to.equal(correctText); }); }; @@ -141,47 +148,47 @@ describe('easysync-mutations', function () { const lines = ['1\n', '2\n', '3\n', '4\n']; let mu; - mu = new Changeset.exportedForTestingOnly.TextLinesMutator(lines); - expect(mu.hasMore()).to.be(true); + mu = new TextLinesMutator(lines); + expect(mu.hasMore()).toBeTruthy(); mu.skip(8, 4); - expect(mu.hasMore()).to.be(false); + expect(mu.hasMore()).toBeFalsy(); mu.close(); - expect(mu.hasMore()).to.be(false); + expect(mu.hasMore()).toBeFalsy(); // still 1,2,3,4 - mu = new Changeset.exportedForTestingOnly.TextLinesMutator(lines); - expect(mu.hasMore()).to.be(true); + mu = new TextLinesMutator(lines); + expect(mu.hasMore()).toBeTruthy(); mu.remove(2, 1); - expect(mu.hasMore()).to.be(true); + expect(mu.hasMore()).toBeTruthy(); mu.skip(2, 1); - expect(mu.hasMore()).to.be(true); + expect(mu.hasMore()).toBeTruthy(); mu.skip(2, 1); - expect(mu.hasMore()).to.be(true); + expect(mu.hasMore()).toBeTruthy(); mu.skip(2, 1); - expect(mu.hasMore()).to.be(false); + expect(mu.hasMore()).toBeFalsy(); mu.insert('5\n', 1); - expect(mu.hasMore()).to.be(false); + expect(mu.hasMore()).toBeFalsy(); mu.close(); - expect(mu.hasMore()).to.be(false); + expect(mu.hasMore()).toBeFalsy(); // 2,3,4,5 now - mu = new Changeset.exportedForTestingOnly.TextLinesMutator(lines); - expect(mu.hasMore()).to.be(true); + mu = new TextLinesMutator(lines); + expect(mu.hasMore()).toBeTruthy(); mu.remove(6, 3); - expect(mu.hasMore()).to.be(true); + expect(mu.hasMore()).toBeTruthy(); mu.remove(2, 1); - expect(mu.hasMore()).to.be(false); + expect(mu.hasMore()).toBeFalsy(); mu.insert('hello\n', 1); - expect(mu.hasMore()).to.be(false); + expect(mu.hasMore()).toBeFalsy(); mu.close(); - expect(mu.hasMore()).to.be(false); + expect(mu.hasMore()).toBeFalsy(); }); describe('mutateTextLines', function () { - const testMutateTextLines = (testId, cs, lines, correctLines) => { + const testMutateTextLines = (testId: number, cs: string, lines: string[], correctLines: string[]) => { it(`testMutateTextLines#${testId}`, async function () { const a = lines.slice(); - Changeset.mutateTextLines(cs, a); + mutateTextLines(cs, a); expect(a).to.eql(correctLines); }); }; @@ -194,7 +201,7 @@ describe('easysync-mutations', function () { const result = lines.slice(); const cs = 'Z:8>0*0|1=2|2=2'; - Changeset.mutateTextLines(cs, lines); + mutateTextLines(cs, lines); expect(result).to.eql(lines); }); }); @@ -204,23 +211,23 @@ describe('easysync-mutations', function () { const p = new AttributePool(); p.putAttrib(['char', 'newline']); for (let i = 1; i < 36; i++) { - p.putAttrib(['char', Changeset.numToString(i)]); + p.putAttrib(['char', numToString(i)]); } p.putAttrib(['char', '']); return p; })(); - const runMutateAttributionTest = (testId, attribs, cs, alines, outCorrect) => { + const runMutateAttributionTest = (testId: number, attribs: string[] | AttributePool, cs: string, alines: string[], outCorrect: string[]) => { it(`runMutateAttributionTest#${testId}`, async function () { const p = poolOrArray(attribs); const alines2 = Array.prototype.slice.call(alines); - Changeset.mutateAttributionLines(Changeset.checkRep(cs), alines2, p); + mutateAttributionLines(checkRep(cs), alines2, p); expect(alines2).to.eql(outCorrect); - const removeQuestionMarks = (a) => a.replace(/\?/g, ''); - const inMerged = Changeset.joinAttributionLines(alines.map(removeQuestionMarks)); - const correctMerged = Changeset.joinAttributionLines(outCorrect.map(removeQuestionMarks)); - const mergedResult = Changeset.applyToAttribution(cs, inMerged, p); + const removeQuestionMarks = (a: string) => a.replace(/\?/g, ''); + const inMerged = joinAttributionLines(alines.map(removeQuestionMarks)); + const correctMerged = joinAttributionLines(outCorrect.map(removeQuestionMarks)); + const mergedResult = applyToAttribution(cs, inMerged, p); expect(mergedResult).to.equal(correctMerged); }); }; diff --git a/src/tests/backend-new/specs/easysync-other.test.ts b/src/tests/backend-new/specs/easysync-other.test.ts new file mode 100644 index 00000000000..9a24dee6f83 --- /dev/null +++ b/src/tests/backend-new/specs/easysync-other.test.ts @@ -0,0 +1,166 @@ +'use strict'; + +import {applyToAttribution, applyToText, checkRep, deserializeOps, exportedForTestingOnly, filterAttribNumbers, joinAttributionLines, makeAttribsString, makeSplice, moveOpsToNewPool, opAttributeValue, splitAttributionLines} from '../../../static/js/Changeset'; +import AttributePool from '../../../static/js/AttributePool'; +import {randomMultiline, poolOrArray} from '../easysync-helper'; +import padutils from '../../../static/js/pad_utils'; +import {describe, it, expect} from 'vitest' +import Op from "../../../static/js/Op"; +import {MergingOpAssembler} from "../../../static/js/MergingOpAssembler"; +import {Attribute} from "../../../static/js/types/Attribute"; + + +describe('easysync-other', function () { + describe('filter attribute numbers', function () { + const testFilterAttribNumbers = (testId: number, cs: string, filter: Function, correctOutput: string) => { + it(`testFilterAttribNumbers#${testId}`, async function () { + const str = filterAttribNumbers(cs, filter); + expect(str).to.equal(correctOutput); + }); + }; + + testFilterAttribNumbers(1, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', + (n: number) => (n % 2) === 0, '*0+1+2+3+4*2+5*0*2*c+6'); + testFilterAttribNumbers(2, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', + (n: number) => (n % 2) === 1, '*1+1+2+3*1+4+5*1*b+6'); + }); + + describe('make attribs string', function () { + const testMakeAttribsString = (testId: number, pool: string[], opcode: string, attribs: string | Attribute[], correctString: string) => { + it(`testMakeAttribsString#${testId}`, async function () { + const p = poolOrArray(pool); + padutils.warnDeprecatedFlags.disabledForTestingOnly = true; + try { + expect(makeAttribsString(opcode, attribs, p)).to.equal(correctString); + } finally { + // @ts-ignore + delete padutils.warnDeprecatedFlags.disabledForTestingOnly; + } + }); + }; + + testMakeAttribsString(1, ['bold,'], '+', [ + ['bold', ''], + ], ''); + testMakeAttribsString(2, ['abc,def', 'bold,'], '=', [ + ['bold', ''], + ], '*1'); + testMakeAttribsString(3, ['abc,def', 'bold,true'], '+', [ + ['abc', 'def'], + ['bold', 'true'], + ], '*0*1'); + testMakeAttribsString(4, ['abc,def', 'bold,true'], '+', [ + ['bold', 'true'], + ['abc', 'def'], + ], '*0*1'); + }); + + describe('other', function () { + it('testMoveOpsToNewPool', async function () { + const pool1 = new AttributePool(); + const pool2 = new AttributePool(); + + pool1.putAttrib(['baz', 'qux']); + pool1.putAttrib(['foo', 'bar']); + + pool2.putAttrib(['foo', 'bar']); + + expect(moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2)) + .to.equal('Z:1>2*0+1*1+1$ab'); + expect(moveOpsToNewPool('*1+1*0+1', pool1, pool2)).to.equal('*0+1*1+1'); + }); + + it('testMakeSplice', async function () { + const t = 'a\nb\nc\n'; + let splice = makeSplice(t, 5, 0, 'def') + const t2 = applyToText(splice, t); + expect(t2).to.equal('a\nb\ncdef\n'); + }); + + it('makeSplice at the end', async function () { + const orig = '123'; + const ins = '456'; + expect(applyToText(makeSplice(orig, orig.length, 0, ins), orig)) + .to.equal(`${orig}${ins}`); + }); + + it('testToSplices', async function () { + const cs = checkRep('Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk'); + const correctSplices = [ + [5, 8, '123456789'], + [9, 17, 'abcdefghijk'], + ]; + expect(exportedForTestingOnly.toSplices(cs)).to.eql(correctSplices); + }); + + it('opAttributeValue', async function () { + const p = new AttributePool(); + p.putAttrib(['name', 'david']); + p.putAttrib(['color', 'green']); + + const stringOp = (str: string) => deserializeOps(str).next().value as Op; + + padutils.warnDeprecatedFlags.disabledForTestingOnly = true; + try { + expect(opAttributeValue(stringOp('*0*1+1'), 'name', p)).to.equal('david'); + expect(opAttributeValue(stringOp('*0+1'), 'name', p)).to.equal('david'); + expect(opAttributeValue(stringOp('*1+1'), 'name', p)).to.equal(''); + expect(opAttributeValue(stringOp('+1'), 'name', p)).to.equal(''); + expect(opAttributeValue(stringOp('*0*1+1'), 'color', p)).to.equal('green'); + expect(opAttributeValue(stringOp('*1+1'), 'color', p)).to.equal('green'); + expect(opAttributeValue(stringOp('*0+1'), 'color', p)).to.equal(''); + expect(opAttributeValue(stringOp('+1'), 'color', p)).to.equal(''); + } finally { + // @ts-ignore + delete padutils.warnDeprecatedFlags.disabledForTestingOnly; + } + }); + + describe('applyToAttribution', function () { + const runApplyToAttributionTest = (testId: number, attribs: string[], cs: string, inAttr: string, outCorrect: string) => { + it(`applyToAttribution#${testId}`, async function () { + const p = poolOrArray(attribs); + const result = applyToAttribution(checkRep(cs), inAttr, p); + expect(result).to.equal(outCorrect); + }); + }; + + // turn c<b>a</b>ctus\n into a<b>c</b>tusabcd\n + runApplyToAttributionTest(1, + ['bold,', 'bold,true'], 'Z:7>3-1*0=1*1=1=3+4$abcd', '+1*1+1|1+5', '+1*1+1|1+8'); + + // turn "david\ngreenspan\n" into "<b>david\ngreen</b>\n" + runApplyToAttributionTest(2, + ['bold,', 'bold,true'], 'Z:g<4*1|1=6*1=5-4$', '|2+g', '*1|1+6*1+5|1+1'); + }); + + describe('split/join attribution lines', function () { + const testSplitJoinAttributionLines = (randomSeed: number) => { + const stringToOps = (str: string) => { + const assem = new MergingOpAssembler(); + const o = new Op('+'); + o.chars = 1; + for (let i = 0; i < str.length; i++) { + const c = str.charAt(i); + o.lines = (c === '\n' ? 1 : 0); + o.attribs = (c === 'a' || c === 'b' ? `*${c}` : ''); + assem.append(o); + } + return assem.toString(); + }; + + it(`testSplitJoinAttributionLines#${randomSeed}`, async function () { + const doc = `${randomMultiline(10, 20)}\n`; + + const theJoined = stringToOps(doc); + const theSplit = doc.match(/[^\n]*\n/g)!.map(stringToOps); + + expect(splitAttributionLines(theJoined, doc)).to.eql(theSplit); + expect(joinAttributionLines(theSplit)).to.equal(theJoined); + }); + }; + + for (let i = 0; i < 10; i++) testSplitJoinAttributionLines(i); + }); + }); +}); diff --git a/src/tests/frontend/specs/easysync-subAttribution.js b/src/tests/backend-new/specs/easysync-subAttribution.ts similarity index 90% rename from src/tests/frontend/specs/easysync-subAttribution.js rename to src/tests/backend-new/specs/easysync-subAttribution.ts index 0cb4e8f7c33..90760d9be99 100644 --- a/src/tests/frontend/specs/easysync-subAttribution.js +++ b/src/tests/backend-new/specs/easysync-subAttribution.ts @@ -1,11 +1,11 @@ 'use strict'; -const Changeset = require('../../../static/js/Changeset'); - +import {subattribution} from '../../../static/js/Changeset'; +import {expect, describe, it} from 'vitest'; describe('easysync-subAttribution', function () { - const testSubattribution = (testId, astr, start, end, correctOutput) => { + const testSubattribution = (testId: number, astr: string, start: number, end: number | undefined, correctOutput: string) => { it(`subattribution#${testId}`, async function () { - const str = Changeset.subattribution(astr, start, end); + const str = subattribution(astr, start, end); expect(str).to.equal(correctOutput); }); }; diff --git a/src/tests/backend/specs/pad_utils.ts b/src/tests/backend-new/specs/pad_utils.ts similarity index 57% rename from src/tests/backend/specs/pad_utils.ts rename to src/tests/backend-new/specs/pad_utils.ts index 3ca3c0858a7..569f49d04c9 100644 --- a/src/tests/backend/specs/pad_utils.ts +++ b/src/tests/backend-new/specs/pad_utils.ts @@ -1,22 +1,19 @@ -'use strict'; - import {MapArrayType} from "../../../node/types/MapType"; - -import {strict as assert} from "assert"; -const {padutils} = require('../../../static/js/pad_utils'); +import padutils from '../../../static/js/pad_utils'; +import {describe, it, expect, afterEach, beforeAll} from "vitest"; describe(__filename, function () { describe('warnDeprecated', function () { - const {warnDeprecated} = padutils; + const {warnDeprecatedFlags, warnDeprecated} = padutils; const backups:MapArrayType<any> = {}; - before(async function () { - backups.logger = warnDeprecated.logger; + beforeAll(async function () { + backups.logger = warnDeprecatedFlags.logger; }); afterEach(async function () { - warnDeprecated.logger = backups.logger; - delete warnDeprecated._rl; // Reset internal rate limiter state. + warnDeprecatedFlags.logger = backups.logger; + delete warnDeprecatedFlags._rl; // Reset internal rate limiter state. }); /*it('includes the stack', async function () { @@ -28,18 +25,18 @@ describe(__filename, function () { it('rate limited', async function () { let got = 0; - warnDeprecated.logger = {warn: () => ++got}; + warnDeprecatedFlags.logger = {warn: () => ++got}; warnDeprecated(); // Initialize internal rate limiter state. - const {period} = warnDeprecated._rl; + const {period} = warnDeprecatedFlags._rl!; got = 0; const testCases = [[0, 1], [0, 1], [period - 1, 1], [period, 2]]; for (const [now, want] of testCases) { // In a loop so that the stack trace is the same. - warnDeprecated._rl.now = () => now; + warnDeprecatedFlags._rl!.now = () => now; warnDeprecated(); - assert.equal(got, want); + expect(got).toEqual(want); } warnDeprecated(); // Should have a different stack trace. - assert.equal(got, testCases[testCases.length - 1][1] + 1); + expect(got).toEqual(testCases[testCases.length - 1][1] + 1); }); }); }); diff --git a/src/tests/backend-new/specs/path_exists.ts b/src/tests/backend-new/specs/path_exists.ts new file mode 100644 index 00000000000..5c719d05e98 --- /dev/null +++ b/src/tests/backend-new/specs/path_exists.ts @@ -0,0 +1,22 @@ +import check from "../../../node/utils/path_exists"; +import {expect, describe, it} from "vitest"; + +describe('Test path exists', function () { + it('should return true if the path exists - directory', function () { + const path = './locales'; + const result = check(path); + expect(result).toBeTruthy(); + }); + + it('should return true if the path exists - file', function () { + const path = './locales/en.json'; + const result = check(path); + expect(result).toBeTruthy(); + }) + + it('should return false if the path does not exist', function () { + const path = './path_not_exists.ts'; + const result = check(path); + expect(result).toEqual(false); + }); +}) diff --git a/src/tests/backend/specs/promises.ts b/src/tests/backend-new/specs/promises.ts similarity index 70% rename from src/tests/backend/specs/promises.ts rename to src/tests/backend-new/specs/promises.ts index 66be235622a..2ce6aed27c7 100644 --- a/src/tests/backend/specs/promises.ts +++ b/src/tests/backend-new/specs/promises.ts @@ -1,7 +1,7 @@ import {MapArrayType} from "../../../node/types/MapType"; -const assert = require('assert').strict; -const promises = require('../../../node/utils/promises'); +import {timesLimit} from '../../../node/utils/promises'; +import {describe, it, expect} from "vitest"; describe(__filename, function () { describe('promises.timesLimit', function () { @@ -15,7 +15,7 @@ describe(__filename, function () { const testPromises: TestPromise[] = []; const makePromise = (index: number) => { // Make sure index increases by one each time. - assert.equal(index, wantIndex++); + expect(index).toEqual(wantIndex++); // Save the resolve callback (so the test can trigger resolution) // and the promise itself (to wait for resolve to take effect). const p:TestPromise = {}; @@ -28,17 +28,17 @@ describe(__filename, function () { const total = 11; const concurrency = 7; - const timesLimitPromise = promises.timesLimit(total, concurrency, makePromise); + const timesLimitPromise = timesLimit(total, concurrency, makePromise); it('honors concurrency', async function () { - assert.equal(wantIndex, concurrency); + expect(wantIndex).toEqual(concurrency); }); it('creates another when one completes', async function () { const {promise, resolve} = testPromises.shift()!; resolve!(); await promise; - assert.equal(wantIndex, concurrency + 1); + expect(wantIndex).toEqual(concurrency + 1); }); it('creates the expected total number of promises', async function () { @@ -49,7 +49,7 @@ describe(__filename, function () { resolve!(); await promise; } - assert.equal(wantIndex, total); + expect(wantIndex).toEqual(total); }); it('resolves', async function () { @@ -58,35 +58,35 @@ describe(__filename, function () { it('does not create too many promises if total < concurrency', async function () { wantIndex = 0; - assert.equal(testPromises.length, 0); + expect(testPromises.length).toEqual(0); const total = 7; const concurrency = 11; - const timesLimitPromise = promises.timesLimit(total, concurrency, makePromise); + const timesLimitPromise = timesLimit(total, concurrency, makePromise); while (testPromises.length > 0) { const {promise, resolve} = testPromises.pop()!; resolve!(); await promise; } await timesLimitPromise; - assert.equal(wantIndex, total); + expect(wantIndex).toEqual(total); }); it('accepts total === 0, concurrency > 0', async function () { wantIndex = 0; - assert.equal(testPromises.length, 0); - await promises.timesLimit(0, concurrency, makePromise); - assert.equal(wantIndex, 0); + expect(testPromises.length).toEqual(0); + await timesLimit(0, concurrency, makePromise); + expect(wantIndex).toEqual(0); }); it('accepts total === 0, concurrency === 0', async function () { wantIndex = 0; - assert.equal(testPromises.length, 0); - await promises.timesLimit(0, 0, makePromise); - assert.equal(wantIndex, 0); + expect(testPromises.length).toEqual(0); + await timesLimit(0, 0, makePromise); + expect(wantIndex).toEqual(0); }); it('rejects total > 0, concurrency === 0', async function () { - await assert.rejects(promises.timesLimit(total, 0, makePromise), RangeError); + expect(timesLimit(total, 0, makePromise)).rejects.toThrow(RangeError); }); }); }); diff --git a/src/tests/backend/specs/sanitizePathname.ts b/src/tests/backend-new/specs/sanitizePathname.ts similarity index 92% rename from src/tests/backend/specs/sanitizePathname.ts rename to src/tests/backend-new/specs/sanitizePathname.ts index fd3cbb2e7f3..e841ae1551b 100644 --- a/src/tests/backend/specs/sanitizePathname.ts +++ b/src/tests/backend-new/specs/sanitizePathname.ts @@ -1,8 +1,7 @@ -'use strict'; - import {strict as assert} from "assert"; import path from 'path'; -const sanitizePathname = require('../../../node/utils/sanitizePathname'); +import sanitizePathname from '../../../node/utils/sanitizePathname'; +import {describe, it, expect} from 'vitest'; describe(__filename, function () { describe('absolute paths rejected', function () { @@ -21,7 +20,7 @@ describe(__filename, function () { for (const [platform, p] of testCases) { it(`${platform} ${p}`, async function () { // @ts-ignore - assert.throws(() => sanitizePathname(p, path[platform]), {message: /absolute path/}); + expect(() => sanitizePathname(p, path[platform] as any)).toThrowError(/absolute path/); }); } }); diff --git a/src/tests/backend-new/specs/skiplist.ts b/src/tests/backend-new/specs/skiplist.ts new file mode 100644 index 00000000000..23ad46ae650 --- /dev/null +++ b/src/tests/backend-new/specs/skiplist.ts @@ -0,0 +1,56 @@ +'use strict'; + +import SkipList from 'ep_etherpad-lite/static/js/skiplist'; +import {expect, describe, it} from 'vitest'; + +describe('skiplist.js', function () { + it('rejects null keys', async function () { + const skiplist = new SkipList(); + for (const key of [undefined, null]) { + // @ts-ignore + expect(() => skiplist.push({key})).toThrowError(); + } + }); + + it('rejects duplicate keys', async function () { + const skiplist = new SkipList(); + skiplist.push({key: 'foo'}); + expect(() => skiplist.push({key: 'foo'})).toThrowError(); + }); + + it('atOffset() returns last entry that touches offset', async function () { + const skiplist = new SkipList(); + const entries: { key: string; width: number; }[] = []; + let nextId = 0; + const makeEntry = (width: number) => { + const entry = {key: `id${nextId++}`, width}; + entries.push(entry); + return entry; + }; + + skiplist.push(makeEntry(5)); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[0]); + expect(() => skiplist.atOffset(6)).toThrowError(); + + skiplist.push(makeEntry(0)); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[1]); + expect(() => skiplist.atOffset(6)).toThrowError(); + + skiplist.push(makeEntry(0)); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[2]); + expect(() => skiplist.atOffset(6)).toThrowError(); + + skiplist.splice(2, 0, [makeEntry(0)]); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[2]); + expect(() => skiplist.atOffset(6)).toThrowError(); + + skiplist.push(makeEntry(3)); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[4]); + expect(skiplist.atOffset(6)).toBe(entries[4]); + }); +}); diff --git a/src/tests/backend/common.ts b/src/tests/backend/common.ts index c0cfd1377aa..4f39375462c 100644 --- a/src/tests/backend/common.ts +++ b/src/tests/backend/common.ts @@ -2,29 +2,30 @@ import {MapArrayType} from "../../node/types/MapType"; -const AttributePool = require('../../static/js/AttributePool'); -const apiHandler = require('../../node/handler/APIHandler'); +import AttributePool from '../../static/js/AttributePool'; const assert = require('assert').strict; const io = require('socket.io-client'); const log4js = require('log4js'); -const {padutils} = require('../../static/js/pad_utils'); +import padutils from '../../static/js/pad_utils'; const process = require('process'); const server = require('../../node/server'); const setCookieParser = require('set-cookie-parser'); const settings = require('../../node/utils/Settings'); import supertest from 'supertest'; +import TestAgent from "supertest/lib/agent"; +import {Http2Server} from "node:http2"; +import {SignJWT} from "jose"; +import {privateKeyExported} from "../../node/security/OAuth2Provider"; const webaccess = require('../../node/hooks/express/webaccess'); const backups:MapArrayType<any> = {}; let agentPromise:Promise<any>|null = null; -exports.apiKey = apiHandler.exportedForTestingOnly.apiKey; -exports.agent = null; -exports.baseUrl = null; -exports.httpServer = null; -exports.logger = log4js.getLogger('test'); +export let agent: TestAgent|null = null; +export let baseUrl:string|null = null; +export let httpServer: Http2Server|null = null; +export const logger = log4js.getLogger('test'); -const logger = exports.logger; const logLevel = logger.level; // Mocha doesn't monitor unhandled Promise rejections, so convert them to uncaught exceptions. @@ -33,10 +34,37 @@ process.on('unhandledRejection', (reason: string) => { throw reason; }); before(async function () { this.timeout(60000); - await exports.init(); + await init(); }); -exports.init = async function () { + +export const generateJWTToken = () => { + const jwt = new SignJWT({ + sub: 'admin', + jti: '123', + exp: Math.floor(Date.now() / 1000) + 60 * 60, + aud: 'account', + iss: 'http://localhost:9001', + admin: true + }) + jwt.setProtectedHeader({alg: 'RS256'}) + return jwt.sign(privateKeyExported!) +} + + +export const generateJWTTokenUser = () => { + const jwt = new SignJWT({ + sub: 'admin', + jti: '123', + exp: Math.floor(Date.now() / 1000) + 60 * 60, + aud: 'account', + iss: 'http://localhost:9001', + }) + jwt.setProtectedHeader({alg: 'RS256'}) + return jwt.sign(privateKeyExported!) +} + +export const init = async function () { if (agentPromise != null) return await agentPromise; let agentResolve; agentPromise = new Promise((resolve) => { agentResolve = resolve; }); @@ -53,11 +81,13 @@ exports.init = async function () { settings.ip = 'localhost'; settings.importExportRateLimiting = {max: 999999}; settings.commitRateLimiting = {duration: 0.001, points: 1e6}; - exports.httpServer = await server.start(); - exports.baseUrl = `http://localhost:${exports.httpServer.address().port}`; - logger.debug(`HTTP server at ${exports.baseUrl}`); + httpServer = await server.start(); + // @ts-ignore + baseUrl = `http://localhost:${httpServer!.address()!.port}`; + logger.debug(`HTTP server at ${baseUrl}`); // Create a supertest user agent for the HTTP server. - exports.agent = supertest(exports.baseUrl); + agent = supertest(baseUrl) + //.set('Authorization', `Bearer ${await generateJWTToken()}`); // Speed up authn tests. backups.authnFailureDelayMs = webaccess.authnFailureDelayMs; webaccess.authnFailureDelayMs = 0; @@ -69,8 +99,8 @@ exports.init = async function () { await server.exit(); }); - agentResolve!(exports.agent); - return exports.agent; + agentResolve!(agent); + return agent; }; /** @@ -81,7 +111,7 @@ exports.init = async function () { * @param {string} event - The socket.io Socket event to listen for. * @returns The argument(s) passed to the event handler. */ -exports.waitForSocketEvent = async (socket: any, event:string) => { +export const waitForSocketEvent = async (socket: any, event:string) => { const errorEvents = [ 'error', 'connect_error', @@ -136,7 +166,7 @@ exports.waitForSocketEvent = async (socket: any, event:string) => { * nullish, no cookies are passed to the server. * @returns {io.Socket} A socket.io client Socket object. */ -exports.connect = async (res:any = null) => { +export const connect = async (res:any = null) => { // Convert the `set-cookie` header(s) into a `cookie` header. const resCookies = (res == null) ? {} : setCookieParser.parse(res, {map: true}); const reqCookieHdr = Object.entries(resCookies).map( @@ -148,14 +178,14 @@ exports.connect = async (res:any = null) => { if (res) { padId = res.req.path.split('/p/')[1]; } - const socket = io(`${exports.baseUrl}/`, { + const socket = io(`${baseUrl}/`, { forceNew: true, // Different tests will have different query parameters. // socketio.js-client on node.js doesn't support cookies (see https://git.io/JU8u9), so the // express_sid cookie must be passed as a query parameter. query: {cookie: reqCookieHdr, padId}, }); try { - await exports.waitForSocketEvent(socket, 'connect'); + await waitForSocketEvent(socket, 'connect'); } catch (e) { socket.close(); throw e; @@ -173,7 +203,7 @@ exports.connect = async (res:any = null) => { * @param token * @returns The CLIENT_VARS message from the server. */ -exports.handshake = async (socket: any, padId:string, token = padutils.generateAuthorToken()) => { +export const handshake = async (socket: any, padId:string, token = padutils.generateAuthorToken()) => { logger.debug('sending CLIENT_READY...'); socket.emit('message', { component: 'pad', @@ -183,7 +213,7 @@ exports.handshake = async (socket: any, padId:string, token = padutils.generateA token, }); logger.debug('waiting for CLIENT_VARS response...'); - const msg = await exports.waitForSocketEvent(socket, 'message'); + const msg = await waitForSocketEvent(socket, 'message'); logger.debug('received CLIENT_VARS message'); return msg; }; @@ -191,7 +221,7 @@ exports.handshake = async (socket: any, padId:string, token = padutils.generateA /** * Convenience wrapper around `socket.send()` that waits for acknowledgement. */ -exports.sendMessage = async (socket: any, message:any) => await new Promise<void>((resolve, reject) => { +export const sendMessage = async (socket: any, message:any) => await new Promise<void>((resolve, reject) => { socket.emit('message', message, (errInfo:{ name: string, message: string, @@ -210,7 +240,7 @@ exports.sendMessage = async (socket: any, message:any) => await new Promise<void /** * Convenience function to send a USER_CHANGES message. Waits for acknowledgement. */ -exports.sendUserChanges = async (socket:any, data:any) => await exports.sendMessage(socket, { +export const sendUserChanges = async (socket:any, data:any) => await sendMessage(socket, { type: 'COLLABROOM', component: 'pad', data: { @@ -232,8 +262,8 @@ exports.sendUserChanges = async (socket:any, data:any) => await exports.sendMess * common.sendUserChanges(socket, {baseRev: rev, changeset}), * ]); */ -exports.waitForAcceptCommit = async (socket:any, wantRev: number) => { - const msg = await exports.waitForSocketEvent(socket, 'message'); +export const waitForAcceptCommit = async (socket:any, wantRev: number) => { + const msg = await waitForSocketEvent(socket, 'message'); assert.deepEqual(msg, { type: 'COLLABROOM', data: { @@ -252,7 +282,7 @@ const alphabet = 'abcdefghijklmnopqrstuvwxyz'; * @param {string} [charset] - Characters to pick from. * @returns {string} */ -exports.randomString = (len: number = 10, charset: string = `${alphabet}${alphabet.toUpperCase()}0123456789`): string => { +export const randomString = (len: number = 10, charset: string = `${alphabet}${alphabet.toUpperCase()}0123456789`): string => { let ret = ''; while (ret.length < len) ret += charset[Math.floor(Math.random() * charset.length)]; return ret; diff --git a/src/tests/backend/fuzzImportTest.ts b/src/tests/backend/fuzzImportTest.ts index 8366e62d853..5b959bbc23a 100644 --- a/src/tests/backend/fuzzImportTest.ts +++ b/src/tests/backend/fuzzImportTest.ts @@ -7,13 +7,12 @@ const common = require('./common'); const host = `http://${settings.ip}:${settings.port}`; const froth = require('mocha-froth'); const axios = require('axios'); -const apiKey = common.apiKey; const apiVersion = 1; const testPadId = `TEST_fuzz${makeid()}`; const endPoint = function (point: string, version?:number) { version = version || apiVersion; - return `/api/${version}/${point}?apikey=${apiKey}`; + return `/api/${version}/${point}}`; }; console.log('Testing against padID', testPadId); @@ -29,7 +28,12 @@ setTimeout(() => { }, 5000); // wait 5 seconds async function runTest(number: number) { - await axios.get(`${host + endPoint('createPad')}&padID=${testPadId}`) + await axios + .get(`${host + endPoint('createPad')}?padID=${testPadId}`, { + headers: { + Authorization: await common.generateJWTToken(), + } + }) .then(() => { const req = axios.post(`${host}/p/${testPadId}/import`) .then(() => { diff --git a/src/tests/backend/specs/ImportEtherpad.ts b/src/tests/backend/specs/ImportEtherpad.ts index b0a208b4ac6..b9ac9baaf96 100644 --- a/src/tests/backend/specs/ImportEtherpad.ts +++ b/src/tests/backend/specs/ImportEtherpad.ts @@ -8,7 +8,7 @@ const db = require('../../../node/db/DB'); const importEtherpad = require('../../../node/utils/ImportEtherpad'); const padManager = require('../../../node/db/PadManager'); const plugins = require('../../../static/js/pluginfw/plugin_defs'); -const {randomString} = require('../../../static/js/pad_utils'); +import {randomString} from '../../../static/js/pad_utils'; describe(__filename, function () { let padId: string; diff --git a/src/tests/backend/specs/api/api.ts b/src/tests/backend/specs/api/api.ts index 32681e5c981..bab70b47ca6 100644 --- a/src/tests/backend/specs/api/api.ts +++ b/src/tests/backend/specs/api/api.ts @@ -12,7 +12,6 @@ const common = require('../../common'); const validateOpenAPI = require('openapi-schema-validation').validate; let agent: any; -const apiKey = common.apiKey; let apiVersion = 1; const makeid = () => { @@ -27,7 +26,7 @@ const makeid = () => { const testPadId = makeid(); -const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point:string) => `/api/${apiVersion}/${point}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); diff --git a/src/tests/backend/specs/api/characterEncoding.ts b/src/tests/backend/specs/api/characterEncoding.ts index 7c2202a094c..80093437b49 100644 --- a/src/tests/backend/specs/api/characterEncoding.ts +++ b/src/tests/backend/specs/api/characterEncoding.ts @@ -6,17 +6,18 @@ * TODO: maybe unify those two files and merge in a single one. */ +import {generateJWTToken, generateJWTTokenUser} from "../../common"; + const assert = require('assert').strict; const common = require('../../common'); const fs = require('fs'); const fsp = fs.promises; let agent:any; -const apiKey = common.apiKey; let apiVersion = 1; const testPadId = makeid(); -const endPoint = (point:string, version?:number) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point:string, version?:number) => `/api/${version || apiVersion}/${point}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); @@ -24,28 +25,38 @@ describe(__filename, function () { describe('Sanity checks', function () { it('can connect', async function () { await agent.get('/api/') + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/); }); it('finds the version tag', async function () { const res = await agent.get('/api/') + .set("Authorization", await generateJWTToken()) .expect(200); apiVersion = res.body.currentVersion; assert(apiVersion); }); - it('errors with invalid APIKey', async function () { + it('errors with invalid OAuth token', async function () { + // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 + await agent.get(`/api/${apiVersion}/createPad?padID=test`) + .set("Authorization", (await generateJWTToken()).substring(0,10)) + .expect(401); + }); + + it('errors with unprivileged OAuth token', async function () { // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 - // If your APIKey is password you deserve to fail all tests anyway - await agent.get(`/api/${apiVersion}/createPad?apikey=password&padID=test`) + await agent.get(`/api/${apiVersion}/createPad?padID=test`) + .set("Authorization", (await generateJWTTokenUser()).substring(0,10)) .expect(401); }); }); describe('Tests', function () { it('creates a new Pad', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -53,6 +64,7 @@ describe(__filename, function () { it('Sets the HTML of a Pad attempting to weird utf8 encoded content', async function () { const res = await agent.post(endPoint('setHTML')) + .set("Authorization", await generateJWTToken()) .send({ padID: testPadId, html: await fsp.readFile('tests/backend/specs/api/emojis.html', 'utf8'), @@ -63,7 +75,8 @@ describe(__filename, function () { }); it('get the HTML of Pad with emojis', async function () { - const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.match(res.body.data.html, /🇼/); diff --git a/src/tests/backend/specs/api/chat.ts b/src/tests/backend/specs/api/chat.ts index 6debe146214..d2c0ba8a83b 100644 --- a/src/tests/backend/specs/api/chat.ts +++ b/src/tests/backend/specs/api/chat.ts @@ -1,18 +1,18 @@ 'use strict'; +import {generateJWTToken} from "../../common"; + const common = require('../../common'); -const assert = require('assert').strict; import {strict as assert} from "assert"; let agent:any; -const apiKey = common.apiKey; let apiVersion = 1; let authorID = ''; const padID = makeid(); const timestamp = Date.now(); -const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point:string) => `/api/${apiVersion}/${point}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); @@ -43,16 +43,18 @@ describe(__filename, function () { describe('Chat functionality', function () { it('creates a new Pad', async function () { - await agent.get(`${endPoint('createPad')}&padID=${padID}`) + await agent.get(`${endPoint('createPad')}?padID=${padID}`) + .set("authorization", await generateJWTToken()) + .expect(200) .expect((res:any) => { if (res.body.code !== 0) throw new Error('Unable to create new Pad'); }) - .expect('Content-Type', /json/) - .expect(200); + .expect('Content-Type', /json/); }); it('Creates an author with a name set', async function () { await agent.get(endPoint('createAuthor')) + .set("authorization", await generateJWTToken()) .expect((res:any) => { if (res.body.code !== 0 || !res.body.data.authorID) { throw new Error('Unable to create author'); @@ -64,7 +66,8 @@ describe(__filename, function () { }); it('Gets the head of chat before the first chat msg', async function () { - await agent.get(`${endPoint('getChatHead')}&padID=${padID}`) + await agent.get(`${endPoint('getChatHead')}?padID=${padID}`) + .set("authorization", await generateJWTToken()) .expect((res:any) => { if (res.body.data.chatHead !== -1) throw new Error('Chat Head Length is wrong'); if (res.body.code !== 0) throw new Error('Unable to get chat head'); @@ -74,8 +77,9 @@ describe(__filename, function () { }); it('Adds a chat message to the pad', async function () { - await agent.get(`${endPoint('appendChatMessage')}&padID=${padID}&text=blalblalbha` + + await agent.get(`${endPoint('appendChatMessage')}?padID=${padID}&text=blalblalbha` + `&authorID=${authorID}&time=${timestamp}`) + .set("authorization", await generateJWTToken()) .expect((res:any) => { if (res.body.code !== 0) throw new Error('Unable to create chat message'); }) @@ -84,7 +88,8 @@ describe(__filename, function () { }); it('Gets the head of chat', async function () { - await agent.get(`${endPoint('getChatHead')}&padID=${padID}`) + await agent.get(`${endPoint('getChatHead')}?padID=${padID}`) + .set("authorization", await generateJWTToken()) .expect((res:any) => { if (res.body.data.chatHead !== 0) throw new Error('Chat Head Length is wrong'); @@ -95,7 +100,8 @@ describe(__filename, function () { }); it('Gets Chat History of a Pad', async function () { - await agent.get(`${endPoint('getChatHistory')}&padID=${padID}`) + await agent.get(`${endPoint('getChatHistory')}?padID=${padID}`) + .set("authorization", await generateJWTToken()) .expect('Content-Type', /json/) .expect(200) .expect((res:any) => { diff --git a/src/tests/backend/specs/api/fuzzImportTest.ts b/src/tests/backend/specs/api/fuzzImportTest.ts index 85f4c81f20e..3caa185da34 100644 --- a/src/tests/backend/specs/api/fuzzImportTest.ts +++ b/src/tests/backend/specs/api/fuzzImportTest.ts @@ -9,7 +9,6 @@ const settings = require('../../../container/loadSettings.js').loadSettings(); const host = "http://" + settings.ip + ":" + settings.port; -const apiKey = common.apiKey; var apiVersion = 1; var testPadId = "TEST_fuzz" + makeid(); diff --git a/src/tests/backend/specs/api/importexport.ts b/src/tests/backend/specs/api/importexport.ts index a1ef64d87e8..5b93ea346d0 100644 --- a/src/tests/backend/specs/api/importexport.ts +++ b/src/tests/backend/specs/api/importexport.ts @@ -11,10 +11,9 @@ import {MapArrayType} from "../../../../node/types/MapType"; const common = require('../../common'); let agent:any; -const apiKey = common.apiKey; const apiVersion = 1; -const endPoint = (point: string, version?:string) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point: string, version?:string) => `/api/${version || apiVersion}/${point}`; const testImports:MapArrayType<any> = { 'malformed': { @@ -243,29 +242,33 @@ describe(__filename, function () { } it('createPad', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('setHTML', async function () { - const res = await agent.get(`${endPoint('setHTML')}&padID=${testPadId}` + + const res = await agent.get(`${endPoint('setHTML')}?padID=${testPadId}` + `&html=${encodeURIComponent(test.input)}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('getHTML', async function () { - const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.html, test.wantHTML); }); it('getText', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, test.wantText); diff --git a/src/tests/backend/specs/api/importexportGetPost.ts b/src/tests/backend/specs/api/importexportGetPost.ts index 40bfb55522d..355699bc2d3 100644 --- a/src/tests/backend/specs/api/importexportGetPost.ts +++ b/src/tests/backend/specs/api/importexportGetPost.ts @@ -5,6 +5,8 @@ */ import {MapArrayType} from "../../../../node/types/MapType"; +import {SuperTestStatic} from "supertest"; +import TestAgent from "supertest/lib/agent"; const assert = require('assert').strict; const common = require('../../common'); @@ -21,8 +23,7 @@ const wordXDoc = fs.readFileSync(`${__dirname}/test.docx`); const odtDoc = fs.readFileSync(`${__dirname}/test.odt`); const pdfDoc = fs.readFileSync(`${__dirname}/test.pdf`); -let agent:any; -const apiKey = common.apiKey; +let agent: TestAgent; const apiVersion = 1; const testPadId = makeid(); const testPadIdEnc = encodeURIComponent(testPadId); @@ -41,6 +42,7 @@ describe(__filename, function () { describe('Connectivity', function () { it('can connect', async function () { await agent.get('/api/') + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/); }); @@ -49,6 +51,7 @@ describe(__filename, function () { describe('API Versioning', function () { it('finds the version tag', async function () { await agent.get('/api/') + .set("authorization", await common.generateJWTToken()) .expect(200) .expect((res:any) => assert(res.body.currentVersion)); }); @@ -103,14 +106,17 @@ describe(__filename, function () { }); it('creates a new Pad, imports content to it, checks that content', async function () { - await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => assert.equal(res.body.code, 0)); await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); - await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect((res:any) => assert.equal(res.body.data.text, padText.toString())); }); @@ -122,9 +128,11 @@ describe(__filename, function () { beforeEach(async function () { if (readOnlyId != null) return; await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); - const res = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getReadOnlyID')}?padID=${testPadId}`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => assert.equal(res.body.code, 0)); @@ -145,7 +153,8 @@ describe(__filename, function () { // This ought to be before(), but it must run after the top-level beforeEach() above. beforeEach(async function () { if (text != null) return; - let req = agent.get(`/p/${readOnlyId}/export/${exportType}`); + let req = agent.get(`/p/${readOnlyId}/export/${exportType}`) + .set("authorization", await common.generateJWTToken()); if (authn) req = req.auth('user', 'user-password'); const res = await req .expect(200) @@ -163,6 +172,7 @@ describe(__filename, function () { it('re-import to read-only pad ID gives 403 forbidden', async function () { let req = agent.post(`/p/${readOnlyId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', Buffer.from(text), { filename: `/test.${exportType}`, contentType: 'text/plain', @@ -175,6 +185,7 @@ describe(__filename, function () { // The new pad ID must differ from testPadId because Etherpad refuses to import // .etherpad files on top of a pad that already has edits. let req = agent.post(`/p/${testPadId}_import/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', Buffer.from(text), { filename: `/test.${exportType}`, contentType: 'text/plain', @@ -200,6 +211,7 @@ describe(__filename, function () { // TODO: fix support for .doc files.. it('Tries to import .doc that uses soffice or abiword', async function () { await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'}) .expect(200) .expect('Content-Type', /json/) @@ -212,6 +224,7 @@ describe(__filename, function () { it('exports DOC', async function () { await agent.get(`/p/${testPadId}/export/doc`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) .expect((res:any) => assert(res.body.length >= 9000)); @@ -219,6 +232,7 @@ describe(__filename, function () { it('Tries to import .docx that uses soffice or abiword', async function () { await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', wordXDoc, { filename: '/test.docx', contentType: @@ -235,6 +249,7 @@ describe(__filename, function () { it('exports DOC from imported DOCX', async function () { await agent.get(`/p/${testPadId}/export/doc`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) .expect((res:any) => assert(res.body.length >= 9100)); @@ -242,6 +257,7 @@ describe(__filename, function () { it('Tries to import .pdf that uses soffice or abiword', async function () { await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', pdfDoc, {filename: '/test.pdf', contentType: 'application/pdf'}) .expect(200) .expect('Content-Type', /json/) @@ -254,6 +270,7 @@ describe(__filename, function () { it('exports PDF', async function () { await agent.get(`/p/${testPadId}/export/pdf`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) .expect((res:any) => assert(res.body.length >= 1000)); @@ -261,6 +278,7 @@ describe(__filename, function () { it('Tries to import .odt that uses soffice or abiword', async function () { await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', odtDoc, {filename: '/test.odt', contentType: 'application/odt'}) .expect(200) .expect('Content-Type', /json/) @@ -273,6 +291,7 @@ describe(__filename, function () { it('exports ODT', async function () { await agent.get(`/p/${testPadId}/export/odt`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) .expect((res:any) => assert(res.body.length >= 7000)); @@ -282,6 +301,7 @@ describe(__filename, function () { it('Tries to import .etherpad', async function () { this.timeout(3000); await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', etherpadDoc, { filename: '/test.etherpad', contentType: 'application/etherpad', @@ -298,6 +318,7 @@ describe(__filename, function () { it('exports Etherpad', async function () { this.timeout(3000); await agent.get(`/p/${testPadId}/export/etherpad`) + .set("authorization", await common.generateJWTToken()) .buffer(true).parse(superagent.parse.text) .expect(200) .expect(/hello/); @@ -306,6 +327,7 @@ describe(__filename, function () { it('exports HTML for this Etherpad file', async function () { this.timeout(3000); await agent.get(`/p/${testPadId}/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .expect('content-type', 'text/html; charset=utf-8') .expect(/<ul class="bullet"><li><ul class="bullet"><li>hello<\/ul><\/li><\/ul>/); @@ -315,6 +337,7 @@ describe(__filename, function () { this.timeout(3000); settings.allowUnknownFileEnds = false; await agent.post(`/p/${testPadId}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.xasdasdxx', contentType: 'weirdness/jobby'}) .expect(400) .expect('Content-Type', /json/) @@ -380,6 +403,8 @@ describe(__filename, function () { // that a buggy makeGoodExport() doesn't cause checks to accidentally pass. const records = makeGoodExport(); await deleteTestPad(); + const importedPads = await importEtherpad(records) + console.log(importedPads) await importEtherpad(records) .expect(200) .expect('Content-Type', /json/) @@ -389,6 +414,7 @@ describe(__filename, function () { data: {directDatabaseAccess: true}, })); await agent.get(`/p/${testPadId}/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /foo/)); @@ -397,19 +423,19 @@ describe(__filename, function () { it('missing rev', async function () { const records:MapArrayType<any> = makeGoodExport(); delete records['pad:testing:revs:0']; - await importEtherpad(records).expect(500); + importEtherpad(records).expect(500); }); it('bad changeset', async function () { const records = makeGoodExport(); records['pad:testing:revs:0'].changeset = 'garbage'; - await importEtherpad(records).expect(500); + importEtherpad(records).expect(500); }); it('missing attrib in pool', async function () { const records = makeGoodExport(); records['pad:testing'].pool.nextNum++; - await importEtherpad(records).expect(500); + (importEtherpad(records)).expect(500); }); it('extra attrib in pool', async function () { @@ -417,7 +443,7 @@ describe(__filename, function () { const pool = records['pad:testing'].pool; // @ts-ignore pool.numToAttrib[pool.nextNum] = ['key', 'value']; - await importEtherpad(records).expect(500); + (importEtherpad(records)).expect(500); }); it('changeset refers to non-existent attrib', async function () { @@ -434,19 +460,19 @@ describe(__filename, function () { text: 'asdffoo\n', attribs: '*1+4|1+4', }; - await importEtherpad(records).expect(500); + (importEtherpad(records)).expect(500); }); it('pad atext does not match', async function () { const records = makeGoodExport(); records['pad:testing'].atext.attribs = `*0${records['pad:testing'].atext.attribs}`; - await importEtherpad(records).expect(500); + (importEtherpad(records)).expect(500); }); it('missing chat message', async function () { const records:MapArrayType<any> = makeGoodExport(); delete records['pad:testing:chat:0']; - await importEtherpad(records).expect(500); + importEtherpad(records).expect(500); }); }); @@ -523,7 +549,7 @@ describe(__filename, function () { }, }); - const importEtherpad = (records:MapArrayType<any>) => agent.post(`/p/${testPadId}/import`) + const importEtherpad = (records: MapArrayType<any>) => agent.post(`/p/${testPadId}/import`) .attach('file', Buffer.from(JSON.stringify(records), 'utf8'), { filename: '/test.etherpad', contentType: 'application/etherpad', @@ -543,6 +569,7 @@ describe(__filename, function () { data: {directDatabaseAccess: true}, })); await agent.get(`/p/${testPadId}/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'oofoo\n')); @@ -550,6 +577,7 @@ describe(__filename, function () { it('txt request rev 1', async function () { await agent.get(`/p/${testPadId}/1/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'ofoo\n')); @@ -557,6 +585,7 @@ describe(__filename, function () { it('txt request rev 2', async function () { await agent.get(`/p/${testPadId}/2/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'oofoo\n')); @@ -564,6 +593,7 @@ describe(__filename, function () { it('txt request rev 1test returns rev 1', async function () { await agent.get(`/p/${testPadId}/1test/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'ofoo\n')); @@ -571,6 +601,7 @@ describe(__filename, function () { it('txt request rev test1 is 403', async function () { await agent.get(`/p/${testPadId}/test1/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(500) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /rev is not a number/)); @@ -578,6 +609,7 @@ describe(__filename, function () { it('txt request rev 5 returns head rev', async function () { await agent.get(`/p/${testPadId}/5/export/txt`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.equal(res.text, 'oofoo\n')); @@ -585,6 +617,7 @@ describe(__filename, function () { it('html request rev 1', async function () { await agent.get(`/p/${testPadId}/1/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /ofoo<br>/)); @@ -592,6 +625,7 @@ describe(__filename, function () { it('html request rev 2', async function () { await agent.get(`/p/${testPadId}/2/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /oofoo<br>/)); @@ -599,6 +633,7 @@ describe(__filename, function () { it('html request rev 1test returns rev 1', async function () { await agent.get(`/p/${testPadId}/1test/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /ofoo<br>/)); @@ -606,6 +641,7 @@ describe(__filename, function () { it('html request rev test1 results in 500 response', async function () { await agent.get(`/p/${testPadId}/test1/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(500) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /rev is not a number/)); @@ -613,6 +649,7 @@ describe(__filename, function () { it('html request rev 5 returns head rev', async function () { await agent.get(`/p/${testPadId}/5/export/html`) + .set("authorization", await common.generateJWTToken()) .expect(200) .buffer(true).parse(superagent.parse.text) .expect((res:any) => assert.match(res.text, /oofoo<br>/)); @@ -643,6 +680,7 @@ describe(__filename, function () { it('!authn !exist -> create', async function () { await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); assert(await padManager.doesPadExist(testPadId)); @@ -653,6 +691,7 @@ describe(__filename, function () { it('!authn exist -> replace', async function () { const pad = await createTestPad('before import'); await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); assert(await padManager.doesPadExist(testPadId)); @@ -662,6 +701,7 @@ describe(__filename, function () { it('authn anonymous !exist -> fail', async function () { settings.requireAuthentication = true; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(401); assert(!(await padManager.doesPadExist(testPadId))); @@ -671,6 +711,7 @@ describe(__filename, function () { settings.requireAuthentication = true; const pad = await createTestPad('before import\n'); await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(401); assert.equal(pad.text(), 'before import\n'); @@ -679,6 +720,7 @@ describe(__filename, function () { it('authn user create !exist -> create', async function () { settings.requireAuthentication = true; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); @@ -691,6 +733,7 @@ describe(__filename, function () { settings.requireAuthentication = true; authorize = () => 'modify'; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(403); @@ -701,6 +744,7 @@ describe(__filename, function () { settings.requireAuthentication = true; authorize = () => 'readOnly'; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(403); @@ -711,6 +755,7 @@ describe(__filename, function () { settings.requireAuthentication = true; const pad = await createTestPad('before import\n'); await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); @@ -722,6 +767,7 @@ describe(__filename, function () { authorize = () => 'modify'; const pad = await createTestPad('before import\n'); await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); @@ -733,6 +779,7 @@ describe(__filename, function () { settings.requireAuthentication = true; authorize = () => 'readOnly'; await agent.post(`/p/${testPadIdEnc}/import`) + .set("authorization", await common.generateJWTToken()) .auth('user', 'user-password') .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(403); @@ -744,7 +791,7 @@ describe(__filename, function () { const endPoint = (point: string, version?:string) => { - return `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; + return `/api/${version || apiVersion}/${point}`; }; function makeid() { diff --git a/src/tests/backend/specs/api/instance.ts b/src/tests/backend/specs/api/instance.ts index fc348e5aff8..2bf51bf86aa 100644 --- a/src/tests/backend/specs/api/instance.ts +++ b/src/tests/backend/specs/api/instance.ts @@ -8,10 +8,9 @@ const common = require('../../common'); let agent:any; -const apiKey = common.apiKey; const apiVersion = '1.2.14'; -const endPoint = (point: string, version?: number) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point: string, version?: number) => `/api/${version || apiVersion}/${point}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); @@ -27,6 +26,7 @@ describe(__filename, function () { describe('getStats', function () { it('Gets the stats of a running instance', async function () { await agent.get(endPoint('getStats')) + .set("Authorization", await common.generateJWTToken()) .expect((res:any) => { if (res.body.code !== 0) throw new Error('getStats() failed'); diff --git a/src/tests/backend/specs/api/pad.ts b/src/tests/backend/specs/api/pad.ts index 180494bb2cb..f4d081ef4a7 100644 --- a/src/tests/backend/specs/api/pad.ts +++ b/src/tests/backend/specs/api/pad.ts @@ -12,7 +12,6 @@ const common = require('../../common'); const padManager = require('../../../../node/db/PadManager'); let agent:any; -const apiKey = common.apiKey; let apiVersion = 1; const testPadId = makeid(); const newPadId = makeid(); @@ -21,7 +20,7 @@ const anotherPadId = makeid(); let lastEdited = ''; const text = generateLongText(); -const endPoint = (point: string, version?: string) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point: string, version?: string) => `/api/${version || apiVersion}/${point}`; /* * Html document with nested lists of different types, to test its import and @@ -60,10 +59,10 @@ describe(__filename, function () { }); describe('Sanity checks', function () { - it('errors with invalid APIKey', async function () { + it('errors with invalid oauth token', async function () { // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 - // If your APIKey is password you deserve to fail all tests anyway - await agent.get(`/api/${apiVersion}/createPad?apikey=password&padID=test`) + await agent.get(`/api/${apiVersion}/createPad?padID=test`) + .set("Authorization", (await common.generateJWTToken()).substring(0, 10)) .expect(401); }); }); @@ -113,20 +112,23 @@ describe(__filename, function () { describe('Tests', function () { it('deletes a Pad that does not exist', async function () { - await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`) + await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) // @TODO: we shouldn't expect 200 here since the pad may not exist .expect('Content-Type', /json/); }); it('creates a new Pad', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('gets revision count of Pad', async function () { - const res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -134,7 +136,8 @@ describe(__filename, function () { }); it('gets saved revisions count of Pad', async function () { - const res = await agent.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getSavedRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -142,7 +145,8 @@ describe(__filename, function () { }); it('gets saved revision list of Pad', async function () { - const res = await agent.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('listSavedRevisions')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -150,7 +154,8 @@ describe(__filename, function () { }); it('get the HTML of Pad', async function () { - const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.html.length > 1); @@ -158,13 +163,15 @@ describe(__filename, function () { it('list all pads', async function () { const res = await agent.get(endPoint('listAllPads')) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.padIDs.includes(testPadId)); }); it('deletes the Pad', async function () { - const res = await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -172,27 +179,31 @@ describe(__filename, function () { it('list all pads again', async function () { const res = await agent.get(endPoint('listAllPads')) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(!res.body.data.padIDs.includes(testPadId)); }); it('get the HTML of a Pad -- Should return a failure', async function () { - const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 1); }); it('creates a new Pad with text', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}&text=testText`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}&text=testText`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('gets the Pad text and expect it to be testText with trailing \\n', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, 'testText\n'); @@ -200,6 +211,7 @@ describe(__filename, function () { it('set text', async function () { const res = await agent.post(endPoint('setText')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: testPadId, text: 'testTextTwo', @@ -210,28 +222,32 @@ describe(__filename, function () { }); it('gets the Pad text', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, 'testTextTwo\n'); }); it('gets Revision Count of a Pad', async function () { - const res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.revisions, 1); }); it('saves Revision', async function () { - const res = await agent.get(`${endPoint('saveRevision')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('saveRevision')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('gets saved revisions count of Pad again', async function () { - const res = await agent.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getSavedRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -239,7 +255,8 @@ describe(__filename, function () { }); it('gets saved revision list of Pad again', async function () { - const res = await agent.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('listSavedRevisions')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -247,28 +264,32 @@ describe(__filename, function () { }); it('gets User Count of a Pad', async function () { - const res = await agent.get(`${endPoint('padUsersCount')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('padUsersCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.padUsersCount, 0); }); it('Gets the Read Only ID of a Pad', async function () { - const res = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getReadOnlyID')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.readOnlyID); }); it('Get Authors of the Pad', async function () { - const res = await agent.get(`${endPoint('listAuthorsOfPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('listAuthorsOfPad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.authorIDs.length, 0); }); it('Get When Pad was left Edited', async function () { - const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.lastEdited); @@ -277,6 +298,7 @@ describe(__filename, function () { it('set text again', async function () { const res = await agent.post(endPoint('setText')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: testPadId, text: 'testTextThree', @@ -287,35 +309,40 @@ describe(__filename, function () { }); it('Get When Pad was left Edited again', async function () { - const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert(res.body.data.lastEdited > lastEdited); }); it('gets User Count of a Pad again', async function () { - const res = await agent.get(`${endPoint('padUsers')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('padUsers')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.padUsers.length, 0); }); it('deletes a Pad', async function () { - const res = await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('creates the Pad again', async function () { - const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('Sets text on a pad Id', async function () { - const res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`) + const res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .field({text}) .expect(200) .expect('Content-Type', /json/); @@ -323,7 +350,8 @@ describe(__filename, function () { }); it('Gets text on a pad Id', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -331,7 +359,8 @@ describe(__filename, function () { }); it('Sets text on a pad Id including an explicit newline', async function () { - const res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`) + const res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .field({text: `${text}\n`}) .expect(200) .expect('Content-Type', /json/); @@ -339,7 +368,8 @@ describe(__filename, function () { }); it("Gets text on a pad Id and doesn't have an excess newline", async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -347,7 +377,8 @@ describe(__filename, function () { }); it('Gets when pad was last edited', async function () { - const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.notEqual(res.body.lastEdited, 0); @@ -355,14 +386,16 @@ describe(__filename, function () { it('Move a Pad to a different Pad ID', async function () { const res = await agent.get( - `${endPoint('movePad')}&sourceID=${testPadId}&destinationID=${newPadId}&force=true`) + `${endPoint('movePad')}?sourceID=${testPadId}&destinationID=${newPadId}&force=true`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('Gets text from new pad', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${newPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${newPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, `${text}\n`); @@ -370,21 +403,24 @@ describe(__filename, function () { it('Move pad back to original ID', async function () { const res = await agent.get( - `${endPoint('movePad')}&sourceID=${newPadId}&destinationID=${testPadId}&force=false`) + `${endPoint('movePad')}?sourceID=${newPadId}&destinationID=${testPadId}&force=false`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('Get text using original ID', async function () { - const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, `${text}\n`); }); it('Get last edit of original ID', async function () { - const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) + const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.notEqual(res.body.lastEdited, 0); @@ -392,11 +428,13 @@ describe(__filename, function () { it('Append text to a pad Id', async function () { let res = await agent.get( - `${endPoint('appendText', '1.2.13')}&padID=${testPadId}&text=hello`) + `${endPoint('appendText', '1.2.13')}?padID=${testPadId}&text=hello`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -404,7 +442,8 @@ describe(__filename, function () { }); it('getText of old revision', async function () { - let res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + let res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -412,7 +451,8 @@ describe(__filename, function () { assert(rev != null); assert(Number.isInteger(rev)); assert(rev > 0); - res = await agent.get(`${endPoint('getText')}&padID=${testPadId}&rev=${rev - 1}`) + res = await agent.get(`${endPoint('getText')}?padID=${testPadId}&rev=${rev - 1}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -422,6 +462,7 @@ describe(__filename, function () { it('Sets the HTML of a Pad attempting to pass ugly HTML', async function () { const html = '<div><b>Hello HTML</title></head></div>'; const res = await agent.post(endPoint('setHTML')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: testPadId, html, @@ -433,6 +474,7 @@ describe(__filename, function () { it('Pad with complex nested lists of different types', async function () { let res = await agent.post(endPoint('setHTML')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: testPadId, html: ulHtml, @@ -440,7 +482,8 @@ describe(__filename, function () { .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); const receivedHtml = res.body.data.html.replace('<br></body>', '</body>').toLowerCase(); @@ -448,11 +491,13 @@ describe(__filename, function () { }); it('Pad with white space between list items', async function () { - let res = await agent.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${ulSpaceHtml}`) + let res = await agent.get(`${endPoint('setHTML')}?padID=${testPadId}&html=${ulSpaceHtml}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); const receivedHtml = res.body.data.html.replace('<br></body>', '</body>').toLowerCase(); @@ -461,7 +506,8 @@ describe(__filename, function () { it('errors if pad can be created', async function () { await Promise.all(['/', '%23', '%3F', '%26'].map(async (badUrlChar) => { - const res = await agent.get(`${endPoint('createPad')}&padID=${badUrlChar}`) + const res = await agent.get(`${endPoint('createPad')}?padID=${badUrlChar}`) + .set("Authorization", (await common.generateJWTToken())) .expect('Content-Type', /json/); assert.equal(res.body.code, 1); })); @@ -469,49 +515,57 @@ describe(__filename, function () { it('copies the content of a existent pad', async function () { const res = await agent.get( - `${endPoint('copyPad')}&sourceID=${testPadId}&destinationID=${copiedPadId}&force=true`) + `${endPoint('copyPad')}?sourceID=${testPadId}&destinationID=${copiedPadId}&force=true`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); }); it('does not add an useless revision', async function () { - let res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`) + let res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .field({text: 'identical text\n'}) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.text, 'identical text\n'); - res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); const revCount = res.body.data.revisions; - res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`) + res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .field({text: 'identical text\n'}) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) + res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.data.revisions, revCount); }); it('creates a new Pad with empty text', async function () { - await agent.get(`${endPoint('createPad')}&padID=${anotherPadId}&text=`) + await agent.get(`${endPoint('createPad')}?padID=${anotherPadId}&text=`) + .set("Authorization", (await common.generateJWTToken())) .expect('Content-Type', /json/) .expect(200) .expect((res:any) => { assert.equal(res.body.code, 0, 'Unable to create new Pad'); }); - await agent.get(`${endPoint('getText')}&padID=${anotherPadId}`) + await agent.get(`${endPoint('getText')}?padID=${anotherPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect('Content-Type', /json/) .expect(200) .expect((res:any) => { @@ -521,7 +575,8 @@ describe(__filename, function () { }); it('deletes with empty text', async function () { - await agent.get(`${endPoint('deletePad')}&padID=${anotherPadId}`) + await agent.get(`${endPoint('deletePad')}?padID=${anotherPadId}`) + .set("Authorization", (await common.generateJWTToken())) .expect('Content-Type', /json/) .expect(200) .expect((res: any) => { @@ -543,8 +598,9 @@ describe(__filename, function () { }); it('returns a successful response', async function () { - const res = await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` + + const res = await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` + `&destinationID=${newPad}&force=false`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -552,10 +608,12 @@ describe(__filename, function () { // this test validates if the source pad's text and attributes are kept it('creates a new pad with the same content as the source pad', async function () { - let res = await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` + - `&destinationID=${newPad}&force=false`); + let res = await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` + + `&destinationID=${newPad}&force=false`) + .set("Authorization", (await common.generateJWTToken())); assert.equal(res.body.code, 0); - res = await agent.get(`${endPoint('getHTML')}&padID=${newPad}`) + res = await agent.get(`${endPoint('getHTML')}?padID=${newPad}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200); const receivedHtml = res.body.data.html.replace('<br><br></body>', '</body>').toLowerCase(); assert.equal(receivedHtml, expectedHtml); @@ -564,8 +622,9 @@ describe(__filename, function () { it('copying to a non-existent group throws an error', async function () { const padWithNonExistentGroup = `notExistentGroup$${newPad}`; const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` + - `&sourceID=${sourcePadId}` + + `?sourceID=${sourcePadId}` + `&destinationID=${padWithNonExistentGroup}&force=true`) + .set("Authorization", (await common.generateJWTToken())) .expect(200); assert.equal(res.body.code, 1); }); @@ -577,16 +636,18 @@ describe(__filename, function () { it('force=false fails', async function () { const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` + - `&sourceID=${sourcePadId}` + + `?sourceID=${sourcePadId}` + `&destinationID=${newPad}&force=false`) + .set("Authorization", (await common.generateJWTToken())) .expect(200); assert.equal(res.body.code, 1); }); it('force=true succeeds', async function () { const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` + - `&sourceID=${sourcePadId}` + + `?sourceID=${sourcePadId}` + `&destinationID=${newPad}&force=true`) + .set("Authorization", (await common.generateJWTToken())) .expect(200); assert.equal(res.body.code, 0); }); @@ -613,7 +674,8 @@ describe(__filename, function () { // state between the two attribute pools caused corruption. const getHtml = async (padId:string) => { - const res = await agent.get(`${endPoint('getHTML')}&padID=${padId}`) + const res = await agent.get(`${endPoint('getHTML')}?padID=${padId}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); @@ -622,6 +684,7 @@ describe(__filename, function () { const setBody = async (padId: string, bodyHtml: string) => { await agent.post(endPoint('setHTML')) + .set("Authorization", (await common.generateJWTToken())) .send({padID: padId, html: `<!DOCTYPE HTML><html><body>${bodyHtml}</body></html>`}) .expect(200) .expect('Content-Type', /json/) @@ -631,8 +694,9 @@ describe(__filename, function () { const origHtml = await getHtml(sourcePadId); assert.doesNotMatch(origHtml, /<strong>/); assert.doesNotMatch(origHtml, /<em>/); - await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` + + await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` + `&destinationID=${newPad}&force=false`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => assert.equal(res.body.code, 0)); @@ -672,8 +736,10 @@ describe(__filename, function () { */ const createNewPadWithHtml = async (padId: string, html: string) => { - await agent.get(`${endPoint('createPad')}&padID=${padId}`); + await agent.get(`${endPoint('createPad')}?padID=${padId}`) + .set("Authorization", (await common.generateJWTToken())); await agent.post(endPoint('setHTML')) + .set("Authorization", (await common.generateJWTToken())) .send({ padID: padId, html, diff --git a/src/tests/backend/specs/api/restoreRevision.ts b/src/tests/backend/specs/api/restoreRevision.ts index 28a5090122c..e30c8aa251b 100644 --- a/src/tests/backend/specs/api/restoreRevision.ts +++ b/src/tests/backend/specs/api/restoreRevision.ts @@ -14,13 +14,14 @@ describe(__filename, function () { let pad: PadType; const restoreRevision = async (v:string, padId: string, rev: number, authorId:string|null = null) => { + // @ts-ignore const p = new URLSearchParams(Object.entries({ - apikey: common.apiKey, padID: padId, rev, ...(authorId == null ? {} : {authorId}), })); const res = await agent.get(`/api/${v}/restoreRevision?${p}`) + .set("Authorization", (await common.generateJWTToken())) .expect(200) .expect('Content-Type', /json/); assert.equal(res.body.code, 0); diff --git a/src/tests/backend/specs/api/sessionsAndGroups.ts b/src/tests/backend/specs/api/sessionsAndGroups.ts index 1c3196214b5..16e62db4670 100644 --- a/src/tests/backend/specs/api/sessionsAndGroups.ts +++ b/src/tests/backend/specs/api/sessionsAndGroups.ts @@ -1,25 +1,33 @@ 'use strict'; +import {agent, generateJWTToken, init, logger} from "../../common"; + +import TestAgent from "supertest/lib/agent"; +import supertest from "supertest"; const assert = require('assert').strict; -const common = require('../../common'); const db = require('../../../../node/db/DB'); -let agent:any; -const apiKey = common.apiKey; let apiVersion = 1; let groupID = ''; let authorID = ''; let sessionID = ''; let padID = makeid(); -const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; +const endPoint = (point:string) => { + return `/api/${apiVersion}/${point}`; +} + +let preparedAgent: TestAgent<supertest.Test> describe(__filename, function () { - before(async function () { agent = await common.init(); }); + before(async function () { + preparedAgent = await init(); + }); describe('API Versioning', function () { it('errors if can not connect', async function () { - await agent.get('/api/') + await agent!.get('/api/') + .set('Accept', 'application/json') .expect(200) .expect((res:any) => { assert(res.body.currentVersion); @@ -60,7 +68,8 @@ describe(__filename, function () { describe('API: Group creation and deletion', function () { it('createGroup', async function () { - await agent.get(endPoint('createGroup')) + await agent!.get(endPoint('createGroup')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -71,7 +80,8 @@ describe(__filename, function () { }); it('listSessionsOfGroup for empty group', async function () { - await agent.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`) + await agent!.get(`${endPoint('listSessionsOfGroup')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -81,7 +91,9 @@ describe(__filename, function () { }); it('deleteGroup', async function () { - await agent.get(`${endPoint('deleteGroup')}&groupID=${groupID}`) + await agent! + .get(`${endPoint('deleteGroup')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -92,7 +104,8 @@ describe(__filename, function () { it('createGroupIfNotExistsFor', async function () { const mapper = makeid(); let groupId: string; - await agent.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=${mapper}`) + await preparedAgent.get(`${endPoint('createGroupIfNotExistsFor')}?groupMapper=${mapper}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -101,7 +114,8 @@ describe(__filename, function () { assert(groupId); }); // Passing the same mapper should return the same group ID. - await agent.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=${mapper}`) + await preparedAgent.get(`${endPoint('createGroupIfNotExistsFor')}?groupMapper=${mapper}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -110,7 +124,8 @@ describe(__filename, function () { }); // Deleting the group should clean up the mapping. assert.equal(await db.get(`mapper2group:${mapper}`), groupId!); - await agent.get(`${endPoint('deleteGroup')}&groupID=${groupId!}`) + await preparedAgent.get(`${endPoint('deleteGroup')}?groupID=${groupId!}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -122,7 +137,8 @@ describe(__filename, function () { // Test coverage for https://github.com/ether/etherpad-lite/issues/4227 // Creates a group, creates 2 sessions, 2 pads and then deletes the group. it('createGroup', async function () { - await agent.get(endPoint('createGroup')) + await preparedAgent.get(endPoint('createGroup')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -133,7 +149,8 @@ describe(__filename, function () { }); it('createAuthor', async function () { - await agent.get(endPoint('createAuthor')) + await preparedAgent.get(endPoint('createAuthor')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -144,8 +161,9 @@ describe(__filename, function () { }); it('createSession', async function () { - await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` + + await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` + '&validUntil=999999999999') + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -156,8 +174,9 @@ describe(__filename, function () { }); it('createSession', async function () { - await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` + + await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` + '&validUntil=999999999999') + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -168,7 +187,8 @@ describe(__filename, function () { }); it('createGroupPad', async function () { - await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x1234567`) + await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=x1234567`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -177,7 +197,8 @@ describe(__filename, function () { }); it('createGroupPad', async function () { - await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x12345678`) + await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=x12345678`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -186,7 +207,8 @@ describe(__filename, function () { }); it('deleteGroup', async function () { - await agent.get(`${endPoint('deleteGroup')}&groupID=${groupID}`) + await preparedAgent.get(`${endPoint('deleteGroup')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -198,7 +220,8 @@ describe(__filename, function () { describe('API: Author creation', function () { it('createGroup', async function () { - await agent.get(endPoint('createGroup')) + await preparedAgent.get(endPoint('createGroup')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -209,7 +232,8 @@ describe(__filename, function () { }); it('createAuthor', async function () { - await agent.get(endPoint('createAuthor')) + await preparedAgent.get(endPoint('createAuthor')) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -219,7 +243,8 @@ describe(__filename, function () { }); it('createAuthor with name', async function () { - await agent.get(`${endPoint('createAuthor')}&name=john`) + await preparedAgent.get(`${endPoint('createAuthor')}?name=john`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -230,7 +255,8 @@ describe(__filename, function () { }); it('createAuthorIfNotExistsFor', async function () { - await agent.get(`${endPoint('createAuthorIfNotExistsFor')}&authorMapper=chris`) + await preparedAgent.get(`${endPoint('createAuthorIfNotExistsFor')}?authorMapper=chris`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -240,7 +266,8 @@ describe(__filename, function () { }); it('getAuthorName', async function () { - await agent.get(`${endPoint('getAuthorName')}&authorID=${authorID}`) + await preparedAgent.get(`${endPoint('getAuthorName')}?authorID=${authorID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -252,8 +279,9 @@ describe(__filename, function () { describe('API: Sessions', function () { it('createSession', async function () { - await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` + + await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` + '&validUntil=999999999999') + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -264,7 +292,8 @@ describe(__filename, function () { }); it('getSessionInfo', async function () { - await agent.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`) + await preparedAgent.get(`${endPoint('getSessionInfo')}?sessionID=${sessionID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -276,7 +305,8 @@ describe(__filename, function () { }); it('listSessionsOfGroup', async function () { - await agent.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`) + await preparedAgent.get(`${endPoint('listSessionsOfGroup')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -286,7 +316,8 @@ describe(__filename, function () { }); it('deleteSession', async function () { - await agent.get(`${endPoint('deleteSession')}&sessionID=${sessionID}`) + await preparedAgent.get(`${endPoint('deleteSession')}?sessionID=${sessionID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -295,7 +326,8 @@ describe(__filename, function () { }); it('getSessionInfo of deleted session', async function () { - await agent.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`) + await preparedAgent.get(`${endPoint('getSessionInfo')}?sessionID=${sessionID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -306,7 +338,8 @@ describe(__filename, function () { describe('API: Group pad management', function () { it('listPads', async function () { - await agent.get(`${endPoint('listPads')}&groupID=${groupID}`) + await preparedAgent.get(`${endPoint('listPads')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -316,7 +349,8 @@ describe(__filename, function () { }); it('createGroupPad', async function () { - await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=${padID}`) + await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=${padID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -326,7 +360,8 @@ describe(__filename, function () { }); it('listPads after creating a group pad', async function () { - await agent.get(`${endPoint('listPads')}&groupID=${groupID}`) + await preparedAgent.get(`${endPoint('listPads')}?groupID=${groupID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -338,7 +373,8 @@ describe(__filename, function () { describe('API: Pad security', function () { it('getPublicStatus', async function () { - await agent.get(`${endPoint('getPublicStatus')}&padID=${padID}`) + await preparedAgent.get(`${endPoint('getPublicStatus')}?padID=${padID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -348,7 +384,8 @@ describe(__filename, function () { }); it('setPublicStatus', async function () { - await agent.get(`${endPoint('setPublicStatus')}&padID=${padID}&publicStatus=true`) + await preparedAgent.get(`${endPoint('setPublicStatus')}?padID=${padID}&publicStatus=true`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -357,7 +394,8 @@ describe(__filename, function () { }); it('getPublicStatus after changing public status', async function () { - await agent.get(`${endPoint('getPublicStatus')}&padID=${padID}`) + await preparedAgent.get(`${endPoint('getPublicStatus')}?padID=${padID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { @@ -373,7 +411,8 @@ describe(__filename, function () { describe('API: Misc', function () { it('listPadsOfAuthor', async function () { - await agent.get(`${endPoint('listPadsOfAuthor')}&authorID=${authorID}`) + await preparedAgent.get(`${endPoint('listPadsOfAuthor')}?authorID=${authorID}`) + .set("Authorization", await generateJWTToken()) .expect(200) .expect('Content-Type', /json/) .expect((res:any) => { diff --git a/src/tests/backend/specs/caching_middleware.ts b/src/tests/backend/specs/caching_middleware.ts deleted file mode 100644 index 3051ee1e7fc..00000000000 --- a/src/tests/backend/specs/caching_middleware.ts +++ /dev/null @@ -1,125 +0,0 @@ -'use strict'; - -import {MapArrayType} from "../../../node/types/MapType"; - -/** - * caching_middleware is responsible for serving everything under path `/javascripts/` - * That includes packages as defined in `src/node/utils/tar.json` and probably also plugin code - * - */ - -const common = require('../common'); -import {strict as assert} from 'assert'; -import queryString from 'querystring'; -const settings = require('../../../node/utils/Settings'); -import {it, describe} from 'mocha' - -let agent: any; - -/** - * Hack! Returns true if the resource is not plaintext - * The file should start with the callback method, so we need the - * URL. - * - * @param {string} fileContent the response body - * @param {URL} resource resource URI - * @returns {boolean} if it is plaintext - */ -const isPlaintextResponse = (fileContent: string, resource:string): boolean => { - // callback=require.define&v=1234 - const query = (new URL(resource, 'http://localhost')).search.slice(1); - // require.define - const jsonp = queryString.parse(query).callback; - - // returns true if the first letters in fileContent equal the content of `jsonp` - return fileContent.substring(0, jsonp!.length) === jsonp; -}; - - -type RequestType = { - _shouldUnzip: () => boolean; -} - -/** - * A hack to disable `superagent`'s auto unzip functionality - * - * @param {Request} request - */ -const disableAutoDeflate = (request: RequestType) => { - request._shouldUnzip = () => false; -}; - -describe(__filename, function () { - const backups:MapArrayType<any> = {}; - const fantasyEncoding = 'brainwaves'; // non-working encoding until https://github.com/visionmedia/superagent/pull/1560 is resolved - const packages = [ - '/javascripts/lib/ep_etherpad-lite/static/js/ace2_common.js?callback=require.define', - '/javascripts/lib/ep_etherpad-lite/static/js/ace2_inner.js?callback=require.define', - '/javascripts/lib/ep_etherpad-lite/static/js/pad.js?callback=require.define', - '/javascripts/lib/ep_etherpad-lite/static/js/timeslider.js?callback=require.define', - ]; - - before(async function () { - agent = await common.init(); - backups.settings = {}; - backups.settings.minify = settings.minify; - }); - after(async function () { - Object.assign(settings, backups.settings); - }); - - for (const minify of [false, true]) { - context(`when minify is ${minify}`, function () { - before(async function () { - settings.minify = minify; - }); - - describe('gets packages uncompressed without Accept-Encoding gzip', function () { - for (const resource of packages) { - it(resource, async function () { - await agent.get(resource) - .set('Accept-Encoding', fantasyEncoding) - .use(disableAutoDeflate) - .expect(200) - .expect('Content-Type', /application\/javascript/) - .expect((res:any) => { - assert.equal(res.header['content-encoding'], undefined); - assert(isPlaintextResponse(res.text, resource)); - }); - }); - } - }); - - describe('gets packages compressed with Accept-Encoding gzip', function () { - for (const resource of packages) { - it(resource, async function () { - await agent.get(resource) - .set('Accept-Encoding', 'gzip') - .use(disableAutoDeflate) - .expect(200) - .expect('Content-Type', /application\/javascript/) - .expect('Content-Encoding', 'gzip') - .expect((res:any) => { - assert(!isPlaintextResponse(res.text, resource)); - }); - }); - } - }); - - it('does not cache content-encoding headers', async function () { - await agent.get(packages[0]) - .set('Accept-Encoding', fantasyEncoding) - .expect(200) - .expect((res:any) => assert.equal(res.header['content-encoding'], undefined)); - await agent.get(packages[0]) - .set('Accept-Encoding', 'gzip') - .expect(200) - .expect('Content-Encoding', 'gzip'); - await agent.get(packages[0]) - .set('Accept-Encoding', fantasyEncoding) - .expect(200) - .expect((res:any) => assert.equal(res.header['content-encoding'], undefined)); - }); - }); - } -}); diff --git a/src/tests/backend/specs/chat.ts b/src/tests/backend/specs/chat.ts index 5070a30a1c1..62ac9701252 100644 --- a/src/tests/backend/specs/chat.ts +++ b/src/tests/backend/specs/chat.ts @@ -3,7 +3,7 @@ import {MapArrayType} from "../../../node/types/MapType"; import {PluginDef} from "../../../node/types/PartType"; -const ChatMessage = require('../../../static/js/ChatMessage'); +import ChatMessage from '../../../static/js/ChatMessage'; const {Pad} = require('../../../node/db/Pad'); const assert = require('assert').strict; const common = require('../common'); @@ -103,10 +103,14 @@ describe(__filename, function () { checkHook('chatNewMessage', ({message}) => { assert(message != null); assert(message instanceof ChatMessage); - assert.equal(message.authorId, authorId); - assert.equal(message.text, this.test!.title); - assert(message.time >= start); - assert(message.time <= Date.now()); + // @ts-ignore + assert.equal(message!.authorId, authorId); + // @ts-ignore + assert.equal(message!.text, this.test!.title); + // @ts-ignore + assert(message!.time >= start); + // @ts-ignore + assert(message!.time <= Date.now()); }), sendChat(socket, {text: this.test!.title}), ]); @@ -153,7 +157,9 @@ describe(__filename, function () { const customMetadata = {foo: this.test!.title}; await Promise.all([ checkHook('chatNewMessage', ({message}) => { + // @ts-ignore message.text = modifiedText; + // @ts-ignore message.customMetadata = customMetadata; }), (async () => { diff --git a/src/tests/backend/specs/contentcollector.ts b/src/tests/backend/specs/contentcollector.ts index 51ae0002f2b..50178fbae06 100644 --- a/src/tests/backend/specs/contentcollector.ts +++ b/src/tests/backend/specs/contentcollector.ts @@ -11,16 +11,17 @@ import {APool} from "../../../node/types/PadType"; -const AttributePool = require('../../../static/js/AttributePool'); +import AttributePool from '../../../static/js/AttributePool'; const Changeset = require('../../../static/js/Changeset'); const assert = require('assert').strict; -const attributes = require('../../../static/js/attributes'); +import attributes from '../../../static/js/attributes'; const contentcollector = require('../../../static/js/contentcollector'); -const jsdom = require('jsdom'); +import jsdom from 'jsdom'; +import {Attribute} from "../../../static/js/types/Attribute"; // All test case `wantAlines` values must only refer to attributes in this list so that the // attribute numbers do not change due to changes in pool insertion order. -const knownAttribs = [ +const knownAttribs: Attribute[] = [ ['insertorder', 'first'], ['italic', 'true'], ['list', 'bullet1'], @@ -336,7 +337,7 @@ pre describe(__filename, function () { for (const tc of testCases) { describe(tc.description, function () { - let apool: APool; + let apool: AttributePool; let result: { lines: string[], lineAttribs: string[], @@ -376,11 +377,12 @@ describe(__filename, function () { for (const aline of result.lineAttribs) { const gotAlineAttribs:string[][] = []; gotAttribs.push(gotAlineAttribs); - const wantAlineAttribs:string[] = []; + const wantAlineAttribs:Attribute[] = []; wantAttribs.push(wantAlineAttribs); for (const op of Changeset.deserializeOps(aline)) { - const gotOpAttribs:string[] = [...attributes.attribsFromString(op.attribs, apool)]; + const gotOpAttribs = [...attributes.attribsFromString(op.attribs, apool)] as unknown as Attribute; gotAlineAttribs.push(gotOpAttribs); + // @ts-ignore wantAlineAttribs.push(attributes.sort([...gotOpAttribs])); } } diff --git a/src/tests/backend/specs/lowerCasePadIds.ts b/src/tests/backend/specs/lowerCasePadIds.ts index 359f85d2ce3..c85d16c3fc8 100644 --- a/src/tests/backend/specs/lowerCasePadIds.ts +++ b/src/tests/backend/specs/lowerCasePadIds.ts @@ -40,7 +40,7 @@ describe(__filename, function () { it('do nothing', async function () { await agent.get('/p/UPPERCASEpad') - .expect(200); + .expect(200); }); }); @@ -50,8 +50,8 @@ describe(__filename, function () { }); it('lowercase pad ids', async function () { await agent.get('/p/UPPERCASEpad') - .expect(302) - .expect('location', 'uppercasepad'); + .expect(302) + .expect('location', 'uppercasepad'); }); it('keeps old pads accessible', async function () { diff --git a/src/tests/backend/specs/settings.ts b/src/tests/backend/specs/settings.ts index 4ed447931d7..d6dcaf71ae9 100644 --- a/src/tests/backend/specs/settings.ts +++ b/src/tests/backend/specs/settings.ts @@ -7,7 +7,7 @@ import process from 'process'; describe(__filename, function () { describe('parseSettings', function () { - let settings:any; + let settings: any; const envVarSubstTestCases = [ {name: 'true', val: 'true', var: 'SET_VAR_TRUE', want: true}, {name: 'false', val: 'false', var: 'SET_VAR_FALSE', want: false}, @@ -58,4 +58,35 @@ describe(__filename, function () { }); }); }); + + + describe("Parse plugin settings", function () { + + before(async function () { + process.env["EP__ADMIN__PASSWORD"] = "test" + }) + + it('should parse plugin settings', async function () { + let settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert.equal(settings.ADMIN.PASSWORD, "test"); + }) + + it('should bundle settings with same path', async function () { + process.env["EP__ADMIN__USERNAME"] = "test" + let settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert.deepEqual(settings.ADMIN, {PASSWORD: "test", USERNAME: "test"}); + }) + + it("Can set the ep themes", async function () { + process.env["EP__ep_themes__default_theme"] = "hacker" + let settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert.deepEqual(settings.ep_themes, {"default_theme": "hacker"}); + }) + + it("can set the ep_webrtc settings", async function () { + process.env["EP__ep_webrtc__enabled"] = "true" + let settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert.deepEqual(settings.ep_webrtc, {"enabled": true}); + }) + }) }); diff --git a/src/tests/container/specs/api/pad.js b/src/tests/container/specs/api/pad.js index 04067f0e374..f6ff8ebf529 100644 --- a/src/tests/container/specs/api/pad.js +++ b/src/tests/container/specs/api/pad.js @@ -31,8 +31,8 @@ describe('API Versioning', function () { }); describe('Permission', function () { - it('errors with invalid APIKey', function (done) { - api.get(`/api/${apiVersion}/createPad?apikey=wrong_password&padID=test`) + it('errors with invalid OAuth token', function (done) { + api.get(`/api/${apiVersion}/createPad?padID=test`) .expect(401, done); }); }); diff --git a/src/tests/frontend-new/admin-spec/adminsettings.spec.ts b/src/tests/frontend-new/admin-spec/adminsettings.spec.ts index ad3a0c441c3..4c28874dc32 100644 --- a/src/tests/frontend-new/admin-spec/adminsettings.spec.ts +++ b/src/tests/frontend-new/admin-spec/adminsettings.spec.ts @@ -17,10 +17,10 @@ test.describe('admin settings',()=> { const settingsVal = await settings.inputValue() const settingsLength = settingsVal.length - await settings.fill(`/* test */\n${settingsVal}`) + await settings.fill(`{"title": "Etherpad123"}`) const newValue = await settings.inputValue() - expect(newValue).toContain('/* test */') - expect(newValue.length).toEqual(settingsLength+11) + expect(newValue).toContain('{"title": "Etherpad123"}') + expect(newValue.length).toEqual(24) await saveSettings(page) // Check if the changes were actually saved @@ -31,7 +31,7 @@ test.describe('admin settings',()=> { const newSettings = page.locator('.settings'); const newSettingsVal = await newSettings.inputValue() - expect(newSettingsVal).toContain('/* test */') + expect(newSettingsVal).toContain('{"title": "Etherpad123"}') // Change back to old settings diff --git a/src/tests/frontend-new/admin-spec/admintroubleshooting.spec.ts b/src/tests/frontend-new/admin-spec/admintroubleshooting.spec.ts index 9dc7c7a2097..9155e9cbdf3 100644 --- a/src/tests/frontend-new/admin-spec/admintroubleshooting.spec.ts +++ b/src/tests/frontend-new/admin-spec/admintroubleshooting.spec.ts @@ -10,7 +10,7 @@ test('Shows troubleshooting page manager', async ({page}) => { await page.goto('http://localhost:9001/admin/help') await page.waitForSelector('.menu') const menu = page.locator('.menu'); - await expect(menu.locator('li')).toHaveCount(4); + await expect(menu.locator('li')).toHaveCount(5); }) test('Shows a version number', async function ({page}) { diff --git a/src/tests/frontend-new/specs/embed_value.spec.ts b/src/tests/frontend-new/specs/embed_value.spec.ts index 674e001d180..a65276cc9fc 100644 --- a/src/tests/frontend-new/specs/embed_value.spec.ts +++ b/src/tests/frontend-new/specs/embed_value.spec.ts @@ -75,7 +75,7 @@ test.describe('embed links', function () { expect(shareLink).toBe(padURL); }); - test('is an iframe with the the correct url parameters and correct size', async function ({page}) { + test('is an iframe with the correct url parameters and correct size', async function ({page}) { const shareButton = page.locator('.buttonicon-embed') await shareButton.click() @@ -111,7 +111,7 @@ test.describe('embed links', function () { expect(containsReadOnlyLink).toBe(true); }); - test('the embed as iframe code is an iframe with the the correct url parameters and correct size', async function ({page}) { + test('the embed as iframe code is an iframe with the correct url parameters and correct size', async function ({page}) { // open share dropdown diff --git a/src/tests/frontend/helper/methods.js b/src/tests/frontend/helper/methods.ts similarity index 99% rename from src/tests/frontend/helper/methods.js rename to src/tests/frontend/helper/methods.ts index 97ea6a643b4..f1da92371c3 100644 --- a/src/tests/frontend/helper/methods.js +++ b/src/tests/frontend/helper/methods.ts @@ -1,4 +1,4 @@ -'use strict'; +// @ts-nocheck /** * Spys on socket.io messages and saves them into several arrays diff --git a/src/tests/frontend/helper/multipleUsers.js b/src/tests/frontend/helper/multipleUsers.ts similarity index 99% rename from src/tests/frontend/helper/multipleUsers.js rename to src/tests/frontend/helper/multipleUsers.ts index 831bf403ea4..261b8f63c60 100644 --- a/src/tests/frontend/helper/multipleUsers.js +++ b/src/tests/frontend/helper/multipleUsers.ts @@ -1,4 +1,4 @@ -'use strict'; +// @ts-nocheck const getCookies = () => helper.padChrome$.window.require('ep_etherpad-lite/static/js/pad_utils').Cookies; diff --git a/src/tests/frontend/helper/ui.js b/src/tests/frontend/helper/ui.ts similarity index 99% rename from src/tests/frontend/helper/ui.js rename to src/tests/frontend/helper/ui.ts index 60911e08df9..69e6b7d406b 100644 --- a/src/tests/frontend/helper/ui.js +++ b/src/tests/frontend/helper/ui.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/tests/frontend/index.html b/src/tests/frontend/index.html index c3b7a4633f0..a03677b2610 100644 --- a/src/tests/frontend/index.html +++ b/src/tests/frontend/index.html @@ -14,7 +14,6 @@ <div id="iframe-container"></div> </div> - <script src="../../static/js/require-kernel.js"></script> <script src="../../static/js/vendors/jquery.js"></script> <script src="lib/sendkeys.js"></script> <script src="../../static/js/vendors/browser.js"></script> diff --git a/src/tests/frontend/lib/expect.js b/src/tests/frontend/lib/expect.js deleted file mode 100644 index c647cf2be06..00000000000 --- a/src/tests/frontend/lib/expect.js +++ /dev/null @@ -1,1247 +0,0 @@ - -(function (global, module) { - - if ('undefined' == typeof module) { - var module = { exports: {} } - , exports = module.exports - } - - /** - * Exports. - */ - - module.exports = expect; - expect.Assertion = Assertion; - - /** - * Exports version. - */ - - expect.version = '0.1.2'; - - /** - * Possible assertion flags. - */ - - var flags = { - not: ['to', 'be', 'have', 'include', 'only'] - , to: ['be', 'have', 'include', 'only', 'not'] - , only: ['have'] - , have: ['own'] - , be: ['an'] - }; - - function expect (obj) { - return new Assertion(obj); - } - - /** - * Constructor - * - * @api private - */ - - function Assertion (obj, flag, parent) { - this.obj = obj; - this.flags = {}; - - if (undefined != parent) { - this.flags[flag] = true; - - for (var i in parent.flags) { - if (parent.flags.hasOwnProperty(i)) { - this.flags[i] = true; - } - } - } - - var $flags = flag ? flags[flag] : keys(flags) - , self = this - - if ($flags) { - for (var i = 0, l = $flags.length; i < l; i++) { - // avoid recursion - if (this.flags[$flags[i]]) continue; - - var name = $flags[i] - , assertion = new Assertion(this.obj, name, this) - - if ('function' == typeof Assertion.prototype[name]) { - // clone the function, make sure we dont touch the prot reference - var old = this[name]; - this[name] = function () { - return old.apply(self, arguments); - } - - for (var fn in Assertion.prototype) { - if (Assertion.prototype.hasOwnProperty(fn) && fn != name) { - this[name][fn] = bind(assertion[fn], assertion); - } - } - } else { - this[name] = assertion; - } - } - } - }; - - /** - * Performs an assertion - * - * @api private - */ - - Assertion.prototype.assert = function (truth, msg, error) { - var msg = this.flags.not ? error : msg - , ok = this.flags.not ? !truth : truth; - - if (!ok) { - throw new Error(msg.call(this)); - } - - this.and = new Assertion(this.obj); - }; - - /** - * Check if the value is truthy - * - * @api public - */ - - Assertion.prototype.ok = function () { - this.assert( - !!this.obj - , function(){ return 'expected ' + i(this.obj) + ' to be truthy' } - , function(){ return 'expected ' + i(this.obj) + ' to be falsy' }); - }; - - /** - * Assert that the function throws. - * - * @param {Function|RegExp} callback, or regexp to match error string against - * @api public - */ - - Assertion.prototype.throwError = - Assertion.prototype.throwException = function (fn) { - expect(this.obj).to.be.a('function'); - - var thrown = false - , not = this.flags.not - - try { - this.obj(); - } catch (e) { - if ('function' == typeof fn) { - fn(e); - } else if ('object' == typeof fn) { - var subject = 'string' == typeof e ? e : e.message; - if (not) { - expect(subject).to.not.match(fn); - } else { - expect(subject).to.match(fn); - } - } - thrown = true; - } - - if ('object' == typeof fn && not) { - // in the presence of a matcher, ensure the `not` only applies to - // the matching. - this.flags.not = false; - } - - var name = this.obj.name || 'fn'; - this.assert( - thrown - , function(){ return 'expected ' + name + ' to throw an exception' } - , function(){ return 'expected ' + name + ' not to throw an exception' }); - }; - - /** - * Checks if the array is empty. - * - * @api public - */ - - Assertion.prototype.empty = function () { - var expectation; - - if ('object' == typeof this.obj && null !== this.obj && !isArray(this.obj)) { - if ('number' == typeof this.obj.length) { - expectation = !this.obj.length; - } else { - expectation = !keys(this.obj).length; - } - } else { - if ('string' != typeof this.obj) { - expect(this.obj).to.be.an('object'); - } - - expect(this.obj).to.have.property('length'); - expectation = !this.obj.length; - } - - this.assert( - expectation - , function(){ return 'expected ' + i(this.obj) + ' to be empty' } - , function(){ return 'expected ' + i(this.obj) + ' to not be empty' }); - return this; - }; - - /** - * Checks if the obj exactly equals another. - * - * @api public - */ - - Assertion.prototype.be = - Assertion.prototype.equal = function (obj) { - this.assert( - obj === this.obj - , function(){ return 'expected ' + i(this.obj) + ' to equal ' + i(obj) } - , function(){ return 'expected ' + i(this.obj) + ' to not equal ' + i(obj) }); - return this; - }; - - /** - * Checks if the obj sortof equals another. - * - * @api public - */ - - Assertion.prototype.eql = function (obj) { - this.assert( - expect.eql(obj, this.obj) - , function(){ return 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj) } - , function(){ return 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj) }); - return this; - }; - - /** - * Assert within start to finish (inclusive). - * - * @param {Number} start - * @param {Number} finish - * @api public - */ - - Assertion.prototype.within = function (start, finish) { - var range = start + '..' + finish; - this.assert( - this.obj >= start && this.obj <= finish - , function(){ return 'expected ' + i(this.obj) + ' to be within ' + range } - , function(){ return 'expected ' + i(this.obj) + ' to not be within ' + range }); - return this; - }; - - /** - * Assert typeof / instance of - * - * @api public - */ - - Assertion.prototype.a = - Assertion.prototype.an = function (type) { - if ('string' == typeof type) { - // proper english in error msg - var n = /^[aeiou]/.test(type) ? 'n' : ''; - - // typeof with support for 'array' - this.assert( - 'array' == type ? isArray(this.obj) : - 'object' == type - ? 'object' == typeof this.obj && null !== this.obj - : type == typeof this.obj - , function(){ return 'expected ' + i(this.obj) + ' to be a' + n + ' ' + type } - , function(){ return 'expected ' + i(this.obj) + ' not to be a' + n + ' ' + type }); - } else { - // instanceof - var name = type.name || 'supplied constructor'; - this.assert( - this.obj instanceof type - , function(){ return 'expected ' + i(this.obj) + ' to be an instance of ' + name } - , function(){ return 'expected ' + i(this.obj) + ' not to be an instance of ' + name }); - } - - return this; - }; - - /** - * Assert numeric value above _n_. - * - * @param {Number} n - * @api public - */ - - Assertion.prototype.greaterThan = - Assertion.prototype.above = function (n) { - this.assert( - this.obj > n - , function(){ return 'expected ' + i(this.obj) + ' to be above ' + n } - , function(){ return 'expected ' + i(this.obj) + ' to be below ' + n }); - return this; - }; - - /** - * Assert numeric value below _n_. - * - * @param {Number} n - * @api public - */ - - Assertion.prototype.lessThan = - Assertion.prototype.below = function (n) { - this.assert( - this.obj < n - , function(){ return 'expected ' + i(this.obj) + ' to be below ' + n } - , function(){ return 'expected ' + i(this.obj) + ' to be above ' + n }); - return this; - }; - - /** - * Assert string value matches _regexp_. - * - * @param {RegExp} regexp - * @api public - */ - - Assertion.prototype.match = function (regexp) { - this.assert( - regexp.exec(this.obj) - , function(){ return 'expected ' + i(this.obj) + ' to match ' + regexp } - , function(){ return 'expected ' + i(this.obj) + ' not to match ' + regexp }); - return this; - }; - - /** - * Assert property "length" exists and has value of _n_. - * - * @param {Number} n - * @api public - */ - - Assertion.prototype.length = function (n) { - expect(this.obj).to.have.property('length'); - var len = this.obj.length; - this.assert( - n == len - , function(){ return 'expected ' + i(this.obj) + ' to have a length of ' + n + ' but got ' + len } - , function(){ return 'expected ' + i(this.obj) + ' to not have a length of ' + len }); - return this; - }; - - /** - * Assert property _name_ exists, with optional _val_. - * - * @param {String} name - * @param {Mixed} val - * @api public - */ - - Assertion.prototype.property = function (name, val) { - if (this.flags.own) { - this.assert( - Object.prototype.hasOwnProperty.call(this.obj, name) - , function(){ return 'expected ' + i(this.obj) + ' to have own property ' + i(name) } - , function(){ return 'expected ' + i(this.obj) + ' to not have own property ' + i(name) }); - return this; - } - - if (this.flags.not && undefined !== val) { - if (undefined === this.obj[name]) { - throw new Error(i(this.obj) + ' has no property ' + i(name)); - } - } else { - var hasProp; - try { - hasProp = name in this.obj - } catch (e) { - hasProp = undefined !== this.obj[name] - } - - this.assert( - hasProp - , function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name) } - , function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name) }); - } - - if (undefined !== val) { - this.assert( - val === this.obj[name] - , function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name) - + ' of ' + i(val) + ', but got ' + i(this.obj[name]) } - , function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name) - + ' of ' + i(val) }); - } - - this.obj = this.obj[name]; - return this; - }; - - /** - * Assert that the array contains _obj_ or string contains _obj_. - * - * @param {Mixed} obj|string - * @api public - */ - - Assertion.prototype.string = - Assertion.prototype.contain = function (obj) { - if ('string' == typeof this.obj) { - this.assert( - ~this.obj.indexOf(obj) - , function(){ return 'expected ' + i(this.obj) + ' to contain ' + i(obj) } - , function(){ return 'expected ' + i(this.obj) + ' to not contain ' + i(obj) }); - } else { - this.assert( - ~indexOf(this.obj, obj) - , function(){ return 'expected ' + i(this.obj) + ' to contain ' + i(obj) } - , function(){ return 'expected ' + i(this.obj) + ' to not contain ' + i(obj) }); - } - return this; - }; - - /** - * Assert exact keys or inclusion of keys by using - * the `.own` modifier. - * - * @param {Array|String ...} keys - * @api public - */ - - Assertion.prototype.key = - Assertion.prototype.keys = function ($keys) { - var str - , ok = true; - - $keys = isArray($keys) - ? $keys - : Array.prototype.slice.call(arguments); - - if (!$keys.length) throw new Error('keys required'); - - var actual = keys(this.obj) - , len = $keys.length; - - // Inclusion - ok = every($keys, function (key) { - return ~indexOf(actual, key); - }); - - // Strict - if (!this.flags.not && this.flags.only) { - ok = ok && $keys.length == actual.length; - } - - // Key string - if (len > 1) { - $keys = map($keys, function (key) { - return i(key); - }); - var last = $keys.pop(); - str = $keys.join(', ') + ', and ' + last; - } else { - str = i($keys[0]); - } - - // Form - str = (len > 1 ? 'keys ' : 'key ') + str; - - // Have / include - str = (!this.flags.only ? 'include ' : 'only have ') + str; - - // Assertion - this.assert( - ok - , function(){ return 'expected ' + i(this.obj) + ' to ' + str } - , function(){ return 'expected ' + i(this.obj) + ' to not ' + str }); - - return this; - }; - /** - * Assert a failure. - * - * @param {String ...} custom message - * @api public - */ - Assertion.prototype.fail = function (msg) { - msg = msg || "explicit failure"; - this.assert(false, msg, msg); - return this; - }; - - /** - * Function bind implementation. - */ - - function bind (fn, scope) { - return function () { - return fn.apply(scope, arguments); - } - } - - /** - * Array every compatibility - * - * @see bit.ly/5Fq1N2 - * @api public - */ - - function every (arr, fn, thisObj) { - var scope = thisObj || global; - for (var i = 0, j = arr.length; i < j; ++i) { - if (!fn.call(scope, arr[i], i, arr)) { - return false; - } - } - return true; - }; - - /** - * Array indexOf compatibility. - * - * @see bit.ly/a5Dxa2 - * @api public - */ - - function indexOf (arr, o, i) { - if (Array.prototype.indexOf) { - return Array.prototype.indexOf.call(arr, o, i); - } - - if (arr.length === undefined) { - return -1; - } - - for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0 - ; i < j && arr[i] !== o; i++); - - return j <= i ? -1 : i; - }; - - // https://gist.github.com/1044128/ - var getOuterHTML = function(element) { - if ('outerHTML' in element) return element.outerHTML; - var ns = "http://www.w3.org/1999/xhtml"; - var container = document.createElementNS(ns, '_'); - var elemProto = (window.HTMLElement || window.Element).prototype; - var xmlSerializer = new XMLSerializer(); - var html; - if (document.xmlVersion) { - return xmlSerializer.serializeToString(element); - } else { - container.appendChild(element.cloneNode(false)); - html = container.innerHTML.replace('><', '>' + element.innerHTML + '<'); - container.innerHTML = ''; - return html; - } - }; - - // Returns true if object is a DOM element. - var isDOMElement = function (object) { - if (typeof HTMLElement === 'object') { - return object instanceof HTMLElement; - } else { - return object && - typeof object === 'object' && - object.nodeType === 1 && - typeof object.nodeName === 'string'; - } - }; - - /** - * Inspects an object. - * - * @see taken from node.js `util` module (copyright Joyent, MIT license) - * @api private - */ - - function i (obj, showHidden, depth) { - var seen = []; - - function stylize (str) { - return str; - }; - - function format (value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (value && typeof value.inspect === 'function' && - // Filter out the util module, it's inspect function is special - value !== exports && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - return value.inspect(recurseTimes); - } - - // Primitive types cannot have properties - switch (typeof value) { - case 'undefined': - return stylize('undefined', 'undefined'); - - case 'string': - var simple = '\'' + json.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return stylize(simple, 'string'); - - case 'number': - return stylize('' + value, 'number'); - - case 'boolean': - return stylize('' + value, 'boolean'); - } - // For some reason typeof null is "object", so special case here. - if (value === null) { - return stylize('null', 'null'); - } - - if (isDOMElement(value)) { - return getOuterHTML(value); - } - - // Look up the keys of the object. - var visible_keys = keys(value); - var $keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys; - - // Functions without properties can be shortcutted. - if (typeof value === 'function' && $keys.length === 0) { - if (isRegExp(value)) { - return stylize('' + value, 'regexp'); - } else { - var name = value.name ? ': ' + value.name : ''; - return stylize('[Function' + name + ']', 'special'); - } - } - - // Dates without properties can be shortcutted - if (isDate(value) && $keys.length === 0) { - return stylize(value.toUTCString(), 'date'); - } - - var base, type, braces; - // Determine the object type - if (isArray(value)) { - type = 'Array'; - braces = ['[', ']']; - } else { - type = 'Object'; - braces = ['{', '}']; - } - - // Make functions say that they are functions - if (typeof value === 'function') { - var n = value.name ? ': ' + value.name : ''; - base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']'; - } else { - base = ''; - } - - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + value.toUTCString(); - } - - if ($keys.length === 0) { - return braces[0] + base + braces[1]; - } - - if (recurseTimes < 0) { - if (isRegExp(value)) { - return stylize('' + value, 'regexp'); - } else { - return stylize('[Object]', 'special'); - } - } - - seen.push(value); - - var output = map($keys, function (key) { - var name, str; - if (value.__lookupGetter__) { - if (value.__lookupGetter__(key)) { - if (value.__lookupSetter__(key)) { - str = stylize('[Getter/Setter]', 'special'); - } else { - str = stylize('[Getter]', 'special'); - } - } else { - if (value.__lookupSetter__(key)) { - str = stylize('[Setter]', 'special'); - } - } - } - if (indexOf(visible_keys, key) < 0) { - name = '[' + key + ']'; - } - if (!str) { - if (indexOf(seen, value[key]) < 0) { - if (recurseTimes === null) { - str = format(value[key]); - } else { - str = format(value[key], recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (isArray(value)) { - str = map(str.split('\n'), function (line) { - return ' ' + line; - }).join('\n').substr(2); - } else { - str = '\n' + map(str.split('\n'), function (line) { - return ' ' + line; - }).join('\n'); - } - } - } else { - str = stylize('[Circular]', 'special'); - } - } - if (typeof name === 'undefined') { - if (type === 'Array' && key.match(/^\d+$/)) { - return str; - } - name = json.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.substr(1, name.length - 2); - name = stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = stylize(name, 'string'); - } - } - - return name + ': ' + str; - }); - - seen.pop(); - - var numLinesEst = 0; - var length = reduce(output, function (prev, cur) { - numLinesEst++; - if (indexOf(cur, '\n') >= 0) numLinesEst++; - return prev + cur.length + 1; - }, 0); - - if (length > 50) { - output = braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; - - } else { - output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; - } - - return output; - } - return format(obj, (typeof depth === 'undefined' ? 2 : depth)); - }; - - function isArray (ar) { - return Object.prototype.toString.call(ar) == '[object Array]'; - }; - - function isRegExp(re) { - var s = '' + re; - return re instanceof RegExp || // easy case - // duck-type for context-switching evalcx case - typeof(re) === 'function' && - re.constructor.name === 'RegExp' && - re.compile && - re.test && - re.exec && - s.match(/^\/.*\/[gim]{0,3}$/); - }; - - function isDate(d) { - if (d instanceof Date) return true; - return false; - }; - - function keys (obj) { - if (Object.keys) { - return Object.keys(obj); - } - - var keys = []; - - for (var i in obj) { - if (Object.prototype.hasOwnProperty.call(obj, i)) { - keys.push(i); - } - } - - return keys; - } - - function map (arr, mapper, that) { - if (Array.prototype.map) { - return Array.prototype.map.call(arr, mapper, that); - } - - var other= new Array(arr.length); - - for (var i= 0, n = arr.length; i<n; i++) - if (i in arr) - other[i] = mapper.call(that, arr[i], i, arr); - - return other; - }; - - function reduce (arr, fun) { - if (Array.prototype.reduce) { - return Array.prototype.reduce.apply( - arr - , Array.prototype.slice.call(arguments, 1) - ); - } - - var len = +this.length; - - if (typeof fun !== "function") - throw new TypeError(); - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) - throw new TypeError(); - - var i = 0; - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - - // if array contains no values, no initial value to return - if (++i >= len) - throw new TypeError(); - } while (true); - } - - for (; i < len; i++) { - if (i in this) - rv = fun.call(null, rv, this[i], i, this); - } - - return rv; - }; - - /** - * Asserts deep equality - * - * @see taken from node.js `assert` module (copyright Joyent, MIT license) - * @api private - */ - - expect.eql = function eql (actual, expected) { - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - } else if ('undefined' != typeof Buffer - && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { - if (actual.length != expected.length) return false; - - for (var i = 0; i < actual.length; i++) { - if (actual[i] !== expected[i]) return false; - } - - return true; - - // 7.2. If the expected value is a Date object, the actual value is - // equivalent if it is also a Date object that refers to the same time. - } else if (actual instanceof Date && expected instanceof Date) { - return actual.getTime() === expected.getTime(); - - // 7.3. Other pairs that do not both pass typeof value == "object", - // equivalence is determined by ==. - } else if (typeof actual != 'object' && typeof expected != 'object') { - return actual == expected; - - // 7.4. For all other Object pairs, including Array objects, equivalence is - // determined by having the same number of owned properties (as verified - // with Object.prototype.hasOwnProperty.call), the same set of keys - // (although not necessarily the same order), equivalent values for every - // corresponding key, and an identical "prototype" property. Note: this - // accounts for both named and indexed properties on Arrays. - } else { - return objEquiv(actual, expected); - } - } - - function isUndefinedOrNull (value) { - return value === null || value === undefined; - } - - function isArguments (object) { - return Object.prototype.toString.call(object) == '[object Arguments]'; - } - - function objEquiv (a, b) { - if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) - return false; - // an identical "prototype" property. - if (a.prototype !== b.prototype) return false; - //~~~I've managed to break Object.keys through screwy arguments passing. - // Converting to array solves the problem. - if (isArguments(a)) { - if (!isArguments(b)) { - return false; - } - a = pSlice.call(a); - b = pSlice.call(b); - return expect.eql(a, b); - } - try{ - var ka = keys(a), - kb = keys(b), - key, i; - } catch (e) {//happens when one is a string literal and the other isn't - return false; - } - // having the same number of owned properties (keys incorporates hasOwnProperty) - if (ka.length != kb.length) - return false; - //the same set of keys (although not necessarily the same order), - ka.sort(); - kb.sort(); - //~~~cheap key test - for (i = ka.length - 1; i >= 0; i--) { - if (ka[i] != kb[i]) - return false; - } - //equivalent values for every corresponding key, and - //~~~possibly expensive deep test - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!expect.eql(a[key], b[key])) - return false; - } - return true; - } - - var json = (function () { - "use strict"; - - if ('object' == typeof JSON && JSON.parse && JSON.stringify) { - return { - parse: nativeJSON.parse - , stringify: nativeJSON.stringify - } - } - - var JSON = {}; - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - function date(d, key) { - return isFinite(d.valueOf()) ? - d.getUTCFullYear() + '-' + - f(d.getUTCMonth() + 1) + '-' + - f(d.getUTCDate()) + 'T' + - f(d.getUTCHours()) + ':' + - f(d.getUTCMinutes()) + ':' + - f(d.getUTCSeconds()) + 'Z' : null; - }; - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - - // If the string contains no control characters, no quote characters, and no - // backslash characters, then we can safely slap some quotes around it. - // Otherwise we must also replace the offending characters with safe escape - // sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - - - function str(key, holder) { - - // Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - - // If the value has a toJSON method, call it to obtain a replacement value. - - if (value instanceof Date) { - value = date(key); - } - - // If we were called with a replacer function, then call the replacer to - // obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - - // What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - - // JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - - // If the value is a boolean or null, convert it to a string. Note: - // typeof null does not produce 'null'. The case is included here in - // the remote chance that this gets fixed someday. - - return String(value); - - // If the type is 'object', we might be dealing with an object or an array or - // null. - - case 'object': - - // Due to a specification blunder in ECMAScript, typeof null is 'object', - // so watch out for that case. - - if (!value) { - return 'null'; - } - - // Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - - // Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - - // The value is an array. Stringify every element. Use null as a placeholder - // for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - - // Join all of the elements together, separated with commas, and wrap them in - // brackets. - - v = partial.length === 0 ? '[]' : gap ? - '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : - '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - - // If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - - // Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - - // Join all of the member texts together, separated with commas, - // and wrap them in braces. - - v = partial.length === 0 ? '{}' : gap ? - '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : - '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - - // If the JSON object does not yet have a stringify method, give it one. - - JSON.stringify = function (value, replacer, space) { - - // The stringify method takes a value and an optional replacer, and an optional - // space parameter, and returns a JSON text. The replacer can be a function - // that can replace values, or an array of strings that will select the keys. - // A default replacer method can be provided. Use of the space parameter can - // produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - - // If the space parameter is a number, make an indent string containing that - // many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - - // If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - - // If there is a replacer, it must be a function or an array. - // Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - - // Make a fake root object containing our value under the key of ''. - // Return the result of stringifying the value. - - return str('', {'': value}); - }; - - // If the JSON object does not yet have a parse method, give it one. - - JSON.parse = function (text, reviver) { - // The parse method takes a text and an optional reviver function, and returns - // a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - - // The walk method is used to recursively walk the resulting structure so - // that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - - // Parsing happens in four stages. In the first stage, we replace certain - // Unicode characters with escape sequences. JavaScript handles many characters - // incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - - // In the second stage, we run the text against regular expressions that look - // for non-JSON patterns. We are especially concerned with '()' and 'new' - // because they can cause invocation, and '=' because it can cause mutation. - // But just to be safe, we want to reject all unexpected forms. - - // We split the second stage into 4 regexp operations in order to work around - // crippling inefficiencies in IE's and Safari's regexp engines. First we - // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we - // replace all simple value tokens with ']' characters. Third, we delete all - // open brackets that follow a colon or comma or that begin the text. Finally, - // we look to see that the remaining characters are only whitespace or ']' or - // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - - // In the third stage we use the eval function to compile the text into a - // JavaScript structure. The '{' operator is subject to a syntactic ambiguity - // in JavaScript: it can begin a block or an object literal. We wrap the text - // in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - - // In the optional fourth stage, we recursively walk the new structure, passing - // each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' ? - walk({'': j}, '') : j; - } - - // If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - - return JSON; - })(); - - if ('undefined' != typeof window) { - window.expect = module.exports; - } - -})( - this - , 'undefined' != typeof module ? module : {} - , 'undefined' != typeof exports ? exports : {} -); diff --git a/src/tests/frontend/lib/mocha.js b/src/tests/frontend/lib/mocha.js deleted file mode 100644 index 031b6e4463f..00000000000 --- a/src/tests/frontend/lib/mocha.js +++ /dev/null @@ -1,18115 +0,0 @@ -(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ -(function (process,global){ -'use strict'; - -/* eslint no-unused-vars: off */ -/* eslint-env commonjs */ - -/** - * Shim process.stdout. - */ - -process.stdout = require('browser-stdout')({label: false}); - -var Mocha = require('./lib/mocha'); - -/** - * Create a Mocha instance. - * - * @return {undefined} - */ - -var mocha = new Mocha({reporter: 'html'}); - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date; -var setTimeout = global.setTimeout; -var setInterval = global.setInterval; -var clearTimeout = global.clearTimeout; -var clearInterval = global.clearInterval; - -var uncaughtExceptionHandlers = []; - -var originalOnerrorHandler = global.onerror; - -/** - * Remove uncaughtException listener. - * Revert to original onerror handler if previously defined. - */ - -process.removeListener = function(e, fn) { - if (e === 'uncaughtException') { - if (originalOnerrorHandler) { - global.onerror = originalOnerrorHandler; - } else { - global.onerror = function() {}; - } - var i = uncaughtExceptionHandlers.indexOf(fn); - if (i !== -1) { - uncaughtExceptionHandlers.splice(i, 1); - } - } -}; - -/** - * Implements uncaughtException listener. - */ - -process.on = function(e, fn) { - if (e === 'uncaughtException') { - global.onerror = function(err, url, line) { - fn(new Error(err + ' (' + url + ':' + line + ')')); - return !mocha.options.allowUncaught; - }; - uncaughtExceptionHandlers.push(fn); - } -}; - -// The BDD UI is registered by default, but no UI will be functional in the -// browser without an explicit call to the overridden `mocha.ui` (see below). -// Ensure that this default UI does not expose its methods to the global scope. -mocha.suite.removeAllListeners('pre-require'); - -var immediateQueue = []; -var immediateTimeout; - -function timeslice() { - var immediateStart = new Date().getTime(); - while (immediateQueue.length && new Date().getTime() - immediateStart < 100) { - immediateQueue.shift()(); - } - if (immediateQueue.length) { - immediateTimeout = setTimeout(timeslice, 0); - } else { - immediateTimeout = null; - } -} - -/** - * High-performance override of Runner.immediately. - */ - -Mocha.Runner.immediately = function(callback) { - immediateQueue.push(callback); - if (!immediateTimeout) { - immediateTimeout = setTimeout(timeslice, 0); - } -}; - -/** - * Function to allow assertion libraries to throw errors directly into mocha. - * This is useful when running tests in a browser because window.onerror will - * only receive the 'message' attribute of the Error. - */ -mocha.throwError = function(err) { - uncaughtExceptionHandlers.forEach(function(fn) { - fn(err); - }); - throw err; -}; - -/** - * Override ui to ensure that the ui functions are initialized. - * Normally this would happen in Mocha.prototype.loadFiles. - */ - -mocha.ui = function(ui) { - Mocha.prototype.ui.call(this, ui); - this.suite.emit('pre-require', global, null, this); - return this; -}; - -/** - * Setup mocha with the given setting options. - */ - -mocha.setup = function(opts) { - if (typeof opts === 'string') { - opts = {ui: opts}; - } - for (var opt in opts) { - if (Object.prototype.hasOwnProperty.call(opts, opt)) { - this[opt](opts[opt]); - } - } - return this; -}; - -/** - * Run mocha, returning the Runner. - */ - -mocha.run = function(fn) { - var options = mocha.options; - mocha.globals('location'); - - var query = Mocha.utils.parseQuery(global.location.search || ''); - if (query.grep) { - mocha.grep(query.grep); - } - if (query.fgrep) { - mocha.fgrep(query.fgrep); - } - if (query.invert) { - mocha.invert(); - } - - return Mocha.prototype.run.call(mocha, function(err) { - // The DOM Document is not available in Web Workers. - var document = global.document; - if ( - document && - document.getElementById('mocha') && - options.noHighlighting !== true - ) { - Mocha.utils.highlightTags('code'); - } - if (fn) { - fn(err); - } - }); -}; - -/** - * Expose the process shim. - * https://github.com/mochajs/mocha/pull/916 - */ - -Mocha.process = process; - -/** - * Expose mocha. - */ - -global.Mocha = Mocha; -global.mocha = mocha; - -// this allows test/acceptance/required-tokens.js to pass; thus, -// you can now do `const describe = require('mocha').describe` in a -// browser context (assuming browserification). should fix #880 -module.exports = global; - -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./lib/mocha":14,"_process":69,"browser-stdout":41}],2:[function(require,module,exports){ -(function (process,global){ -'use strict'; - -/** - * Web Notifications module. - * @module Growl - */ - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ -var Date = global.Date; -var setTimeout = global.setTimeout; -var EVENT_RUN_END = require('../runner').constants.EVENT_RUN_END; - -/** - * Checks if browser notification support exists. - * - * @public - * @see {@link https://caniuse.com/#feat=notifications|Browser support (notifications)} - * @see {@link https://caniuse.com/#feat=promises|Browser support (promises)} - * @see {@link Mocha#growl} - * @see {@link Mocha#isGrowlCapable} - * @return {boolean} whether browser notification support exists - */ -exports.isCapable = function() { - var hasNotificationSupport = 'Notification' in window; - var hasPromiseSupport = typeof Promise === 'function'; - return process.browser && hasNotificationSupport && hasPromiseSupport; -}; - -/** - * Implements browser notifications as a pseudo-reporter. - * - * @public - * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/notification|Notification API} - * @see {@link https://developers.google.com/web/fundamentals/push-notifications/display-a-notification|Displaying a Notification} - * @see {@link Growl#isPermitted} - * @see {@link Mocha#_growl} - * @param {Runner} runner - Runner instance. - */ -exports.notify = function(runner) { - var promise = isPermitted(); - - /** - * Attempt notification. - */ - var sendNotification = function() { - // If user hasn't responded yet... "No notification for you!" (Seinfeld) - Promise.race([promise, Promise.resolve(undefined)]) - .then(canNotify) - .then(function() { - display(runner); - }) - .catch(notPermitted); - }; - - runner.once(EVENT_RUN_END, sendNotification); -}; - -/** - * Checks if browser notification is permitted by user. - * - * @private - * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission|Notification.permission} - * @see {@link Mocha#growl} - * @see {@link Mocha#isGrowlPermitted} - * @returns {Promise<boolean>} promise determining if browser notification - * permissible when fulfilled. - */ -function isPermitted() { - var permitted = { - granted: function allow() { - return Promise.resolve(true); - }, - denied: function deny() { - return Promise.resolve(false); - }, - default: function ask() { - return Notification.requestPermission().then(function(permission) { - return permission === 'granted'; - }); - } - }; - - return permitted[Notification.permission](); -} - -/** - * @summary - * Determines if notification should proceed. - * - * @description - * Notification shall <strong>not</strong> proceed unless `value` is true. - * - * `value` will equal one of: - * <ul> - * <li><code>true</code> (from `isPermitted`)</li> - * <li><code>false</code> (from `isPermitted`)</li> - * <li><code>undefined</code> (from `Promise.race`)</li> - * </ul> - * - * @private - * @param {boolean|undefined} value - Determines if notification permissible. - * @returns {Promise<undefined>} Notification can proceed - */ -function canNotify(value) { - if (!value) { - var why = value === false ? 'blocked' : 'unacknowledged'; - var reason = 'not permitted by user (' + why + ')'; - return Promise.reject(new Error(reason)); - } - return Promise.resolve(); -} - -/** - * Displays the notification. - * - * @private - * @param {Runner} runner - Runner instance. - */ -function display(runner) { - var stats = runner.stats; - var symbol = { - cross: '\u274C', - tick: '\u2705' - }; - var logo = require('../../package').notifyLogo; - var _message; - var message; - var title; - - if (stats.failures) { - _message = stats.failures + ' of ' + stats.tests + ' tests failed'; - message = symbol.cross + ' ' + _message; - title = 'Failed'; - } else { - _message = stats.passes + ' tests passed in ' + stats.duration + 'ms'; - message = symbol.tick + ' ' + _message; - title = 'Passed'; - } - - // Send notification - var options = { - badge: logo, - body: message, - dir: 'ltr', - icon: logo, - lang: 'en-US', - name: 'mocha', - requireInteraction: false, - timestamp: Date.now() - }; - var notification = new Notification(title, options); - - // Autoclose after brief delay (makes various browsers act same) - var FORCE_DURATION = 4000; - setTimeout(notification.close.bind(notification), FORCE_DURATION); -} - -/** - * As notifications are tangential to our purpose, just log the error. - * - * @private - * @param {Error} err - Why notification didn't happen. - */ -function notPermitted(err) { - console.error('notification error:', err.message); -} - -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../../package":90,"../runner":34,"_process":69}],3:[function(require,module,exports){ -'use strict'; - -/** - * Expose `Progress`. - */ - -module.exports = Progress; - -/** - * Initialize a new `Progress` indicator. - */ -function Progress() { - this.percent = 0; - this.size(0); - this.fontSize(11); - this.font('helvetica, arial, sans-serif'); -} - -/** - * Set progress size to `size`. - * - * @public - * @param {number} size - * @return {Progress} Progress instance. - */ -Progress.prototype.size = function(size) { - this._size = size; - return this; -}; - -/** - * Set text to `text`. - * - * @public - * @param {string} text - * @return {Progress} Progress instance. - */ -Progress.prototype.text = function(text) { - this._text = text; - return this; -}; - -/** - * Set font size to `size`. - * - * @public - * @param {number} size - * @return {Progress} Progress instance. - */ -Progress.prototype.fontSize = function(size) { - this._fontSize = size; - return this; -}; - -/** - * Set font to `family`. - * - * @param {string} family - * @return {Progress} Progress instance. - */ -Progress.prototype.font = function(family) { - this._font = family; - return this; -}; - -/** - * Update percentage to `n`. - * - * @param {number} n - * @return {Progress} Progress instance. - */ -Progress.prototype.update = function(n) { - this.percent = n; - return this; -}; - -/** - * Draw on `ctx`. - * - * @param {CanvasRenderingContext2d} ctx - * @return {Progress} Progress instance. - */ -Progress.prototype.draw = function(ctx) { - try { - var percent = Math.min(this.percent, 100); - var size = this._size; - var half = size / 2; - var x = half; - var y = half; - var rad = half - 1; - var fontSize = this._fontSize; - - ctx.font = fontSize + 'px ' + this._font; - - var angle = Math.PI * 2 * (percent / 100); - ctx.clearRect(0, 0, size, size); - - // outer circle - ctx.strokeStyle = '#9f9f9f'; - ctx.beginPath(); - ctx.arc(x, y, rad, 0, angle, false); - ctx.stroke(); - - // inner circle - ctx.strokeStyle = '#eee'; - ctx.beginPath(); - ctx.arc(x, y, rad - 1, 0, angle, true); - ctx.stroke(); - - // text - var text = this._text || (percent | 0) + '%'; - var w = ctx.measureText(text).width; - - ctx.fillText(text, x - w / 2 + 1, y + fontSize / 2 - 1); - } catch (ignore) { - // don't fail if we can't render progress - } - return this; -}; - -},{}],4:[function(require,module,exports){ -(function (global){ -'use strict'; - -exports.isatty = function isatty() { - return true; -}; - -exports.getWindowSize = function getWindowSize() { - if ('innerHeight' in global) { - return [global.innerHeight, global.innerWidth]; - } - // In a Web Worker, the DOM Window is not available. - return [640, 480]; -}; - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],5:[function(require,module,exports){ -'use strict'; -/** - * @module Context - */ -/** - * Expose `Context`. - */ - -module.exports = Context; - -/** - * Initialize a new `Context`. - * - * @private - */ -function Context() {} - -/** - * Set or get the context `Runnable` to `runnable`. - * - * @private - * @param {Runnable} runnable - * @return {Context} context - */ -Context.prototype.runnable = function(runnable) { - if (!arguments.length) { - return this._runnable; - } - this.test = this._runnable = runnable; - return this; -}; - -/** - * Set or get test timeout `ms`. - * - * @private - * @param {number} ms - * @return {Context} self - */ -Context.prototype.timeout = function(ms) { - if (!arguments.length) { - return this.runnable().timeout(); - } - this.runnable().timeout(ms); - return this; -}; - -/** - * Set test timeout `enabled`. - * - * @private - * @param {boolean} enabled - * @return {Context} self - */ -Context.prototype.enableTimeouts = function(enabled) { - if (!arguments.length) { - return this.runnable().enableTimeouts(); - } - this.runnable().enableTimeouts(enabled); - return this; -}; - -/** - * Set or get test slowness threshold `ms`. - * - * @private - * @param {number} ms - * @return {Context} self - */ -Context.prototype.slow = function(ms) { - if (!arguments.length) { - return this.runnable().slow(); - } - this.runnable().slow(ms); - return this; -}; - -/** - * Mark a test as skipped. - * - * @private - * @throws Pending - */ -Context.prototype.skip = function() { - this.runnable().skip(); -}; - -/** - * Set or get a number of allowed retries on failed tests - * - * @private - * @param {number} n - * @return {Context} self - */ -Context.prototype.retries = function(n) { - if (!arguments.length) { - return this.runnable().retries(); - } - this.runnable().retries(n); - return this; -}; - -},{}],6:[function(require,module,exports){ -'use strict'; -/** - * @module Errors - */ -/** - * Factory functions to create throwable error objects - */ - -/** - * Creates an error object to be thrown when no files to be tested could be found using specified pattern. - * - * @public - * @param {string} message - Error message to be displayed. - * @param {string} pattern - User-specified argument value. - * @returns {Error} instance detailing the error condition - */ -function createNoFilesMatchPatternError(message, pattern) { - var err = new Error(message); - err.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN'; - err.pattern = pattern; - return err; -} - -/** - * Creates an error object to be thrown when the reporter specified in the options was not found. - * - * @public - * @param {string} message - Error message to be displayed. - * @param {string} reporter - User-specified reporter value. - * @returns {Error} instance detailing the error condition - */ -function createInvalidReporterError(message, reporter) { - var err = new TypeError(message); - err.code = 'ERR_MOCHA_INVALID_REPORTER'; - err.reporter = reporter; - return err; -} - -/** - * Creates an error object to be thrown when the interface specified in the options was not found. - * - * @public - * @param {string} message - Error message to be displayed. - * @param {string} ui - User-specified interface value. - * @returns {Error} instance detailing the error condition - */ -function createInvalidInterfaceError(message, ui) { - var err = new Error(message); - err.code = 'ERR_MOCHA_INVALID_INTERFACE'; - err.interface = ui; - return err; -} - -/** - * Creates an error object to be thrown when a behavior, option, or parameter is unsupported. - * - * @public - * @param {string} message - Error message to be displayed. - * @returns {Error} instance detailing the error condition - */ -function createUnsupportedError(message) { - var err = new Error(message); - err.code = 'ERR_MOCHA_UNSUPPORTED'; - return err; -} - -/** - * Creates an error object to be thrown when an argument is missing. - * - * @public - * @param {string} message - Error message to be displayed. - * @param {string} argument - Argument name. - * @param {string} expected - Expected argument datatype. - * @returns {Error} instance detailing the error condition - */ -function createMissingArgumentError(message, argument, expected) { - return createInvalidArgumentTypeError(message, argument, expected); -} - -/** - * Creates an error object to be thrown when an argument did not use the supported type - * - * @public - * @param {string} message - Error message to be displayed. - * @param {string} argument - Argument name. - * @param {string} expected - Expected argument datatype. - * @returns {Error} instance detailing the error condition - */ -function createInvalidArgumentTypeError(message, argument, expected) { - var err = new TypeError(message); - err.code = 'ERR_MOCHA_INVALID_ARG_TYPE'; - err.argument = argument; - err.expected = expected; - err.actual = typeof argument; - return err; -} - -/** - * Creates an error object to be thrown when an argument did not use the supported value - * - * @public - * @param {string} message - Error message to be displayed. - * @param {string} argument - Argument name. - * @param {string} value - Argument value. - * @param {string} [reason] - Why value is invalid. - * @returns {Error} instance detailing the error condition - */ -function createInvalidArgumentValueError(message, argument, value, reason) { - var err = new TypeError(message); - err.code = 'ERR_MOCHA_INVALID_ARG_VALUE'; - err.argument = argument; - err.value = value; - err.reason = typeof reason !== 'undefined' ? reason : 'is invalid'; - return err; -} - -/** - * Creates an error object to be thrown when an exception was caught, but the `Error` is falsy or undefined. - * - * @public - * @param {string} message - Error message to be displayed. - * @returns {Error} instance detailing the error condition - */ -function createInvalidExceptionError(message, value) { - var err = new Error(message); - err.code = 'ERR_MOCHA_INVALID_EXCEPTION'; - err.valueType = typeof value; - err.value = value; - return err; -} - -module.exports = { - createInvalidArgumentTypeError: createInvalidArgumentTypeError, - createInvalidArgumentValueError: createInvalidArgumentValueError, - createInvalidExceptionError: createInvalidExceptionError, - createInvalidInterfaceError: createInvalidInterfaceError, - createInvalidReporterError: createInvalidReporterError, - createMissingArgumentError: createMissingArgumentError, - createNoFilesMatchPatternError: createNoFilesMatchPatternError, - createUnsupportedError: createUnsupportedError -}; - -},{}],7:[function(require,module,exports){ -'use strict'; - -var Runnable = require('./runnable'); -var inherits = require('./utils').inherits; - -/** - * Expose `Hook`. - */ - -module.exports = Hook; - -/** - * Initialize a new `Hook` with the given `title` and callback `fn` - * - * @class - * @extends Runnable - * @param {String} title - * @param {Function} fn - */ -function Hook(title, fn) { - Runnable.call(this, title, fn); - this.type = 'hook'; -} - -/** - * Inherit from `Runnable.prototype`. - */ -inherits(Hook, Runnable); - -/** - * Get or set the test `err`. - * - * @memberof Hook - * @public - * @param {Error} err - * @return {Error} - */ -Hook.prototype.error = function(err) { - if (!arguments.length) { - err = this._error; - this._error = null; - return err; - } - - this._error = err; -}; - -},{"./runnable":33,"./utils":38}],8:[function(require,module,exports){ -'use strict'; - -var Test = require('../test'); -var EVENT_FILE_PRE_REQUIRE = require('../suite').constants - .EVENT_FILE_PRE_REQUIRE; - -/** - * BDD-style interface: - * - * describe('Array', function() { - * describe('#indexOf()', function() { - * it('should return -1 when not present', function() { - * // ... - * }); - * - * it('should return the index when present', function() { - * // ... - * }); - * }); - * }); - * - * @param {Suite} suite Root suite. - */ -module.exports = function bddInterface(suite) { - var suites = [suite]; - - suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { - var common = require('./common')(suites, context, mocha); - - context.before = common.before; - context.after = common.after; - context.beforeEach = common.beforeEach; - context.afterEach = common.afterEach; - context.run = mocha.options.delay && common.runWithSuite(suite); - /** - * Describe a "suite" with the given `title` - * and callback `fn` containing nested suites - * and/or tests. - */ - - context.describe = context.context = function(title, fn) { - return common.suite.create({ - title: title, - file: file, - fn: fn - }); - }; - - /** - * Pending describe. - */ - - context.xdescribe = context.xcontext = context.describe.skip = function( - title, - fn - ) { - return common.suite.skip({ - title: title, - file: file, - fn: fn - }); - }; - - /** - * Exclusive suite. - */ - - context.describe.only = function(title, fn) { - return common.suite.only({ - title: title, - file: file, - fn: fn - }); - }; - - /** - * Describe a specification or test-case - * with the given `title` and callback `fn` - * acting as a thunk. - */ - - context.it = context.specify = function(title, fn) { - var suite = suites[0]; - if (suite.isPending()) { - fn = null; - } - var test = new Test(title, fn); - test.file = file; - suite.addTest(test); - return test; - }; - - /** - * Exclusive test-case. - */ - - context.it.only = function(title, fn) { - return common.test.only(mocha, context.it(title, fn)); - }; - - /** - * Pending test case. - */ - - context.xit = context.xspecify = context.it.skip = function(title) { - return context.it(title); - }; - - /** - * Number of attempts to retry. - */ - context.it.retries = function(n) { - context.retries(n); - }; - }); -}; - -module.exports.description = 'BDD or RSpec style [default]'; - -},{"../suite":36,"../test":37,"./common":9}],9:[function(require,module,exports){ -'use strict'; - -var Suite = require('../suite'); -var errors = require('../errors'); -var createMissingArgumentError = errors.createMissingArgumentError; - -/** - * Functions common to more than one interface. - * - * @param {Suite[]} suites - * @param {Context} context - * @param {Mocha} mocha - * @return {Object} An object containing common functions. - */ -module.exports = function(suites, context, mocha) { - /** - * Check if the suite should be tested. - * - * @private - * @param {Suite} suite - suite to check - * @returns {boolean} - */ - function shouldBeTested(suite) { - return ( - !mocha.options.grep || - (mocha.options.grep && - mocha.options.grep.test(suite.fullTitle()) && - !mocha.options.invert) - ); - } - - return { - /** - * This is only present if flag --delay is passed into Mocha. It triggers - * root suite execution. - * - * @param {Suite} suite The root suite. - * @return {Function} A function which runs the root suite - */ - runWithSuite: function runWithSuite(suite) { - return function run() { - suite.run(); - }; - }, - - /** - * Execute before running tests. - * - * @param {string} name - * @param {Function} fn - */ - before: function(name, fn) { - suites[0].beforeAll(name, fn); - }, - - /** - * Execute after running tests. - * - * @param {string} name - * @param {Function} fn - */ - after: function(name, fn) { - suites[0].afterAll(name, fn); - }, - - /** - * Execute before each test case. - * - * @param {string} name - * @param {Function} fn - */ - beforeEach: function(name, fn) { - suites[0].beforeEach(name, fn); - }, - - /** - * Execute after each test case. - * - * @param {string} name - * @param {Function} fn - */ - afterEach: function(name, fn) { - suites[0].afterEach(name, fn); - }, - - suite: { - /** - * Create an exclusive Suite; convenience function - * See docstring for create() below. - * - * @param {Object} opts - * @returns {Suite} - */ - only: function only(opts) { - opts.isOnly = true; - return this.create(opts); - }, - - /** - * Create a Suite, but skip it; convenience function - * See docstring for create() below. - * - * @param {Object} opts - * @returns {Suite} - */ - skip: function skip(opts) { - opts.pending = true; - return this.create(opts); - }, - - /** - * Creates a suite. - * - * @param {Object} opts Options - * @param {string} opts.title Title of Suite - * @param {Function} [opts.fn] Suite Function (not always applicable) - * @param {boolean} [opts.pending] Is Suite pending? - * @param {string} [opts.file] Filepath where this Suite resides - * @param {boolean} [opts.isOnly] Is Suite exclusive? - * @returns {Suite} - */ - create: function create(opts) { - var suite = Suite.create(suites[0], opts.title); - suite.pending = Boolean(opts.pending); - suite.file = opts.file; - suites.unshift(suite); - if (opts.isOnly) { - if (mocha.options.forbidOnly && shouldBeTested(suite)) { - throw new Error('`.only` forbidden'); - } - - suite.parent.appendOnlySuite(suite); - } - if (suite.pending) { - if (mocha.options.forbidPending && shouldBeTested(suite)) { - throw new Error('Pending test forbidden'); - } - } - if (typeof opts.fn === 'function') { - opts.fn.call(suite); - suites.shift(); - } else if (typeof opts.fn === 'undefined' && !suite.pending) { - throw createMissingArgumentError( - 'Suite "' + - suite.fullTitle() + - '" was defined but no callback was supplied. ' + - 'Supply a callback or explicitly skip the suite.', - 'callback', - 'function' - ); - } else if (!opts.fn && suite.pending) { - suites.shift(); - } - - return suite; - } - }, - - test: { - /** - * Exclusive test-case. - * - * @param {Object} mocha - * @param {Function} test - * @returns {*} - */ - only: function(mocha, test) { - test.parent.appendOnlyTest(test); - return test; - }, - - /** - * Pending test case. - * - * @param {string} title - */ - skip: function(title) { - context.test(title); - }, - - /** - * Number of retry attempts - * - * @param {number} n - */ - retries: function(n) { - context.retries(n); - } - } - }; -}; - -},{"../errors":6,"../suite":36}],10:[function(require,module,exports){ -'use strict'; -var Suite = require('../suite'); -var Test = require('../test'); - -/** - * Exports-style (as Node.js module) interface: - * - * exports.Array = { - * '#indexOf()': { - * 'should return -1 when the value is not present': function() { - * - * }, - * - * 'should return the correct index when the value is present': function() { - * - * } - * } - * }; - * - * @param {Suite} suite Root suite. - */ -module.exports = function(suite) { - var suites = [suite]; - - suite.on(Suite.constants.EVENT_FILE_REQUIRE, visit); - - function visit(obj, file) { - var suite; - for (var key in obj) { - if (typeof obj[key] === 'function') { - var fn = obj[key]; - switch (key) { - case 'before': - suites[0].beforeAll(fn); - break; - case 'after': - suites[0].afterAll(fn); - break; - case 'beforeEach': - suites[0].beforeEach(fn); - break; - case 'afterEach': - suites[0].afterEach(fn); - break; - default: - var test = new Test(key, fn); - test.file = file; - suites[0].addTest(test); - } - } else { - suite = Suite.create(suites[0], key); - suites.unshift(suite); - visit(obj[key], file); - suites.shift(); - } - } - } -}; - -module.exports.description = 'Node.js module ("exports") style'; - -},{"../suite":36,"../test":37}],11:[function(require,module,exports){ -'use strict'; - -exports.bdd = require('./bdd'); -exports.tdd = require('./tdd'); -exports.qunit = require('./qunit'); -exports.exports = require('./exports'); - -},{"./bdd":8,"./exports":10,"./qunit":12,"./tdd":13}],12:[function(require,module,exports){ -'use strict'; - -var Test = require('../test'); -var EVENT_FILE_PRE_REQUIRE = require('../suite').constants - .EVENT_FILE_PRE_REQUIRE; - -/** - * QUnit-style interface: - * - * suite('Array'); - * - * test('#length', function() { - * var arr = [1,2,3]; - * ok(arr.length == 3); - * }); - * - * test('#indexOf()', function() { - * var arr = [1,2,3]; - * ok(arr.indexOf(1) == 0); - * ok(arr.indexOf(2) == 1); - * ok(arr.indexOf(3) == 2); - * }); - * - * suite('String'); - * - * test('#length', function() { - * ok('foo'.length == 3); - * }); - * - * @param {Suite} suite Root suite. - */ -module.exports = function qUnitInterface(suite) { - var suites = [suite]; - - suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { - var common = require('./common')(suites, context, mocha); - - context.before = common.before; - context.after = common.after; - context.beforeEach = common.beforeEach; - context.afterEach = common.afterEach; - context.run = mocha.options.delay && common.runWithSuite(suite); - /** - * Describe a "suite" with the given `title`. - */ - - context.suite = function(title) { - if (suites.length > 1) { - suites.shift(); - } - return common.suite.create({ - title: title, - file: file, - fn: false - }); - }; - - /** - * Exclusive Suite. - */ - - context.suite.only = function(title) { - if (suites.length > 1) { - suites.shift(); - } - return common.suite.only({ - title: title, - file: file, - fn: false - }); - }; - - /** - * Describe a specification or test-case - * with the given `title` and callback `fn` - * acting as a thunk. - */ - - context.test = function(title, fn) { - var test = new Test(title, fn); - test.file = file; - suites[0].addTest(test); - return test; - }; - - /** - * Exclusive test-case. - */ - - context.test.only = function(title, fn) { - return common.test.only(mocha, context.test(title, fn)); - }; - - context.test.skip = common.test.skip; - context.test.retries = common.test.retries; - }); -}; - -module.exports.description = 'QUnit style'; - -},{"../suite":36,"../test":37,"./common":9}],13:[function(require,module,exports){ -'use strict'; - -var Test = require('../test'); -var EVENT_FILE_PRE_REQUIRE = require('../suite').constants - .EVENT_FILE_PRE_REQUIRE; - -/** - * TDD-style interface: - * - * suite('Array', function() { - * suite('#indexOf()', function() { - * suiteSetup(function() { - * - * }); - * - * test('should return -1 when not present', function() { - * - * }); - * - * test('should return the index when present', function() { - * - * }); - * - * suiteTeardown(function() { - * - * }); - * }); - * }); - * - * @param {Suite} suite Root suite. - */ -module.exports = function(suite) { - var suites = [suite]; - - suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { - var common = require('./common')(suites, context, mocha); - - context.setup = common.beforeEach; - context.teardown = common.afterEach; - context.suiteSetup = common.before; - context.suiteTeardown = common.after; - context.run = mocha.options.delay && common.runWithSuite(suite); - - /** - * Describe a "suite" with the given `title` and callback `fn` containing - * nested suites and/or tests. - */ - context.suite = function(title, fn) { - return common.suite.create({ - title: title, - file: file, - fn: fn - }); - }; - - /** - * Pending suite. - */ - context.suite.skip = function(title, fn) { - return common.suite.skip({ - title: title, - file: file, - fn: fn - }); - }; - - /** - * Exclusive test-case. - */ - context.suite.only = function(title, fn) { - return common.suite.only({ - title: title, - file: file, - fn: fn - }); - }; - - /** - * Describe a specification or test-case with the given `title` and - * callback `fn` acting as a thunk. - */ - context.test = function(title, fn) { - var suite = suites[0]; - if (suite.isPending()) { - fn = null; - } - var test = new Test(title, fn); - test.file = file; - suite.addTest(test); - return test; - }; - - /** - * Exclusive test-case. - */ - - context.test.only = function(title, fn) { - return common.test.only(mocha, context.test(title, fn)); - }; - - context.test.skip = common.test.skip; - context.test.retries = common.test.retries; - }); -}; - -module.exports.description = - 'traditional "suite"/"test" instead of BDD\'s "describe"/"it"'; - -},{"../suite":36,"../test":37,"./common":9}],14:[function(require,module,exports){ -(function (process,global){ -'use strict'; - -/*! - * mocha - * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca> - * MIT Licensed - */ - -var escapeRe = require('escape-string-regexp'); -var path = require('path'); -var builtinReporters = require('./reporters'); -var growl = require('./growl'); -var utils = require('./utils'); -var mocharc = require('./mocharc.json'); -var errors = require('./errors'); -var Suite = require('./suite'); -var esmUtils = utils.supportsEsModules() ? require('./esm-utils') : undefined; -var createStatsCollector = require('./stats-collector'); -var createInvalidReporterError = errors.createInvalidReporterError; -var createInvalidInterfaceError = errors.createInvalidInterfaceError; -var EVENT_FILE_PRE_REQUIRE = Suite.constants.EVENT_FILE_PRE_REQUIRE; -var EVENT_FILE_POST_REQUIRE = Suite.constants.EVENT_FILE_POST_REQUIRE; -var EVENT_FILE_REQUIRE = Suite.constants.EVENT_FILE_REQUIRE; -var sQuote = utils.sQuote; - -exports = module.exports = Mocha; - -/** - * To require local UIs and reporters when running in node. - */ - -if (!process.browser) { - var cwd = process.cwd(); - module.paths.push(cwd, path.join(cwd, 'node_modules')); -} - -/** - * Expose internals. - */ - -/** - * @public - * @class utils - * @memberof Mocha - */ -exports.utils = utils; -exports.interfaces = require('./interfaces'); -/** - * @public - * @memberof Mocha - */ -exports.reporters = builtinReporters; -exports.Runnable = require('./runnable'); -exports.Context = require('./context'); -/** - * - * @memberof Mocha - */ -exports.Runner = require('./runner'); -exports.Suite = Suite; -exports.Hook = require('./hook'); -exports.Test = require('./test'); - -/** - * Constructs a new Mocha instance with `options`. - * - * @public - * @class Mocha - * @param {Object} [options] - Settings object. - * @param {boolean} [options.allowUncaught] - Propagate uncaught errors? - * @param {boolean} [options.asyncOnly] - Force `done` callback or promise? - * @param {boolean} [options.bail] - Bail after first test failure? - * @param {boolean} [options.checkLeaks] - Check for global variable leaks? - * @param {boolean} [options.color] - Color TTY output from reporter? - * @param {boolean} [options.delay] - Delay root suite execution? - * @param {boolean} [options.diff] - Show diff on failure? - * @param {string} [options.fgrep] - Test filter given string. - * @param {boolean} [options.forbidOnly] - Tests marked `only` fail the suite? - * @param {boolean} [options.forbidPending] - Pending tests fail the suite? - * @param {boolean} [options.fullTrace] - Full stacktrace upon failure? - * @param {string[]} [options.global] - Variables expected in global scope. - * @param {RegExp|string} [options.grep] - Test filter given regular expression. - * @param {boolean} [options.growl] - Enable desktop notifications? - * @param {boolean} [options.inlineDiffs] - Display inline diffs? - * @param {boolean} [options.invert] - Invert test filter matches? - * @param {boolean} [options.noHighlighting] - Disable syntax highlighting? - * @param {string|constructor} [options.reporter] - Reporter name or constructor. - * @param {Object} [options.reporterOption] - Reporter settings object. - * @param {number} [options.retries] - Number of times to retry failed tests. - * @param {number} [options.slow] - Slow threshold value. - * @param {number|string} [options.timeout] - Timeout threshold value. - * @param {string} [options.ui] - Interface name. - */ -function Mocha(options) { - options = utils.assign({}, mocharc, options || {}); - this.files = []; - this.options = options; - // root suite - this.suite = new exports.Suite('', new exports.Context(), true); - - this.grep(options.grep) - .fgrep(options.fgrep) - .ui(options.ui) - .reporter( - options.reporter, - options.reporterOption || options.reporterOptions // reporterOptions was previously the only way to specify options to reporter - ) - .slow(options.slow) - .global(options.global); - - // this guard exists because Suite#timeout does not consider `undefined` to be valid input - if (typeof options.timeout !== 'undefined') { - this.timeout(options.timeout === false ? 0 : options.timeout); - } - - if ('retries' in options) { - this.retries(options.retries); - } - - [ - 'allowUncaught', - 'asyncOnly', - 'bail', - 'checkLeaks', - 'color', - 'delay', - 'diff', - 'forbidOnly', - 'forbidPending', - 'fullTrace', - 'growl', - 'inlineDiffs', - 'invert' - ].forEach(function(opt) { - if (options[opt]) { - this[opt](); - } - }, this); -} - -/** - * Enables or disables bailing on the first failure. - * - * @public - * @see [CLI option](../#-bail-b) - * @param {boolean} [bail=true] - Whether to bail on first error. - * @returns {Mocha} this - * @chainable - */ -Mocha.prototype.bail = function(bail) { - this.suite.bail(bail !== false); - return this; -}; - -/** - * @summary - * Adds `file` to be loaded for execution. - * - * @description - * Useful for generic setup code that must be included within test suite. - * - * @public - * @see [CLI option](../#-file-filedirectoryglob) - * @param {string} file - Pathname of file to be loaded. - * @returns {Mocha} this - * @chainable - */ -Mocha.prototype.addFile = function(file) { - this.files.push(file); - return this; -}; - -/** - * Sets reporter to `reporter`, defaults to "spec". - * - * @public - * @see [CLI option](../#-reporter-name-r-name) - * @see [Reporters](../#reporters) - * @param {String|Function} reporter - Reporter name or constructor. - * @param {Object} [reporterOptions] - Options used to configure the reporter. - * @returns {Mocha} this - * @chainable - * @throws {Error} if requested reporter cannot be loaded - * @example - * - * // Use XUnit reporter and direct its output to file - * mocha.reporter('xunit', { output: '/path/to/testspec.xunit.xml' }); - */ -Mocha.prototype.reporter = function(reporter, reporterOptions) { - if (typeof reporter === 'function') { - this._reporter = reporter; - } else { - reporter = reporter || 'spec'; - var _reporter; - // Try to load a built-in reporter. - if (builtinReporters[reporter]) { - _reporter = builtinReporters[reporter]; - } - // Try to load reporters from process.cwd() and node_modules - if (!_reporter) { - try { - _reporter = require(reporter); - } catch (err) { - if ( - err.code !== 'MODULE_NOT_FOUND' || - err.message.indexOf('Cannot find module') !== -1 - ) { - // Try to load reporters from a path (absolute or relative) - try { - _reporter = require(path.resolve(process.cwd(), reporter)); - } catch (_err) { - _err.code !== 'MODULE_NOT_FOUND' || - _err.message.indexOf('Cannot find module') !== -1 - ? console.warn(sQuote(reporter) + ' reporter not found') - : console.warn( - sQuote(reporter) + - ' reporter blew up with error:\n' + - err.stack - ); - } - } else { - console.warn( - sQuote(reporter) + ' reporter blew up with error:\n' + err.stack - ); - } - } - } - if (!_reporter) { - throw createInvalidReporterError( - 'invalid reporter ' + sQuote(reporter), - reporter - ); - } - this._reporter = _reporter; - } - this.options.reporterOption = reporterOptions; - // alias option name is used in public reporters xunit/tap/progress - this.options.reporterOptions = reporterOptions; - return this; -}; - -/** - * Sets test UI `name`, defaults to "bdd". - * - * @public - * @see [CLI option](../#-ui-name-u-name) - * @see [Interface DSLs](../#interfaces) - * @param {string|Function} [ui=bdd] - Interface name or class. - * @returns {Mocha} this - * @chainable - * @throws {Error} if requested interface cannot be loaded - */ -Mocha.prototype.ui = function(ui) { - var bindInterface; - if (typeof ui === 'function') { - bindInterface = ui; - } else { - ui = ui || 'bdd'; - bindInterface = exports.interfaces[ui]; - if (!bindInterface) { - try { - bindInterface = require(ui); - } catch (err) { - throw createInvalidInterfaceError( - 'invalid interface ' + sQuote(ui), - ui - ); - } - } - } - bindInterface(this.suite); - - this.suite.on(EVENT_FILE_PRE_REQUIRE, function(context) { - exports.afterEach = context.afterEach || context.teardown; - exports.after = context.after || context.suiteTeardown; - exports.beforeEach = context.beforeEach || context.setup; - exports.before = context.before || context.suiteSetup; - exports.describe = context.describe || context.suite; - exports.it = context.it || context.test; - exports.xit = context.xit || (context.test && context.test.skip); - exports.setup = context.setup || context.beforeEach; - exports.suiteSetup = context.suiteSetup || context.before; - exports.suiteTeardown = context.suiteTeardown || context.after; - exports.suite = context.suite || context.describe; - exports.teardown = context.teardown || context.afterEach; - exports.test = context.test || context.it; - exports.run = context.run; - }); - - return this; -}; - -/** - * Loads `files` prior to execution. Does not support ES Modules. - * - * @description - * The implementation relies on Node's `require` to execute - * the test interface functions and will be subject to its cache. - * Supports only CommonJS modules. To load ES modules, use Mocha#loadFilesAsync. - * - * @private - * @see {@link Mocha#addFile} - * @see {@link Mocha#run} - * @see {@link Mocha#unloadFiles} - * @see {@link Mocha#loadFilesAsync} - * @param {Function} [fn] - Callback invoked upon completion. - */ -Mocha.prototype.loadFiles = function(fn) { - var self = this; - var suite = this.suite; - this.files.forEach(function(file) { - file = path.resolve(file); - suite.emit(EVENT_FILE_PRE_REQUIRE, global, file, self); - suite.emit(EVENT_FILE_REQUIRE, require(file), file, self); - suite.emit(EVENT_FILE_POST_REQUIRE, global, file, self); - }); - fn && fn(); -}; - -/** - * Loads `files` prior to execution. Supports Node ES Modules. - * - * @description - * The implementation relies on Node's `require` and `import` to execute - * the test interface functions and will be subject to its cache. - * Supports both CJS and ESM modules. - * - * @public - * @see {@link Mocha#addFile} - * @see {@link Mocha#run} - * @see {@link Mocha#unloadFiles} - * @returns {Promise} - * @example - * - * // loads ESM (and CJS) test files asynchronously, then runs root suite - * mocha.loadFilesAsync() - * .then(() => mocha.run(failures => process.exitCode = failures ? 1 : 0)) - * .catch(() => process.exitCode = 1); - */ -Mocha.prototype.loadFilesAsync = function() { - var self = this; - var suite = this.suite; - this.loadAsync = true; - - if (!esmUtils) { - return new Promise(function(resolve) { - self.loadFiles(resolve); - }); - } - - return esmUtils.loadFilesAsync( - this.files, - function(file) { - suite.emit(EVENT_FILE_PRE_REQUIRE, global, file, self); - }, - function(file, resultModule) { - suite.emit(EVENT_FILE_REQUIRE, resultModule, file, self); - suite.emit(EVENT_FILE_POST_REQUIRE, global, file, self); - } - ); -}; - -/** - * Removes a previously loaded file from Node's `require` cache. - * - * @private - * @static - * @see {@link Mocha#unloadFiles} - * @param {string} file - Pathname of file to be unloaded. - */ -Mocha.unloadFile = function(file) { - delete require.cache[require.resolve(file)]; -}; - -/** - * Unloads `files` from Node's `require` cache. - * - * @description - * This allows required files to be "freshly" reloaded, providing the ability - * to reuse a Mocha instance programmatically. - * Note: does not clear ESM module files from the cache - * - * <strong>Intended for consumers — not used internally</strong> - * - * @public - * @see {@link Mocha#run} - * @returns {Mocha} this - * @chainable - */ -Mocha.prototype.unloadFiles = function() { - this.files.forEach(Mocha.unloadFile); - return this; -}; - -/** - * Sets `grep` filter after escaping RegExp special characters. - * - * @public - * @see {@link Mocha#grep} - * @param {string} str - Value to be converted to a regexp. - * @returns {Mocha} this - * @chainable - * @example - * - * // Select tests whose full title begins with `"foo"` followed by a period - * mocha.fgrep('foo.'); - */ -Mocha.prototype.fgrep = function(str) { - if (!str) { - return this; - } - return this.grep(new RegExp(escapeRe(str))); -}; - -/** - * @summary - * Sets `grep` filter used to select specific tests for execution. - * - * @description - * If `re` is a regexp-like string, it will be converted to regexp. - * The regexp is tested against the full title of each test (i.e., the - * name of the test preceded by titles of each its ancestral suites). - * As such, using an <em>exact-match</em> fixed pattern against the - * test name itself will not yield any matches. - * <br> - * <strong>Previous filter value will be overwritten on each call!</strong> - * - * @public - * @see [CLI option](../#-grep-regexp-g-regexp) - * @see {@link Mocha#fgrep} - * @see {@link Mocha#invert} - * @param {RegExp|String} re - Regular expression used to select tests. - * @return {Mocha} this - * @chainable - * @example - * - * // Select tests whose full title contains `"match"`, ignoring case - * mocha.grep(/match/i); - * @example - * - * // Same as above but with regexp-like string argument - * mocha.grep('/match/i'); - * @example - * - * // ## Anti-example - * // Given embedded test `it('only-this-test')`... - * mocha.grep('/^only-this-test$/'); // NO! Use `.only()` to do this! - */ -Mocha.prototype.grep = function(re) { - if (utils.isString(re)) { - // extract args if it's regex-like, i.e: [string, pattern, flag] - var arg = re.match(/^\/(.*)\/(g|i|)$|.*/); - this.options.grep = new RegExp(arg[1] || arg[0], arg[2]); - } else { - this.options.grep = re; - } - return this; -}; - -/** - * Inverts `grep` matches. - * - * @public - * @see {@link Mocha#grep} - * @return {Mocha} this - * @chainable - * @example - * - * // Select tests whose full title does *not* contain `"match"`, ignoring case - * mocha.grep(/match/i).invert(); - */ -Mocha.prototype.invert = function() { - this.options.invert = true; - return this; -}; - -/** - * Enables or disables ignoring global leaks. - * - * @deprecated since v7.0.0 - * @public - * @see {@link Mocha#checkLeaks} - * @param {boolean} [ignoreLeaks=false] - Whether to ignore global leaks. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.ignoreLeaks = function(ignoreLeaks) { - utils.deprecate( - '"ignoreLeaks()" is DEPRECATED, please use "checkLeaks()" instead.' - ); - this.options.checkLeaks = !ignoreLeaks; - return this; -}; - -/** - * Enables or disables checking for global variables leaked while running tests. - * - * @public - * @see [CLI option](../#-check-leaks) - * @param {boolean} [checkLeaks=true] - Whether to check for global variable leaks. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.checkLeaks = function(checkLeaks) { - this.options.checkLeaks = checkLeaks !== false; - return this; -}; - -/** - * Displays full stack trace upon test failure. - * - * @public - * @see [CLI option](../#-full-trace) - * @param {boolean} [fullTrace=true] - Whether to print full stacktrace upon failure. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.fullTrace = function(fullTrace) { - this.options.fullTrace = fullTrace !== false; - return this; -}; - -/** - * Enables desktop notification support if prerequisite software installed. - * - * @public - * @see [CLI option](../#-growl-g) - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.growl = function() { - this.options.growl = this.isGrowlCapable(); - if (!this.options.growl) { - var detail = process.browser - ? 'notification support not available in this browser...' - : 'notification support prerequisites not installed...'; - console.error(detail + ' cannot enable!'); - } - return this; -}; - -/** - * @summary - * Determines if Growl support seems likely. - * - * @description - * <strong>Not available when run in browser.</strong> - * - * @private - * @see {@link Growl#isCapable} - * @see {@link Mocha#growl} - * @return {boolean} whether Growl support can be expected - */ -Mocha.prototype.isGrowlCapable = growl.isCapable; - -/** - * Implements desktop notifications using a pseudo-reporter. - * - * @private - * @see {@link Mocha#growl} - * @see {@link Growl#notify} - * @param {Runner} runner - Runner instance. - */ -Mocha.prototype._growl = growl.notify; - -/** - * Specifies whitelist of variable names to be expected in global scope. - * - * @public - * @see [CLI option](../#-global-variable-name) - * @see {@link Mocha#checkLeaks} - * @param {String[]|String} global - Accepted global variable name(s). - * @return {Mocha} this - * @chainable - * @example - * - * // Specify variables to be expected in global scope - * mocha.global(['jQuery', 'MyLib']); - */ -Mocha.prototype.global = function(global) { - this.options.global = (this.options.global || []) - .concat(global) - .filter(Boolean) - .filter(function(elt, idx, arr) { - return arr.indexOf(elt) === idx; - }); - return this; -}; -// for backwards compability, 'globals' is an alias of 'global' -Mocha.prototype.globals = Mocha.prototype.global; - -/** - * Enables or disables TTY color output by screen-oriented reporters. - * - * @deprecated since v7.0.0 - * @public - * @see {@link Mocha#color} - * @param {boolean} colors - Whether to enable color output. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.useColors = function(colors) { - utils.deprecate('"useColors()" is DEPRECATED, please use "color()" instead.'); - if (colors !== undefined) { - this.options.color = colors; - } - return this; -}; - -/** - * Enables or disables TTY color output by screen-oriented reporters. - * - * @public - * @see [CLI option](../#-color-c-colors) - * @param {boolean} [color=true] - Whether to enable color output. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.color = function(color) { - this.options.color = color !== false; - return this; -}; - -/** - * Determines if reporter should use inline diffs (rather than +/-) - * in test failure output. - * - * @deprecated since v7.0.0 - * @public - * @see {@link Mocha#inlineDiffs} - * @param {boolean} [inlineDiffs=false] - Whether to use inline diffs. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.useInlineDiffs = function(inlineDiffs) { - utils.deprecate( - '"useInlineDiffs()" is DEPRECATED, please use "inlineDiffs()" instead.' - ); - this.options.inlineDiffs = inlineDiffs !== undefined && inlineDiffs; - return this; -}; - -/** - * Enables or disables reporter to use inline diffs (rather than +/-) - * in test failure output. - * - * @public - * @see [CLI option](../#-inline-diffs) - * @param {boolean} [inlineDiffs=true] - Whether to use inline diffs. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.inlineDiffs = function(inlineDiffs) { - this.options.inlineDiffs = inlineDiffs !== false; - return this; -}; - -/** - * Determines if reporter should include diffs in test failure output. - * - * @deprecated since v7.0.0 - * @public - * @see {@link Mocha#diff} - * @param {boolean} [hideDiff=false] - Whether to hide diffs. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.hideDiff = function(hideDiff) { - utils.deprecate('"hideDiff()" is DEPRECATED, please use "diff()" instead.'); - this.options.diff = !(hideDiff === true); - return this; -}; - -/** - * Enables or disables reporter to include diff in test failure output. - * - * @public - * @see [CLI option](../#-diff) - * @param {boolean} [diff=true] - Whether to show diff on failure. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.diff = function(diff) { - this.options.diff = diff !== false; - return this; -}; - -/** - * @summary - * Sets timeout threshold value. - * - * @description - * A string argument can use shorthand (such as "2s") and will be converted. - * If the value is `0`, timeouts will be disabled. - * - * @public - * @see [CLI option](../#-timeout-ms-t-ms) - * @see [Timeouts](../#timeouts) - * @see {@link Mocha#enableTimeouts} - * @param {number|string} msecs - Timeout threshold value. - * @return {Mocha} this - * @chainable - * @example - * - * // Sets timeout to one second - * mocha.timeout(1000); - * @example - * - * // Same as above but using string argument - * mocha.timeout('1s'); - */ -Mocha.prototype.timeout = function(msecs) { - this.suite.timeout(msecs); - return this; -}; - -/** - * Sets the number of times to retry failed tests. - * - * @public - * @see [CLI option](../#-retries-n) - * @see [Retry Tests](../#retry-tests) - * @param {number} retry - Number of times to retry failed tests. - * @return {Mocha} this - * @chainable - * @example - * - * // Allow any failed test to retry one more time - * mocha.retries(1); - */ -Mocha.prototype.retries = function(n) { - this.suite.retries(n); - return this; -}; - -/** - * Sets slowness threshold value. - * - * @public - * @see [CLI option](../#-slow-ms-s-ms) - * @param {number} msecs - Slowness threshold value. - * @return {Mocha} this - * @chainable - * @example - * - * // Sets "slow" threshold to half a second - * mocha.slow(500); - * @example - * - * // Same as above but using string argument - * mocha.slow('0.5s'); - */ -Mocha.prototype.slow = function(msecs) { - this.suite.slow(msecs); - return this; -}; - -/** - * Enables or disables timeouts. - * - * @public - * @see [CLI option](../#-timeout-ms-t-ms) - * @param {boolean} enableTimeouts - Whether to enable timeouts. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.enableTimeouts = function(enableTimeouts) { - this.suite.enableTimeouts( - arguments.length && enableTimeouts !== undefined ? enableTimeouts : true - ); - return this; -}; - -/** - * Forces all tests to either accept a `done` callback or return a promise. - * - * @public - * @see [CLI option](../#-async-only-a) - * @param {boolean} [asyncOnly=true] - Wether to force `done` callback or promise. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.asyncOnly = function(asyncOnly) { - this.options.asyncOnly = asyncOnly !== false; - return this; -}; - -/** - * Disables syntax highlighting (in browser). - * - * @public - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.noHighlighting = function() { - this.options.noHighlighting = true; - return this; -}; - -/** - * Enables or disables uncaught errors to propagate. - * - * @public - * @see [CLI option](../#-allow-uncaught) - * @param {boolean} [allowUncaught=true] - Whether to propagate uncaught errors. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.allowUncaught = function(allowUncaught) { - this.options.allowUncaught = allowUncaught !== false; - return this; -}; - -/** - * @summary - * Delays root suite execution. - * - * @description - * Used to perform asynch operations before any suites are run. - * - * @public - * @see [delayed root suite](../#delayed-root-suite) - * @returns {Mocha} this - * @chainable - */ -Mocha.prototype.delay = function delay() { - this.options.delay = true; - return this; -}; - -/** - * Causes tests marked `only` to fail the suite. - * - * @public - * @see [CLI option](../#-forbid-only) - * @param {boolean} [forbidOnly=true] - Whether tests marked `only` fail the suite. - * @returns {Mocha} this - * @chainable - */ -Mocha.prototype.forbidOnly = function(forbidOnly) { - this.options.forbidOnly = forbidOnly !== false; - return this; -}; - -/** - * Causes pending tests and tests marked `skip` to fail the suite. - * - * @public - * @see [CLI option](../#-forbid-pending) - * @param {boolean} [forbidPending=true] - Whether pending tests fail the suite. - * @returns {Mocha} this - * @chainable - */ -Mocha.prototype.forbidPending = function(forbidPending) { - this.options.forbidPending = forbidPending !== false; - return this; -}; - -/** - * Mocha version as specified by "package.json". - * - * @name Mocha#version - * @type string - * @readonly - */ -Object.defineProperty(Mocha.prototype, 'version', { - value: require('../package.json').version, - configurable: false, - enumerable: true, - writable: false -}); - -/** - * Callback to be invoked when test execution is complete. - * - * @callback DoneCB - * @param {number} failures - Number of failures that occurred. - */ - -/** - * Runs root suite and invokes `fn()` when complete. - * - * @description - * To run tests multiple times (or to run tests in files that are - * already in the `require` cache), make sure to clear them from - * the cache first! - * - * @public - * @see {@link Mocha#unloadFiles} - * @see {@link Runner#run} - * @param {DoneCB} [fn] - Callback invoked when test execution completed. - * @returns {Runner} runner instance - * @example - * - * // exit with non-zero status if there were test failures - * mocha.run(failures => process.exitCode = failures ? 1 : 0); - */ -Mocha.prototype.run = function(fn) { - if (this.files.length && !this.loadAsync) { - this.loadFiles(); - } - var suite = this.suite; - var options = this.options; - options.files = this.files; - var runner = new exports.Runner(suite, options.delay); - createStatsCollector(runner); - var reporter = new this._reporter(runner, options); - runner.checkLeaks = options.checkLeaks === true; - runner.fullStackTrace = options.fullTrace; - runner.asyncOnly = options.asyncOnly; - runner.allowUncaught = options.allowUncaught; - runner.forbidOnly = options.forbidOnly; - runner.forbidPending = options.forbidPending; - if (options.grep) { - runner.grep(options.grep, options.invert); - } - if (options.global) { - runner.globals(options.global); - } - if (options.growl) { - this._growl(runner); - } - if (options.color !== undefined) { - exports.reporters.Base.useColors = options.color; - } - exports.reporters.Base.inlineDiffs = options.inlineDiffs; - exports.reporters.Base.hideDiff = !options.diff; - - function done(failures) { - fn = fn || utils.noop; - if (reporter.done) { - reporter.done(failures, fn); - } else { - fn(failures); - } - } - - return runner.run(done); -}; - -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../package.json":90,"./context":5,"./errors":6,"./esm-utils":42,"./growl":2,"./hook":7,"./interfaces":11,"./mocharc.json":15,"./reporters":21,"./runnable":33,"./runner":34,"./stats-collector":35,"./suite":36,"./test":37,"./utils":38,"_process":69,"escape-string-regexp":49,"path":42}],15:[function(require,module,exports){ -module.exports={ - "diff": true, - "extension": ["js", "cjs", "mjs"], - "opts": "./test/mocha.opts", - "package": "./package.json", - "reporter": "spec", - "slow": 75, - "timeout": 2000, - "ui": "bdd", - "watch-ignore": ["node_modules", ".git"] -} - -},{}],16:[function(require,module,exports){ -'use strict'; - -module.exports = Pending; - -/** - * Initialize a new `Pending` error with the given message. - * - * @param {string} message - */ -function Pending(message) { - this.message = message; -} - -},{}],17:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module Base - */ -/** - * Module dependencies. - */ - -var tty = require('tty'); -var diff = require('diff'); -var milliseconds = require('ms'); -var utils = require('../utils'); -var supportsColor = process.browser ? null : require('supports-color'); -var constants = require('../runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; - -/** - * Expose `Base`. - */ - -exports = module.exports = Base; - -/** - * Check if both stdio streams are associated with a tty. - */ - -var isatty = process.stdout.isTTY && process.stderr.isTTY; - -/** - * Save log references to avoid tests interfering (see GH-3604). - */ -var consoleLog = console.log; - -/** - * Enable coloring by default, except in the browser interface. - */ - -exports.useColors = - !process.browser && - (supportsColor.stdout || process.env.MOCHA_COLORS !== undefined); - -/** - * Inline diffs instead of +/- - */ - -exports.inlineDiffs = false; - -/** - * Default color map. - */ - -exports.colors = { - pass: 90, - fail: 31, - 'bright pass': 92, - 'bright fail': 91, - 'bright yellow': 93, - pending: 36, - suite: 0, - 'error title': 0, - 'error message': 31, - 'error stack': 90, - checkmark: 32, - fast: 90, - medium: 33, - slow: 31, - green: 32, - light: 90, - 'diff gutter': 90, - 'diff added': 32, - 'diff removed': 31 -}; - -/** - * Default symbol map. - */ - -exports.symbols = { - ok: '✓', - err: '✖', - dot: '․', - comma: ',', - bang: '!' -}; - -// With node.js on Windows: use symbols available in terminal default fonts -if (process.platform === 'win32') { - exports.symbols.ok = '\u221A'; - exports.symbols.err = '\u00D7'; - exports.symbols.dot = '.'; -} - -/** - * Color `str` with the given `type`, - * allowing colors to be disabled, - * as well as user-defined color - * schemes. - * - * @private - * @param {string} type - * @param {string} str - * @return {string} - */ -var color = (exports.color = function(type, str) { - if (!exports.useColors) { - return String(str); - } - return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; -}); - -/** - * Expose term window size, with some defaults for when stderr is not a tty. - */ - -exports.window = { - width: 75 -}; - -if (isatty) { - exports.window.width = process.stdout.getWindowSize - ? process.stdout.getWindowSize(1)[0] - : tty.getWindowSize()[1]; -} - -/** - * Expose some basic cursor interactions that are common among reporters. - */ - -exports.cursor = { - hide: function() { - isatty && process.stdout.write('\u001b[?25l'); - }, - - show: function() { - isatty && process.stdout.write('\u001b[?25h'); - }, - - deleteLine: function() { - isatty && process.stdout.write('\u001b[2K'); - }, - - beginningOfLine: function() { - isatty && process.stdout.write('\u001b[0G'); - }, - - CR: function() { - if (isatty) { - exports.cursor.deleteLine(); - exports.cursor.beginningOfLine(); - } else { - process.stdout.write('\r'); - } - } -}; - -var showDiff = (exports.showDiff = function(err) { - return ( - err && - err.showDiff !== false && - sameType(err.actual, err.expected) && - err.expected !== undefined - ); -}); - -function stringifyDiffObjs(err) { - if (!utils.isString(err.actual) || !utils.isString(err.expected)) { - err.actual = utils.stringify(err.actual); - err.expected = utils.stringify(err.expected); - } -} - -/** - * Returns a diff between 2 strings with coloured ANSI output. - * - * @description - * The diff will be either inline or unified dependent on the value - * of `Base.inlineDiff`. - * - * @param {string} actual - * @param {string} expected - * @return {string} Diff - */ -var generateDiff = (exports.generateDiff = function(actual, expected) { - try { - return exports.inlineDiffs - ? inlineDiff(actual, expected) - : unifiedDiff(actual, expected); - } catch (err) { - var msg = - '\n ' + - color('diff added', '+ expected') + - ' ' + - color('diff removed', '- actual: failed to generate Mocha diff') + - '\n'; - return msg; - } -}); - -/** - * Outputs the given `failures` as a list. - * - * @public - * @memberof Mocha.reporters.Base - * @variation 1 - * @param {Object[]} failures - Each is Test instance with corresponding - * Error property - */ -exports.list = function(failures) { - var multipleErr, multipleTest; - Base.consoleLog(); - failures.forEach(function(test, i) { - // format - var fmt = - color('error title', ' %s) %s:\n') + - color('error message', ' %s') + - color('error stack', '\n%s\n'); - - // msg - var msg; - var err; - if (test.err && test.err.multiple) { - if (multipleTest !== test) { - multipleTest = test; - multipleErr = [test.err].concat(test.err.multiple); - } - err = multipleErr.shift(); - } else { - err = test.err; - } - var message; - if (err.message && typeof err.message.toString === 'function') { - message = err.message + ''; - } else if (typeof err.inspect === 'function') { - message = err.inspect() + ''; - } else { - message = ''; - } - var stack = err.stack || message; - var index = message ? stack.indexOf(message) : -1; - - if (index === -1) { - msg = message; - } else { - index += message.length; - msg = stack.slice(0, index); - // remove msg from stack - stack = stack.slice(index + 1); - } - - // uncaught - if (err.uncaught) { - msg = 'Uncaught ' + msg; - } - // explicitly show diff - if (!exports.hideDiff && showDiff(err)) { - stringifyDiffObjs(err); - fmt = - color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); - var match = message.match(/^([^:]+): expected/); - msg = '\n ' + color('error message', match ? match[1] : msg); - - msg += generateDiff(err.actual, err.expected); - } - - // indent stack trace - stack = stack.replace(/^/gm, ' '); - - // indented test title - var testTitle = ''; - test.titlePath().forEach(function(str, index) { - if (index !== 0) { - testTitle += '\n '; - } - for (var i = 0; i < index; i++) { - testTitle += ' '; - } - testTitle += str; - }); - - Base.consoleLog(fmt, i + 1, testTitle, msg, stack); - }); -}; - -/** - * Constructs a new `Base` reporter instance. - * - * @description - * All other reporters generally inherit from this reporter. - * - * @public - * @class - * @memberof Mocha.reporters - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function Base(runner, options) { - var failures = (this.failures = []); - - if (!runner) { - throw new TypeError('Missing runner argument'); - } - this.options = options || {}; - this.runner = runner; - this.stats = runner.stats; // assigned so Reporters keep a closer reference - - runner.on(EVENT_TEST_PASS, function(test) { - if (test.duration > test.slow()) { - test.speed = 'slow'; - } else if (test.duration > test.slow() / 2) { - test.speed = 'medium'; - } else { - test.speed = 'fast'; - } - }); - - runner.on(EVENT_TEST_FAIL, function(test, err) { - if (showDiff(err)) { - stringifyDiffObjs(err); - } - // more than one error per test - if (test.err && err instanceof Error) { - test.err.multiple = (test.err.multiple || []).concat(err); - } else { - test.err = err; - } - failures.push(test); - }); -} - -/** - * Outputs common epilogue used by many of the bundled reporters. - * - * @public - * @memberof Mocha.reporters - */ -Base.prototype.epilogue = function() { - var stats = this.stats; - var fmt; - - Base.consoleLog(); - - // passes - fmt = - color('bright pass', ' ') + - color('green', ' %d passing') + - color('light', ' (%s)'); - - Base.consoleLog(fmt, stats.passes || 0, milliseconds(stats.duration)); - - // pending - if (stats.pending) { - fmt = color('pending', ' ') + color('pending', ' %d pending'); - - Base.consoleLog(fmt, stats.pending); - } - - // failures - if (stats.failures) { - fmt = color('fail', ' %d failing'); - - Base.consoleLog(fmt, stats.failures); - - Base.list(this.failures); - Base.consoleLog(); - } - - Base.consoleLog(); -}; - -/** - * Pads the given `str` to `len`. - * - * @private - * @param {string} str - * @param {string} len - * @return {string} - */ -function pad(str, len) { - str = String(str); - return Array(len - str.length + 1).join(' ') + str; -} - -/** - * Returns inline diff between 2 strings with coloured ANSI output. - * - * @private - * @param {String} actual - * @param {String} expected - * @return {string} Diff - */ -function inlineDiff(actual, expected) { - var msg = errorDiff(actual, expected); - - // linenos - var lines = msg.split('\n'); - if (lines.length > 4) { - var width = String(lines.length).length; - msg = lines - .map(function(str, i) { - return pad(++i, width) + ' |' + ' ' + str; - }) - .join('\n'); - } - - // legend - msg = - '\n' + - color('diff removed', 'actual') + - ' ' + - color('diff added', 'expected') + - '\n\n' + - msg + - '\n'; - - // indent - msg = msg.replace(/^/gm, ' '); - return msg; -} - -/** - * Returns unified diff between two strings with coloured ANSI output. - * - * @private - * @param {String} actual - * @param {String} expected - * @return {string} The diff. - */ -function unifiedDiff(actual, expected) { - var indent = ' '; - function cleanUp(line) { - if (line[0] === '+') { - return indent + colorLines('diff added', line); - } - if (line[0] === '-') { - return indent + colorLines('diff removed', line); - } - if (line.match(/@@/)) { - return '--'; - } - if (line.match(/\\ No newline/)) { - return null; - } - return indent + line; - } - function notBlank(line) { - return typeof line !== 'undefined' && line !== null; - } - var msg = diff.createPatch('string', actual, expected); - var lines = msg.split('\n').splice(5); - return ( - '\n ' + - colorLines('diff added', '+ expected') + - ' ' + - colorLines('diff removed', '- actual') + - '\n\n' + - lines - .map(cleanUp) - .filter(notBlank) - .join('\n') - ); -} - -/** - * Returns character diff for `err`. - * - * @private - * @param {String} actual - * @param {String} expected - * @return {string} the diff - */ -function errorDiff(actual, expected) { - return diff - .diffWordsWithSpace(actual, expected) - .map(function(str) { - if (str.added) { - return colorLines('diff added', str.value); - } - if (str.removed) { - return colorLines('diff removed', str.value); - } - return str.value; - }) - .join(''); -} - -/** - * Colors lines for `str`, using the color `name`. - * - * @private - * @param {string} name - * @param {string} str - * @return {string} - */ -function colorLines(name, str) { - return str - .split('\n') - .map(function(str) { - return color(name, str); - }) - .join('\n'); -} - -/** - * Object#toString reference. - */ -var objToString = Object.prototype.toString; - -/** - * Checks that a / b have the same type. - * - * @private - * @param {Object} a - * @param {Object} b - * @return {boolean} - */ -function sameType(a, b) { - return objToString.call(a) === objToString.call(b); -} - -Base.consoleLog = consoleLog; - -Base.abstract = true; - -}).call(this,require('_process')) -},{"../runner":34,"../utils":38,"_process":69,"diff":48,"ms":60,"supports-color":42,"tty":4}],18:[function(require,module,exports){ -'use strict'; -/** - * @module Doc - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var utils = require('../utils'); -var constants = require('../runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; -var EVENT_SUITE_END = constants.EVENT_SUITE_END; - -/** - * Expose `Doc`. - */ - -exports = module.exports = Doc; - -/** - * Constructs a new `Doc` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function Doc(runner, options) { - Base.call(this, runner, options); - - var indents = 2; - - function indent() { - return Array(indents).join(' '); - } - - runner.on(EVENT_SUITE_BEGIN, function(suite) { - if (suite.root) { - return; - } - ++indents; - Base.consoleLog('%s<section class="suite">', indent()); - ++indents; - Base.consoleLog('%s<h1>%s</h1>', indent(), utils.escape(suite.title)); - Base.consoleLog('%s<dl>', indent()); - }); - - runner.on(EVENT_SUITE_END, function(suite) { - if (suite.root) { - return; - } - Base.consoleLog('%s</dl>', indent()); - --indents; - Base.consoleLog('%s</section>', indent()); - --indents; - }); - - runner.on(EVENT_TEST_PASS, function(test) { - Base.consoleLog('%s <dt>%s</dt>', indent(), utils.escape(test.title)); - var code = utils.escape(utils.clean(test.body)); - Base.consoleLog('%s <dd><pre><code>%s</code></pre></dd>', indent(), code); - }); - - runner.on(EVENT_TEST_FAIL, function(test, err) { - Base.consoleLog( - '%s <dt class="error">%s</dt>', - indent(), - utils.escape(test.title) - ); - var code = utils.escape(utils.clean(test.body)); - Base.consoleLog( - '%s <dd class="error"><pre><code>%s</code></pre></dd>', - indent(), - code - ); - Base.consoleLog( - '%s <dd class="error">%s</dd>', - indent(), - utils.escape(err) - ); - }); -} - -Doc.description = 'HTML documentation'; - -},{"../runner":34,"../utils":38,"./base":17}],19:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module Dot - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var inherits = require('../utils').inherits; -var constants = require('../runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; -var EVENT_RUN_END = constants.EVENT_RUN_END; - -/** - * Expose `Dot`. - */ - -exports = module.exports = Dot; - -/** - * Constructs a new `Dot` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function Dot(runner, options) { - Base.call(this, runner, options); - - var self = this; - var width = (Base.window.width * 0.75) | 0; - var n = -1; - - runner.on(EVENT_RUN_BEGIN, function() { - process.stdout.write('\n'); - }); - - runner.on(EVENT_TEST_PENDING, function() { - if (++n % width === 0) { - process.stdout.write('\n '); - } - process.stdout.write(Base.color('pending', Base.symbols.comma)); - }); - - runner.on(EVENT_TEST_PASS, function(test) { - if (++n % width === 0) { - process.stdout.write('\n '); - } - if (test.speed === 'slow') { - process.stdout.write(Base.color('bright yellow', Base.symbols.dot)); - } else { - process.stdout.write(Base.color(test.speed, Base.symbols.dot)); - } - }); - - runner.on(EVENT_TEST_FAIL, function() { - if (++n % width === 0) { - process.stdout.write('\n '); - } - process.stdout.write(Base.color('fail', Base.symbols.bang)); - }); - - runner.once(EVENT_RUN_END, function() { - process.stdout.write('\n'); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(Dot, Base); - -Dot.description = 'dot matrix representation'; - -}).call(this,require('_process')) -},{"../runner":34,"../utils":38,"./base":17,"_process":69}],20:[function(require,module,exports){ -(function (global){ -'use strict'; - -/* eslint-env browser */ -/** - * @module HTML - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var utils = require('../utils'); -var Progress = require('../browser/progress'); -var escapeRe = require('escape-string-regexp'); -var constants = require('../runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; -var EVENT_SUITE_END = constants.EVENT_SUITE_END; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; -var escape = utils.escape; - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date; - -/** - * Expose `HTML`. - */ - -exports = module.exports = HTML; - -/** - * Stats template. - */ - -var statsTemplate = - '<ul id="mocha-stats">' + - '<li class="progress"><canvas width="40" height="40"></canvas></li>' + - '<li class="passes"><a href="javascript:void(0);">passes:</a> <em>0</em></li>' + - '<li class="failures"><a href="javascript:void(0);">failures:</a> <em>0</em></li>' + - '<li class="duration">duration: <em>0</em>s</li>' + - '</ul>'; - -var playIcon = '‣'; - -/** - * Constructs a new `HTML` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function HTML(runner, options) { - Base.call(this, runner, options); - - var self = this; - var stats = this.stats; - var stat = fragment(statsTemplate); - var items = stat.getElementsByTagName('li'); - var passes = items[1].getElementsByTagName('em')[0]; - var passesLink = items[1].getElementsByTagName('a')[0]; - var failures = items[2].getElementsByTagName('em')[0]; - var failuresLink = items[2].getElementsByTagName('a')[0]; - var duration = items[3].getElementsByTagName('em')[0]; - var canvas = stat.getElementsByTagName('canvas')[0]; - var report = fragment('<ul id="mocha-report"></ul>'); - var stack = [report]; - var progress; - var ctx; - var root = document.getElementById('mocha'); - - if (canvas.getContext) { - var ratio = window.devicePixelRatio || 1; - canvas.style.width = canvas.width; - canvas.style.height = canvas.height; - canvas.width *= ratio; - canvas.height *= ratio; - ctx = canvas.getContext('2d'); - ctx.scale(ratio, ratio); - progress = new Progress(); - } - - if (!root) { - return error('#mocha div missing, add it to your document'); - } - - // pass toggle - on(passesLink, 'click', function(evt) { - evt.preventDefault(); - unhide(); - var name = /pass/.test(report.className) ? '' : ' pass'; - report.className = report.className.replace(/fail|pass/g, '') + name; - if (report.className.trim()) { - hideSuitesWithout('test pass'); - } - }); - - // failure toggle - on(failuresLink, 'click', function(evt) { - evt.preventDefault(); - unhide(); - var name = /fail/.test(report.className) ? '' : ' fail'; - report.className = report.className.replace(/fail|pass/g, '') + name; - if (report.className.trim()) { - hideSuitesWithout('test fail'); - } - }); - - root.appendChild(stat); - root.appendChild(report); - - if (progress) { - progress.size(40); - } - - runner.on(EVENT_SUITE_BEGIN, function(suite) { - if (suite.root) { - return; - } - - // suite - var url = self.suiteURL(suite); - var el = fragment( - '<li class="suite"><h1><a href="%s">%s</a></h1></li>', - url, - escape(suite.title) - ); - - // container - stack[0].appendChild(el); - stack.unshift(document.createElement('ul')); - el.appendChild(stack[0]); - }); - - runner.on(EVENT_SUITE_END, function(suite) { - if (suite.root) { - updateStats(); - return; - } - stack.shift(); - }); - - runner.on(EVENT_TEST_PASS, function(test) { - var url = self.testURL(test); - var markup = - '<li class="test pass %e"><h2>%e</h2><span class="duration">%ems</span> ' + - '<a href="%s" class="replay">' + - playIcon + - '</a></li>'; - var el = fragment(markup, test.speed, test.title, test.duration, url); - self.addCodeToggle(el, test.body); - appendToStack(el); - updateStats(); - }); - - runner.on(EVENT_TEST_FAIL, function(test) { - var el = fragment( - '<li class="test fail"><h2>%e <a href="%e" class="replay">' + - playIcon + - '</a></h2></li>', - test.title, - self.testURL(test) - ); - var stackString; // Note: Includes leading newline - var message = test.err.toString(); - - // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we - // check for the result of the stringifying. - if (message === '[object Error]') { - message = test.err.message; - } - - if (test.err.stack) { - var indexOfMessage = test.err.stack.indexOf(test.err.message); - if (indexOfMessage === -1) { - stackString = test.err.stack; - } else { - stackString = test.err.stack.substr( - test.err.message.length + indexOfMessage - ); - } - } else if (test.err.sourceURL && test.err.line !== undefined) { - // Safari doesn't give you a stack. Let's at least provide a source line. - stackString = '\n(' + test.err.sourceURL + ':' + test.err.line + ')'; - } - - stackString = stackString || ''; - - if (test.err.htmlMessage && stackString) { - el.appendChild( - fragment( - '<div class="html-error">%s\n<pre class="error">%e</pre></div>', - test.err.htmlMessage, - stackString - ) - ); - } else if (test.err.htmlMessage) { - el.appendChild( - fragment('<div class="html-error">%s</div>', test.err.htmlMessage) - ); - } else { - el.appendChild( - fragment('<pre class="error">%e%e</pre>', message, stackString) - ); - } - - self.addCodeToggle(el, test.body); - appendToStack(el); - updateStats(); - }); - - runner.on(EVENT_TEST_PENDING, function(test) { - var el = fragment( - '<li class="test pass pending"><h2>%e</h2></li>', - test.title - ); - appendToStack(el); - updateStats(); - }); - - function appendToStack(el) { - // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. - if (stack[0]) { - stack[0].appendChild(el); - } - } - - function updateStats() { - // TODO: add to stats - var percent = ((stats.tests / runner.total) * 100) | 0; - if (progress) { - progress.update(percent).draw(ctx); - } - - // update stats - var ms = new Date() - stats.start; - text(passes, stats.passes); - text(failures, stats.failures); - text(duration, (ms / 1000).toFixed(2)); - } -} - -/** - * Makes a URL, preserving querystring ("search") parameters. - * - * @param {string} s - * @return {string} A new URL. - */ -function makeUrl(s) { - var search = window.location.search; - - // Remove previous grep query parameter if present - if (search) { - search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?'); - } - - return ( - window.location.pathname + - (search ? search + '&' : '?') + - 'grep=' + - encodeURIComponent(escapeRe(s)) - ); -} - -/** - * Provide suite URL. - * - * @param {Object} [suite] - */ -HTML.prototype.suiteURL = function(suite) { - return makeUrl(suite.fullTitle()); -}; - -/** - * Provide test URL. - * - * @param {Object} [test] - */ -HTML.prototype.testURL = function(test) { - return makeUrl(test.fullTitle()); -}; - -/** - * Adds code toggle functionality for the provided test's list element. - * - * @param {HTMLLIElement} el - * @param {string} contents - */ -HTML.prototype.addCodeToggle = function(el, contents) { - var h2 = el.getElementsByTagName('h2')[0]; - - on(h2, 'click', function() { - pre.style.display = pre.style.display === 'none' ? 'block' : 'none'; - }); - - var pre = fragment('<pre><code>%e</code></pre>', utils.clean(contents)); - el.appendChild(pre); - pre.style.display = 'none'; -}; - -/** - * Display error `msg`. - * - * @param {string} msg - */ -function error(msg) { - document.body.appendChild(fragment('<div id="mocha-error">%s</div>', msg)); -} - -/** - * Return a DOM fragment from `html`. - * - * @param {string} html - */ -function fragment(html) { - var args = arguments; - var div = document.createElement('div'); - var i = 1; - - div.innerHTML = html.replace(/%([se])/g, function(_, type) { - switch (type) { - case 's': - return String(args[i++]); - case 'e': - return escape(args[i++]); - // no default - } - }); - - return div.firstChild; -} - -/** - * Check for suites that do not have elements - * with `classname`, and hide them. - * - * @param {text} classname - */ -function hideSuitesWithout(classname) { - var suites = document.getElementsByClassName('suite'); - for (var i = 0; i < suites.length; i++) { - var els = suites[i].getElementsByClassName(classname); - if (!els.length) { - suites[i].className += ' hidden'; - } - } -} - -/** - * Unhide .hidden suites. - */ -function unhide() { - var els = document.getElementsByClassName('suite hidden'); - while (els.length > 0) { - els[0].className = els[0].className.replace('suite hidden', 'suite'); - } -} - -/** - * Set an element's text contents. - * - * @param {HTMLElement} el - * @param {string} contents - */ -function text(el, contents) { - if (el.textContent) { - el.textContent = contents; - } else { - el.innerText = contents; - } -} - -/** - * Listen on `event` with callback `fn`. - */ -function on(el, event, fn) { - if (el.addEventListener) { - el.addEventListener(event, fn, false); - } else { - el.attachEvent('on' + event, fn); - } -} - -HTML.browserOnly = true; - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../browser/progress":3,"../runner":34,"../utils":38,"./base":17,"escape-string-regexp":49}],21:[function(require,module,exports){ -'use strict'; - -// Alias exports to a their normalized format Mocha#reporter to prevent a need -// for dynamic (try/catch) requires, which Browserify doesn't handle. -exports.Base = exports.base = require('./base'); -exports.Dot = exports.dot = require('./dot'); -exports.Doc = exports.doc = require('./doc'); -exports.TAP = exports.tap = require('./tap'); -exports.JSON = exports.json = require('./json'); -exports.HTML = exports.html = require('./html'); -exports.List = exports.list = require('./list'); -exports.Min = exports.min = require('./min'); -exports.Spec = exports.spec = require('./spec'); -exports.Nyan = exports.nyan = require('./nyan'); -exports.XUnit = exports.xunit = require('./xunit'); -exports.Markdown = exports.markdown = require('./markdown'); -exports.Progress = exports.progress = require('./progress'); -exports.Landing = exports.landing = require('./landing'); -exports.JSONStream = exports['json-stream'] = require('./json-stream'); - -},{"./base":17,"./doc":18,"./dot":19,"./html":20,"./json":23,"./json-stream":22,"./landing":24,"./list":25,"./markdown":26,"./min":27,"./nyan":28,"./progress":29,"./spec":30,"./tap":31,"./xunit":32}],22:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module JSONStream - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var constants = require('../runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_RUN_END = constants.EVENT_RUN_END; - -/** - * Expose `JSONStream`. - */ - -exports = module.exports = JSONStream; - -/** - * Constructs a new `JSONStream` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function JSONStream(runner, options) { - Base.call(this, runner, options); - - var self = this; - var total = runner.total; - - runner.once(EVENT_RUN_BEGIN, function() { - writeEvent(['start', {total: total}]); - }); - - runner.on(EVENT_TEST_PASS, function(test) { - writeEvent(['pass', clean(test)]); - }); - - runner.on(EVENT_TEST_FAIL, function(test, err) { - test = clean(test); - test.err = err.message; - test.stack = err.stack || null; - writeEvent(['fail', test]); - }); - - runner.once(EVENT_RUN_END, function() { - writeEvent(['end', self.stats]); - }); -} - -/** - * Mocha event to be written to the output stream. - * @typedef {Array} JSONStream~MochaEvent - */ - -/** - * Writes Mocha event to reporter output stream. - * - * @private - * @param {JSONStream~MochaEvent} event - Mocha event to be output. - */ -function writeEvent(event) { - process.stdout.write(JSON.stringify(event) + '\n'); -} - -/** - * Returns an object literal representation of `test` - * free of cyclic properties, etc. - * - * @private - * @param {Test} test - Instance used as data source. - * @return {Object} object containing pared-down test instance data - */ -function clean(test) { - return { - title: test.title, - fullTitle: test.fullTitle(), - duration: test.duration, - currentRetry: test.currentRetry() - }; -} - -JSONStream.description = 'newline delimited JSON events'; - -}).call(this,require('_process')) -},{"../runner":34,"./base":17,"_process":69}],23:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module JSON - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var constants = require('../runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_TEST_END = constants.EVENT_TEST_END; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; - -/** - * Expose `JSON`. - */ - -exports = module.exports = JSONReporter; - -/** - * Constructs a new `JSON` reporter instance. - * - * @public - * @class JSON - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function JSONReporter(runner, options) { - Base.call(this, runner, options); - - var self = this; - var tests = []; - var pending = []; - var failures = []; - var passes = []; - - runner.on(EVENT_TEST_END, function(test) { - tests.push(test); - }); - - runner.on(EVENT_TEST_PASS, function(test) { - passes.push(test); - }); - - runner.on(EVENT_TEST_FAIL, function(test) { - failures.push(test); - }); - - runner.on(EVENT_TEST_PENDING, function(test) { - pending.push(test); - }); - - runner.once(EVENT_RUN_END, function() { - var obj = { - stats: self.stats, - tests: tests.map(clean), - pending: pending.map(clean), - failures: failures.map(clean), - passes: passes.map(clean) - }; - - runner.testResults = obj; - - process.stdout.write(JSON.stringify(obj, null, 2)); - }); -} - -/** - * Return a plain-object representation of `test` - * free of cyclic properties etc. - * - * @private - * @param {Object} test - * @return {Object} - */ -function clean(test) { - var err = test.err || {}; - if (err instanceof Error) { - err = errorJSON(err); - } - - return { - title: test.title, - fullTitle: test.fullTitle(), - duration: test.duration, - currentRetry: test.currentRetry(), - err: cleanCycles(err) - }; -} - -/** - * Replaces any circular references inside `obj` with '[object Object]' - * - * @private - * @param {Object} obj - * @return {Object} - */ -function cleanCycles(obj) { - var cache = []; - return JSON.parse( - JSON.stringify(obj, function(key, value) { - if (typeof value === 'object' && value !== null) { - if (cache.indexOf(value) !== -1) { - // Instead of going in a circle, we'll print [object Object] - return '' + value; - } - cache.push(value); - } - - return value; - }) - ); -} - -/** - * Transform an Error object into a JSON object. - * - * @private - * @param {Error} err - * @return {Object} - */ -function errorJSON(err) { - var res = {}; - Object.getOwnPropertyNames(err).forEach(function(key) { - res[key] = err[key]; - }, err); - return res; -} - -JSONReporter.description = 'single JSON object'; - -}).call(this,require('_process')) -},{"../runner":34,"./base":17,"_process":69}],24:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module Landing - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var inherits = require('../utils').inherits; -var constants = require('../runner').constants; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_TEST_END = constants.EVENT_TEST_END; -var STATE_FAILED = require('../runnable').constants.STATE_FAILED; - -var cursor = Base.cursor; -var color = Base.color; - -/** - * Expose `Landing`. - */ - -exports = module.exports = Landing; - -/** - * Airplane color. - */ - -Base.colors.plane = 0; - -/** - * Airplane crash color. - */ - -Base.colors['plane crash'] = 31; - -/** - * Runway color. - */ - -Base.colors.runway = 90; - -/** - * Constructs a new `Landing` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function Landing(runner, options) { - Base.call(this, runner, options); - - var self = this; - var width = (Base.window.width * 0.75) | 0; - var total = runner.total; - var stream = process.stdout; - var plane = color('plane', '✈'); - var crashed = -1; - var n = 0; - - function runway() { - var buf = Array(width).join('-'); - return ' ' + color('runway', buf); - } - - runner.on(EVENT_RUN_BEGIN, function() { - stream.write('\n\n\n '); - cursor.hide(); - }); - - runner.on(EVENT_TEST_END, function(test) { - // check if the plane crashed - var col = crashed === -1 ? ((width * ++n) / total) | 0 : crashed; - - // show the crash - if (test.state === STATE_FAILED) { - plane = color('plane crash', '✈'); - crashed = col; - } - - // render landing strip - stream.write('\u001b[' + (width + 1) + 'D\u001b[2A'); - stream.write(runway()); - stream.write('\n '); - stream.write(color('runway', Array(col).join('⋅'))); - stream.write(plane); - stream.write(color('runway', Array(width - col).join('⋅') + '\n')); - stream.write(runway()); - stream.write('\u001b[0m'); - }); - - runner.once(EVENT_RUN_END, function() { - cursor.show(); - process.stdout.write('\n'); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(Landing, Base); - -Landing.description = 'Unicode landing strip'; - -}).call(this,require('_process')) -},{"../runnable":33,"../runner":34,"../utils":38,"./base":17,"_process":69}],25:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module List - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var inherits = require('../utils').inherits; -var constants = require('../runner').constants; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; -var color = Base.color; -var cursor = Base.cursor; - -/** - * Expose `List`. - */ - -exports = module.exports = List; - -/** - * Constructs a new `List` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function List(runner, options) { - Base.call(this, runner, options); - - var self = this; - var n = 0; - - runner.on(EVENT_RUN_BEGIN, function() { - Base.consoleLog(); - }); - - runner.on(EVENT_TEST_BEGIN, function(test) { - process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); - }); - - runner.on(EVENT_TEST_PENDING, function(test) { - var fmt = color('checkmark', ' -') + color('pending', ' %s'); - Base.consoleLog(fmt, test.fullTitle()); - }); - - runner.on(EVENT_TEST_PASS, function(test) { - var fmt = - color('checkmark', ' ' + Base.symbols.ok) + - color('pass', ' %s: ') + - color(test.speed, '%dms'); - cursor.CR(); - Base.consoleLog(fmt, test.fullTitle(), test.duration); - }); - - runner.on(EVENT_TEST_FAIL, function(test) { - cursor.CR(); - Base.consoleLog(color('fail', ' %d) %s'), ++n, test.fullTitle()); - }); - - runner.once(EVENT_RUN_END, self.epilogue.bind(self)); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(List, Base); - -List.description = 'like "spec" reporter but flat'; - -}).call(this,require('_process')) -},{"../runner":34,"../utils":38,"./base":17,"_process":69}],26:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module Markdown - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var utils = require('../utils'); -var constants = require('../runner').constants; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; -var EVENT_SUITE_END = constants.EVENT_SUITE_END; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; - -/** - * Constants - */ - -var SUITE_PREFIX = '$'; - -/** - * Expose `Markdown`. - */ - -exports = module.exports = Markdown; - -/** - * Constructs a new `Markdown` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function Markdown(runner, options) { - Base.call(this, runner, options); - - var level = 0; - var buf = ''; - - function title(str) { - return Array(level).join('#') + ' ' + str; - } - - function mapTOC(suite, obj) { - var ret = obj; - var key = SUITE_PREFIX + suite.title; - - obj = obj[key] = obj[key] || {suite: suite}; - suite.suites.forEach(function(suite) { - mapTOC(suite, obj); - }); - - return ret; - } - - function stringifyTOC(obj, level) { - ++level; - var buf = ''; - var link; - for (var key in obj) { - if (key === 'suite') { - continue; - } - if (key !== SUITE_PREFIX) { - link = ' - [' + key.substring(1) + ']'; - link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; - buf += Array(level).join(' ') + link; - } - buf += stringifyTOC(obj[key], level); - } - return buf; - } - - function generateTOC(suite) { - var obj = mapTOC(suite, {}); - return stringifyTOC(obj, 0); - } - - generateTOC(runner.suite); - - runner.on(EVENT_SUITE_BEGIN, function(suite) { - ++level; - var slug = utils.slug(suite.fullTitle()); - buf += '<a name="' + slug + '"></a>' + '\n'; - buf += title(suite.title) + '\n'; - }); - - runner.on(EVENT_SUITE_END, function() { - --level; - }); - - runner.on(EVENT_TEST_PASS, function(test) { - var code = utils.clean(test.body); - buf += test.title + '.\n'; - buf += '\n```js\n'; - buf += code + '\n'; - buf += '```\n\n'; - }); - - runner.once(EVENT_RUN_END, function() { - process.stdout.write('# TOC\n'); - process.stdout.write(generateTOC(runner.suite)); - process.stdout.write(buf); - }); -} - -Markdown.description = 'GitHub Flavored Markdown'; - -}).call(this,require('_process')) -},{"../runner":34,"../utils":38,"./base":17,"_process":69}],27:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module Min - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var inherits = require('../utils').inherits; -var constants = require('../runner').constants; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; - -/** - * Expose `Min`. - */ - -exports = module.exports = Min; - -/** - * Constructs a new `Min` reporter instance. - * - * @description - * This minimal test reporter is best used with '--watch'. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function Min(runner, options) { - Base.call(this, runner, options); - - runner.on(EVENT_RUN_BEGIN, function() { - // clear screen - process.stdout.write('\u001b[2J'); - // set cursor position - process.stdout.write('\u001b[1;3H'); - }); - - runner.once(EVENT_RUN_END, this.epilogue.bind(this)); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(Min, Base); - -Min.description = 'essentially just a summary'; - -}).call(this,require('_process')) -},{"../runner":34,"../utils":38,"./base":17,"_process":69}],28:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module Nyan - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var constants = require('../runner').constants; -var inherits = require('../utils').inherits; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; - -/** - * Expose `Dot`. - */ - -exports = module.exports = NyanCat; - -/** - * Constructs a new `Nyan` reporter instance. - * - * @public - * @class Nyan - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function NyanCat(runner, options) { - Base.call(this, runner, options); - - var self = this; - var width = (Base.window.width * 0.75) | 0; - var nyanCatWidth = (this.nyanCatWidth = 11); - - this.colorIndex = 0; - this.numberOfLines = 4; - this.rainbowColors = self.generateColors(); - this.scoreboardWidth = 5; - this.tick = 0; - this.trajectories = [[], [], [], []]; - this.trajectoryWidthMax = width - nyanCatWidth; - - runner.on(EVENT_RUN_BEGIN, function() { - Base.cursor.hide(); - self.draw(); - }); - - runner.on(EVENT_TEST_PENDING, function() { - self.draw(); - }); - - runner.on(EVENT_TEST_PASS, function() { - self.draw(); - }); - - runner.on(EVENT_TEST_FAIL, function() { - self.draw(); - }); - - runner.once(EVENT_RUN_END, function() { - Base.cursor.show(); - for (var i = 0; i < self.numberOfLines; i++) { - write('\n'); - } - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(NyanCat, Base); - -/** - * Draw the nyan cat - * - * @private - */ - -NyanCat.prototype.draw = function() { - this.appendRainbow(); - this.drawScoreboard(); - this.drawRainbow(); - this.drawNyanCat(); - this.tick = !this.tick; -}; - -/** - * Draw the "scoreboard" showing the number - * of passes, failures and pending tests. - * - * @private - */ - -NyanCat.prototype.drawScoreboard = function() { - var stats = this.stats; - - function draw(type, n) { - write(' '); - write(Base.color(type, n)); - write('\n'); - } - - draw('green', stats.passes); - draw('fail', stats.failures); - draw('pending', stats.pending); - write('\n'); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Append the rainbow. - * - * @private - */ - -NyanCat.prototype.appendRainbow = function() { - var segment = this.tick ? '_' : '-'; - var rainbowified = this.rainbowify(segment); - - for (var index = 0; index < this.numberOfLines; index++) { - var trajectory = this.trajectories[index]; - if (trajectory.length >= this.trajectoryWidthMax) { - trajectory.shift(); - } - trajectory.push(rainbowified); - } -}; - -/** - * Draw the rainbow. - * - * @private - */ - -NyanCat.prototype.drawRainbow = function() { - var self = this; - - this.trajectories.forEach(function(line) { - write('\u001b[' + self.scoreboardWidth + 'C'); - write(line.join('')); - write('\n'); - }); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Draw the nyan cat - * - * @private - */ -NyanCat.prototype.drawNyanCat = function() { - var self = this; - var startWidth = this.scoreboardWidth + this.trajectories[0].length; - var dist = '\u001b[' + startWidth + 'C'; - var padding = ''; - - write(dist); - write('_,------,'); - write('\n'); - - write(dist); - padding = self.tick ? ' ' : ' '; - write('_|' + padding + '/\\_/\\ '); - write('\n'); - - write(dist); - padding = self.tick ? '_' : '__'; - var tail = self.tick ? '~' : '^'; - write(tail + '|' + padding + this.face() + ' '); - write('\n'); - - write(dist); - padding = self.tick ? ' ' : ' '; - write(padding + '"" "" '); - write('\n'); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Draw nyan cat face. - * - * @private - * @return {string} - */ - -NyanCat.prototype.face = function() { - var stats = this.stats; - if (stats.failures) { - return '( x .x)'; - } else if (stats.pending) { - return '( o .o)'; - } else if (stats.passes) { - return '( ^ .^)'; - } - return '( - .-)'; -}; - -/** - * Move cursor up `n`. - * - * @private - * @param {number} n - */ - -NyanCat.prototype.cursorUp = function(n) { - write('\u001b[' + n + 'A'); -}; - -/** - * Move cursor down `n`. - * - * @private - * @param {number} n - */ - -NyanCat.prototype.cursorDown = function(n) { - write('\u001b[' + n + 'B'); -}; - -/** - * Generate rainbow colors. - * - * @private - * @return {Array} - */ -NyanCat.prototype.generateColors = function() { - var colors = []; - - for (var i = 0; i < 6 * 7; i++) { - var pi3 = Math.floor(Math.PI / 3); - var n = i * (1.0 / 6); - var r = Math.floor(3 * Math.sin(n) + 3); - var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); - var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); - colors.push(36 * r + 6 * g + b + 16); - } - - return colors; -}; - -/** - * Apply rainbow to the given `str`. - * - * @private - * @param {string} str - * @return {string} - */ -NyanCat.prototype.rainbowify = function(str) { - if (!Base.useColors) { - return str; - } - var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; - this.colorIndex += 1; - return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; -}; - -/** - * Stdout helper. - * - * @param {string} string A message to write to stdout. - */ -function write(string) { - process.stdout.write(string); -} - -NyanCat.description = '"nyan cat"'; - -}).call(this,require('_process')) -},{"../runner":34,"../utils":38,"./base":17,"_process":69}],29:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module Progress - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var constants = require('../runner').constants; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_TEST_END = constants.EVENT_TEST_END; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var inherits = require('../utils').inherits; -var color = Base.color; -var cursor = Base.cursor; - -/** - * Expose `Progress`. - */ - -exports = module.exports = Progress; - -/** - * General progress bar color. - */ - -Base.colors.progress = 90; - -/** - * Constructs a new `Progress` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function Progress(runner, options) { - Base.call(this, runner, options); - - var self = this; - var width = (Base.window.width * 0.5) | 0; - var total = runner.total; - var complete = 0; - var lastN = -1; - - // default chars - options = options || {}; - var reporterOptions = options.reporterOptions || {}; - - options.open = reporterOptions.open || '['; - options.complete = reporterOptions.complete || '▬'; - options.incomplete = reporterOptions.incomplete || Base.symbols.dot; - options.close = reporterOptions.close || ']'; - options.verbose = reporterOptions.verbose || false; - - // tests started - runner.on(EVENT_RUN_BEGIN, function() { - process.stdout.write('\n'); - cursor.hide(); - }); - - // tests complete - runner.on(EVENT_TEST_END, function() { - complete++; - - var percent = complete / total; - var n = (width * percent) | 0; - var i = width - n; - - if (n === lastN && !options.verbose) { - // Don't re-render the line if it hasn't changed - return; - } - lastN = n; - - cursor.CR(); - process.stdout.write('\u001b[J'); - process.stdout.write(color('progress', ' ' + options.open)); - process.stdout.write(Array(n).join(options.complete)); - process.stdout.write(Array(i).join(options.incomplete)); - process.stdout.write(color('progress', options.close)); - if (options.verbose) { - process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); - } - }); - - // tests are complete, output some stats - // and the failures if any - runner.once(EVENT_RUN_END, function() { - cursor.show(); - process.stdout.write('\n'); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(Progress, Base); - -Progress.description = 'a progress bar'; - -}).call(this,require('_process')) -},{"../runner":34,"../utils":38,"./base":17,"_process":69}],30:[function(require,module,exports){ -'use strict'; -/** - * @module Spec - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var constants = require('../runner').constants; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; -var EVENT_SUITE_END = constants.EVENT_SUITE_END; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; -var inherits = require('../utils').inherits; -var color = Base.color; - -/** - * Expose `Spec`. - */ - -exports = module.exports = Spec; - -/** - * Constructs a new `Spec` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function Spec(runner, options) { - Base.call(this, runner, options); - - var self = this; - var indents = 0; - var n = 0; - - function indent() { - return Array(indents).join(' '); - } - - runner.on(EVENT_RUN_BEGIN, function() { - Base.consoleLog(); - }); - - runner.on(EVENT_SUITE_BEGIN, function(suite) { - ++indents; - Base.consoleLog(color('suite', '%s%s'), indent(), suite.title); - }); - - runner.on(EVENT_SUITE_END, function() { - --indents; - if (indents === 1) { - Base.consoleLog(); - } - }); - - runner.on(EVENT_TEST_PENDING, function(test) { - var fmt = indent() + color('pending', ' - %s'); - Base.consoleLog(fmt, test.title); - }); - - runner.on(EVENT_TEST_PASS, function(test) { - var fmt; - if (test.speed === 'fast') { - fmt = - indent() + - color('checkmark', ' ' + Base.symbols.ok) + - color('pass', ' %s'); - Base.consoleLog(fmt, test.title); - } else { - fmt = - indent() + - color('checkmark', ' ' + Base.symbols.ok) + - color('pass', ' %s') + - color(test.speed, ' (%dms)'); - Base.consoleLog(fmt, test.title, test.duration); - } - }); - - runner.on(EVENT_TEST_FAIL, function(test) { - Base.consoleLog(indent() + color('fail', ' %d) %s'), ++n, test.title); - }); - - runner.once(EVENT_RUN_END, self.epilogue.bind(self)); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(Spec, Base); - -Spec.description = 'hierarchical & verbose [default]'; - -},{"../runner":34,"../utils":38,"./base":17}],31:[function(require,module,exports){ -(function (process){ -'use strict'; -/** - * @module TAP - */ -/** - * Module dependencies. - */ - -var util = require('util'); -var Base = require('./base'); -var constants = require('../runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; -var EVENT_TEST_END = constants.EVENT_TEST_END; -var inherits = require('../utils').inherits; -var sprintf = util.format; - -/** - * Expose `TAP`. - */ - -exports = module.exports = TAP; - -/** - * Constructs a new `TAP` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function TAP(runner, options) { - Base.call(this, runner, options); - - var self = this; - var n = 1; - - var tapVersion = '12'; - if (options && options.reporterOptions) { - if (options.reporterOptions.tapVersion) { - tapVersion = options.reporterOptions.tapVersion.toString(); - } - } - - this._producer = createProducer(tapVersion); - - runner.once(EVENT_RUN_BEGIN, function() { - var ntests = runner.grepTotal(runner.suite); - self._producer.writeVersion(); - self._producer.writePlan(ntests); - }); - - runner.on(EVENT_TEST_END, function() { - ++n; - }); - - runner.on(EVENT_TEST_PENDING, function(test) { - self._producer.writePending(n, test); - }); - - runner.on(EVENT_TEST_PASS, function(test) { - self._producer.writePass(n, test); - }); - - runner.on(EVENT_TEST_FAIL, function(test, err) { - self._producer.writeFail(n, test, err); - }); - - runner.once(EVENT_RUN_END, function() { - self._producer.writeEpilogue(runner.stats); - }); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(TAP, Base); - -/** - * Returns a TAP-safe title of `test`. - * - * @private - * @param {Test} test - Test instance. - * @return {String} title with any hash character removed - */ -function title(test) { - return test.fullTitle().replace(/#/g, ''); -} - -/** - * Writes newline-terminated formatted string to reporter output stream. - * - * @private - * @param {string} format - `printf`-like format string - * @param {...*} [varArgs] - Format string arguments - */ -function println(format, varArgs) { - var vargs = Array.from(arguments); - vargs[0] += '\n'; - process.stdout.write(sprintf.apply(null, vargs)); -} - -/** - * Returns a `tapVersion`-appropriate TAP producer instance, if possible. - * - * @private - * @param {string} tapVersion - Version of TAP specification to produce. - * @returns {TAPProducer} specification-appropriate instance - * @throws {Error} if specification version has no associated producer. - */ -function createProducer(tapVersion) { - var producers = { - '12': new TAP12Producer(), - '13': new TAP13Producer() - }; - var producer = producers[tapVersion]; - - if (!producer) { - throw new Error( - 'invalid or unsupported TAP version: ' + JSON.stringify(tapVersion) - ); - } - - return producer; -} - -/** - * @summary - * Constructs a new TAPProducer. - * - * @description - * <em>Only</em> to be used as an abstract base class. - * - * @private - * @constructor - */ -function TAPProducer() {} - -/** - * Writes the TAP version to reporter output stream. - * - * @abstract - */ -TAPProducer.prototype.writeVersion = function() {}; - -/** - * Writes the plan to reporter output stream. - * - * @abstract - * @param {number} ntests - Number of tests that are planned to run. - */ -TAPProducer.prototype.writePlan = function(ntests) { - println('%d..%d', 1, ntests); -}; - -/** - * Writes that test passed to reporter output stream. - * - * @abstract - * @param {number} n - Index of test that passed. - * @param {Test} test - Instance containing test information. - */ -TAPProducer.prototype.writePass = function(n, test) { - println('ok %d %s', n, title(test)); -}; - -/** - * Writes that test was skipped to reporter output stream. - * - * @abstract - * @param {number} n - Index of test that was skipped. - * @param {Test} test - Instance containing test information. - */ -TAPProducer.prototype.writePending = function(n, test) { - println('ok %d %s # SKIP -', n, title(test)); -}; - -/** - * Writes that test failed to reporter output stream. - * - * @abstract - * @param {number} n - Index of test that failed. - * @param {Test} test - Instance containing test information. - * @param {Error} err - Reason the test failed. - */ -TAPProducer.prototype.writeFail = function(n, test, err) { - println('not ok %d %s', n, title(test)); -}; - -/** - * Writes the summary epilogue to reporter output stream. - * - * @abstract - * @param {Object} stats - Object containing run statistics. - */ -TAPProducer.prototype.writeEpilogue = function(stats) { - // :TBD: Why is this not counting pending tests? - println('# tests ' + (stats.passes + stats.failures)); - println('# pass ' + stats.passes); - // :TBD: Why are we not showing pending results? - println('# fail ' + stats.failures); -}; - -/** - * @summary - * Constructs a new TAP12Producer. - * - * @description - * Produces output conforming to the TAP12 specification. - * - * @private - * @constructor - * @extends TAPProducer - * @see {@link https://testanything.org/tap-specification.html|Specification} - */ -function TAP12Producer() { - /** - * Writes that test failed to reporter output stream, with error formatting. - * @override - */ - this.writeFail = function(n, test, err) { - TAPProducer.prototype.writeFail.call(this, n, test, err); - if (err.message) { - println(err.message.replace(/^/gm, ' ')); - } - if (err.stack) { - println(err.stack.replace(/^/gm, ' ')); - } - }; -} - -/** - * Inherit from `TAPProducer.prototype`. - */ -inherits(TAP12Producer, TAPProducer); - -/** - * @summary - * Constructs a new TAP13Producer. - * - * @description - * Produces output conforming to the TAP13 specification. - * - * @private - * @constructor - * @extends TAPProducer - * @see {@link https://testanything.org/tap-version-13-specification.html|Specification} - */ -function TAP13Producer() { - /** - * Writes the TAP version to reporter output stream. - * @override - */ - this.writeVersion = function() { - println('TAP version 13'); - }; - - /** - * Writes that test failed to reporter output stream, with error formatting. - * @override - */ - this.writeFail = function(n, test, err) { - TAPProducer.prototype.writeFail.call(this, n, test, err); - var emitYamlBlock = err.message != null || err.stack != null; - if (emitYamlBlock) { - println(indent(1) + '---'); - if (err.message) { - println(indent(2) + 'message: |-'); - println(err.message.replace(/^/gm, indent(3))); - } - if (err.stack) { - println(indent(2) + 'stack: |-'); - println(err.stack.replace(/^/gm, indent(3))); - } - println(indent(1) + '...'); - } - }; - - function indent(level) { - return Array(level + 1).join(' '); - } -} - -/** - * Inherit from `TAPProducer.prototype`. - */ -inherits(TAP13Producer, TAPProducer); - -TAP.description = 'TAP-compatible output'; - -}).call(this,require('_process')) -},{"../runner":34,"../utils":38,"./base":17,"_process":69,"util":89}],32:[function(require,module,exports){ -(function (process,global){ -'use strict'; -/** - * @module XUnit - */ -/** - * Module dependencies. - */ - -var Base = require('./base'); -var utils = require('../utils'); -var fs = require('fs'); -var mkdirp = require('mkdirp'); -var path = require('path'); -var errors = require('../errors'); -var createUnsupportedError = errors.createUnsupportedError; -var constants = require('../runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; -var STATE_FAILED = require('../runnable').constants.STATE_FAILED; -var inherits = utils.inherits; -var escape = utils.escape; - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ -var Date = global.Date; - -/** - * Expose `XUnit`. - */ - -exports = module.exports = XUnit; - -/** - * Constructs a new `XUnit` reporter instance. - * - * @public - * @class - * @memberof Mocha.reporters - * @extends Mocha.reporters.Base - * @param {Runner} runner - Instance triggers reporter actions. - * @param {Object} [options] - runner options - */ -function XUnit(runner, options) { - Base.call(this, runner, options); - - var stats = this.stats; - var tests = []; - var self = this; - - // the name of the test suite, as it will appear in the resulting XML file - var suiteName; - - // the default name of the test suite if none is provided - var DEFAULT_SUITE_NAME = 'Mocha Tests'; - - if (options && options.reporterOptions) { - if (options.reporterOptions.output) { - if (!fs.createWriteStream) { - throw createUnsupportedError('file output not supported in browser'); - } - - mkdirp.sync(path.dirname(options.reporterOptions.output)); - self.fileStream = fs.createWriteStream(options.reporterOptions.output); - } - - // get the suite name from the reporter options (if provided) - suiteName = options.reporterOptions.suiteName; - } - - // fall back to the default suite name - suiteName = suiteName || DEFAULT_SUITE_NAME; - - runner.on(EVENT_TEST_PENDING, function(test) { - tests.push(test); - }); - - runner.on(EVENT_TEST_PASS, function(test) { - tests.push(test); - }); - - runner.on(EVENT_TEST_FAIL, function(test) { - tests.push(test); - }); - - runner.once(EVENT_RUN_END, function() { - self.write( - tag( - 'testsuite', - { - name: suiteName, - tests: stats.tests, - failures: 0, - errors: stats.failures, - skipped: stats.tests - stats.failures - stats.passes, - timestamp: new Date().toUTCString(), - time: stats.duration / 1000 || 0 - }, - false - ) - ); - - tests.forEach(function(t) { - self.test(t); - }); - - self.write('</testsuite>'); - }); -} - -/** - * Inherit from `Base.prototype`. - */ -inherits(XUnit, Base); - -/** - * Override done to close the stream (if it's a file). - * - * @param failures - * @param {Function} fn - */ -XUnit.prototype.done = function(failures, fn) { - if (this.fileStream) { - this.fileStream.end(function() { - fn(failures); - }); - } else { - fn(failures); - } -}; - -/** - * Write out the given line. - * - * @param {string} line - */ -XUnit.prototype.write = function(line) { - if (this.fileStream) { - this.fileStream.write(line + '\n'); - } else if (typeof process === 'object' && process.stdout) { - process.stdout.write(line + '\n'); - } else { - Base.consoleLog(line); - } -}; - -/** - * Output tag for the given `test.` - * - * @param {Test} test - */ -XUnit.prototype.test = function(test) { - Base.useColors = false; - - var attrs = { - classname: test.parent.fullTitle(), - name: test.title, - time: test.duration / 1000 || 0 - }; - - if (test.state === STATE_FAILED) { - var err = test.err; - var diff = - !Base.hideDiff && Base.showDiff(err) - ? '\n' + Base.generateDiff(err.actual, err.expected) - : ''; - this.write( - tag( - 'testcase', - attrs, - false, - tag( - 'failure', - {}, - false, - escape(err.message) + escape(diff) + '\n' + escape(err.stack) - ) - ) - ); - } else if (test.isPending()) { - this.write(tag('testcase', attrs, false, tag('skipped', {}, true))); - } else { - this.write(tag('testcase', attrs, true)); - } -}; - -/** - * HTML tag helper. - * - * @param name - * @param attrs - * @param close - * @param content - * @return {string} - */ -function tag(name, attrs, close, content) { - var end = close ? '/>' : '>'; - var pairs = []; - var tag; - - for (var key in attrs) { - if (Object.prototype.hasOwnProperty.call(attrs, key)) { - pairs.push(key + '="' + escape(attrs[key]) + '"'); - } - } - - tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; - if (content) { - tag += content + '</' + name + end; - } - return tag; -} - -XUnit.description = 'XUnit-compatible XML output'; - -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../errors":6,"../runnable":33,"../runner":34,"../utils":38,"./base":17,"_process":69,"fs":42,"mkdirp":59,"path":42}],33:[function(require,module,exports){ -(function (global){ -'use strict'; - -var EventEmitter = require('events').EventEmitter; -var Pending = require('./pending'); -var debug = require('debug')('mocha:runnable'); -var milliseconds = require('ms'); -var utils = require('./utils'); -var createInvalidExceptionError = require('./errors') - .createInvalidExceptionError; - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ -var Date = global.Date; -var setTimeout = global.setTimeout; -var clearTimeout = global.clearTimeout; -var toString = Object.prototype.toString; - -module.exports = Runnable; - -/** - * Initialize a new `Runnable` with the given `title` and callback `fn`. - * - * @class - * @extends external:EventEmitter - * @public - * @param {String} title - * @param {Function} fn - */ -function Runnable(title, fn) { - this.title = title; - this.fn = fn; - this.body = (fn || '').toString(); - this.async = fn && fn.length; - this.sync = !this.async; - this._timeout = 2000; - this._slow = 75; - this._enableTimeouts = true; - this.timedOut = false; - this._retries = -1; - this._currentRetry = 0; - this.pending = false; -} - -/** - * Inherit from `EventEmitter.prototype`. - */ -utils.inherits(Runnable, EventEmitter); - -/** - * Get current timeout value in msecs. - * - * @private - * @returns {number} current timeout threshold value - */ -/** - * @summary - * Set timeout threshold value (msecs). - * - * @description - * A string argument can use shorthand (e.g., "2s") and will be converted. - * The value will be clamped to range [<code>0</code>, <code>2^<sup>31</sup>-1</code>]. - * If clamped value matches either range endpoint, timeouts will be disabled. - * - * @private - * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value} - * @param {number|string} ms - Timeout threshold value. - * @returns {Runnable} this - * @chainable - */ -Runnable.prototype.timeout = function(ms) { - if (!arguments.length) { - return this._timeout; - } - if (typeof ms === 'string') { - ms = milliseconds(ms); - } - - // Clamp to range - var INT_MAX = Math.pow(2, 31) - 1; - var range = [0, INT_MAX]; - ms = utils.clamp(ms, range); - - // see #1652 for reasoning - if (ms === range[0] || ms === range[1]) { - this._enableTimeouts = false; - } - debug('timeout %d', ms); - this._timeout = ms; - if (this.timer) { - this.resetTimeout(); - } - return this; -}; - -/** - * Set or get slow `ms`. - * - * @private - * @param {number|string} ms - * @return {Runnable|number} ms or Runnable instance. - */ -Runnable.prototype.slow = function(ms) { - if (!arguments.length || typeof ms === 'undefined') { - return this._slow; - } - if (typeof ms === 'string') { - ms = milliseconds(ms); - } - debug('slow %d', ms); - this._slow = ms; - return this; -}; - -/** - * Set and get whether timeout is `enabled`. - * - * @private - * @param {boolean} enabled - * @return {Runnable|boolean} enabled or Runnable instance. - */ -Runnable.prototype.enableTimeouts = function(enabled) { - if (!arguments.length) { - return this._enableTimeouts; - } - debug('enableTimeouts %s', enabled); - this._enableTimeouts = enabled; - return this; -}; - -/** - * Halt and mark as pending. - * - * @memberof Mocha.Runnable - * @public - */ -Runnable.prototype.skip = function() { - this.pending = true; - throw new Pending('sync skip; aborting execution'); -}; - -/** - * Check if this runnable or its parent suite is marked as pending. - * - * @private - */ -Runnable.prototype.isPending = function() { - return this.pending || (this.parent && this.parent.isPending()); -}; - -/** - * Return `true` if this Runnable has failed. - * @return {boolean} - * @private - */ -Runnable.prototype.isFailed = function() { - return !this.isPending() && this.state === constants.STATE_FAILED; -}; - -/** - * Return `true` if this Runnable has passed. - * @return {boolean} - * @private - */ -Runnable.prototype.isPassed = function() { - return !this.isPending() && this.state === constants.STATE_PASSED; -}; - -/** - * Set or get number of retries. - * - * @private - */ -Runnable.prototype.retries = function(n) { - if (!arguments.length) { - return this._retries; - } - this._retries = n; -}; - -/** - * Set or get current retry - * - * @private - */ -Runnable.prototype.currentRetry = function(n) { - if (!arguments.length) { - return this._currentRetry; - } - this._currentRetry = n; -}; - -/** - * Return the full title generated by recursively concatenating the parent's - * full title. - * - * @memberof Mocha.Runnable - * @public - * @return {string} - */ -Runnable.prototype.fullTitle = function() { - return this.titlePath().join(' '); -}; - -/** - * Return the title path generated by concatenating the parent's title path with the title. - * - * @memberof Mocha.Runnable - * @public - * @return {string} - */ -Runnable.prototype.titlePath = function() { - return this.parent.titlePath().concat([this.title]); -}; - -/** - * Clear the timeout. - * - * @private - */ -Runnable.prototype.clearTimeout = function() { - clearTimeout(this.timer); -}; - -/** - * Reset the timeout. - * - * @private - */ -Runnable.prototype.resetTimeout = function() { - var self = this; - var ms = this.timeout() || 1e9; - - if (!this._enableTimeouts) { - return; - } - this.clearTimeout(); - this.timer = setTimeout(function() { - if (!self._enableTimeouts) { - return; - } - self.callback(self._timeoutError(ms)); - self.timedOut = true; - }, ms); -}; - -/** - * Set or get a list of whitelisted globals for this test run. - * - * @private - * @param {string[]} globals - */ -Runnable.prototype.globals = function(globals) { - if (!arguments.length) { - return this._allowedGlobals; - } - this._allowedGlobals = globals; -}; - -/** - * Run the test and invoke `fn(err)`. - * - * @param {Function} fn - * @private - */ -Runnable.prototype.run = function(fn) { - var self = this; - var start = new Date(); - var ctx = this.ctx; - var finished; - var emitted; - - // Sometimes the ctx exists, but it is not runnable - if (ctx && ctx.runnable) { - ctx.runnable(this); - } - - // called multiple times - function multiple(err) { - if (emitted) { - return; - } - emitted = true; - var msg = 'done() called multiple times'; - if (err && err.message) { - err.message += " (and Mocha's " + msg + ')'; - self.emit('error', err); - } else { - self.emit('error', new Error(msg)); - } - } - - // finished - function done(err) { - var ms = self.timeout(); - if (self.timedOut) { - return; - } - - if (finished) { - return multiple(err); - } - - self.clearTimeout(); - self.duration = new Date() - start; - finished = true; - if (!err && self.duration > ms && self._enableTimeouts) { - err = self._timeoutError(ms); - } - fn(err); - } - - // for .resetTimeout() and Runner#uncaught() - this.callback = done; - - if (this.fn && typeof this.fn.call !== 'function') { - done( - new TypeError( - 'A runnable must be passed a function as its second argument.' - ) - ); - return; - } - - // explicit async with `done` argument - if (this.async) { - this.resetTimeout(); - - // allows skip() to be used in an explicit async context - this.skip = function asyncSkip() { - this.pending = true; - done(); - // halt execution, the uncaught handler will ignore the failure. - throw new Pending('async skip; aborting execution'); - }; - - try { - callFnAsync(this.fn); - } catch (err) { - // handles async runnables which actually run synchronously - emitted = true; - if (err instanceof Pending) { - return; // done() is already called in this.skip() - } else if (this.allowUncaught) { - throw err; - } - done(Runnable.toValueOrError(err)); - } - return; - } - - // sync or promise-returning - try { - if (this.isPending()) { - done(); - } else { - callFn(this.fn); - } - } catch (err) { - emitted = true; - if (err instanceof Pending) { - return done(); - } else if (this.allowUncaught) { - throw err; - } - done(Runnable.toValueOrError(err)); - } - - function callFn(fn) { - var result = fn.call(ctx); - if (result && typeof result.then === 'function') { - self.resetTimeout(); - result.then( - function() { - done(); - // Return null so libraries like bluebird do not warn about - // subsequently constructed Promises. - return null; - }, - function(reason) { - done(reason || new Error('Promise rejected with no or falsy reason')); - } - ); - } else { - if (self.asyncOnly) { - return done( - new Error( - '--async-only option in use without declaring `done()` or returning a promise' - ) - ); - } - - done(); - } - } - - function callFnAsync(fn) { - var result = fn.call(ctx, function(err) { - if (err instanceof Error || toString.call(err) === '[object Error]') { - return done(err); - } - if (err) { - if (Object.prototype.toString.call(err) === '[object Object]') { - return done( - new Error('done() invoked with non-Error: ' + JSON.stringify(err)) - ); - } - return done(new Error('done() invoked with non-Error: ' + err)); - } - if (result && utils.isPromise(result)) { - return done( - new Error( - 'Resolution method is overspecified. Specify a callback *or* return a Promise; not both.' - ) - ); - } - - done(); - }); - } -}; - -/** - * Instantiates a "timeout" error - * - * @param {number} ms - Timeout (in milliseconds) - * @returns {Error} a "timeout" error - * @private - */ -Runnable.prototype._timeoutError = function(ms) { - var msg = - 'Timeout of ' + - ms + - 'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.'; - if (this.file) { - msg += ' (' + this.file + ')'; - } - return new Error(msg); -}; - -var constants = utils.defineConstants( - /** - * {@link Runnable}-related constants. - * @public - * @memberof Runnable - * @readonly - * @static - * @alias constants - * @enum {string} - */ - { - /** - * Value of `state` prop when a `Runnable` has failed - */ - STATE_FAILED: 'failed', - /** - * Value of `state` prop when a `Runnable` has passed - */ - STATE_PASSED: 'passed' - } -); - -/** - * Given `value`, return identity if truthy, otherwise create an "invalid exception" error and return that. - * @param {*} [value] - Value to return, if present - * @returns {*|Error} `value`, otherwise an `Error` - * @private - */ -Runnable.toValueOrError = function(value) { - return ( - value || - createInvalidExceptionError( - 'Runnable failed with falsy or undefined exception. Please throw an Error instead.', - value - ) - ); -}; - -Runnable.constants = constants; - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./errors":6,"./pending":16,"./utils":38,"debug":45,"events":50,"ms":60}],34:[function(require,module,exports){ -(function (process,global){ -'use strict'; - -/** - * Module dependencies. - */ -var util = require('util'); -var EventEmitter = require('events').EventEmitter; -var Pending = require('./pending'); -var utils = require('./utils'); -var inherits = utils.inherits; -var debug = require('debug')('mocha:runner'); -var Runnable = require('./runnable'); -var Suite = require('./suite'); -var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH; -var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH; -var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL; -var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL; -var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN; -var STATE_FAILED = Runnable.constants.STATE_FAILED; -var STATE_PASSED = Runnable.constants.STATE_PASSED; -var dQuote = utils.dQuote; -var sQuote = utils.sQuote; -var stackFilter = utils.stackTraceFilter(); -var stringify = utils.stringify; -var type = utils.type; -var errors = require('./errors'); -var createInvalidExceptionError = errors.createInvalidExceptionError; -var createUnsupportedError = errors.createUnsupportedError; - -/** - * Non-enumerable globals. - * @readonly - */ -var globals = [ - 'setTimeout', - 'clearTimeout', - 'setInterval', - 'clearInterval', - 'XMLHttpRequest', - 'Date', - 'setImmediate', - 'clearImmediate' -]; - -var constants = utils.defineConstants( - /** - * {@link Runner}-related constants. - * @public - * @memberof Runner - * @readonly - * @alias constants - * @static - * @enum {string} - */ - { - /** - * Emitted when {@link Hook} execution begins - */ - EVENT_HOOK_BEGIN: 'hook', - /** - * Emitted when {@link Hook} execution ends - */ - EVENT_HOOK_END: 'hook end', - /** - * Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution) - */ - EVENT_RUN_BEGIN: 'start', - /** - * Emitted when Root {@link Suite} execution has been delayed via `delay` option - */ - EVENT_DELAY_BEGIN: 'waiting', - /** - * Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()` - */ - EVENT_DELAY_END: 'ready', - /** - * Emitted when Root {@link Suite} execution ends - */ - EVENT_RUN_END: 'end', - /** - * Emitted when {@link Suite} execution begins - */ - EVENT_SUITE_BEGIN: 'suite', - /** - * Emitted when {@link Suite} execution ends - */ - EVENT_SUITE_END: 'suite end', - /** - * Emitted when {@link Test} execution begins - */ - EVENT_TEST_BEGIN: 'test', - /** - * Emitted when {@link Test} execution ends - */ - EVENT_TEST_END: 'test end', - /** - * Emitted when {@link Test} execution fails - */ - EVENT_TEST_FAIL: 'fail', - /** - * Emitted when {@link Test} execution succeeds - */ - EVENT_TEST_PASS: 'pass', - /** - * Emitted when {@link Test} becomes pending - */ - EVENT_TEST_PENDING: 'pending', - /** - * Emitted when {@link Test} execution has failed, but will retry - */ - EVENT_TEST_RETRY: 'retry' - } -); - -module.exports = Runner; - -/** - * Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}. - * - * @extends external:EventEmitter - * @public - * @class - * @param {Suite} suite Root suite - * @param {boolean} [delay] Whether or not to delay execution of root suite - * until ready. - */ -function Runner(suite, delay) { - var self = this; - this._globals = []; - this._abort = false; - this._delay = delay; - this.suite = suite; - this.started = false; - this.total = suite.total(); - this.failures = 0; - this.on(constants.EVENT_TEST_END, function(test) { - if (test.type === 'test' && test.retriedTest() && test.parent) { - var idx = - test.parent.tests && test.parent.tests.indexOf(test.retriedTest()); - if (idx > -1) test.parent.tests[idx] = test; - } - self.checkGlobals(test); - }); - this.on(constants.EVENT_HOOK_END, function(hook) { - self.checkGlobals(hook); - }); - this._defaultGrep = /.*/; - this.grep(this._defaultGrep); - this.globals(this.globalProps()); -} - -/** - * Wrapper for setImmediate, process.nextTick, or browser polyfill. - * - * @param {Function} fn - * @private - */ -Runner.immediately = global.setImmediate || process.nextTick; - -/** - * Inherit from `EventEmitter.prototype`. - */ -inherits(Runner, EventEmitter); - -/** - * Run tests with full titles matching `re`. Updates runner.total - * with number of tests matched. - * - * @public - * @memberof Runner - * @param {RegExp} re - * @param {boolean} invert - * @return {Runner} Runner instance. - */ -Runner.prototype.grep = function(re, invert) { - debug('grep %s', re); - this._grep = re; - this._invert = invert; - this.total = this.grepTotal(this.suite); - return this; -}; - -/** - * Returns the number of tests matching the grep search for the - * given suite. - * - * @memberof Runner - * @public - * @param {Suite} suite - * @return {number} - */ -Runner.prototype.grepTotal = function(suite) { - var self = this; - var total = 0; - - suite.eachTest(function(test) { - var match = self._grep.test(test.fullTitle()); - if (self._invert) { - match = !match; - } - if (match) { - total++; - } - }); - - return total; -}; - -/** - * Return a list of global properties. - * - * @return {Array} - * @private - */ -Runner.prototype.globalProps = function() { - var props = Object.keys(global); - - // non-enumerables - for (var i = 0; i < globals.length; ++i) { - if (~props.indexOf(globals[i])) { - continue; - } - props.push(globals[i]); - } - - return props; -}; - -/** - * Allow the given `arr` of globals. - * - * @public - * @memberof Runner - * @param {Array} arr - * @return {Runner} Runner instance. - */ -Runner.prototype.globals = function(arr) { - if (!arguments.length) { - return this._globals; - } - debug('globals %j', arr); - this._globals = this._globals.concat(arr); - return this; -}; - -/** - * Check for global variable leaks. - * - * @private - */ -Runner.prototype.checkGlobals = function(test) { - if (!this.checkLeaks) { - return; - } - var ok = this._globals; - - var globals = this.globalProps(); - var leaks; - - if (test) { - ok = ok.concat(test._allowedGlobals || []); - } - - if (this.prevGlobalsLength === globals.length) { - return; - } - this.prevGlobalsLength = globals.length; - - leaks = filterLeaks(ok, globals); - this._globals = this._globals.concat(leaks); - - if (leaks.length) { - var msg = 'global leak(s) detected: %s'; - var error = new Error(util.format(msg, leaks.map(sQuote).join(', '))); - this.fail(test, error); - } -}; - -/** - * Fail the given `test`. - * - * @private - * @param {Test} test - * @param {Error} err - */ -Runner.prototype.fail = function(test, err) { - if (test.isPending()) { - return; - } - - ++this.failures; - test.state = STATE_FAILED; - - if (!isError(err)) { - err = thrown2Error(err); - } - - try { - err.stack = - this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack); - } catch (ignore) { - // some environments do not take kindly to monkeying with the stack - } - - this.emit(constants.EVENT_TEST_FAIL, test, err); -}; - -/** - * Fail the given `hook` with `err`. - * - * Hook failures work in the following pattern: - * - If bail, run corresponding `after each` and `after` hooks, - * then exit - * - Failed `before` hook skips all tests in a suite and subsuites, - * but jumps to corresponding `after` hook - * - Failed `before each` hook skips remaining tests in a - * suite and jumps to corresponding `after each` hook, - * which is run only once - * - Failed `after` hook does not alter execution order - * - Failed `after each` hook skips remaining tests in a - * suite and subsuites, but executes other `after each` - * hooks - * - * @private - * @param {Hook} hook - * @param {Error} err - */ -Runner.prototype.failHook = function(hook, err) { - hook.originalTitle = hook.originalTitle || hook.title; - if (hook.ctx && hook.ctx.currentTest) { - hook.title = - hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title); - } else { - var parentTitle; - if (hook.parent.title) { - parentTitle = hook.parent.title; - } else { - parentTitle = hook.parent.root ? '{root}' : ''; - } - hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle); - } - - this.fail(hook, err); -}; - -/** - * Run hook `name` callbacks and then invoke `fn()`. - * - * @private - * @param {string} name - * @param {Function} fn - */ - -Runner.prototype.hook = function(name, fn) { - var suite = this.suite; - var hooks = suite.getHooks(name); - var self = this; - - function next(i) { - var hook = hooks[i]; - if (!hook) { - return fn(); - } - self.currentRunnable = hook; - - if (name === HOOK_TYPE_BEFORE_ALL) { - hook.ctx.currentTest = hook.parent.tests[0]; - } else if (name === HOOK_TYPE_AFTER_ALL) { - hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1]; - } else { - hook.ctx.currentTest = self.test; - } - - hook.allowUncaught = self.allowUncaught; - - self.emit(constants.EVENT_HOOK_BEGIN, hook); - - if (!hook.listeners('error').length) { - hook.on('error', function(err) { - self.failHook(hook, err); - }); - } - - hook.run(function(err) { - var testError = hook.error(); - if (testError) { - self.fail(self.test, testError); - } - // conditional skip - if (hook.pending) { - if (name === HOOK_TYPE_AFTER_EACH) { - // TODO define and implement use case - if (self.test) { - self.test.pending = true; - } - } else if (name === HOOK_TYPE_BEFORE_EACH) { - if (self.test) { - self.test.pending = true; - } - self.emit(constants.EVENT_HOOK_END, hook); - hook.pending = false; // activates hook for next test - return fn(new Error('abort hookDown')); - } else if (name === HOOK_TYPE_BEFORE_ALL) { - suite.tests.forEach(function(test) { - test.pending = true; - }); - suite.suites.forEach(function(suite) { - suite.pending = true; - }); - } else { - hook.pending = false; - var errForbid = createUnsupportedError('`this.skip` forbidden'); - self.failHook(hook, errForbid); - return fn(errForbid); - } - } else if (err) { - self.failHook(hook, err); - // stop executing hooks, notify callee of hook err - return fn(err); - } - self.emit(constants.EVENT_HOOK_END, hook); - delete hook.ctx.currentTest; - next(++i); - }); - } - - Runner.immediately(function() { - next(0); - }); -}; - -/** - * Run hook `name` for the given array of `suites` - * in order, and callback `fn(err, errSuite)`. - * - * @private - * @param {string} name - * @param {Array} suites - * @param {Function} fn - */ -Runner.prototype.hooks = function(name, suites, fn) { - var self = this; - var orig = this.suite; - - function next(suite) { - self.suite = suite; - - if (!suite) { - self.suite = orig; - return fn(); - } - - self.hook(name, function(err) { - if (err) { - var errSuite = self.suite; - self.suite = orig; - return fn(err, errSuite); - } - - next(suites.pop()); - }); - } - - next(suites.pop()); -}; - -/** - * Run hooks from the top level down. - * - * @param {String} name - * @param {Function} fn - * @private - */ -Runner.prototype.hookUp = function(name, fn) { - var suites = [this.suite].concat(this.parents()).reverse(); - this.hooks(name, suites, fn); -}; - -/** - * Run hooks from the bottom up. - * - * @param {String} name - * @param {Function} fn - * @private - */ -Runner.prototype.hookDown = function(name, fn) { - var suites = [this.suite].concat(this.parents()); - this.hooks(name, suites, fn); -}; - -/** - * Return an array of parent Suites from - * closest to furthest. - * - * @return {Array} - * @private - */ -Runner.prototype.parents = function() { - var suite = this.suite; - var suites = []; - while (suite.parent) { - suite = suite.parent; - suites.push(suite); - } - return suites; -}; - -/** - * Run the current test and callback `fn(err)`. - * - * @param {Function} fn - * @private - */ -Runner.prototype.runTest = function(fn) { - var self = this; - var test = this.test; - - if (!test) { - return; - } - - var suite = this.parents().reverse()[0] || this.suite; - if (this.forbidOnly && suite.hasOnly()) { - fn(new Error('`.only` forbidden')); - return; - } - if (this.asyncOnly) { - test.asyncOnly = true; - } - test.on('error', function(err) { - if (err instanceof Pending) { - return; - } - self.fail(test, err); - }); - if (this.allowUncaught) { - test.allowUncaught = true; - return test.run(fn); - } - try { - test.run(fn); - } catch (err) { - fn(err); - } -}; - -/** - * Run tests in the given `suite` and invoke the callback `fn()` when complete. - * - * @private - * @param {Suite} suite - * @param {Function} fn - */ -Runner.prototype.runTests = function(suite, fn) { - var self = this; - var tests = suite.tests.slice(); - var test; - - function hookErr(_, errSuite, after) { - // before/after Each hook for errSuite failed: - var orig = self.suite; - - // for failed 'after each' hook start from errSuite parent, - // otherwise start from errSuite itself - self.suite = after ? errSuite.parent : errSuite; - - if (self.suite) { - // call hookUp afterEach - self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) { - self.suite = orig; - // some hooks may fail even now - if (err2) { - return hookErr(err2, errSuite2, true); - } - // report error suite - fn(errSuite); - }); - } else { - // there is no need calling other 'after each' hooks - self.suite = orig; - fn(errSuite); - } - } - - function next(err, errSuite) { - // if we bail after first err - if (self.failures && suite._bail) { - tests = []; - } - - if (self._abort) { - return fn(); - } - - if (err) { - return hookErr(err, errSuite, true); - } - - // next test - test = tests.shift(); - - // all done - if (!test) { - return fn(); - } - - // grep - var match = self._grep.test(test.fullTitle()); - if (self._invert) { - match = !match; - } - if (!match) { - // Run immediately only if we have defined a grep. When we - // define a grep — It can cause maximum callstack error if - // the grep is doing a large recursive loop by neglecting - // all tests. The run immediately function also comes with - // a performance cost. So we don't want to run immediately - // if we run the whole test suite, because running the whole - // test suite don't do any immediate recursive loops. Thus, - // allowing a JS runtime to breathe. - if (self._grep !== self._defaultGrep) { - Runner.immediately(next); - } else { - next(); - } - return; - } - - // static skip, no hooks are executed - if (test.isPending()) { - if (self.forbidPending) { - test.isPending = alwaysFalse; - self.fail(test, new Error('Pending test forbidden')); - delete test.isPending; - } else { - self.emit(constants.EVENT_TEST_PENDING, test); - } - self.emit(constants.EVENT_TEST_END, test); - return next(); - } - - // execute test and hook(s) - self.emit(constants.EVENT_TEST_BEGIN, (self.test = test)); - self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) { - // conditional skip within beforeEach - if (test.isPending()) { - if (self.forbidPending) { - test.isPending = alwaysFalse; - self.fail(test, new Error('Pending test forbidden')); - delete test.isPending; - } else { - self.emit(constants.EVENT_TEST_PENDING, test); - } - self.emit(constants.EVENT_TEST_END, test); - // skip inner afterEach hooks below errSuite level - var origSuite = self.suite; - self.suite = errSuite || self.suite; - return self.hookUp(HOOK_TYPE_AFTER_EACH, function(e, eSuite) { - self.suite = origSuite; - next(e, eSuite); - }); - } - if (err) { - return hookErr(err, errSuite, false); - } - self.currentRunnable = self.test; - self.runTest(function(err) { - test = self.test; - // conditional skip within it - if (test.pending) { - if (self.forbidPending) { - test.isPending = alwaysFalse; - self.fail(test, new Error('Pending test forbidden')); - delete test.isPending; - } else { - self.emit(constants.EVENT_TEST_PENDING, test); - } - self.emit(constants.EVENT_TEST_END, test); - return self.hookUp(HOOK_TYPE_AFTER_EACH, next); - } else if (err) { - var retry = test.currentRetry(); - if (retry < test.retries()) { - var clonedTest = test.clone(); - clonedTest.currentRetry(retry + 1); - tests.unshift(clonedTest); - - self.emit(constants.EVENT_TEST_RETRY, test, err); - - // Early return + hook trigger so that it doesn't - // increment the count wrong - return self.hookUp(HOOK_TYPE_AFTER_EACH, next); - } else { - self.fail(test, err); - } - self.emit(constants.EVENT_TEST_END, test); - return self.hookUp(HOOK_TYPE_AFTER_EACH, next); - } - - test.state = STATE_PASSED; - self.emit(constants.EVENT_TEST_PASS, test); - self.emit(constants.EVENT_TEST_END, test); - self.hookUp(HOOK_TYPE_AFTER_EACH, next); - }); - }); - } - - this.next = next; - this.hookErr = hookErr; - next(); -}; - -function alwaysFalse() { - return false; -} - -/** - * Run the given `suite` and invoke the callback `fn()` when complete. - * - * @private - * @param {Suite} suite - * @param {Function} fn - */ -Runner.prototype.runSuite = function(suite, fn) { - var i = 0; - var self = this; - var total = this.grepTotal(suite); - - debug('run suite %s', suite.fullTitle()); - - if (!total || (self.failures && suite._bail)) { - return fn(); - } - - this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite)); - - function next(errSuite) { - if (errSuite) { - // current suite failed on a hook from errSuite - if (errSuite === suite) { - // if errSuite is current suite - // continue to the next sibling suite - return done(); - } - // errSuite is among the parents of current suite - // stop execution of errSuite and all sub-suites - return done(errSuite); - } - - if (self._abort) { - return done(); - } - - var curr = suite.suites[i++]; - if (!curr) { - return done(); - } - - // Avoid grep neglecting large number of tests causing a - // huge recursive loop and thus a maximum call stack error. - // See comment in `this.runTests()` for more information. - if (self._grep !== self._defaultGrep) { - Runner.immediately(function() { - self.runSuite(curr, next); - }); - } else { - self.runSuite(curr, next); - } - } - - function done(errSuite) { - self.suite = suite; - self.nextSuite = next; - - // remove reference to test - delete self.test; - - self.hook(HOOK_TYPE_AFTER_ALL, function() { - self.emit(constants.EVENT_SUITE_END, suite); - fn(errSuite); - }); - } - - this.nextSuite = next; - - this.hook(HOOK_TYPE_BEFORE_ALL, function(err) { - if (err) { - return done(); - } - self.runTests(suite, next); - }); -}; - -/** - * Handle uncaught exceptions within runner. - * - * @param {Error} err - * @private - */ -Runner.prototype.uncaught = function(err) { - if (err instanceof Pending) { - return; - } - // browser does not exit script when throwing in global.onerror() - if (this.allowUncaught && !process.browser) { - throw err; - } - - if (err) { - debug('uncaught exception %O', err); - } else { - debug('uncaught undefined/falsy exception'); - err = createInvalidExceptionError( - 'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger', - err - ); - } - - if (!isError(err)) { - err = thrown2Error(err); - } - err.uncaught = true; - - var runnable = this.currentRunnable; - - if (!runnable) { - runnable = new Runnable('Uncaught error outside test suite'); - runnable.parent = this.suite; - - if (this.started) { - this.fail(runnable, err); - } else { - // Can't recover from this failure - this.emit(constants.EVENT_RUN_BEGIN); - this.fail(runnable, err); - this.emit(constants.EVENT_RUN_END); - } - - return; - } - - runnable.clearTimeout(); - - if (runnable.isFailed()) { - // Ignore error if already failed - return; - } else if (runnable.isPending()) { - // report 'pending test' retrospectively as failed - runnable.isPending = alwaysFalse; - this.fail(runnable, err); - delete runnable.isPending; - return; - } - - // we cannot recover gracefully if a Runnable has already passed - // then fails asynchronously - if (runnable.isPassed()) { - this.fail(runnable, err); - this.abort(); - } else { - debug(runnable); - return runnable.callback(err); - } -}; - -/** - * Handle uncaught exceptions after runner's end event. - * - * @param {Error} err - * @private - */ -Runner.prototype.uncaughtEnd = function uncaughtEnd(err) { - if (err instanceof Pending) return; - throw err; -}; - -/** - * Run the root suite and invoke `fn(failures)` - * on completion. - * - * @public - * @memberof Runner - * @param {Function} fn - * @return {Runner} Runner instance. - */ -Runner.prototype.run = function(fn) { - var self = this; - var rootSuite = this.suite; - - fn = fn || function() {}; - - function uncaught(err) { - self.uncaught(err); - } - - function start() { - // If there is an `only` filter - if (rootSuite.hasOnly()) { - rootSuite.filterOnly(); - } - self.started = true; - if (self._delay) { - self.emit(constants.EVENT_DELAY_END); - } - self.emit(constants.EVENT_RUN_BEGIN); - - self.runSuite(rootSuite, function() { - debug('finished running'); - self.emit(constants.EVENT_RUN_END); - }); - } - - debug(constants.EVENT_RUN_BEGIN); - - // references cleanup to avoid memory leaks - this.on(constants.EVENT_SUITE_END, function(suite) { - suite.cleanReferences(); - }); - - // callback - this.on(constants.EVENT_RUN_END, function() { - debug(constants.EVENT_RUN_END); - process.removeListener('uncaughtException', uncaught); - process.on('uncaughtException', self.uncaughtEnd); - fn(self.failures); - }); - - // uncaught exception - process.removeListener('uncaughtException', self.uncaughtEnd); - process.on('uncaughtException', uncaught); - - if (this._delay) { - // for reporters, I guess. - // might be nice to debounce some dots while we wait. - this.emit(constants.EVENT_DELAY_BEGIN, rootSuite); - rootSuite.once(EVENT_ROOT_SUITE_RUN, start); - } else { - Runner.immediately(function() { - start(); - }); - } - - return this; -}; - -/** - * Cleanly abort execution. - * - * @memberof Runner - * @public - * @return {Runner} Runner instance. - */ -Runner.prototype.abort = function() { - debug('aborting'); - this._abort = true; - - return this; -}; - -/** - * Filter leaks with the given globals flagged as `ok`. - * - * @private - * @param {Array} ok - * @param {Array} globals - * @return {Array} - */ -function filterLeaks(ok, globals) { - return globals.filter(function(key) { - // Firefox and Chrome exposes iframes as index inside the window object - if (/^\d+/.test(key)) { - return false; - } - - // in firefox - // if runner runs in an iframe, this iframe's window.getInterface method - // not init at first it is assigned in some seconds - if (global.navigator && /^getInterface/.test(key)) { - return false; - } - - // an iframe could be approached by window[iframeIndex] - // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak - if (global.navigator && /^\d+/.test(key)) { - return false; - } - - // Opera and IE expose global variables for HTML element IDs (issue #243) - if (/^mocha-/.test(key)) { - return false; - } - - var matched = ok.filter(function(ok) { - if (~ok.indexOf('*')) { - return key.indexOf(ok.split('*')[0]) === 0; - } - return key === ok; - }); - return !matched.length && (!global.navigator || key !== 'onerror'); - }); -} - -/** - * Check if argument is an instance of Error object or a duck-typed equivalent. - * - * @private - * @param {Object} err - object to check - * @param {string} err.message - error message - * @returns {boolean} - */ -function isError(err) { - return err instanceof Error || (err && typeof err.message === 'string'); -} - -/** - * - * Converts thrown non-extensible type into proper Error. - * - * @private - * @param {*} thrown - Non-extensible type thrown by code - * @return {Error} - */ -function thrown2Error(err) { - return new Error( - 'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)' - ); -} - -Runner.constants = constants; - -/** - * Node.js' `EventEmitter` - * @external EventEmitter - * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter} - */ - -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./errors":6,"./pending":16,"./runnable":33,"./suite":36,"./utils":38,"_process":69,"debug":45,"events":50,"util":89}],35:[function(require,module,exports){ -(function (global){ -'use strict'; - -/** - * Provides a factory function for a {@link StatsCollector} object. - * @module - */ - -var constants = require('./runner').constants; -var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; -var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; -var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; -var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; -var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; -var EVENT_RUN_END = constants.EVENT_RUN_END; -var EVENT_TEST_END = constants.EVENT_TEST_END; - -/** - * Test statistics collector. - * - * @public - * @typedef {Object} StatsCollector - * @property {number} suites - integer count of suites run. - * @property {number} tests - integer count of tests run. - * @property {number} passes - integer count of passing tests. - * @property {number} pending - integer count of pending tests. - * @property {number} failures - integer count of failed tests. - * @property {Date} start - time when testing began. - * @property {Date} end - time when testing concluded. - * @property {number} duration - number of msecs that testing took. - */ - -var Date = global.Date; - -/** - * Provides stats such as test duration, number of tests passed / failed etc., by listening for events emitted by `runner`. - * - * @private - * @param {Runner} runner - Runner instance - * @throws {TypeError} If falsy `runner` - */ -function createStatsCollector(runner) { - /** - * @type StatsCollector - */ - var stats = { - suites: 0, - tests: 0, - passes: 0, - pending: 0, - failures: 0 - }; - - if (!runner) { - throw new TypeError('Missing runner argument'); - } - - runner.stats = stats; - - runner.once(EVENT_RUN_BEGIN, function() { - stats.start = new Date(); - }); - runner.on(EVENT_SUITE_BEGIN, function(suite) { - suite.root || stats.suites++; - }); - runner.on(EVENT_TEST_PASS, function() { - stats.passes++; - }); - runner.on(EVENT_TEST_FAIL, function() { - stats.failures++; - }); - runner.on(EVENT_TEST_PENDING, function() { - stats.pending++; - }); - runner.on(EVENT_TEST_END, function() { - stats.tests++; - }); - runner.once(EVENT_RUN_END, function() { - stats.end = new Date(); - stats.duration = stats.end - stats.start; - }); -} - -module.exports = createStatsCollector; - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./runner":34}],36:[function(require,module,exports){ -'use strict'; - -/** - * Module dependencies. - */ -var EventEmitter = require('events').EventEmitter; -var Hook = require('./hook'); -var utils = require('./utils'); -var inherits = utils.inherits; -var debug = require('debug')('mocha:suite'); -var milliseconds = require('ms'); -var errors = require('./errors'); -var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError; - -/** - * Expose `Suite`. - */ - -exports = module.exports = Suite; - -/** - * Create a new `Suite` with the given `title` and parent `Suite`. - * - * @public - * @param {Suite} parent - Parent suite (required!) - * @param {string} title - Title - * @return {Suite} - */ -Suite.create = function(parent, title) { - var suite = new Suite(title, parent.ctx); - suite.parent = parent; - title = suite.fullTitle(); - parent.addSuite(suite); - return suite; -}; - -/** - * Constructs a new `Suite` instance with the given `title`, `ctx`, and `isRoot`. - * - * @public - * @class - * @extends EventEmitter - * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter|EventEmitter} - * @param {string} title - Suite title. - * @param {Context} parentContext - Parent context instance. - * @param {boolean} [isRoot=false] - Whether this is the root suite. - */ -function Suite(title, parentContext, isRoot) { - if (!utils.isString(title)) { - throw createInvalidArgumentTypeError( - 'Suite argument "title" must be a string. Received type "' + - typeof title + - '"', - 'title', - 'string' - ); - } - this.title = title; - function Context() {} - Context.prototype = parentContext; - this.ctx = new Context(); - this.suites = []; - this.tests = []; - this.pending = false; - this._beforeEach = []; - this._beforeAll = []; - this._afterEach = []; - this._afterAll = []; - this.root = isRoot === true; - this._timeout = 2000; - this._enableTimeouts = true; - this._slow = 75; - this._bail = false; - this._retries = -1; - this._onlyTests = []; - this._onlySuites = []; - this.delayed = false; - - this.on('newListener', function(event) { - if (deprecatedEvents[event]) { - utils.deprecate( - 'Event "' + - event + - '" is deprecated. Please let the Mocha team know about your use case: https://git.io/v6Lwm' - ); - } - }); -} - -/** - * Inherit from `EventEmitter.prototype`. - */ -inherits(Suite, EventEmitter); - -/** - * Return a clone of this `Suite`. - * - * @private - * @return {Suite} - */ -Suite.prototype.clone = function() { - var suite = new Suite(this.title); - debug('clone'); - suite.ctx = this.ctx; - suite.root = this.root; - suite.timeout(this.timeout()); - suite.retries(this.retries()); - suite.enableTimeouts(this.enableTimeouts()); - suite.slow(this.slow()); - suite.bail(this.bail()); - return suite; -}; - -/** - * Set or get timeout `ms` or short-hand such as "2s". - * - * @private - * @todo Do not attempt to set value if `ms` is undefined - * @param {number|string} ms - * @return {Suite|number} for chaining - */ -Suite.prototype.timeout = function(ms) { - if (!arguments.length) { - return this._timeout; - } - if (ms.toString() === '0') { - this._enableTimeouts = false; - } - if (typeof ms === 'string') { - ms = milliseconds(ms); - } - debug('timeout %d', ms); - this._timeout = parseInt(ms, 10); - return this; -}; - -/** - * Set or get number of times to retry a failed test. - * - * @private - * @param {number|string} n - * @return {Suite|number} for chaining - */ -Suite.prototype.retries = function(n) { - if (!arguments.length) { - return this._retries; - } - debug('retries %d', n); - this._retries = parseInt(n, 10) || 0; - return this; -}; - -/** - * Set or get timeout to `enabled`. - * - * @private - * @param {boolean} enabled - * @return {Suite|boolean} self or enabled - */ -Suite.prototype.enableTimeouts = function(enabled) { - if (!arguments.length) { - return this._enableTimeouts; - } - debug('enableTimeouts %s', enabled); - this._enableTimeouts = enabled; - return this; -}; - -/** - * Set or get slow `ms` or short-hand such as "2s". - * - * @private - * @param {number|string} ms - * @return {Suite|number} for chaining - */ -Suite.prototype.slow = function(ms) { - if (!arguments.length) { - return this._slow; - } - if (typeof ms === 'string') { - ms = milliseconds(ms); - } - debug('slow %d', ms); - this._slow = ms; - return this; -}; - -/** - * Set or get whether to bail after first error. - * - * @private - * @param {boolean} bail - * @return {Suite|number} for chaining - */ -Suite.prototype.bail = function(bail) { - if (!arguments.length) { - return this._bail; - } - debug('bail %s', bail); - this._bail = bail; - return this; -}; - -/** - * Check if this suite or its parent suite is marked as pending. - * - * @private - */ -Suite.prototype.isPending = function() { - return this.pending || (this.parent && this.parent.isPending()); -}; - -/** - * Generic hook-creator. - * @private - * @param {string} title - Title of hook - * @param {Function} fn - Hook callback - * @returns {Hook} A new hook - */ -Suite.prototype._createHook = function(title, fn) { - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.retries(this.retries()); - hook.enableTimeouts(this.enableTimeouts()); - hook.slow(this.slow()); - hook.ctx = this.ctx; - hook.file = this.file; - return hook; -}; - -/** - * Run `fn(test[, done])` before running tests. - * - * @private - * @param {string} title - * @param {Function} fn - * @return {Suite} for chaining - */ -Suite.prototype.beforeAll = function(title, fn) { - if (this.isPending()) { - return this; - } - if (typeof title === 'function') { - fn = title; - title = fn.name; - } - title = '"before all" hook' + (title ? ': ' + title : ''); - - var hook = this._createHook(title, fn); - this._beforeAll.push(hook); - this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_ALL, hook); - return this; -}; - -/** - * Run `fn(test[, done])` after running tests. - * - * @private - * @param {string} title - * @param {Function} fn - * @return {Suite} for chaining - */ -Suite.prototype.afterAll = function(title, fn) { - if (this.isPending()) { - return this; - } - if (typeof title === 'function') { - fn = title; - title = fn.name; - } - title = '"after all" hook' + (title ? ': ' + title : ''); - - var hook = this._createHook(title, fn); - this._afterAll.push(hook); - this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_ALL, hook); - return this; -}; - -/** - * Run `fn(test[, done])` before each test case. - * - * @private - * @param {string} title - * @param {Function} fn - * @return {Suite} for chaining - */ -Suite.prototype.beforeEach = function(title, fn) { - if (this.isPending()) { - return this; - } - if (typeof title === 'function') { - fn = title; - title = fn.name; - } - title = '"before each" hook' + (title ? ': ' + title : ''); - - var hook = this._createHook(title, fn); - this._beforeEach.push(hook); - this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_EACH, hook); - return this; -}; - -/** - * Run `fn(test[, done])` after each test case. - * - * @private - * @param {string} title - * @param {Function} fn - * @return {Suite} for chaining - */ -Suite.prototype.afterEach = function(title, fn) { - if (this.isPending()) { - return this; - } - if (typeof title === 'function') { - fn = title; - title = fn.name; - } - title = '"after each" hook' + (title ? ': ' + title : ''); - - var hook = this._createHook(title, fn); - this._afterEach.push(hook); - this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_EACH, hook); - return this; -}; - -/** - * Add a test `suite`. - * - * @private - * @param {Suite} suite - * @return {Suite} for chaining - */ -Suite.prototype.addSuite = function(suite) { - suite.parent = this; - suite.root = false; - suite.timeout(this.timeout()); - suite.retries(this.retries()); - suite.enableTimeouts(this.enableTimeouts()); - suite.slow(this.slow()); - suite.bail(this.bail()); - this.suites.push(suite); - this.emit(constants.EVENT_SUITE_ADD_SUITE, suite); - return this; -}; - -/** - * Add a `test` to this suite. - * - * @private - * @param {Test} test - * @return {Suite} for chaining - */ -Suite.prototype.addTest = function(test) { - test.parent = this; - test.timeout(this.timeout()); - test.retries(this.retries()); - test.enableTimeouts(this.enableTimeouts()); - test.slow(this.slow()); - test.ctx = this.ctx; - this.tests.push(test); - this.emit(constants.EVENT_SUITE_ADD_TEST, test); - return this; -}; - -/** - * Return the full title generated by recursively concatenating the parent's - * full title. - * - * @memberof Suite - * @public - * @return {string} - */ -Suite.prototype.fullTitle = function() { - return this.titlePath().join(' '); -}; - -/** - * Return the title path generated by recursively concatenating the parent's - * title path. - * - * @memberof Suite - * @public - * @return {string} - */ -Suite.prototype.titlePath = function() { - var result = []; - if (this.parent) { - result = result.concat(this.parent.titlePath()); - } - if (!this.root) { - result.push(this.title); - } - return result; -}; - -/** - * Return the total number of tests. - * - * @memberof Suite - * @public - * @return {number} - */ -Suite.prototype.total = function() { - return ( - this.suites.reduce(function(sum, suite) { - return sum + suite.total(); - }, 0) + this.tests.length - ); -}; - -/** - * Iterates through each suite recursively to find all tests. Applies a - * function in the format `fn(test)`. - * - * @private - * @param {Function} fn - * @return {Suite} - */ -Suite.prototype.eachTest = function(fn) { - this.tests.forEach(fn); - this.suites.forEach(function(suite) { - suite.eachTest(fn); - }); - return this; -}; - -/** - * This will run the root suite if we happen to be running in delayed mode. - * @private - */ -Suite.prototype.run = function run() { - if (this.root) { - this.emit(constants.EVENT_ROOT_SUITE_RUN); - } -}; - -/** - * Determines whether a suite has an `only` test or suite as a descendant. - * - * @private - * @returns {Boolean} - */ -Suite.prototype.hasOnly = function hasOnly() { - return ( - this._onlyTests.length > 0 || - this._onlySuites.length > 0 || - this.suites.some(function(suite) { - return suite.hasOnly(); - }) - ); -}; - -/** - * Filter suites based on `isOnly` logic. - * - * @private - * @returns {Boolean} - */ -Suite.prototype.filterOnly = function filterOnly() { - if (this._onlyTests.length) { - // If the suite contains `only` tests, run those and ignore any nested suites. - this.tests = this._onlyTests; - this.suites = []; - } else { - // Otherwise, do not run any of the tests in this suite. - this.tests = []; - this._onlySuites.forEach(function(onlySuite) { - // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite. - // Otherwise, all of the tests on this `only` suite should be run, so don't filter it. - if (onlySuite.hasOnly()) { - onlySuite.filterOnly(); - } - }); - // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants. - var onlySuites = this._onlySuites; - this.suites = this.suites.filter(function(childSuite) { - return onlySuites.indexOf(childSuite) !== -1 || childSuite.filterOnly(); - }); - } - // Keep the suite only if there is something to run - return this.tests.length > 0 || this.suites.length > 0; -}; - -/** - * Adds a suite to the list of subsuites marked `only`. - * - * @private - * @param {Suite} suite - */ -Suite.prototype.appendOnlySuite = function(suite) { - this._onlySuites.push(suite); -}; - -/** - * Adds a test to the list of tests marked `only`. - * - * @private - * @param {Test} test - */ -Suite.prototype.appendOnlyTest = function(test) { - this._onlyTests.push(test); -}; - -/** - * Returns the array of hooks by hook name; see `HOOK_TYPE_*` constants. - * @private - */ -Suite.prototype.getHooks = function getHooks(name) { - return this['_' + name]; -}; - -/** - * Cleans up the references to all the deferred functions - * (before/after/beforeEach/afterEach) and tests of a Suite. - * These must be deleted otherwise a memory leak can happen, - * as those functions may reference variables from closures, - * thus those variables can never be garbage collected as long - * as the deferred functions exist. - * - * @private - */ -Suite.prototype.cleanReferences = function cleanReferences() { - function cleanArrReferences(arr) { - for (var i = 0; i < arr.length; i++) { - delete arr[i].fn; - } - } - - if (Array.isArray(this._beforeAll)) { - cleanArrReferences(this._beforeAll); - } - - if (Array.isArray(this._beforeEach)) { - cleanArrReferences(this._beforeEach); - } - - if (Array.isArray(this._afterAll)) { - cleanArrReferences(this._afterAll); - } - - if (Array.isArray(this._afterEach)) { - cleanArrReferences(this._afterEach); - } - - for (var i = 0; i < this.tests.length; i++) { - delete this.tests[i].fn; - } -}; - -var constants = utils.defineConstants( - /** - * {@link Suite}-related constants. - * @public - * @memberof Suite - * @alias constants - * @readonly - * @static - * @enum {string} - */ - { - /** - * Event emitted after a test file has been loaded Not emitted in browser. - */ - EVENT_FILE_POST_REQUIRE: 'post-require', - /** - * Event emitted before a test file has been loaded. In browser, this is emitted once an interface has been selected. - */ - EVENT_FILE_PRE_REQUIRE: 'pre-require', - /** - * Event emitted immediately after a test file has been loaded. Not emitted in browser. - */ - EVENT_FILE_REQUIRE: 'require', - /** - * Event emitted when `global.run()` is called (use with `delay` option) - */ - EVENT_ROOT_SUITE_RUN: 'run', - - /** - * Namespace for collection of a `Suite`'s "after all" hooks - */ - HOOK_TYPE_AFTER_ALL: 'afterAll', - /** - * Namespace for collection of a `Suite`'s "after each" hooks - */ - HOOK_TYPE_AFTER_EACH: 'afterEach', - /** - * Namespace for collection of a `Suite`'s "before all" hooks - */ - HOOK_TYPE_BEFORE_ALL: 'beforeAll', - /** - * Namespace for collection of a `Suite`'s "before all" hooks - */ - HOOK_TYPE_BEFORE_EACH: 'beforeEach', - - // the following events are all deprecated - - /** - * Emitted after an "after all" `Hook` has been added to a `Suite`. Deprecated - */ - EVENT_SUITE_ADD_HOOK_AFTER_ALL: 'afterAll', - /** - * Emitted after an "after each" `Hook` has been added to a `Suite` Deprecated - */ - EVENT_SUITE_ADD_HOOK_AFTER_EACH: 'afterEach', - /** - * Emitted after an "before all" `Hook` has been added to a `Suite` Deprecated - */ - EVENT_SUITE_ADD_HOOK_BEFORE_ALL: 'beforeAll', - /** - * Emitted after an "before each" `Hook` has been added to a `Suite` Deprecated - */ - EVENT_SUITE_ADD_HOOK_BEFORE_EACH: 'beforeEach', - /** - * Emitted after a child `Suite` has been added to a `Suite`. Deprecated - */ - EVENT_SUITE_ADD_SUITE: 'suite', - /** - * Emitted after a `Test` has been added to a `Suite`. Deprecated - */ - EVENT_SUITE_ADD_TEST: 'test' - } -); - -/** - * @summary There are no known use cases for these events. - * @desc This is a `Set`-like object having all keys being the constant's string value and the value being `true`. - * @todo Remove eventually - * @type {Object<string,boolean>} - * @ignore - */ -var deprecatedEvents = Object.keys(constants) - .filter(function(constant) { - return constant.substring(0, 15) === 'EVENT_SUITE_ADD'; - }) - .reduce(function(acc, constant) { - acc[constants[constant]] = true; - return acc; - }, utils.createMap()); - -Suite.constants = constants; - -},{"./errors":6,"./hook":7,"./utils":38,"debug":45,"events":50,"ms":60}],37:[function(require,module,exports){ -'use strict'; -var Runnable = require('./runnable'); -var utils = require('./utils'); -var errors = require('./errors'); -var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError; -var isString = utils.isString; - -module.exports = Test; - -/** - * Initialize a new `Test` with the given `title` and callback `fn`. - * - * @public - * @class - * @extends Runnable - * @param {String} title - Test title (required) - * @param {Function} [fn] - Test callback. If omitted, the Test is considered "pending" - */ -function Test(title, fn) { - if (!isString(title)) { - throw createInvalidArgumentTypeError( - 'Test argument "title" should be a string. Received type "' + - typeof title + - '"', - 'title', - 'string' - ); - } - Runnable.call(this, title, fn); - this.pending = !fn; - this.type = 'test'; -} - -/** - * Inherit from `Runnable.prototype`. - */ -utils.inherits(Test, Runnable); - -/** - * Set or get retried test - * - * @private - */ -Test.prototype.retriedTest = function(n) { - if (!arguments.length) { - return this._retriedTest; - } - this._retriedTest = n; -}; - -Test.prototype.clone = function() { - var test = new Test(this.title, this.fn); - test.timeout(this.timeout()); - test.slow(this.slow()); - test.enableTimeouts(this.enableTimeouts()); - test.retries(this.retries()); - test.currentRetry(this.currentRetry()); - test.retriedTest(this.retriedTest() || this); - test.globals(this.globals()); - test.parent = this.parent; - test.file = this.file; - test.ctx = this.ctx; - return test; -}; - -},{"./errors":6,"./runnable":33,"./utils":38}],38:[function(require,module,exports){ -(function (process,Buffer){ -'use strict'; - -/** - * Various utility functions used throughout Mocha's codebase. - * @module utils - */ - -/** - * Module dependencies. - */ - -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var glob = require('glob'); -var he = require('he'); -var errors = require('./errors'); -var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError; -var createMissingArgumentError = errors.createMissingArgumentError; - -var assign = (exports.assign = require('object.assign').getPolyfill()); - -/** - * Inherit the prototype methods from one constructor into another. - * - * @param {function} ctor - Constructor function which needs to inherit the - * prototype. - * @param {function} superCtor - Constructor function to inherit prototype from. - * @throws {TypeError} if either constructor is null, or if super constructor - * lacks a prototype. - */ -exports.inherits = util.inherits; - -/** - * Escape special characters in the given string of html. - * - * @private - * @param {string} html - * @return {string} - */ -exports.escape = function(html) { - return he.encode(String(html), {useNamedReferences: false}); -}; - -/** - * Test if the given obj is type of string. - * - * @private - * @param {Object} obj - * @return {boolean} - */ -exports.isString = function(obj) { - return typeof obj === 'string'; -}; - -/** - * Compute a slug from the given `str`. - * - * @private - * @param {string} str - * @return {string} - */ -exports.slug = function(str) { - return str - .toLowerCase() - .replace(/ +/g, '-') - .replace(/[^-\w]/g, ''); -}; - -/** - * Strip the function definition from `str`, and re-indent for pre whitespace. - * - * @param {string} str - * @return {string} - */ -exports.clean = function(str) { - str = str - .replace(/\r\n?|[\n\u2028\u2029]/g, '\n') - .replace(/^\uFEFF/, '') - // (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content - .replace( - /^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, - '$1$2$3' - ); - - var spaces = str.match(/^\n?( *)/)[1].length; - var tabs = str.match(/^\n?(\t*)/)[1].length; - var re = new RegExp( - '^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs || spaces) + '}', - 'gm' - ); - - str = str.replace(re, ''); - - return str.trim(); -}; - -/** - * Parse the given `qs`. - * - * @private - * @param {string} qs - * @return {Object} - */ -exports.parseQuery = function(qs) { - return qs - .replace('?', '') - .split('&') - .reduce(function(obj, pair) { - var i = pair.indexOf('='); - var key = pair.slice(0, i); - var val = pair.slice(++i); - - // Due to how the URLSearchParams API treats spaces - obj[key] = decodeURIComponent(val.replace(/\+/g, '%20')); - - return obj; - }, {}); -}; - -/** - * Highlight the given string of `js`. - * - * @private - * @param {string} js - * @return {string} - */ -function highlight(js) { - return js - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>') - .replace(/('.*?')/gm, '<span class="string">$1</span>') - .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>') - .replace(/(\d+)/gm, '<span class="number">$1</span>') - .replace( - /\bnew[ \t]+(\w+)/gm, - '<span class="keyword">new</span> <span class="init">$1</span>' - ) - .replace( - /\b(function|new|throw|return|var|if|else)\b/gm, - '<span class="keyword">$1</span>' - ); -} - -/** - * Highlight the contents of tag `name`. - * - * @private - * @param {string} name - */ -exports.highlightTags = function(name) { - var code = document.getElementById('mocha').getElementsByTagName(name); - for (var i = 0, len = code.length; i < len; ++i) { - code[i].innerHTML = highlight(code[i].innerHTML); - } -}; - -/** - * If a value could have properties, and has none, this function is called, - * which returns a string representation of the empty value. - * - * Functions w/ no properties return `'[Function]'` - * Arrays w/ length === 0 return `'[]'` - * Objects w/ no properties return `'{}'` - * All else: return result of `value.toString()` - * - * @private - * @param {*} value The value to inspect. - * @param {string} typeHint The type of the value - * @returns {string} - */ -function emptyRepresentation(value, typeHint) { - switch (typeHint) { - case 'function': - return '[Function]'; - case 'object': - return '{}'; - case 'array': - return '[]'; - default: - return value.toString(); - } -} - -/** - * Takes some variable and asks `Object.prototype.toString()` what it thinks it - * is. - * - * @private - * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString - * @param {*} value The value to test. - * @returns {string} Computed type - * @example - * type({}) // 'object' - * type([]) // 'array' - * type(1) // 'number' - * type(false) // 'boolean' - * type(Infinity) // 'number' - * type(null) // 'null' - * type(new Date()) // 'date' - * type(/foo/) // 'regexp' - * type('type') // 'string' - * type(global) // 'global' - * type(new String('foo') // 'object' - */ -var type = (exports.type = function type(value) { - if (value === undefined) { - return 'undefined'; - } else if (value === null) { - return 'null'; - } else if (Buffer.isBuffer(value)) { - return 'buffer'; - } - return Object.prototype.toString - .call(value) - .replace(/^\[.+\s(.+?)]$/, '$1') - .toLowerCase(); -}); - -/** - * Stringify `value`. Different behavior depending on type of value: - * - * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively. - * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes. - * - If `value` is an *empty* object, function, or array, return result of function - * {@link emptyRepresentation}. - * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of - * JSON.stringify(). - * - * @private - * @see exports.type - * @param {*} value - * @return {string} - */ -exports.stringify = function(value) { - var typeHint = type(value); - - if (!~['object', 'array', 'function'].indexOf(typeHint)) { - if (typeHint === 'buffer') { - var json = Buffer.prototype.toJSON.call(value); - // Based on the toJSON result - return jsonStringify( - json.data && json.type ? json.data : json, - 2 - ).replace(/,(\n|$)/g, '$1'); - } - - // IE7/IE8 has a bizarre String constructor; needs to be coerced - // into an array and back to obj. - if (typeHint === 'string' && typeof value === 'object') { - value = value.split('').reduce(function(acc, char, idx) { - acc[idx] = char; - return acc; - }, {}); - typeHint = 'object'; - } else { - return jsonStringify(value); - } - } - - for (var prop in value) { - if (Object.prototype.hasOwnProperty.call(value, prop)) { - return jsonStringify( - exports.canonicalize(value, null, typeHint), - 2 - ).replace(/,(\n|$)/g, '$1'); - } - } - - return emptyRepresentation(value, typeHint); -}; - -/** - * like JSON.stringify but more sense. - * - * @private - * @param {Object} object - * @param {number=} spaces - * @param {number=} depth - * @returns {*} - */ -function jsonStringify(object, spaces, depth) { - if (typeof spaces === 'undefined') { - // primitive types - return _stringify(object); - } - - depth = depth || 1; - var space = spaces * depth; - var str = Array.isArray(object) ? '[' : '{'; - var end = Array.isArray(object) ? ']' : '}'; - var length = - typeof object.length === 'number' - ? object.length - : Object.keys(object).length; - // `.repeat()` polyfill - function repeat(s, n) { - return new Array(n).join(s); - } - - function _stringify(val) { - switch (type(val)) { - case 'null': - case 'undefined': - val = '[' + val + ']'; - break; - case 'array': - case 'object': - val = jsonStringify(val, spaces, depth + 1); - break; - case 'boolean': - case 'regexp': - case 'symbol': - case 'number': - val = - val === 0 && 1 / val === -Infinity // `-0` - ? '-0' - : val.toString(); - break; - case 'date': - var sDate = isNaN(val.getTime()) ? val.toString() : val.toISOString(); - val = '[Date: ' + sDate + ']'; - break; - case 'buffer': - var json = val.toJSON(); - // Based on the toJSON result - json = json.data && json.type ? json.data : json; - val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']'; - break; - default: - val = - val === '[Function]' || val === '[Circular]' - ? val - : JSON.stringify(val); // string - } - return val; - } - - for (var i in object) { - if (!Object.prototype.hasOwnProperty.call(object, i)) { - continue; // not my business - } - --length; - str += - '\n ' + - repeat(' ', space) + - (Array.isArray(object) ? '' : '"' + i + '": ') + // key - _stringify(object[i]) + // value - (length ? ',' : ''); // comma - } - - return ( - str + - // [], {} - (str.length !== 1 ? '\n' + repeat(' ', --space) + end : end) - ); -} - -/** - * Return a new Thing that has the keys in sorted order. Recursive. - * - * If the Thing... - * - has already been seen, return string `'[Circular]'` - * - is `undefined`, return string `'[undefined]'` - * - is `null`, return value `null` - * - is some other primitive, return the value - * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method - * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again. - * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()` - * - * @private - * @see {@link exports.stringify} - * @param {*} value Thing to inspect. May or may not have properties. - * @param {Array} [stack=[]] Stack of seen values - * @param {string} [typeHint] Type hint - * @return {(Object|Array|Function|string|undefined)} - */ -exports.canonicalize = function canonicalize(value, stack, typeHint) { - var canonicalizedObj; - /* eslint-disable no-unused-vars */ - var prop; - /* eslint-enable no-unused-vars */ - typeHint = typeHint || type(value); - function withStack(value, fn) { - stack.push(value); - fn(); - stack.pop(); - } - - stack = stack || []; - - if (stack.indexOf(value) !== -1) { - return '[Circular]'; - } - - switch (typeHint) { - case 'undefined': - case 'buffer': - case 'null': - canonicalizedObj = value; - break; - case 'array': - withStack(value, function() { - canonicalizedObj = value.map(function(item) { - return exports.canonicalize(item, stack); - }); - }); - break; - case 'function': - /* eslint-disable guard-for-in */ - for (prop in value) { - canonicalizedObj = {}; - break; - } - /* eslint-enable guard-for-in */ - if (!canonicalizedObj) { - canonicalizedObj = emptyRepresentation(value, typeHint); - break; - } - /* falls through */ - case 'object': - canonicalizedObj = canonicalizedObj || {}; - withStack(value, function() { - Object.keys(value) - .sort() - .forEach(function(key) { - canonicalizedObj[key] = exports.canonicalize(value[key], stack); - }); - }); - break; - case 'date': - case 'number': - case 'regexp': - case 'boolean': - case 'symbol': - canonicalizedObj = value; - break; - default: - canonicalizedObj = value + ''; - } - - return canonicalizedObj; -}; - -/** - * Determines if pathname has a matching file extension. - * - * @private - * @param {string} pathname - Pathname to check for match. - * @param {string[]} exts - List of file extensions (sans period). - * @return {boolean} whether file extension matches. - * @example - * hasMatchingExtname('foo.html', ['js', 'css']); // => false - */ -function hasMatchingExtname(pathname, exts) { - var suffix = path.extname(pathname).slice(1); - return exts.some(function(element) { - return suffix === element; - }); -} - -/** - * Determines if pathname would be a "hidden" file (or directory) on UN*X. - * - * @description - * On UN*X, pathnames beginning with a full stop (aka dot) are hidden during - * typical usage. Dotfiles, plain-text configuration files, are prime examples. - * - * @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names} - * - * @private - * @param {string} pathname - Pathname to check for match. - * @return {boolean} whether pathname would be considered a hidden file. - * @example - * isHiddenOnUnix('.profile'); // => true - */ -function isHiddenOnUnix(pathname) { - return path.basename(pathname)[0] === '.'; -} - -/** - * Lookup file names at the given `path`. - * - * @description - * Filenames are returned in _traversal_ order by the OS/filesystem. - * **Make no assumption that the names will be sorted in any fashion.** - * - * @public - * @memberof Mocha.utils - * @param {string} filepath - Base path to start searching from. - * @param {string[]} [extensions=[]] - File extensions to look for. - * @param {boolean} [recursive=false] - Whether to recurse into subdirectories. - * @return {string[]} An array of paths. - * @throws {Error} if no files match pattern. - * @throws {TypeError} if `filepath` is directory and `extensions` not provided. - */ -exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { - extensions = extensions || []; - recursive = recursive || false; - var files = []; - var stat; - - if (!fs.existsSync(filepath)) { - var pattern; - if (glob.hasMagic(filepath)) { - // Handle glob as is without extensions - pattern = filepath; - } else { - // glob pattern e.g. 'filepath+(.js|.ts)' - var strExtensions = extensions - .map(function(v) { - return '.' + v; - }) - .join('|'); - pattern = filepath + '+(' + strExtensions + ')'; - } - files = glob.sync(pattern, {nodir: true}); - if (!files.length) { - throw createNoFilesMatchPatternError( - 'Cannot find any files matching pattern ' + exports.dQuote(filepath), - filepath - ); - } - return files; - } - - // Handle file - try { - stat = fs.statSync(filepath); - if (stat.isFile()) { - return filepath; - } - } catch (err) { - // ignore error - return; - } - - // Handle directory - fs.readdirSync(filepath).forEach(function(dirent) { - var pathname = path.join(filepath, dirent); - var stat; - - try { - stat = fs.statSync(pathname); - if (stat.isDirectory()) { - if (recursive) { - files = files.concat(lookupFiles(pathname, extensions, recursive)); - } - return; - } - } catch (err) { - // ignore error - return; - } - if (!extensions.length) { - throw createMissingArgumentError( - util.format( - 'Argument %s required when argument %s is a directory', - exports.sQuote('extensions'), - exports.sQuote('filepath') - ), - 'extensions', - 'array' - ); - } - - if ( - !stat.isFile() || - !hasMatchingExtname(pathname, extensions) || - isHiddenOnUnix(pathname) - ) { - return; - } - files.push(pathname); - }); - - return files; -}; - -/** - * process.emitWarning or a polyfill - * @see https://nodejs.org/api/process.html#process_process_emitwarning_warning_options - * @ignore - */ -function emitWarning(msg, type) { - if (process.emitWarning) { - process.emitWarning(msg, type); - } else { - process.nextTick(function() { - console.warn(type + ': ' + msg); - }); - } -} - -/** - * Show a deprecation warning. Each distinct message is only displayed once. - * Ignores empty messages. - * - * @param {string} [msg] - Warning to print - * @private - */ -exports.deprecate = function deprecate(msg) { - msg = String(msg); - if (msg && !deprecate.cache[msg]) { - deprecate.cache[msg] = true; - emitWarning(msg, 'DeprecationWarning'); - } -}; -exports.deprecate.cache = {}; - -/** - * Show a generic warning. - * Ignores empty messages. - * - * @param {string} [msg] - Warning to print - * @private - */ -exports.warn = function warn(msg) { - if (msg) { - emitWarning(msg); - } -}; - -/** - * @summary - * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`) - * @description - * When invoking this function you get a filter function that get the Error.stack as an input, - * and return a prettify output. - * (i.e: strip Mocha and internal node functions from stack trace). - * @returns {Function} - */ -exports.stackTraceFilter = function() { - // TODO: Replace with `process.browser` - var is = typeof document === 'undefined' ? {node: true} : {browser: true}; - var slash = path.sep; - var cwd; - if (is.node) { - cwd = process.cwd() + slash; - } else { - cwd = (typeof location === 'undefined' - ? window.location - : location - ).href.replace(/\/[^/]*$/, '/'); - slash = '/'; - } - - function isMochaInternal(line) { - return ( - ~line.indexOf('node_modules' + slash + 'mocha' + slash) || - ~line.indexOf(slash + 'mocha.js') || - ~line.indexOf(slash + 'mocha.min.js') - ); - } - - function isNodeInternal(line) { - return ( - ~line.indexOf('(timers.js:') || - ~line.indexOf('(events.js:') || - ~line.indexOf('(node.js:') || - ~line.indexOf('(module.js:') || - ~line.indexOf('GeneratorFunctionPrototype.next (native)') || - false - ); - } - - return function(stack) { - stack = stack.split('\n'); - - stack = stack.reduce(function(list, line) { - if (isMochaInternal(line)) { - return list; - } - - if (is.node && isNodeInternal(line)) { - return list; - } - - // Clean up cwd(absolute) - if (/:\d+:\d+\)?$/.test(line)) { - line = line.replace('(' + cwd, '('); - } - - list.push(line); - return list; - }, []); - - return stack.join('\n'); - }; -}; - -/** - * Crude, but effective. - * @public - * @param {*} value - * @returns {boolean} Whether or not `value` is a Promise - */ -exports.isPromise = function isPromise(value) { - return ( - typeof value === 'object' && - value !== null && - typeof value.then === 'function' - ); -}; - -/** - * Clamps a numeric value to an inclusive range. - * - * @param {number} value - Value to be clamped. - * @param {numer[]} range - Two element array specifying [min, max] range. - * @returns {number} clamped value - */ -exports.clamp = function clamp(value, range) { - return Math.min(Math.max(value, range[0]), range[1]); -}; - -/** - * Single quote text by combining with undirectional ASCII quotation marks. - * - * @description - * Provides a simple means of markup for quoting text to be used in output. - * Use this to quote names of variables, methods, and packages. - * - * <samp>package 'foo' cannot be found</samp> - * - * @private - * @param {string} str - Value to be quoted. - * @returns {string} quoted value - * @example - * sQuote('n') // => 'n' - */ -exports.sQuote = function(str) { - return "'" + str + "'"; -}; - -/** - * Double quote text by combining with undirectional ASCII quotation marks. - * - * @description - * Provides a simple means of markup for quoting text to be used in output. - * Use this to quote names of datatypes, classes, pathnames, and strings. - * - * <samp>argument 'value' must be "string" or "number"</samp> - * - * @private - * @param {string} str - Value to be quoted. - * @returns {string} quoted value - * @example - * dQuote('number') // => "number" - */ -exports.dQuote = function(str) { - return '"' + str + '"'; -}; - -/** - * It's a noop. - * @public - */ -exports.noop = function() {}; - -/** - * Creates a map-like object. - * - * @description - * A "map" is an object with no prototype, for our purposes. In some cases - * this would be more appropriate than a `Map`, especially if your environment - * doesn't support it. Recommended for use in Mocha's public APIs. - * - * @public - * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map|MDN:Map} - * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects|MDN:Object.create - Custom objects} - * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign|MDN:Object.assign} - * @param {...*} [obj] - Arguments to `Object.assign()`. - * @returns {Object} An object with no prototype, having `...obj` properties - */ -exports.createMap = function(obj) { - return assign.apply( - null, - [Object.create(null)].concat(Array.prototype.slice.call(arguments)) - ); -}; - -/** - * Creates a read-only map-like object. - * - * @description - * This differs from {@link module:utils.createMap createMap} only in that - * the argument must be non-empty, because the result is frozen. - * - * @see {@link module:utils.createMap createMap} - * @param {...*} [obj] - Arguments to `Object.assign()`. - * @returns {Object} A frozen object with no prototype, having `...obj` properties - * @throws {TypeError} if argument is not a non-empty object. - */ -exports.defineConstants = function(obj) { - if (type(obj) !== 'object' || !Object.keys(obj).length) { - throw new TypeError('Invalid argument; expected a non-empty object'); - } - return Object.freeze(exports.createMap(obj)); -}; - -/** - * Whether current version of Node support ES modules - * - * @description - * Versions prior to 10 did not support ES Modules, and version 10 has an old incompatibile version of ESM. - * This function returns whether Node.JS has ES Module supports that is compatible with Mocha's needs, - * which is version >=12.11. - * - * @returns {Boolean} whether the current version of Node.JS supports ES Modules in a way that is compatible with Mocha - */ -exports.supportsEsModules = function() { - if (!process.browser && process.versions && process.versions.node) { - var versionFields = process.versions.node.split('.'); - var major = +versionFields[0]; - var minor = +versionFields[1]; - - if (major >= 13 || (major === 12 && minor >= 11)) { - return true; - } - } -}; - -}).call(this,require('_process'),require("buffer").Buffer) -},{"./errors":6,"_process":69,"buffer":43,"fs":42,"glob":42,"he":54,"object.assign":65,"path":42,"util":89}],39:[function(require,module,exports){ -'use strict' - -exports.byteLength = byteLength -exports.toByteArray = toByteArray -exports.fromByteArray = fromByteArray - -var lookup = [] -var revLookup = [] -var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array - -var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -for (var i = 0, len = code.length; i < len; ++i) { - lookup[i] = code[i] - revLookup[code.charCodeAt(i)] = i -} - -// Support decoding URL-safe base64 strings, as Node.js does. -// See: https://en.wikipedia.org/wiki/Base64#URL_applications -revLookup['-'.charCodeAt(0)] = 62 -revLookup['_'.charCodeAt(0)] = 63 - -function getLens (b64) { - var len = b64.length - - if (len % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // Trim off extra bytes after placeholder bytes are found - // See: https://github.com/beatgammit/base64-js/issues/42 - var validLen = b64.indexOf('=') - if (validLen === -1) validLen = len - - var placeHoldersLen = validLen === len - ? 0 - : 4 - (validLen % 4) - - return [validLen, placeHoldersLen] -} - -// base64 is 4/3 + up to two characters of the original data -function byteLength (b64) { - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function _byteLength (b64, validLen, placeHoldersLen) { - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function toByteArray (b64) { - var tmp - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - - var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) - - var curByte = 0 - - // if there are placeholders, only get up to the last complete 4 chars - var len = placeHoldersLen > 0 - ? validLen - 4 - : validLen - - for (var i = 0; i < len; i += 4) { - tmp = - (revLookup[b64.charCodeAt(i)] << 18) | - (revLookup[b64.charCodeAt(i + 1)] << 12) | - (revLookup[b64.charCodeAt(i + 2)] << 6) | - revLookup[b64.charCodeAt(i + 3)] - arr[curByte++] = (tmp >> 16) & 0xFF - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 2) { - tmp = - (revLookup[b64.charCodeAt(i)] << 2) | - (revLookup[b64.charCodeAt(i + 1)] >> 4) - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 1) { - tmp = - (revLookup[b64.charCodeAt(i)] << 10) | - (revLookup[b64.charCodeAt(i + 1)] << 4) | - (revLookup[b64.charCodeAt(i + 2)] >> 2) - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - return arr -} - -function tripletToBase64 (num) { - return lookup[num >> 18 & 0x3F] + - lookup[num >> 12 & 0x3F] + - lookup[num >> 6 & 0x3F] + - lookup[num & 0x3F] -} - -function encodeChunk (uint8, start, end) { - var tmp - var output = [] - for (var i = start; i < end; i += 3) { - tmp = - ((uint8[i] << 16) & 0xFF0000) + - ((uint8[i + 1] << 8) & 0xFF00) + - (uint8[i + 2] & 0xFF) - output.push(tripletToBase64(tmp)) - } - return output.join('') -} - -function fromByteArray (uint8) { - var tmp - var len = uint8.length - var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes - var parts = [] - var maxChunkLength = 16383 // must be multiple of 3 - - // go through the array every three bytes, we'll deal with trailing stuff later - for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { - parts.push(encodeChunk( - uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength) - )) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 1) { - tmp = uint8[len - 1] - parts.push( - lookup[tmp >> 2] + - lookup[(tmp << 4) & 0x3F] + - '==' - ) - } else if (extraBytes === 2) { - tmp = (uint8[len - 2] << 8) + uint8[len - 1] - parts.push( - lookup[tmp >> 10] + - lookup[(tmp >> 4) & 0x3F] + - lookup[(tmp << 2) & 0x3F] + - '=' - ) - } - - return parts.join('') -} - -},{}],40:[function(require,module,exports){ - -},{}],41:[function(require,module,exports){ -(function (process){ -var WritableStream = require('stream').Writable -var inherits = require('util').inherits - -module.exports = BrowserStdout - - -inherits(BrowserStdout, WritableStream) - -function BrowserStdout(opts) { - if (!(this instanceof BrowserStdout)) return new BrowserStdout(opts) - - opts = opts || {} - WritableStream.call(this, opts) - this.label = (opts.label !== undefined) ? opts.label : 'stdout' -} - -BrowserStdout.prototype._write = function(chunks, encoding, cb) { - var output = chunks.toString ? chunks.toString() : chunks - if (this.label === false) { - console.log(output) - } else { - console.log(this.label+':', output) - } - process.nextTick(cb) -} - -}).call(this,require('_process')) -},{"_process":69,"stream":84,"util":89}],42:[function(require,module,exports){ -arguments[4][40][0].apply(exports,arguments) -},{"dup":40}],43:[function(require,module,exports){ -(function (Buffer){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh <https://feross.org> - * @license MIT - */ -/* eslint-disable no-proto */ - -'use strict' - -var base64 = require('base64-js') -var ieee754 = require('ieee754') - -exports.Buffer = Buffer -exports.SlowBuffer = SlowBuffer -exports.INSPECT_MAX_BYTES = 50 - -var K_MAX_LENGTH = 0x7fffffff -exports.kMaxLength = K_MAX_LENGTH - -/** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Print warning and recommend using `buffer` v4.x which has an Object - * implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * We report that the browser does not support typed arrays if the are not subclassable - * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` - * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support - * for __proto__ and has a buggy typed array implementation. - */ -Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() - -if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && - typeof console.error === 'function') { - console.error( - 'This browser lacks typed array (Uint8Array) support which is required by ' + - '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' - ) -} - -function typedArraySupport () { - // Can typed array instances can be augmented? - try { - var arr = new Uint8Array(1) - arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } } - return arr.foo() === 42 - } catch (e) { - return false - } -} - -Object.defineProperty(Buffer.prototype, 'parent', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.buffer - } -}) - -Object.defineProperty(Buffer.prototype, 'offset', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.byteOffset - } -}) - -function createBuffer (length) { - if (length > K_MAX_LENGTH) { - throw new RangeError('The value "' + length + '" is invalid for option "size"') - } - // Return an augmented `Uint8Array` instance - var buf = new Uint8Array(length) - buf.__proto__ = Buffer.prototype - return buf -} - -/** - * The Buffer constructor returns instances of `Uint8Array` that have their - * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of - * `Uint8Array`, so the returned instances will have all the node `Buffer` methods - * and the `Uint8Array` methods. Square bracket notation works as expected -- it - * returns a single octet. - * - * The `Uint8Array` prototype remains unmodified. - */ - -function Buffer (arg, encodingOrOffset, length) { - // Common case. - if (typeof arg === 'number') { - if (typeof encodingOrOffset === 'string') { - throw new TypeError( - 'The "string" argument must be of type string. Received type number' - ) - } - return allocUnsafe(arg) - } - return from(arg, encodingOrOffset, length) -} - -// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 -if (typeof Symbol !== 'undefined' && Symbol.species != null && - Buffer[Symbol.species] === Buffer) { - Object.defineProperty(Buffer, Symbol.species, { - value: null, - configurable: true, - enumerable: false, - writable: false - }) -} - -Buffer.poolSize = 8192 // not used by this implementation - -function from (value, encodingOrOffset, length) { - if (typeof value === 'string') { - return fromString(value, encodingOrOffset) - } - - if (ArrayBuffer.isView(value)) { - return fromArrayLike(value) - } - - if (value == null) { - throw TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) - } - - if (isInstance(value, ArrayBuffer) || - (value && isInstance(value.buffer, ArrayBuffer))) { - return fromArrayBuffer(value, encodingOrOffset, length) - } - - if (typeof value === 'number') { - throw new TypeError( - 'The "value" argument must not be of type number. Received type number' - ) - } - - var valueOf = value.valueOf && value.valueOf() - if (valueOf != null && valueOf !== value) { - return Buffer.from(valueOf, encodingOrOffset, length) - } - - var b = fromObject(value) - if (b) return b - - if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && - typeof value[Symbol.toPrimitive] === 'function') { - return Buffer.from( - value[Symbol.toPrimitive]('string'), encodingOrOffset, length - ) - } - - throw new TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) -} - -/** - * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError - * if value is a number. - * Buffer.from(str[, encoding]) - * Buffer.from(array) - * Buffer.from(buffer) - * Buffer.from(arrayBuffer[, byteOffset[, length]]) - **/ -Buffer.from = function (value, encodingOrOffset, length) { - return from(value, encodingOrOffset, length) -} - -// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: -// https://github.com/feross/buffer/pull/148 -Buffer.prototype.__proto__ = Uint8Array.prototype -Buffer.__proto__ = Uint8Array - -function assertSize (size) { - if (typeof size !== 'number') { - throw new TypeError('"size" argument must be of type number') - } else if (size < 0) { - throw new RangeError('The value "' + size + '" is invalid for option "size"') - } -} - -function alloc (size, fill, encoding) { - assertSize(size) - if (size <= 0) { - return createBuffer(size) - } - if (fill !== undefined) { - // Only pay attention to encoding if it's a string. This - // prevents accidentally sending in a number that would - // be interpretted as a start offset. - return typeof encoding === 'string' - ? createBuffer(size).fill(fill, encoding) - : createBuffer(size).fill(fill) - } - return createBuffer(size) -} - -/** - * Creates a new filled Buffer instance. - * alloc(size[, fill[, encoding]]) - **/ -Buffer.alloc = function (size, fill, encoding) { - return alloc(size, fill, encoding) -} - -function allocUnsafe (size) { - assertSize(size) - return createBuffer(size < 0 ? 0 : checked(size) | 0) -} - -/** - * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. - * */ -Buffer.allocUnsafe = function (size) { - return allocUnsafe(size) -} -/** - * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. - */ -Buffer.allocUnsafeSlow = function (size) { - return allocUnsafe(size) -} - -function fromString (string, encoding) { - if (typeof encoding !== 'string' || encoding === '') { - encoding = 'utf8' - } - - if (!Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - - var length = byteLength(string, encoding) | 0 - var buf = createBuffer(length) - - var actual = buf.write(string, encoding) - - if (actual !== length) { - // Writing a hex string, for example, that contains invalid characters will - // cause everything after the first invalid character to be ignored. (e.g. - // 'abxxcd' will be treated as 'ab') - buf = buf.slice(0, actual) - } - - return buf -} - -function fromArrayLike (array) { - var length = array.length < 0 ? 0 : checked(array.length) | 0 - var buf = createBuffer(length) - for (var i = 0; i < length; i += 1) { - buf[i] = array[i] & 255 - } - return buf -} - -function fromArrayBuffer (array, byteOffset, length) { - if (byteOffset < 0 || array.byteLength < byteOffset) { - throw new RangeError('"offset" is outside of buffer bounds') - } - - if (array.byteLength < byteOffset + (length || 0)) { - throw new RangeError('"length" is outside of buffer bounds') - } - - var buf - if (byteOffset === undefined && length === undefined) { - buf = new Uint8Array(array) - } else if (length === undefined) { - buf = new Uint8Array(array, byteOffset) - } else { - buf = new Uint8Array(array, byteOffset, length) - } - - // Return an augmented `Uint8Array` instance - buf.__proto__ = Buffer.prototype - return buf -} - -function fromObject (obj) { - if (Buffer.isBuffer(obj)) { - var len = checked(obj.length) | 0 - var buf = createBuffer(len) - - if (buf.length === 0) { - return buf - } - - obj.copy(buf, 0, 0, len) - return buf - } - - if (obj.length !== undefined) { - if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { - return createBuffer(0) - } - return fromArrayLike(obj) - } - - if (obj.type === 'Buffer' && Array.isArray(obj.data)) { - return fromArrayLike(obj.data) - } -} - -function checked (length) { - // Note: cannot use `length < K_MAX_LENGTH` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= K_MAX_LENGTH) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') - } - return length | 0 -} - -function SlowBuffer (length) { - if (+length != length) { // eslint-disable-line eqeqeq - length = 0 - } - return Buffer.alloc(+length) -} - -Buffer.isBuffer = function isBuffer (b) { - return b != null && b._isBuffer === true && - b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false -} - -Buffer.compare = function compare (a, b) { - if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) - if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) - if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { - throw new TypeError( - 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' - ) - } - - if (a === b) return 0 - - var x = a.length - var y = b.length - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i] - y = b[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -Buffer.isEncoding = function isEncoding (encoding) { - switch (String(encoding).toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'latin1': - case 'binary': - case 'base64': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return true - default: - return false - } -} - -Buffer.concat = function concat (list, length) { - if (!Array.isArray(list)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - - if (list.length === 0) { - return Buffer.alloc(0) - } - - var i - if (length === undefined) { - length = 0 - for (i = 0; i < list.length; ++i) { - length += list[i].length - } - } - - var buffer = Buffer.allocUnsafe(length) - var pos = 0 - for (i = 0; i < list.length; ++i) { - var buf = list[i] - if (isInstance(buf, Uint8Array)) { - buf = Buffer.from(buf) - } - if (!Buffer.isBuffer(buf)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - buf.copy(buffer, pos) - pos += buf.length - } - return buffer -} - -function byteLength (string, encoding) { - if (Buffer.isBuffer(string)) { - return string.length - } - if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { - return string.byteLength - } - if (typeof string !== 'string') { - throw new TypeError( - 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + - 'Received type ' + typeof string - ) - } - - var len = string.length - var mustMatch = (arguments.length > 2 && arguments[2] === true) - if (!mustMatch && len === 0) return 0 - - // Use a for loop to avoid recursion - var loweredCase = false - for (;;) { - switch (encoding) { - case 'ascii': - case 'latin1': - case 'binary': - return len - case 'utf8': - case 'utf-8': - return utf8ToBytes(string).length - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2 - case 'hex': - return len >>> 1 - case 'base64': - return base64ToBytes(string).length - default: - if (loweredCase) { - return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 - } - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} -Buffer.byteLength = byteLength - -function slowToString (encoding, start, end) { - var loweredCase = false - - // No need to verify that "this.length <= MAX_UINT32" since it's a read-only - // property of a typed array. - - // This behaves neither like String nor Uint8Array in that we set start/end - // to their upper/lower bounds if the value passed is out of range. - // undefined is handled specially as per ECMA-262 6th Edition, - // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. - if (start === undefined || start < 0) { - start = 0 - } - // Return early if start > this.length. Done here to prevent potential uint32 - // coercion fail below. - if (start > this.length) { - return '' - } - - if (end === undefined || end > this.length) { - end = this.length - } - - if (end <= 0) { - return '' - } - - // Force coersion to uint32. This will also coerce falsey/NaN values to 0. - end >>>= 0 - start >>>= 0 - - if (end <= start) { - return '' - } - - if (!encoding) encoding = 'utf8' - - while (true) { - switch (encoding) { - case 'hex': - return hexSlice(this, start, end) - - case 'utf8': - case 'utf-8': - return utf8Slice(this, start, end) - - case 'ascii': - return asciiSlice(this, start, end) - - case 'latin1': - case 'binary': - return latin1Slice(this, start, end) - - case 'base64': - return base64Slice(this, start, end) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return utf16leSlice(this, start, end) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = (encoding + '').toLowerCase() - loweredCase = true - } - } -} - -// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) -// to detect a Buffer instance. It's not possible to use `instanceof Buffer` -// reliably in a browserify context because there could be multiple different -// copies of the 'buffer' package in use. This method works even for Buffer -// instances that were created from another copy of the `buffer` package. -// See: https://github.com/feross/buffer/issues/154 -Buffer.prototype._isBuffer = true - -function swap (b, n, m) { - var i = b[n] - b[n] = b[m] - b[m] = i -} - -Buffer.prototype.swap16 = function swap16 () { - var len = this.length - if (len % 2 !== 0) { - throw new RangeError('Buffer size must be a multiple of 16-bits') - } - for (var i = 0; i < len; i += 2) { - swap(this, i, i + 1) - } - return this -} - -Buffer.prototype.swap32 = function swap32 () { - var len = this.length - if (len % 4 !== 0) { - throw new RangeError('Buffer size must be a multiple of 32-bits') - } - for (var i = 0; i < len; i += 4) { - swap(this, i, i + 3) - swap(this, i + 1, i + 2) - } - return this -} - -Buffer.prototype.swap64 = function swap64 () { - var len = this.length - if (len % 8 !== 0) { - throw new RangeError('Buffer size must be a multiple of 64-bits') - } - for (var i = 0; i < len; i += 8) { - swap(this, i, i + 7) - swap(this, i + 1, i + 6) - swap(this, i + 2, i + 5) - swap(this, i + 3, i + 4) - } - return this -} - -Buffer.prototype.toString = function toString () { - var length = this.length - if (length === 0) return '' - if (arguments.length === 0) return utf8Slice(this, 0, length) - return slowToString.apply(this, arguments) -} - -Buffer.prototype.toLocaleString = Buffer.prototype.toString - -Buffer.prototype.equals = function equals (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return true - return Buffer.compare(this, b) === 0 -} - -Buffer.prototype.inspect = function inspect () { - var str = '' - var max = exports.INSPECT_MAX_BYTES - str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() - if (this.length > max) str += ' ... ' - return '<Buffer ' + str + '>' -} - -Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { - if (isInstance(target, Uint8Array)) { - target = Buffer.from(target, target.offset, target.byteLength) - } - if (!Buffer.isBuffer(target)) { - throw new TypeError( - 'The "target" argument must be one of type Buffer or Uint8Array. ' + - 'Received type ' + (typeof target) - ) - } - - if (start === undefined) { - start = 0 - } - if (end === undefined) { - end = target ? target.length : 0 - } - if (thisStart === undefined) { - thisStart = 0 - } - if (thisEnd === undefined) { - thisEnd = this.length - } - - if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { - throw new RangeError('out of range index') - } - - if (thisStart >= thisEnd && start >= end) { - return 0 - } - if (thisStart >= thisEnd) { - return -1 - } - if (start >= end) { - return 1 - } - - start >>>= 0 - end >>>= 0 - thisStart >>>= 0 - thisEnd >>>= 0 - - if (this === target) return 0 - - var x = thisEnd - thisStart - var y = end - start - var len = Math.min(x, y) - - var thisCopy = this.slice(thisStart, thisEnd) - var targetCopy = target.slice(start, end) - - for (var i = 0; i < len; ++i) { - if (thisCopy[i] !== targetCopy[i]) { - x = thisCopy[i] - y = targetCopy[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, -// OR the last index of `val` in `buffer` at offset <= `byteOffset`. -// -// Arguments: -// - buffer - a Buffer to search -// - val - a string, Buffer, or number -// - byteOffset - an index into `buffer`; will be clamped to an int32 -// - encoding - an optional encoding, relevant is val is a string -// - dir - true for indexOf, false for lastIndexOf -function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { - // Empty buffer means no match - if (buffer.length === 0) return -1 - - // Normalize byteOffset - if (typeof byteOffset === 'string') { - encoding = byteOffset - byteOffset = 0 - } else if (byteOffset > 0x7fffffff) { - byteOffset = 0x7fffffff - } else if (byteOffset < -0x80000000) { - byteOffset = -0x80000000 - } - byteOffset = +byteOffset // Coerce to Number. - if (numberIsNaN(byteOffset)) { - // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer - byteOffset = dir ? 0 : (buffer.length - 1) - } - - // Normalize byteOffset: negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = buffer.length + byteOffset - if (byteOffset >= buffer.length) { - if (dir) return -1 - else byteOffset = buffer.length - 1 - } else if (byteOffset < 0) { - if (dir) byteOffset = 0 - else return -1 - } - - // Normalize val - if (typeof val === 'string') { - val = Buffer.from(val, encoding) - } - - // Finally, search either indexOf (if dir is true) or lastIndexOf - if (Buffer.isBuffer(val)) { - // Special case: looking for empty string/buffer always fails - if (val.length === 0) { - return -1 - } - return arrayIndexOf(buffer, val, byteOffset, encoding, dir) - } else if (typeof val === 'number') { - val = val & 0xFF // Search for a byte value [0-255] - if (typeof Uint8Array.prototype.indexOf === 'function') { - if (dir) { - return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) - } else { - return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) - } - } - return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) - } - - throw new TypeError('val must be string, number or Buffer') -} - -function arrayIndexOf (arr, val, byteOffset, encoding, dir) { - var indexSize = 1 - var arrLength = arr.length - var valLength = val.length - - if (encoding !== undefined) { - encoding = String(encoding).toLowerCase() - if (encoding === 'ucs2' || encoding === 'ucs-2' || - encoding === 'utf16le' || encoding === 'utf-16le') { - if (arr.length < 2 || val.length < 2) { - return -1 - } - indexSize = 2 - arrLength /= 2 - valLength /= 2 - byteOffset /= 2 - } - } - - function read (buf, i) { - if (indexSize === 1) { - return buf[i] - } else { - return buf.readUInt16BE(i * indexSize) - } - } - - var i - if (dir) { - var foundIndex = -1 - for (i = byteOffset; i < arrLength; i++) { - if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { - if (foundIndex === -1) foundIndex = i - if (i - foundIndex + 1 === valLength) return foundIndex * indexSize - } else { - if (foundIndex !== -1) i -= i - foundIndex - foundIndex = -1 - } - } - } else { - if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength - for (i = byteOffset; i >= 0; i--) { - var found = true - for (var j = 0; j < valLength; j++) { - if (read(arr, i + j) !== read(val, j)) { - found = false - break - } - } - if (found) return i - } - } - - return -1 -} - -Buffer.prototype.includes = function includes (val, byteOffset, encoding) { - return this.indexOf(val, byteOffset, encoding) !== -1 -} - -Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, true) -} - -Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, false) -} - -function hexWrite (buf, string, offset, length) { - offset = Number(offset) || 0 - var remaining = buf.length - offset - if (!length) { - length = remaining - } else { - length = Number(length) - if (length > remaining) { - length = remaining - } - } - - var strLen = string.length - - if (length > strLen / 2) { - length = strLen / 2 - } - for (var i = 0; i < length; ++i) { - var parsed = parseInt(string.substr(i * 2, 2), 16) - if (numberIsNaN(parsed)) return i - buf[offset + i] = parsed - } - return i -} - -function utf8Write (buf, string, offset, length) { - return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) -} - -function asciiWrite (buf, string, offset, length) { - return blitBuffer(asciiToBytes(string), buf, offset, length) -} - -function latin1Write (buf, string, offset, length) { - return asciiWrite(buf, string, offset, length) -} - -function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) -} - -function ucs2Write (buf, string, offset, length) { - return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) -} - -Buffer.prototype.write = function write (string, offset, length, encoding) { - // Buffer#write(string) - if (offset === undefined) { - encoding = 'utf8' - length = this.length - offset = 0 - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset - length = this.length - offset = 0 - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset >>> 0 - if (isFinite(length)) { - length = length >>> 0 - if (encoding === undefined) encoding = 'utf8' - } else { - encoding = length - length = undefined - } - } else { - throw new Error( - 'Buffer.write(string, encoding, offset[, length]) is no longer supported' - ) - } - - var remaining = this.length - offset - if (length === undefined || length > remaining) length = remaining - - if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { - throw new RangeError('Attempt to write outside buffer bounds') - } - - if (!encoding) encoding = 'utf8' - - var loweredCase = false - for (;;) { - switch (encoding) { - case 'hex': - return hexWrite(this, string, offset, length) - - case 'utf8': - case 'utf-8': - return utf8Write(this, string, offset, length) - - case 'ascii': - return asciiWrite(this, string, offset, length) - - case 'latin1': - case 'binary': - return latin1Write(this, string, offset, length) - - case 'base64': - // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return ucs2Write(this, string, offset, length) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} - -Buffer.prototype.toJSON = function toJSON () { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this._arr || this, 0) - } -} - -function base64Slice (buf, start, end) { - if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) - } else { - return base64.fromByteArray(buf.slice(start, end)) - } -} - -function utf8Slice (buf, start, end) { - end = Math.min(buf.length, end) - var res = [] - - var i = start - while (i < end) { - var firstByte = buf[i] - var codePoint = null - var bytesPerSequence = (firstByte > 0xEF) ? 4 - : (firstByte > 0xDF) ? 3 - : (firstByte > 0xBF) ? 2 - : 1 - - if (i + bytesPerSequence <= end) { - var secondByte, thirdByte, fourthByte, tempCodePoint - - switch (bytesPerSequence) { - case 1: - if (firstByte < 0x80) { - codePoint = firstByte - } - break - case 2: - secondByte = buf[i + 1] - if ((secondByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) - if (tempCodePoint > 0x7F) { - codePoint = tempCodePoint - } - } - break - case 3: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) - if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { - codePoint = tempCodePoint - } - } - break - case 4: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - fourthByte = buf[i + 3] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) - if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { - codePoint = tempCodePoint - } - } - } - } - - if (codePoint === null) { - // we did not generate a valid codePoint so insert a - // replacement char (U+FFFD) and advance only 1 byte - codePoint = 0xFFFD - bytesPerSequence = 1 - } else if (codePoint > 0xFFFF) { - // encode to utf16 (surrogate pair dance) - codePoint -= 0x10000 - res.push(codePoint >>> 10 & 0x3FF | 0xD800) - codePoint = 0xDC00 | codePoint & 0x3FF - } - - res.push(codePoint) - i += bytesPerSequence - } - - return decodeCodePointsArray(res) -} - -// Based on http://stackoverflow.com/a/22747272/680742, the browser with -// the lowest limit is Chrome, with 0x10000 args. -// We go 1 magnitude less, for safety -var MAX_ARGUMENTS_LENGTH = 0x1000 - -function decodeCodePointsArray (codePoints) { - var len = codePoints.length - if (len <= MAX_ARGUMENTS_LENGTH) { - return String.fromCharCode.apply(String, codePoints) // avoid extra slice() - } - - // Decode in chunks to avoid "call stack size exceeded". - var res = '' - var i = 0 - while (i < len) { - res += String.fromCharCode.apply( - String, - codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) - ) - } - return res -} - -function asciiSlice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i] & 0x7F) - } - return ret -} - -function latin1Slice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i]) - } - return ret -} - -function hexSlice (buf, start, end) { - var len = buf.length - - if (!start || start < 0) start = 0 - if (!end || end < 0 || end > len) end = len - - var out = '' - for (var i = start; i < end; ++i) { - out += toHex(buf[i]) - } - return out -} - -function utf16leSlice (buf, start, end) { - var bytes = buf.slice(start, end) - var res = '' - for (var i = 0; i < bytes.length; i += 2) { - res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) - } - return res -} - -Buffer.prototype.slice = function slice (start, end) { - var len = this.length - start = ~~start - end = end === undefined ? len : ~~end - - if (start < 0) { - start += len - if (start < 0) start = 0 - } else if (start > len) { - start = len - } - - if (end < 0) { - end += len - if (end < 0) end = 0 - } else if (end > len) { - end = len - } - - if (end < start) end = start - - var newBuf = this.subarray(start, end) - // Return an augmented `Uint8Array` instance - newBuf.__proto__ = Buffer.prototype - return newBuf -} - -/* - * Need to make sure that buffer isn't trying to write out of bounds. - */ -function checkOffset (offset, ext, length) { - if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') - if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') -} - -Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - - return val -} - -Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - checkOffset(offset, byteLength, this.length) - } - - var val = this[offset + --byteLength] - var mul = 1 - while (byteLength > 0 && (mul *= 0x100)) { - val += this[offset + --byteLength] * mul - } - - return val -} - -Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - return this[offset] -} - -Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return this[offset] | (this[offset + 1] << 8) -} - -Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return (this[offset] << 8) | this[offset + 1] -} - -Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return ((this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16)) + - (this[offset + 3] * 0x1000000) -} - -Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] * 0x1000000) + - ((this[offset + 1] << 16) | - (this[offset + 2] << 8) | - this[offset + 3]) -} - -Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var i = byteLength - var mul = 1 - var val = this[offset + --i] - while (i > 0 && (mul *= 0x100)) { - val += this[offset + --i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - if (!(this[offset] & 0x80)) return (this[offset]) - return ((0xff - this[offset] + 1) * -1) -} - -Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset] | (this[offset + 1] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset + 1] | (this[offset] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16) | - (this[offset + 3] << 24) -} - -Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] << 24) | - (this[offset + 1] << 16) | - (this[offset + 2] << 8) | - (this[offset + 3]) -} - -Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, true, 23, 4) -} - -Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, false, 23, 4) -} - -Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, true, 52, 8) -} - -Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, false, 52, 8) -} - -function checkInt (buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') - if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') - if (offset + ext > buf.length) throw new RangeError('Index out of range') -} - -Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var mul = 1 - var i = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var i = byteLength - 1 - var mul = 1 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 -} - -Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} - -Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset + 3] = (value >>> 24) - this[offset + 2] = (value >>> 16) - this[offset + 1] = (value >>> 8) - this[offset] = (value & 0xff) - return offset + 4 -} - -Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 -} - -Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = 0 - var mul = 1 - var sub = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = byteLength - 1 - var mul = 1 - var sub = 0 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) - if (value < 0) value = 0xff + value + 1 - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 -} - -Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} - -Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - this[offset + 2] = (value >>> 16) - this[offset + 3] = (value >>> 24) - return offset + 4 -} - -Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (value < 0) value = 0xffffffff + value + 1 - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 -} - -function checkIEEE754 (buf, value, offset, ext, max, min) { - if (offset + ext > buf.length) throw new RangeError('Index out of range') - if (offset < 0) throw new RangeError('Index out of range') -} - -function writeFloat (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) - } - ieee754.write(buf, value, offset, littleEndian, 23, 4) - return offset + 4 -} - -Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { - return writeFloat(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { - return writeFloat(this, value, offset, false, noAssert) -} - -function writeDouble (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) - } - ieee754.write(buf, value, offset, littleEndian, 52, 8) - return offset + 8 -} - -Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { - return writeDouble(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { - return writeDouble(this, value, offset, false, noAssert) -} - -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function copy (target, targetStart, start, end) { - if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') - if (!start) start = 0 - if (!end && end !== 0) end = this.length - if (targetStart >= target.length) targetStart = target.length - if (!targetStart) targetStart = 0 - if (end > 0 && end < start) end = start - - // Copy 0 bytes; we're done - if (end === start) return 0 - if (target.length === 0 || this.length === 0) return 0 - - // Fatal error conditions - if (targetStart < 0) { - throw new RangeError('targetStart out of bounds') - } - if (start < 0 || start >= this.length) throw new RangeError('Index out of range') - if (end < 0) throw new RangeError('sourceEnd out of bounds') - - // Are we oob? - if (end > this.length) end = this.length - if (target.length - targetStart < end - start) { - end = target.length - targetStart + start - } - - var len = end - start - - if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { - // Use built-in when available, missing from IE11 - this.copyWithin(targetStart, start, end) - } else if (this === target && start < targetStart && targetStart < end) { - // descending copy from end - for (var i = len - 1; i >= 0; --i) { - target[i + targetStart] = this[i + start] - } - } else { - Uint8Array.prototype.set.call( - target, - this.subarray(start, end), - targetStart - ) - } - - return len -} - -// Usage: -// buffer.fill(number[, offset[, end]]) -// buffer.fill(buffer[, offset[, end]]) -// buffer.fill(string[, offset[, end]][, encoding]) -Buffer.prototype.fill = function fill (val, start, end, encoding) { - // Handle string cases: - if (typeof val === 'string') { - if (typeof start === 'string') { - encoding = start - start = 0 - end = this.length - } else if (typeof end === 'string') { - encoding = end - end = this.length - } - if (encoding !== undefined && typeof encoding !== 'string') { - throw new TypeError('encoding must be a string') - } - if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - if (val.length === 1) { - var code = val.charCodeAt(0) - if ((encoding === 'utf8' && code < 128) || - encoding === 'latin1') { - // Fast path: If `val` fits into a single byte, use that numeric value. - val = code - } - } - } else if (typeof val === 'number') { - val = val & 255 - } - - // Invalid ranges are not set to a default, so can range check early. - if (start < 0 || this.length < start || this.length < end) { - throw new RangeError('Out of range index') - } - - if (end <= start) { - return this - } - - start = start >>> 0 - end = end === undefined ? this.length : end >>> 0 - - if (!val) val = 0 - - var i - if (typeof val === 'number') { - for (i = start; i < end; ++i) { - this[i] = val - } - } else { - var bytes = Buffer.isBuffer(val) - ? val - : Buffer.from(val, encoding) - var len = bytes.length - if (len === 0) { - throw new TypeError('The value "' + val + - '" is invalid for argument "value"') - } - for (i = 0; i < end - start; ++i) { - this[i + start] = bytes[i % len] - } - } - - return this -} - -// HELPER FUNCTIONS -// ================ - -var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g - -function base64clean (str) { - // Node takes equal signs as end of the Base64 encoding - str = str.split('=')[0] - // Node strips out invalid characters like \n and \t from the string, base64-js does not - str = str.trim().replace(INVALID_BASE64_RE, '') - // Node converts strings with length < 2 to '' - if (str.length < 2) return '' - // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not - while (str.length % 4 !== 0) { - str = str + '=' - } - return str -} - -function toHex (n) { - if (n < 16) return '0' + n.toString(16) - return n.toString(16) -} - -function utf8ToBytes (string, units) { - units = units || Infinity - var codePoint - var length = string.length - var leadSurrogate = null - var bytes = [] - - for (var i = 0; i < length; ++i) { - codePoint = string.charCodeAt(i) - - // is surrogate component - if (codePoint > 0xD7FF && codePoint < 0xE000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xDBFF) { - // unexpected trail - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } else if (i + 1 === length) { - // unpaired lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } - - // valid lead - leadSurrogate = codePoint - - continue - } - - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = codePoint - continue - } - - // valid surrogate pair - codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - } - - leadSurrogate = null - - // encode utf8 - if (codePoint < 0x80) { - if ((units -= 1) < 0) break - bytes.push(codePoint) - } else if (codePoint < 0x800) { - if ((units -= 2) < 0) break - bytes.push( - codePoint >> 0x6 | 0xC0, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x10000) { - if ((units -= 3) < 0) break - bytes.push( - codePoint >> 0xC | 0xE0, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x110000) { - if ((units -= 4) < 0) break - bytes.push( - codePoint >> 0x12 | 0xF0, - codePoint >> 0xC & 0x3F | 0x80, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else { - throw new Error('Invalid code point') - } - } - - return bytes -} - -function asciiToBytes (str) { - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push(str.charCodeAt(i) & 0xFF) - } - return byteArray -} - -function utf16leToBytes (str, units) { - var c, hi, lo - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - if ((units -= 2) < 0) break - - c = str.charCodeAt(i) - hi = c >> 8 - lo = c % 256 - byteArray.push(lo) - byteArray.push(hi) - } - - return byteArray -} - -function base64ToBytes (str) { - return base64.toByteArray(base64clean(str)) -} - -function blitBuffer (src, dst, offset, length) { - for (var i = 0; i < length; ++i) { - if ((i + offset >= dst.length) || (i >= src.length)) break - dst[i + offset] = src[i] - } - return i -} - -// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass -// the `instanceof` check but they should be treated as of that type. -// See: https://github.com/feross/buffer/issues/166 -function isInstance (obj, type) { - return obj instanceof type || - (obj != null && obj.constructor != null && obj.constructor.name != null && - obj.constructor.name === type.name) -} -function numberIsNaN (obj) { - // For IE11 support - return obj !== obj // eslint-disable-line no-self-compare -} - -}).call(this,require("buffer").Buffer) -},{"base64-js":39,"buffer":43,"ieee754":55}],44:[function(require,module,exports){ -(function (Buffer){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. - -function isArray(arg) { - if (Array.isArray) { - return Array.isArray(arg); - } - return objectToString(arg) === '[object Array]'; -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; - -function isError(e) { - return (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = Buffer.isBuffer; - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - -}).call(this,{"isBuffer":require("../../is-buffer/index.js")}) -},{"../../is-buffer/index.js":57}],45:[function(require,module,exports){ -(function (process){ -"use strict"; - -function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } - -/* eslint-env browser */ - -/** - * This is the web browser implementation of `debug()`. - */ -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = localstorage(); -/** - * Colors. - */ - -exports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33']; -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ -// eslint-disable-next-line complexity - -function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { - return true; - } // Internet Explorer and Edge do not support colors. - - - if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { - return false; - } // Is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - - - return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 - typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker - typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); -} -/** - * Colorize log arguments if enabled. - * - * @api public - */ - - -function formatArgs(args) { - args[0] = (this.useColors ? '%c' : '') + this.namespace + (this.useColors ? ' %c' : ' ') + args[0] + (this.useColors ? '%c ' : ' ') + '+' + module.exports.humanize(this.diff); - - if (!this.useColors) { - return; - } - - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit'); // The final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function (match) { - if (match === '%%') { - return; - } - - index++; - - if (match === '%c') { - // We only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - args.splice(lastC, 0, c); -} -/** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public - */ - - -function log() { - var _console; - - // This hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return (typeof console === "undefined" ? "undefined" : _typeof(console)) === 'object' && console.log && (_console = console).log.apply(_console, arguments); -} -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - - -function save(namespaces) { - try { - if (namespaces) { - exports.storage.setItem('debug', namespaces); - } else { - exports.storage.removeItem('debug'); - } - } catch (error) {// Swallow - // XXX (@Qix-) should we be logging these? - } -} -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - - -function load() { - var r; - - try { - r = exports.storage.getItem('debug'); - } catch (error) {} // Swallow - // XXX (@Qix-) should we be logging these? - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - - - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; - } - - return r; -} -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ - - -function localstorage() { - try { - // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context - // The Browser also has localStorage in the global context. - return localStorage; - } catch (error) {// Swallow - // XXX (@Qix-) should we be logging these? - } -} - -module.exports = require('./common')(exports); -var formatters = module.exports.formatters; -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - -formatters.j = function (v) { - try { - return JSON.stringify(v); - } catch (error) { - return '[UnexpectedJSONParseError]: ' + error.message; - } -}; - - -}).call(this,require('_process')) -},{"./common":46,"_process":69}],46:[function(require,module,exports){ -"use strict"; - -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - */ -function setup(env) { - createDebug.debug = createDebug; - createDebug.default = createDebug; - createDebug.coerce = coerce; - createDebug.disable = disable; - createDebug.enable = enable; - createDebug.enabled = enabled; - createDebug.humanize = require('ms'); - Object.keys(env).forEach(function (key) { - createDebug[key] = env[key]; - }); - /** - * Active `debug` instances. - */ - - createDebug.instances = []; - /** - * The currently active debug mode names, and names to skip. - */ - - createDebug.names = []; - createDebug.skips = []; - /** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". - */ - - createDebug.formatters = {}; - /** - * Selects a color for a debug namespace - * @param {String} namespace The namespace string for the for the debug instance to be colored - * @return {Number|String} An ANSI color code for the given namespace - * @api private - */ - - function selectColor(namespace) { - var hash = 0; - - for (var i = 0; i < namespace.length; i++) { - hash = (hash << 5) - hash + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } - - return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; - } - - createDebug.selectColor = selectColor; - /** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ - - function createDebug(namespace) { - var prevTime; - - function debug() { - // Disabled? - if (!debug.enabled) { - return; - } - - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - var self = debug; // Set `diff` timestamp - - var curr = Number(new Date()); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - args[0] = createDebug.coerce(args[0]); - - if (typeof args[0] !== 'string') { - // Anything else let's inspect with %O - args.unshift('%O'); - } // Apply any `formatters` transformations - - - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function (match, format) { - // If we encounter an escaped % then don't increase the array index - if (match === '%%') { - return match; - } - - index++; - var formatter = createDebug.formatters[format]; - - if (typeof formatter === 'function') { - var val = args[index]; - match = formatter.call(self, val); // Now we need to remove `args[index]` since it's inlined in the `format` - - args.splice(index, 1); - index--; - } - - return match; - }); // Apply env-specific formatting (colors, etc.) - - createDebug.formatArgs.call(self, args); - var logFn = self.log || createDebug.log; - logFn.apply(self, args); - } - - debug.namespace = namespace; - debug.enabled = createDebug.enabled(namespace); - debug.useColors = createDebug.useColors(); - debug.color = selectColor(namespace); - debug.destroy = destroy; - debug.extend = extend; // Debug.formatArgs = formatArgs; - // debug.rawLog = rawLog; - // env-specific initialization logic for debug instances - - if (typeof createDebug.init === 'function') { - createDebug.init(debug); - } - - createDebug.instances.push(debug); - return debug; - } - - function destroy() { - var index = createDebug.instances.indexOf(this); - - if (index !== -1) { - createDebug.instances.splice(index, 1); - return true; - } - - return false; - } - - function extend(namespace, delimiter) { - return createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); - } - /** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ - - - function enable(namespaces) { - createDebug.save(namespaces); - createDebug.names = []; - createDebug.skips = []; - var i; - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; - - for (i = 0; i < len; i++) { - if (!split[i]) { - // ignore empty strings - continue; - } - - namespaces = split[i].replace(/\*/g, '.*?'); - - if (namespaces[0] === '-') { - createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - createDebug.names.push(new RegExp('^' + namespaces + '$')); - } - } - - for (i = 0; i < createDebug.instances.length; i++) { - var instance = createDebug.instances[i]; - instance.enabled = createDebug.enabled(instance.namespace); - } - } - /** - * Disable debug output. - * - * @api public - */ - - - function disable() { - createDebug.enable(''); - } - /** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - - - function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - - var i; - var len; - - for (i = 0, len = createDebug.skips.length; i < len; i++) { - if (createDebug.skips[i].test(name)) { - return false; - } - } - - for (i = 0, len = createDebug.names.length; i < len; i++) { - if (createDebug.names[i].test(name)) { - return true; - } - } - - return false; - } - /** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ - - - function coerce(val) { - if (val instanceof Error) { - return val.stack || val.message; - } - - return val; - } - - createDebug.enable(createDebug.load()); - return createDebug; -} - -module.exports = setup; - - -},{"ms":60}],47:[function(require,module,exports){ -'use strict'; - -var keys = require('object-keys'); -var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol'; - -var toStr = Object.prototype.toString; -var concat = Array.prototype.concat; -var origDefineProperty = Object.defineProperty; - -var isFunction = function (fn) { - return typeof fn === 'function' && toStr.call(fn) === '[object Function]'; -}; - -var arePropertyDescriptorsSupported = function () { - var obj = {}; - try { - origDefineProperty(obj, 'x', { enumerable: false, value: obj }); - // eslint-disable-next-line no-unused-vars, no-restricted-syntax - for (var _ in obj) { // jscs:ignore disallowUnusedVariables - return false; - } - return obj.x === obj; - } catch (e) { /* this is IE 8. */ - return false; - } -}; -var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported(); - -var defineProperty = function (object, name, value, predicate) { - if (name in object && (!isFunction(predicate) || !predicate())) { - return; - } - if (supportsDescriptors) { - origDefineProperty(object, name, { - configurable: true, - enumerable: false, - value: value, - writable: true - }); - } else { - object[name] = value; - } -}; - -var defineProperties = function (object, map) { - var predicates = arguments.length > 2 ? arguments[2] : {}; - var props = keys(map); - if (hasSymbols) { - props = concat.call(props, Object.getOwnPropertySymbols(map)); - } - for (var i = 0; i < props.length; i += 1) { - defineProperty(object, props[i], map[props[i]], predicates[props[i]]); - } -}; - -defineProperties.supportsDescriptors = !!supportsDescriptors; - -module.exports = defineProperties; - -},{"object-keys":62}],48:[function(require,module,exports){ -/*! - - diff v3.5.0 - -Software License Agreement (BSD License) - -Copyright (c) 2009-2015, Kevin Decker <kpdecker@gmail.com> - -All rights reserved. - -Redistribution and use of this software in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of Kevin Decker nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -@license -*/ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(false) - define([], factory); - else if(typeof exports === 'object') - exports["JsDiff"] = factory(); - else - root["JsDiff"] = factory(); -})(this, function() { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports.canonicalize = exports.convertChangesToXML = exports.convertChangesToDMP = exports.merge = exports.parsePatch = exports.applyPatches = exports.applyPatch = exports.createPatch = exports.createTwoFilesPatch = exports.structuredPatch = exports.diffArrays = exports.diffJson = exports.diffCss = exports.diffSentences = exports.diffTrimmedLines = exports.diffLines = exports.diffWordsWithSpace = exports.diffWords = exports.diffChars = exports.Diff = undefined; - - /*istanbul ignore end*/var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); - - /*istanbul ignore end*/var /*istanbul ignore start*/_character = __webpack_require__(2) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_word = __webpack_require__(3) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_sentence = __webpack_require__(6) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_css = __webpack_require__(7) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_json = __webpack_require__(8) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_array = __webpack_require__(9) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_apply = __webpack_require__(10) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_merge = __webpack_require__(13) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_create = __webpack_require__(14) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_dmp = __webpack_require__(16) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_xml = __webpack_require__(17) /*istanbul ignore end*/; - - /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /* See LICENSE file for terms of use */ - - /* - * Text diff implementation. - * - * This library supports the following APIS: - * JsDiff.diffChars: Character by character diff - * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace - * JsDiff.diffLines: Line based diff - * - * JsDiff.diffCss: Diff targeted at CSS content - * - * These methods are based on the implementation proposed in - * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). - * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - */ - exports. /*istanbul ignore end*/Diff = _base2['default']; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffChars = _character.diffChars; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWords = _word.diffWords; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = _word.diffWordsWithSpace; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffLines = _line.diffLines; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = _line.diffTrimmedLines; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffSentences = _sentence.diffSentences; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffCss = _css.diffCss; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffJson = _json.diffJson; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffArrays = _array.diffArrays; - /*istanbul ignore start*/exports. /*istanbul ignore end*/structuredPatch = _create.structuredPatch; - /*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = _create.createTwoFilesPatch; - /*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = _create.createPatch; - /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatch = _apply.applyPatch; - /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = _apply.applyPatches; - /*istanbul ignore start*/exports. /*istanbul ignore end*/parsePatch = _parse.parsePatch; - /*istanbul ignore start*/exports. /*istanbul ignore end*/merge = _merge.merge; - /*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToDMP = _dmp.convertChangesToDMP; - /*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToXML = _xml.convertChangesToXML; - /*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = _json.canonicalize; - - - -/***/ }), -/* 1 */ -/***/ (function(module, exports) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports['default'] = /*istanbul ignore end*/Diff; - function Diff() {} - - Diff.prototype = { - /*istanbul ignore start*/ /*istanbul ignore end*/diff: function diff(oldString, newString) { - /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - - var callback = options.callback; - if (typeof options === 'function') { - callback = options; - options = {}; - } - this.options = options; - - var self = this; - - function done(value) { - if (callback) { - setTimeout(function () { - callback(undefined, value); - }, 0); - return true; - } else { - return value; - } - } - - // Allow subclasses to massage the input prior to running - oldString = this.castInput(oldString); - newString = this.castInput(newString); - - oldString = this.removeEmpty(this.tokenize(oldString)); - newString = this.removeEmpty(this.tokenize(newString)); - - var newLen = newString.length, - oldLen = oldString.length; - var editLength = 1; - var maxEditLength = newLen + oldLen; - var bestPath = [{ newPos: -1, components: [] }]; - - // Seed editLength = 0, i.e. the content starts with the same values - var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); - if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { - // Identity per the equality and tokenizer - return done([{ value: this.join(newString), count: newString.length }]); - } - - // Main worker method. checks all permutations of a given edit length for acceptance. - function execEditLength() { - for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { - var basePath = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; - var addPath = bestPath[diagonalPath - 1], - removePath = bestPath[diagonalPath + 1], - _oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; - if (addPath) { - // No one else is going to attempt to use this value, clear it - bestPath[diagonalPath - 1] = undefined; - } - - var canAdd = addPath && addPath.newPos + 1 < newLen, - canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen; - if (!canAdd && !canRemove) { - // If this path is a terminal then prune - bestPath[diagonalPath] = undefined; - continue; - } - - // Select the diagonal that we want to branch from. We select the prior - // path whose position in the new string is the farthest from the origin - // and does not pass the bounds of the diff graph - if (!canAdd || canRemove && addPath.newPos < removePath.newPos) { - basePath = clonePath(removePath); - self.pushComponent(basePath.components, undefined, true); - } else { - basePath = addPath; // No need to clone, we've pulled it from the list - basePath.newPos++; - self.pushComponent(basePath.components, true, undefined); - } - - _oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); - - // If we have hit the end of both strings, then we are done - if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) { - return done(buildValues(self, basePath.components, newString, oldString, self.useLongestToken)); - } else { - // Otherwise track this path as a potential candidate and continue. - bestPath[diagonalPath] = basePath; - } - } - - editLength++; - } - - // Performs the length of edit iteration. Is a bit fugly as this has to support the - // sync and async mode which is never fun. Loops over execEditLength until a value - // is produced. - if (callback) { - (function exec() { - setTimeout(function () { - // This should not happen, but we want to be safe. - /* istanbul ignore next */ - if (editLength > maxEditLength) { - return callback(); - } - - if (!execEditLength()) { - exec(); - } - }, 0); - })(); - } else { - while (editLength <= maxEditLength) { - var ret = execEditLength(); - if (ret) { - return ret; - } - } - } - }, - /*istanbul ignore start*/ /*istanbul ignore end*/pushComponent: function pushComponent(components, added, removed) { - var last = components[components.length - 1]; - if (last && last.added === added && last.removed === removed) { - // We need to clone here as the component clone operation is just - // as shallow array clone - components[components.length - 1] = { count: last.count + 1, added: added, removed: removed }; - } else { - components.push({ count: 1, added: added, removed: removed }); - } - }, - /*istanbul ignore start*/ /*istanbul ignore end*/extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { - var newLen = newString.length, - oldLen = oldString.length, - newPos = basePath.newPos, - oldPos = newPos - diagonalPath, - commonCount = 0; - while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { - newPos++; - oldPos++; - commonCount++; - } - - if (commonCount) { - basePath.components.push({ count: commonCount }); - } - - basePath.newPos = newPos; - return oldPos; - }, - /*istanbul ignore start*/ /*istanbul ignore end*/equals: function equals(left, right) { - if (this.options.comparator) { - return this.options.comparator(left, right); - } else { - return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); - } - }, - /*istanbul ignore start*/ /*istanbul ignore end*/removeEmpty: function removeEmpty(array) { - var ret = []; - for (var i = 0; i < array.length; i++) { - if (array[i]) { - ret.push(array[i]); - } - } - return ret; - }, - /*istanbul ignore start*/ /*istanbul ignore end*/castInput: function castInput(value) { - return value; - }, - /*istanbul ignore start*/ /*istanbul ignore end*/tokenize: function tokenize(value) { - return value.split(''); - }, - /*istanbul ignore start*/ /*istanbul ignore end*/join: function join(chars) { - return chars.join(''); - } - }; - - function buildValues(diff, components, newString, oldString, useLongestToken) { - var componentPos = 0, - componentLen = components.length, - newPos = 0, - oldPos = 0; - - for (; componentPos < componentLen; componentPos++) { - var component = components[componentPos]; - if (!component.removed) { - if (!component.added && useLongestToken) { - var value = newString.slice(newPos, newPos + component.count); - value = value.map(function (value, i) { - var oldValue = oldString[oldPos + i]; - return oldValue.length > value.length ? oldValue : value; - }); - - component.value = diff.join(value); - } else { - component.value = diff.join(newString.slice(newPos, newPos + component.count)); - } - newPos += component.count; - - // Common case - if (!component.added) { - oldPos += component.count; - } - } else { - component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); - oldPos += component.count; - - // Reverse add and remove so removes are output first to match common convention - // The diffing algorithm is tied to add then remove output and this is the simplest - // route to get the desired output with minimal overhead. - if (componentPos && components[componentPos - 1].added) { - var tmp = components[componentPos - 1]; - components[componentPos - 1] = components[componentPos]; - components[componentPos] = tmp; - } - } - } - - // Special case handle for when one terminal is ignored (i.e. whitespace). - // For this case we merge the terminal into the prior string and drop the change. - // This is only available for string mode. - var lastComponent = components[componentLen - 1]; - if (componentLen > 1 && typeof lastComponent.value === 'string' && (lastComponent.added || lastComponent.removed) && diff.equals('', lastComponent.value)) { - components[componentLen - 2].value += lastComponent.value; - components.pop(); - } - - return components; - } - - function clonePath(path) { - return { newPos: path.newPos, components: path.components.slice(0) }; - } - - - -/***/ }), -/* 2 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports.characterDiff = undefined; - exports. /*istanbul ignore end*/diffChars = diffChars; - - var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /*istanbul ignore end*/var characterDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/characterDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); - function diffChars(oldStr, newStr, options) { - return characterDiff.diff(oldStr, newStr, options); - } - - - -/***/ }), -/* 3 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports.wordDiff = undefined; - exports. /*istanbul ignore end*/diffWords = diffWords; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = diffWordsWithSpace; - - var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); - - /*istanbul ignore end*/var /*istanbul ignore start*/_params = __webpack_require__(4) /*istanbul ignore end*/; - - /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /*istanbul ignore end*/ // Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode - // - // Ranges and exceptions: - // Latin-1 Supplement, 0080–00FF - // - U+00D7 × Multiplication sign - // - U+00F7 ÷ Division sign - // Latin Extended-A, 0100–017F - // Latin Extended-B, 0180–024F - // IPA Extensions, 0250–02AF - // Spacing Modifier Letters, 02B0–02FF - // - U+02C7 ˇ ˇ Caron - // - U+02D8 ˘ ˘ Breve - // - U+02D9 ˙ ˙ Dot Above - // - U+02DA ˚ ˚ Ring Above - // - U+02DB ˛ ˛ Ogonek - // - U+02DC ˜ ˜ Small Tilde - // - U+02DD ˝ ˝ Double Acute Accent - // Latin Extended Additional, 1E00–1EFF - var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; - - var reWhitespace = /\S/; - - var wordDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/wordDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); - wordDiff.equals = function (left, right) { - if (this.options.ignoreCase) { - left = left.toLowerCase(); - right = right.toLowerCase(); - } - return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); - }; - wordDiff.tokenize = function (value) { - var tokens = value.split(/(\s+|\b)/); - - // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. - for (var i = 0; i < tokens.length - 1; i++) { - // If we have an empty string in the next field and we have only word chars before and after, merge - if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { - tokens[i] += tokens[i + 2]; - tokens.splice(i + 1, 2); - i--; - } - } - - return tokens; - }; - - function diffWords(oldStr, newStr, options) { - options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(options, { ignoreWhitespace: true }); - return wordDiff.diff(oldStr, newStr, options); - } - - function diffWordsWithSpace(oldStr, newStr, options) { - return wordDiff.diff(oldStr, newStr, options); - } - - - -/***/ }), -/* 4 */ -/***/ (function(module, exports) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports. /*istanbul ignore end*/generateOptions = generateOptions; - function generateOptions(options, defaults) { - if (typeof options === 'function') { - defaults.callback = options; - } else if (options) { - for (var name in options) { - /* istanbul ignore else */ - if (options.hasOwnProperty(name)) { - defaults[name] = options[name]; - } - } - } - return defaults; - } - - - -/***/ }), -/* 5 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports.lineDiff = undefined; - exports. /*istanbul ignore end*/diffLines = diffLines; - /*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = diffTrimmedLines; - - var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); - - /*istanbul ignore end*/var /*istanbul ignore start*/_params = __webpack_require__(4) /*istanbul ignore end*/; - - /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /*istanbul ignore end*/var lineDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/lineDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); - lineDiff.tokenize = function (value) { - var retLines = [], - linesAndNewlines = value.split(/(\n|\r\n)/); - - // Ignore the final empty token that occurs if the string ends with a new line - if (!linesAndNewlines[linesAndNewlines.length - 1]) { - linesAndNewlines.pop(); - } - - // Merge the content and line separators into single tokens - for (var i = 0; i < linesAndNewlines.length; i++) { - var line = linesAndNewlines[i]; - - if (i % 2 && !this.options.newlineIsToken) { - retLines[retLines.length - 1] += line; - } else { - if (this.options.ignoreWhitespace) { - line = line.trim(); - } - retLines.push(line); - } - } - - return retLines; - }; - - function diffLines(oldStr, newStr, callback) { - return lineDiff.diff(oldStr, newStr, callback); - } - function diffTrimmedLines(oldStr, newStr, callback) { - var options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(callback, { ignoreWhitespace: true }); - return lineDiff.diff(oldStr, newStr, options); - } - - - -/***/ }), -/* 6 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports.sentenceDiff = undefined; - exports. /*istanbul ignore end*/diffSentences = diffSentences; - - var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /*istanbul ignore end*/var sentenceDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/sentenceDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); - sentenceDiff.tokenize = function (value) { - return value.split(/(\S.+?[.!?])(?=\s+|$)/); - }; - - function diffSentences(oldStr, newStr, callback) { - return sentenceDiff.diff(oldStr, newStr, callback); - } - - - -/***/ }), -/* 7 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports.cssDiff = undefined; - exports. /*istanbul ignore end*/diffCss = diffCss; - - var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /*istanbul ignore end*/var cssDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/cssDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); - cssDiff.tokenize = function (value) { - return value.split(/([{}:;,]|\s+)/); - }; - - function diffCss(oldStr, newStr, callback) { - return cssDiff.diff(oldStr, newStr, callback); - } - - - -/***/ }), -/* 8 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports.jsonDiff = undefined; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - - exports. /*istanbul ignore end*/diffJson = diffJson; - /*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = canonicalize; - - var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); - - /*istanbul ignore end*/var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; - - /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /*istanbul ignore end*/var objectPrototypeToString = Object.prototype.toString; - - var jsonDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/jsonDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); - // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a - // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: - jsonDiff.useLongestToken = true; - - jsonDiff.tokenize = /*istanbul ignore start*/_line.lineDiff /*istanbul ignore end*/.tokenize; - jsonDiff.castInput = function (value) { - /*istanbul ignore start*/var _options = /*istanbul ignore end*/this.options, - undefinedReplacement = _options.undefinedReplacement, - _options$stringifyRep = _options.stringifyReplacer, - stringifyReplacer = _options$stringifyRep === undefined ? function (k, v) /*istanbul ignore start*/{ - return (/*istanbul ignore end*/typeof v === 'undefined' ? undefinedReplacement : v - ); - } : _options$stringifyRep; - - - return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); - }; - jsonDiff.equals = function (left, right) { - return (/*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')) - ); - }; - - function diffJson(oldObj, newObj, options) { - return jsonDiff.diff(oldObj, newObj, options); - } - - // This function handles the presence of circular references by bailing out when encountering an - // object that is already on the "stack" of items being processed. Accepts an optional replacer - function canonicalize(obj, stack, replacementStack, replacer, key) { - stack = stack || []; - replacementStack = replacementStack || []; - - if (replacer) { - obj = replacer(key, obj); - } - - var i = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; - - for (i = 0; i < stack.length; i += 1) { - if (stack[i] === obj) { - return replacementStack[i]; - } - } - - var canonicalizedObj = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; - - if ('[object Array]' === objectPrototypeToString.call(obj)) { - stack.push(obj); - canonicalizedObj = new Array(obj.length); - replacementStack.push(canonicalizedObj); - for (i = 0; i < obj.length; i += 1) { - canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); - } - stack.pop(); - replacementStack.pop(); - return canonicalizedObj; - } - - if (obj && obj.toJSON) { - obj = obj.toJSON(); - } - - if ( /*istanbul ignore start*/(typeof /*istanbul ignore end*/obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null) { - stack.push(obj); - canonicalizedObj = {}; - replacementStack.push(canonicalizedObj); - var sortedKeys = [], - _key = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; - for (_key in obj) { - /* istanbul ignore else */ - if (obj.hasOwnProperty(_key)) { - sortedKeys.push(_key); - } - } - sortedKeys.sort(); - for (i = 0; i < sortedKeys.length; i += 1) { - _key = sortedKeys[i]; - canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); - } - stack.pop(); - replacementStack.pop(); - } else { - canonicalizedObj = obj; - } - return canonicalizedObj; - } - - - -/***/ }), -/* 9 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports.arrayDiff = undefined; - exports. /*istanbul ignore end*/diffArrays = diffArrays; - - var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /*istanbul ignore end*/var arrayDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); - arrayDiff.tokenize = function (value) { - return value.slice(); - }; - arrayDiff.join = arrayDiff.removeEmpty = function (value) { - return value; - }; - - function diffArrays(oldArr, newArr, callback) { - return arrayDiff.diff(oldArr, newArr, callback); - } - - - -/***/ }), -/* 10 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports. /*istanbul ignore end*/applyPatch = applyPatch; - /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = applyPatches; - - var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_distanceIterator = __webpack_require__(12) /*istanbul ignore end*/; - - /*istanbul ignore start*/var _distanceIterator2 = _interopRequireDefault(_distanceIterator); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - /*istanbul ignore end*/function applyPatch(source, uniDiff) { - /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - - if (typeof uniDiff === 'string') { - uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); - } - - if (Array.isArray(uniDiff)) { - if (uniDiff.length > 1) { - throw new Error('applyPatch only works with a single input.'); - } - - uniDiff = uniDiff[0]; - } - - // Apply the diff to the input - var lines = source.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], - hunks = uniDiff.hunks, - compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) /*istanbul ignore start*/{ - return (/*istanbul ignore end*/line === patchContent - ); - }, - errorCount = 0, - fuzzFactor = options.fuzzFactor || 0, - minLine = 0, - offset = 0, - removeEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, - addEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; - - /** - * Checks if the hunk exactly fits on the provided location - */ - function hunkFits(hunk, toPos) { - for (var j = 0; j < hunk.lines.length; j++) { - var line = hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line; - - if (operation === ' ' || operation === '-') { - // Context sanity check - if (!compareLine(toPos + 1, lines[toPos], operation, content)) { - errorCount++; - - if (errorCount > fuzzFactor) { - return false; - } - } - toPos++; - } - } - - return true; - } - - // Search best fit offsets for each hunk based on the previous ones - for (var i = 0; i < hunks.length; i++) { - var hunk = hunks[i], - maxLine = lines.length - hunk.oldLines, - localOffset = 0, - toPos = offset + hunk.oldStart - 1; - - var iterator = /*istanbul ignore start*/(0, _distanceIterator2['default']) /*istanbul ignore end*/(toPos, minLine, maxLine); - - for (; localOffset !== undefined; localOffset = iterator()) { - if (hunkFits(hunk, toPos + localOffset)) { - hunk.offset = offset += localOffset; - break; - } - } - - if (localOffset === undefined) { - return false; - } - - // Set lower text limit to end of the current hunk, so next ones don't try - // to fit over already patched text - minLine = hunk.offset + hunk.oldStart + hunk.oldLines; - } - - // Apply patch hunks - var diffOffset = 0; - for (var _i = 0; _i < hunks.length; _i++) { - var _hunk = hunks[_i], - _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; - diffOffset += _hunk.newLines - _hunk.oldLines; - - if (_toPos < 0) { - // Creating a new file - _toPos = 0; - } - - for (var j = 0; j < _hunk.lines.length; j++) { - var line = _hunk.lines[j], - operation = line.length > 0 ? line[0] : ' ', - content = line.length > 0 ? line.substr(1) : line, - delimiter = _hunk.linedelimiters[j]; - - if (operation === ' ') { - _toPos++; - } else if (operation === '-') { - lines.splice(_toPos, 1); - delimiters.splice(_toPos, 1); - /* istanbul ignore else */ - } else if (operation === '+') { - lines.splice(_toPos, 0, content); - delimiters.splice(_toPos, 0, delimiter); - _toPos++; - } else if (operation === '\\') { - var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; - if (previousOperation === '+') { - removeEOFNL = true; - } else if (previousOperation === '-') { - addEOFNL = true; - } - } - } - } - - // Handle EOFNL insertion/removal - if (removeEOFNL) { - while (!lines[lines.length - 1]) { - lines.pop(); - delimiters.pop(); - } - } else if (addEOFNL) { - lines.push(''); - delimiters.push('\n'); - } - for (var _k = 0; _k < lines.length - 1; _k++) { - lines[_k] = lines[_k] + delimiters[_k]; - } - return lines.join(''); - } - - // Wrapper that supports multiple file patches via callbacks. - function applyPatches(uniDiff, options) { - if (typeof uniDiff === 'string') { - uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); - } - - var currentIndex = 0; - function processIndex() { - var index = uniDiff[currentIndex++]; - if (!index) { - return options.complete(); - } - - options.loadFile(index, function (err, data) { - if (err) { - return options.complete(err); - } - - var updatedContent = applyPatch(data, index, options); - options.patched(index, updatedContent, function (err) { - if (err) { - return options.complete(err); - } - - processIndex(); - }); - }); - } - processIndex(); - } - - - -/***/ }), -/* 11 */ -/***/ (function(module, exports) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports. /*istanbul ignore end*/parsePatch = parsePatch; - function parsePatch(uniDiff) { - /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), - delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], - list = [], - i = 0; - - function parseIndex() { - var index = {}; - list.push(index); - - // Parse diff metadata - while (i < diffstr.length) { - var line = diffstr[i]; - - // File header found, end parsing diff metadata - if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { - break; - } - - // Diff index - var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); - if (header) { - index.index = header[1]; - } - - i++; - } - - // Parse file headers if they are defined. Unified diff requires them, but - // there's no technical issues to have an isolated hunk without file header - parseFileHeader(index); - parseFileHeader(index); - - // Parse hunks - index.hunks = []; - - while (i < diffstr.length) { - var _line = diffstr[i]; - - if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { - break; - } else if (/^@@/.test(_line)) { - index.hunks.push(parseHunk()); - } else if (_line && options.strict) { - // Ignore unexpected content unless in strict mode - throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); - } else { - i++; - } - } - } - - // Parses the --- and +++ headers, if none are found, no lines - // are consumed. - function parseFileHeader(index) { - var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); - if (fileHeader) { - var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; - var data = fileHeader[2].split('\t', 2); - var fileName = data[0].replace(/\\\\/g, '\\'); - if (/^".*"$/.test(fileName)) { - fileName = fileName.substr(1, fileName.length - 2); - } - index[keyPrefix + 'FileName'] = fileName; - index[keyPrefix + 'Header'] = (data[1] || '').trim(); - - i++; - } - } - - // Parses a hunk - // This assumes that we are at the start of a hunk. - function parseHunk() { - var chunkHeaderIndex = i, - chunkHeaderLine = diffstr[i++], - chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); - - var hunk = { - oldStart: +chunkHeader[1], - oldLines: +chunkHeader[2] || 1, - newStart: +chunkHeader[3], - newLines: +chunkHeader[4] || 1, - lines: [], - linedelimiters: [] - }; - - var addCount = 0, - removeCount = 0; - for (; i < diffstr.length; i++) { - // Lines starting with '---' could be mistaken for the "remove line" operation - // But they could be the header for the next file. Therefore prune such cases out. - if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { - break; - } - var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; - - if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { - hunk.lines.push(diffstr[i]); - hunk.linedelimiters.push(delimiters[i] || '\n'); - - if (operation === '+') { - addCount++; - } else if (operation === '-') { - removeCount++; - } else if (operation === ' ') { - addCount++; - removeCount++; - } - } else { - break; - } - } - - // Handle the empty block count case - if (!addCount && hunk.newLines === 1) { - hunk.newLines = 0; - } - if (!removeCount && hunk.oldLines === 1) { - hunk.oldLines = 0; - } - - // Perform optional sanity checking - if (options.strict) { - if (addCount !== hunk.newLines) { - throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } - if (removeCount !== hunk.oldLines) { - throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); - } - } - - return hunk; - } - - while (i < diffstr.length) { - parseIndex(); - } - - return list; - } - - - -/***/ }), -/* 12 */ -/***/ (function(module, exports) { - - /*istanbul ignore start*/"use strict"; - - exports.__esModule = true; - - exports["default"] = /*istanbul ignore end*/function (start, minLine, maxLine) { - var wantForward = true, - backwardExhausted = false, - forwardExhausted = false, - localOffset = 1; - - return function iterator() { - if (wantForward && !forwardExhausted) { - if (backwardExhausted) { - localOffset++; - } else { - wantForward = false; - } - - // Check if trying to fit beyond text length, and if not, check it fits - // after offset location (or desired location on first iteration) - if (start + localOffset <= maxLine) { - return localOffset; - } - - forwardExhausted = true; - } - - if (!backwardExhausted) { - if (!forwardExhausted) { - wantForward = true; - } - - // Check if trying to fit before text beginning, and if not, check it fits - // before offset location - if (minLine <= start - localOffset) { - return -localOffset++; - } - - backwardExhausted = true; - return iterator(); - } - - // We tried to fit hunk before text beginning and beyond text length, then - // hunk can't fit on the text. Return undefined - }; - }; - - - -/***/ }), -/* 13 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports. /*istanbul ignore end*/calcLineCount = calcLineCount; - /*istanbul ignore start*/exports. /*istanbul ignore end*/merge = merge; - - var /*istanbul ignore start*/_create = __webpack_require__(14) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; - - var /*istanbul ignore start*/_array = __webpack_require__(15) /*istanbul ignore end*/; - - /*istanbul ignore start*/function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } - - /*istanbul ignore end*/function calcLineCount(hunk) { - /*istanbul ignore start*/var _calcOldNewLineCount = /*istanbul ignore end*/calcOldNewLineCount(hunk.lines), - oldLines = _calcOldNewLineCount.oldLines, - newLines = _calcOldNewLineCount.newLines; - - if (oldLines !== undefined) { - hunk.oldLines = oldLines; - } else { - delete hunk.oldLines; - } - - if (newLines !== undefined) { - hunk.newLines = newLines; - } else { - delete hunk.newLines; - } - } - - function merge(mine, theirs, base) { - mine = loadPatch(mine, base); - theirs = loadPatch(theirs, base); - - var ret = {}; - - // For index we just let it pass through as it doesn't have any necessary meaning. - // Leaving sanity checks on this to the API consumer that may know more about the - // meaning in their own context. - if (mine.index || theirs.index) { - ret.index = mine.index || theirs.index; - } - - if (mine.newFileName || theirs.newFileName) { - if (!fileNameChanged(mine)) { - // No header or no change in ours, use theirs (and ours if theirs does not exist) - ret.oldFileName = theirs.oldFileName || mine.oldFileName; - ret.newFileName = theirs.newFileName || mine.newFileName; - ret.oldHeader = theirs.oldHeader || mine.oldHeader; - ret.newHeader = theirs.newHeader || mine.newHeader; - } else if (!fileNameChanged(theirs)) { - // No header or no change in theirs, use ours - ret.oldFileName = mine.oldFileName; - ret.newFileName = mine.newFileName; - ret.oldHeader = mine.oldHeader; - ret.newHeader = mine.newHeader; - } else { - // Both changed... figure it out - ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName); - ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName); - ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader); - ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); - } - } - - ret.hunks = []; - - var mineIndex = 0, - theirsIndex = 0, - mineOffset = 0, - theirsOffset = 0; - - while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { - var mineCurrent = mine.hunks[mineIndex] || { oldStart: Infinity }, - theirsCurrent = theirs.hunks[theirsIndex] || { oldStart: Infinity }; - - if (hunkBefore(mineCurrent, theirsCurrent)) { - // This patch does not overlap with any of the others, yay. - ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); - mineIndex++; - theirsOffset += mineCurrent.newLines - mineCurrent.oldLines; - } else if (hunkBefore(theirsCurrent, mineCurrent)) { - // This patch does not overlap with any of the others, yay. - ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset)); - theirsIndex++; - mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines; - } else { - // Overlap, merge as best we can - var mergedHunk = { - oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart), - oldLines: 0, - newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset), - newLines: 0, - lines: [] - }; - mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines); - theirsIndex++; - mineIndex++; - - ret.hunks.push(mergedHunk); - } - } - - return ret; - } - - function loadPatch(param, base) { - if (typeof param === 'string') { - if (/^@@/m.test(param) || /^Index:/m.test(param)) { - return (/*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(param)[0] - ); - } - - if (!base) { - throw new Error('Must provide a base reference or pass in a patch'); - } - return (/*istanbul ignore start*/(0, _create.structuredPatch) /*istanbul ignore end*/(undefined, undefined, base, param) - ); - } - - return param; - } - - function fileNameChanged(patch) { - return patch.newFileName && patch.newFileName !== patch.oldFileName; - } - - function selectField(index, mine, theirs) { - if (mine === theirs) { - return mine; - } else { - index.conflict = true; - return { mine: mine, theirs: theirs }; - } - } - - function hunkBefore(test, check) { - return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; - } - - function cloneHunk(hunk, offset) { - return { - oldStart: hunk.oldStart, oldLines: hunk.oldLines, - newStart: hunk.newStart + offset, newLines: hunk.newLines, - lines: hunk.lines - }; - } - - function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { - // This will generally result in a conflicted hunk, but there are cases where the context - // is the only overlap where we can successfully merge the content here. - var mine = { offset: mineOffset, lines: mineLines, index: 0 }, - their = { offset: theirOffset, lines: theirLines, index: 0 }; - - // Handle any leading content - insertLeading(hunk, mine, their); - insertLeading(hunk, their, mine); - - // Now in the overlap content. Scan through and select the best changes from each. - while (mine.index < mine.lines.length && their.index < their.lines.length) { - var mineCurrent = mine.lines[mine.index], - theirCurrent = their.lines[their.index]; - - if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { - // Both modified ... - mutualChange(hunk, mine, their); - } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { - /*istanbul ignore start*/var _hunk$lines; - - /*istanbul ignore end*/ // Mine inserted - /*istanbul ignore start*/(_hunk$lines = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/collectChange(mine))); - } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { - /*istanbul ignore start*/var _hunk$lines2; - - /*istanbul ignore end*/ // Theirs inserted - /*istanbul ignore start*/(_hunk$lines2 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/collectChange(their))); - } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { - // Mine removed or edited - removal(hunk, mine, their); - } else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') { - // Their removed or edited - removal(hunk, their, mine, true); - } else if (mineCurrent === theirCurrent) { - // Context identity - hunk.lines.push(mineCurrent); - mine.index++; - their.index++; - } else { - // Context mismatch - conflict(hunk, collectChange(mine), collectChange(their)); - } - } - - // Now push anything that may be remaining - insertTrailing(hunk, mine); - insertTrailing(hunk, their); - - calcLineCount(hunk); - } - - function mutualChange(hunk, mine, their) { - var myChanges = collectChange(mine), - theirChanges = collectChange(their); - - if (allRemoves(myChanges) && allRemoves(theirChanges)) { - // Special case for remove changes that are supersets of one another - if ( /*istanbul ignore start*/(0, _array.arrayStartsWith) /*istanbul ignore end*/(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { - /*istanbul ignore start*/var _hunk$lines3; - - /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines3 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/myChanges)); - return; - } else if ( /*istanbul ignore start*/(0, _array.arrayStartsWith) /*istanbul ignore end*/(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { - /*istanbul ignore start*/var _hunk$lines4; - - /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines4 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines4 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/theirChanges)); - return; - } - } else if ( /*istanbul ignore start*/(0, _array.arrayEqual) /*istanbul ignore end*/(myChanges, theirChanges)) { - /*istanbul ignore start*/var _hunk$lines5; - - /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines5 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines5 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/myChanges)); - return; - } - - conflict(hunk, myChanges, theirChanges); - } - - function removal(hunk, mine, their, swap) { - var myChanges = collectChange(mine), - theirChanges = collectContext(their, myChanges); - if (theirChanges.merged) { - /*istanbul ignore start*/var _hunk$lines6; - - /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines6 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines6 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/theirChanges.merged)); - } else { - conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); - } - } - - function conflict(hunk, mine, their) { - hunk.conflict = true; - hunk.lines.push({ - conflict: true, - mine: mine, - theirs: their - }); - } - - function insertLeading(hunk, insert, their) { - while (insert.offset < their.offset && insert.index < insert.lines.length) { - var line = insert.lines[insert.index++]; - hunk.lines.push(line); - insert.offset++; - } - } - function insertTrailing(hunk, insert) { - while (insert.index < insert.lines.length) { - var line = insert.lines[insert.index++]; - hunk.lines.push(line); - } - } - - function collectChange(state) { - var ret = [], - operation = state.lines[state.index][0]; - while (state.index < state.lines.length) { - var line = state.lines[state.index]; - - // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. - if (operation === '-' && line[0] === '+') { - operation = '+'; - } - - if (operation === line[0]) { - ret.push(line); - state.index++; - } else { - break; - } - } - - return ret; - } - function collectContext(state, matchChanges) { - var changes = [], - merged = [], - matchIndex = 0, - contextChanges = false, - conflicted = false; - while (matchIndex < matchChanges.length && state.index < state.lines.length) { - var change = state.lines[state.index], - match = matchChanges[matchIndex]; - - // Once we've hit our add, then we are done - if (match[0] === '+') { - break; - } - - contextChanges = contextChanges || change[0] !== ' '; - - merged.push(match); - matchIndex++; - - // Consume any additions in the other block as a conflict to attempt - // to pull in the remaining context after this - if (change[0] === '+') { - conflicted = true; - - while (change[0] === '+') { - changes.push(change); - change = state.lines[++state.index]; - } - } - - if (match.substr(1) === change.substr(1)) { - changes.push(change); - state.index++; - } else { - conflicted = true; - } - } - - if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { - conflicted = true; - } - - if (conflicted) { - return changes; - } - - while (matchIndex < matchChanges.length) { - merged.push(matchChanges[matchIndex++]); - } - - return { - merged: merged, - changes: changes - }; - } - - function allRemoves(changes) { - return changes.reduce(function (prev, change) { - return prev && change[0] === '-'; - }, true); - } - function skipRemoveSuperset(state, removeChanges, delta) { - for (var i = 0; i < delta; i++) { - var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); - if (state.lines[state.index + i] !== ' ' + changeContent) { - return false; - } - } - - state.index += delta; - return true; - } - - function calcOldNewLineCount(lines) { - var oldLines = 0; - var newLines = 0; - - lines.forEach(function (line) { - if (typeof line !== 'string') { - var myCount = calcOldNewLineCount(line.mine); - var theirCount = calcOldNewLineCount(line.theirs); - - if (oldLines !== undefined) { - if (myCount.oldLines === theirCount.oldLines) { - oldLines += myCount.oldLines; - } else { - oldLines = undefined; - } - } - - if (newLines !== undefined) { - if (myCount.newLines === theirCount.newLines) { - newLines += myCount.newLines; - } else { - newLines = undefined; - } - } - } else { - if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { - newLines++; - } - if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { - oldLines++; - } - } - }); - - return { oldLines: oldLines, newLines: newLines }; - } - - - -/***/ }), -/* 14 */ -/***/ (function(module, exports, __webpack_require__) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports. /*istanbul ignore end*/structuredPatch = structuredPatch; - /*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = createTwoFilesPatch; - /*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = createPatch; - - var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; - - /*istanbul ignore start*/function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } - - /*istanbul ignore end*/function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - if (!options) { - options = {}; - } - if (typeof options.context === 'undefined') { - options.context = 4; - } - - var diff = /*istanbul ignore start*/(0, _line.diffLines) /*istanbul ignore end*/(oldStr, newStr, options); - diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier - - function contextLines(lines) { - return lines.map(function (entry) { - return ' ' + entry; - }); - } - - var hunks = []; - var oldRangeStart = 0, - newRangeStart = 0, - curRange = [], - oldLine = 1, - newLine = 1; - - /*istanbul ignore start*/var _loop = function _loop( /*istanbul ignore end*/i) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - /*istanbul ignore start*/var _curRange; - - /*istanbul ignore end*/ // If we have previous context, start with that - if (!oldRangeStart) { - var prev = diff[i - 1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; - } - } - - // Output our changes - /*istanbul ignore start*/(_curRange = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/lines.map(function (entry) { - return (current.added ? '+' : '-') + entry; - }))); - - // Track the updated file position - if (current.added) { - newLine += lines.length; - } else { - oldLine += lines.length; - } - } else { - // Identical context lines. Track line changes - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= options.context * 2 && i < diff.length - 2) { - /*istanbul ignore start*/var _curRange2; - - /*istanbul ignore end*/ // Overlapping - /*istanbul ignore start*/(_curRange2 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines))); - } else { - /*istanbul ignore start*/var _curRange3; - - /*istanbul ignore end*/ // end the range and output - var contextSize = Math.min(lines.length, options.context); - /*istanbul ignore start*/(_curRange3 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines.slice(0, contextSize)))); - - var hunk = { - oldStart: oldRangeStart, - oldLines: oldLine - oldRangeStart + contextSize, - newStart: newRangeStart, - newLines: newLine - newRangeStart + contextSize, - lines: curRange - }; - if (i >= diff.length - 2 && lines.length <= options.context) { - // EOF is inside this hunk - var oldEOFNewline = /\n$/.test(oldStr); - var newEOFNewline = /\n$/.test(newStr); - if (lines.length == 0 && !oldEOFNewline) { - // special case: old has no eol and no trailing context; no-nl can end up before adds - curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); - } else if (!oldEOFNewline || !newEOFNewline) { - curRange.push('\\ No newline at end of file'); - } - } - hunks.push(hunk); - - oldRangeStart = 0; - newRangeStart = 0; - curRange = []; - } - } - oldLine += lines.length; - newLine += lines.length; - } - }; - - for (var i = 0; i < diff.length; i++) { - /*istanbul ignore start*/_loop( /*istanbul ignore end*/i); - } - - return { - oldFileName: oldFileName, newFileName: newFileName, - oldHeader: oldHeader, newHeader: newHeader, - hunks: hunks - }; - } - - function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); - - var ret = []; - if (oldFileName == newFileName) { - ret.push('Index: ' + oldFileName); - } - ret.push('==================================================================='); - ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); - ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); - - for (var i = 0; i < diff.hunks.length; i++) { - var hunk = diff.hunks[i]; - ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); - ret.push.apply(ret, hunk.lines); - } - - return ret.join('\n') + '\n'; - } - - function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { - return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); - } - - - -/***/ }), -/* 15 */ -/***/ (function(module, exports) { - - /*istanbul ignore start*/"use strict"; - - exports.__esModule = true; - exports. /*istanbul ignore end*/arrayEqual = arrayEqual; - /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayStartsWith = arrayStartsWith; - function arrayEqual(a, b) { - if (a.length !== b.length) { - return false; - } - - return arrayStartsWith(a, b); - } - - function arrayStartsWith(array, start) { - if (start.length > array.length) { - return false; - } - - for (var i = 0; i < start.length; i++) { - if (start[i] !== array[i]) { - return false; - } - } - - return true; - } - - - -/***/ }), -/* 16 */ -/***/ (function(module, exports) { - - /*istanbul ignore start*/"use strict"; - - exports.__esModule = true; - exports. /*istanbul ignore end*/convertChangesToDMP = convertChangesToDMP; - // See: http://code.google.com/p/google-diff-match-patch/wiki/API - function convertChangesToDMP(changes) { - var ret = [], - change = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, - operation = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; - for (var i = 0; i < changes.length; i++) { - change = changes[i]; - if (change.added) { - operation = 1; - } else if (change.removed) { - operation = -1; - } else { - operation = 0; - } - - ret.push([operation, change.value]); - } - return ret; - } - - - -/***/ }), -/* 17 */ -/***/ (function(module, exports) { - - /*istanbul ignore start*/'use strict'; - - exports.__esModule = true; - exports. /*istanbul ignore end*/convertChangesToXML = convertChangesToXML; - function convertChangesToXML(changes) { - var ret = []; - for (var i = 0; i < changes.length; i++) { - var change = changes[i]; - if (change.added) { - ret.push('<ins>'); - } else if (change.removed) { - ret.push('<del>'); - } - - ret.push(escapeHTML(change.value)); - - if (change.added) { - ret.push('</ins>'); - } else if (change.removed) { - ret.push('</del>'); - } - } - return ret.join(''); - } - - function escapeHTML(s) { - var n = s; - n = n.replace(/&/g, '&'); - n = n.replace(/</g, '<'); - n = n.replace(/>/g, '>'); - n = n.replace(/"/g, '"'); - - return n; - } - - - -/***/ }) -/******/ ]) -}); -; -},{}],49:[function(require,module,exports){ -'use strict'; - -var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; - -module.exports = function (str) { - if (typeof str !== 'string') { - throw new TypeError('Expected a string'); - } - - return str.replace(matchOperatorsRe, '\\$&'); -}; - -},{}],50:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var objectCreate = Object.create || objectCreatePolyfill -var objectKeys = Object.keys || objectKeysPolyfill -var bind = Function.prototype.bind || functionBindPolyfill - -function EventEmitter() { - if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) { - this._events = objectCreate(null); - this._eventsCount = 0; - } - - this._maxListeners = this._maxListeners || undefined; -} -module.exports = EventEmitter; - -// Backwards-compat with node 0.10.x -EventEmitter.EventEmitter = EventEmitter; - -EventEmitter.prototype._events = undefined; -EventEmitter.prototype._maxListeners = undefined; - -// By default EventEmitters will print a warning if more than 10 listeners are -// added to it. This is a useful default which helps finding memory leaks. -var defaultMaxListeners = 10; - -var hasDefineProperty; -try { - var o = {}; - if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 }); - hasDefineProperty = o.x === 0; -} catch (err) { hasDefineProperty = false } -if (hasDefineProperty) { - Object.defineProperty(EventEmitter, 'defaultMaxListeners', { - enumerable: true, - get: function() { - return defaultMaxListeners; - }, - set: function(arg) { - // check whether the input is a positive number (whose value is zero or - // greater and not a NaN). - if (typeof arg !== 'number' || arg < 0 || arg !== arg) - throw new TypeError('"defaultMaxListeners" must be a positive number'); - defaultMaxListeners = arg; - } - }); -} else { - EventEmitter.defaultMaxListeners = defaultMaxListeners; -} - -// Obviously not all Emitters should be limited to 10. This function allows -// that to be increased. Set to zero for unlimited. -EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { - if (typeof n !== 'number' || n < 0 || isNaN(n)) - throw new TypeError('"n" argument must be a positive number'); - this._maxListeners = n; - return this; -}; - -function $getMaxListeners(that) { - if (that._maxListeners === undefined) - return EventEmitter.defaultMaxListeners; - return that._maxListeners; -} - -EventEmitter.prototype.getMaxListeners = function getMaxListeners() { - return $getMaxListeners(this); -}; - -// These standalone emit* functions are used to optimize calling of event -// handlers for fast cases because emit() itself often has a variable number of -// arguments and can be deoptimized because of that. These functions always have -// the same number of arguments and thus do not get deoptimized, so the code -// inside them can execute faster. -function emitNone(handler, isFn, self) { - if (isFn) - handler.call(self); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].call(self); - } -} -function emitOne(handler, isFn, self, arg1) { - if (isFn) - handler.call(self, arg1); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].call(self, arg1); - } -} -function emitTwo(handler, isFn, self, arg1, arg2) { - if (isFn) - handler.call(self, arg1, arg2); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].call(self, arg1, arg2); - } -} -function emitThree(handler, isFn, self, arg1, arg2, arg3) { - if (isFn) - handler.call(self, arg1, arg2, arg3); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].call(self, arg1, arg2, arg3); - } -} - -function emitMany(handler, isFn, self, args) { - if (isFn) - handler.apply(self, args); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].apply(self, args); - } -} - -EventEmitter.prototype.emit = function emit(type) { - var er, handler, len, args, i, events; - var doError = (type === 'error'); - - events = this._events; - if (events) - doError = (doError && events.error == null); - else if (!doError) - return false; - - // If there is no 'error' event listener then throw. - if (doError) { - if (arguments.length > 1) - er = arguments[1]; - if (er instanceof Error) { - throw er; // Unhandled 'error' event - } else { - // At least give some kind of context to the user - var err = new Error('Unhandled "error" event. (' + er + ')'); - err.context = er; - throw err; - } - return false; - } - - handler = events[type]; - - if (!handler) - return false; - - var isFn = typeof handler === 'function'; - len = arguments.length; - switch (len) { - // fast cases - case 1: - emitNone(handler, isFn, this); - break; - case 2: - emitOne(handler, isFn, this, arguments[1]); - break; - case 3: - emitTwo(handler, isFn, this, arguments[1], arguments[2]); - break; - case 4: - emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); - break; - // slower - default: - args = new Array(len - 1); - for (i = 1; i < len; i++) - args[i - 1] = arguments[i]; - emitMany(handler, isFn, this, args); - } - - return true; -}; - -function _addListener(target, type, listener, prepend) { - var m; - var events; - var existing; - - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); - - events = target._events; - if (!events) { - events = target._events = objectCreate(null); - target._eventsCount = 0; - } else { - // To avoid recursion in the case that type === "newListener"! Before - // adding it to the listeners, first emit "newListener". - if (events.newListener) { - target.emit('newListener', type, - listener.listener ? listener.listener : listener); - - // Re-assign `events` because a newListener handler could have caused the - // this._events to be assigned to a new object - events = target._events; - } - existing = events[type]; - } - - if (!existing) { - // Optimize the case of one listener. Don't need the extra array object. - existing = events[type] = listener; - ++target._eventsCount; - } else { - if (typeof existing === 'function') { - // Adding the second element, need to change to array. - existing = events[type] = - prepend ? [listener, existing] : [existing, listener]; - } else { - // If we've already got an array, just append. - if (prepend) { - existing.unshift(listener); - } else { - existing.push(listener); - } - } - - // Check for listener leak - if (!existing.warned) { - m = $getMaxListeners(target); - if (m && m > 0 && existing.length > m) { - existing.warned = true; - var w = new Error('Possible EventEmitter memory leak detected. ' + - existing.length + ' "' + String(type) + '" listeners ' + - 'added. Use emitter.setMaxListeners() to ' + - 'increase limit.'); - w.name = 'MaxListenersExceededWarning'; - w.emitter = target; - w.type = type; - w.count = existing.length; - if (typeof console === 'object' && console.warn) { - console.warn('%s: %s', w.name, w.message); - } - } - } - } - - return target; -} - -EventEmitter.prototype.addListener = function addListener(type, listener) { - return _addListener(this, type, listener, false); -}; - -EventEmitter.prototype.on = EventEmitter.prototype.addListener; - -EventEmitter.prototype.prependListener = - function prependListener(type, listener) { - return _addListener(this, type, listener, true); - }; - -function onceWrapper() { - if (!this.fired) { - this.target.removeListener(this.type, this.wrapFn); - this.fired = true; - switch (arguments.length) { - case 0: - return this.listener.call(this.target); - case 1: - return this.listener.call(this.target, arguments[0]); - case 2: - return this.listener.call(this.target, arguments[0], arguments[1]); - case 3: - return this.listener.call(this.target, arguments[0], arguments[1], - arguments[2]); - default: - var args = new Array(arguments.length); - for (var i = 0; i < args.length; ++i) - args[i] = arguments[i]; - this.listener.apply(this.target, args); - } - } -} - -function _onceWrap(target, type, listener) { - var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; - var wrapped = bind.call(onceWrapper, state); - wrapped.listener = listener; - state.wrapFn = wrapped; - return wrapped; -} - -EventEmitter.prototype.once = function once(type, listener) { - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); - this.on(type, _onceWrap(this, type, listener)); - return this; -}; - -EventEmitter.prototype.prependOnceListener = - function prependOnceListener(type, listener) { - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); - this.prependListener(type, _onceWrap(this, type, listener)); - return this; - }; - -// Emits a 'removeListener' event if and only if the listener was removed. -EventEmitter.prototype.removeListener = - function removeListener(type, listener) { - var list, events, position, i, originalListener; - - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); - - events = this._events; - if (!events) - return this; - - list = events[type]; - if (!list) - return this; - - if (list === listener || list.listener === listener) { - if (--this._eventsCount === 0) - this._events = objectCreate(null); - else { - delete events[type]; - if (events.removeListener) - this.emit('removeListener', type, list.listener || listener); - } - } else if (typeof list !== 'function') { - position = -1; - - for (i = list.length - 1; i >= 0; i--) { - if (list[i] === listener || list[i].listener === listener) { - originalListener = list[i].listener; - position = i; - break; - } - } - - if (position < 0) - return this; - - if (position === 0) - list.shift(); - else - spliceOne(list, position); - - if (list.length === 1) - events[type] = list[0]; - - if (events.removeListener) - this.emit('removeListener', type, originalListener || listener); - } - - return this; - }; - -EventEmitter.prototype.removeAllListeners = - function removeAllListeners(type) { - var listeners, events, i; - - events = this._events; - if (!events) - return this; - - // not listening for removeListener, no need to emit - if (!events.removeListener) { - if (arguments.length === 0) { - this._events = objectCreate(null); - this._eventsCount = 0; - } else if (events[type]) { - if (--this._eventsCount === 0) - this._events = objectCreate(null); - else - delete events[type]; - } - return this; - } - - // emit removeListener for all listeners on all events - if (arguments.length === 0) { - var keys = objectKeys(events); - var key; - for (i = 0; i < keys.length; ++i) { - key = keys[i]; - if (key === 'removeListener') continue; - this.removeAllListeners(key); - } - this.removeAllListeners('removeListener'); - this._events = objectCreate(null); - this._eventsCount = 0; - return this; - } - - listeners = events[type]; - - if (typeof listeners === 'function') { - this.removeListener(type, listeners); - } else if (listeners) { - // LIFO order - for (i = listeners.length - 1; i >= 0; i--) { - this.removeListener(type, listeners[i]); - } - } - - return this; - }; - -function _listeners(target, type, unwrap) { - var events = target._events; - - if (!events) - return []; - - var evlistener = events[type]; - if (!evlistener) - return []; - - if (typeof evlistener === 'function') - return unwrap ? [evlistener.listener || evlistener] : [evlistener]; - - return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); -} - -EventEmitter.prototype.listeners = function listeners(type) { - return _listeners(this, type, true); -}; - -EventEmitter.prototype.rawListeners = function rawListeners(type) { - return _listeners(this, type, false); -}; - -EventEmitter.listenerCount = function(emitter, type) { - if (typeof emitter.listenerCount === 'function') { - return emitter.listenerCount(type); - } else { - return listenerCount.call(emitter, type); - } -}; - -EventEmitter.prototype.listenerCount = listenerCount; -function listenerCount(type) { - var events = this._events; - - if (events) { - var evlistener = events[type]; - - if (typeof evlistener === 'function') { - return 1; - } else if (evlistener) { - return evlistener.length; - } - } - - return 0; -} - -EventEmitter.prototype.eventNames = function eventNames() { - return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; -}; - -// About 1.5x faster than the two-arg version of Array#splice(). -function spliceOne(list, index) { - for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) - list[i] = list[k]; - list.pop(); -} - -function arrayClone(arr, n) { - var copy = new Array(n); - for (var i = 0; i < n; ++i) - copy[i] = arr[i]; - return copy; -} - -function unwrapListeners(arr) { - var ret = new Array(arr.length); - for (var i = 0; i < ret.length; ++i) { - ret[i] = arr[i].listener || arr[i]; - } - return ret; -} - -function objectCreatePolyfill(proto) { - var F = function() {}; - F.prototype = proto; - return new F; -} -function objectKeysPolyfill(obj) { - var keys = []; - for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) { - keys.push(k); - } - return k; -} -function functionBindPolyfill(context) { - var fn = this; - return function () { - return fn.apply(context, arguments); - }; -} - -},{}],51:[function(require,module,exports){ -'use strict'; - -/* eslint no-invalid-this: 1 */ - -var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; -var slice = Array.prototype.slice; -var toStr = Object.prototype.toString; -var funcType = '[object Function]'; - -module.exports = function bind(that) { - var target = this; - if (typeof target !== 'function' || toStr.call(target) !== funcType) { - throw new TypeError(ERROR_MESSAGE + target); - } - var args = slice.call(arguments, 1); - - var bound; - var binder = function () { - if (this instanceof bound) { - var result = target.apply( - this, - args.concat(slice.call(arguments)) - ); - if (Object(result) === result) { - return result; - } - return this; - } else { - return target.apply( - that, - args.concat(slice.call(arguments)) - ); - } - }; - - var boundLength = Math.max(0, target.length - args.length); - var boundArgs = []; - for (var i = 0; i < boundLength; i++) { - boundArgs.push('$' + i); - } - - bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); - - if (target.prototype) { - var Empty = function Empty() {}; - Empty.prototype = target.prototype; - bound.prototype = new Empty(); - Empty.prototype = null; - } - - return bound; -}; - -},{}],52:[function(require,module,exports){ -'use strict'; - -var implementation = require('./implementation'); - -module.exports = Function.prototype.bind || implementation; - -},{"./implementation":51}],53:[function(require,module,exports){ -'use strict'; - -/* eslint complexity: [2, 17], max-statements: [2, 33] */ -module.exports = function hasSymbols() { - if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } - if (typeof Symbol.iterator === 'symbol') { return true; } - - var obj = {}; - var sym = Symbol('test'); - var symObj = Object(sym); - if (typeof sym === 'string') { return false; } - - if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } - if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } - - // temp disabled per https://github.com/ljharb/object.assign/issues/17 - // if (sym instanceof Symbol) { return false; } - // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 - // if (!(symObj instanceof Symbol)) { return false; } - - // if (typeof Symbol.prototype.toString !== 'function') { return false; } - // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; } - - var symVal = 42; - obj[sym] = symVal; - for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax - if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } - - if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } - - var syms = Object.getOwnPropertySymbols(obj); - if (syms.length !== 1 || syms[0] !== sym) { return false; } - - if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } - - if (typeof Object.getOwnPropertyDescriptor === 'function') { - var descriptor = Object.getOwnPropertyDescriptor(obj, sym); - if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } - } - - return true; -}; - -},{}],54:[function(require,module,exports){ -(function (global){ -/*! https://mths.be/he v1.2.0 by @mathias | MIT license */ -;(function(root) { - - // Detect free variables `exports`. - var freeExports = typeof exports == 'object' && exports; - - // Detect free variable `module`. - var freeModule = typeof module == 'object' && module && - module.exports == freeExports && module; - - // Detect free variable `global`, from Node.js or Browserified code, - // and use it as `root`. - var freeGlobal = typeof global == 'object' && global; - if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { - root = freeGlobal; - } - - /*--------------------------------------------------------------------------*/ - - // All astral symbols. - var regexAstralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; - // All ASCII symbols (not just printable ASCII) except those listed in the - // first column of the overrides table. - // https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides - var regexAsciiWhitelist = /[\x01-\x7F]/g; - // All BMP symbols that are not ASCII newlines, printable ASCII symbols, or - // code points listed in the first column of the overrides table on - // https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides. - var regexBmpWhitelist = /[\x01-\t\x0B\f\x0E-\x1F\x7F\x81\x8D\x8F\x90\x9D\xA0-\uFFFF]/g; - - var regexEncodeNonAscii = /<\u20D2|=\u20E5|>\u20D2|\u205F\u200A|\u219D\u0338|\u2202\u0338|\u2220\u20D2|\u2229\uFE00|\u222A\uFE00|\u223C\u20D2|\u223D\u0331|\u223E\u0333|\u2242\u0338|\u224B\u0338|\u224D\u20D2|\u224E\u0338|\u224F\u0338|\u2250\u0338|\u2261\u20E5|\u2264\u20D2|\u2265\u20D2|\u2266\u0338|\u2267\u0338|\u2268\uFE00|\u2269\uFE00|\u226A\u0338|\u226A\u20D2|\u226B\u0338|\u226B\u20D2|\u227F\u0338|\u2282\u20D2|\u2283\u20D2|\u228A\uFE00|\u228B\uFE00|\u228F\u0338|\u2290\u0338|\u2293\uFE00|\u2294\uFE00|\u22B4\u20D2|\u22B5\u20D2|\u22D8\u0338|\u22D9\u0338|\u22DA\uFE00|\u22DB\uFE00|\u22F5\u0338|\u22F9\u0338|\u2933\u0338|\u29CF\u0338|\u29D0\u0338|\u2A6D\u0338|\u2A70\u0338|\u2A7D\u0338|\u2A7E\u0338|\u2AA1\u0338|\u2AA2\u0338|\u2AAC\uFE00|\u2AAD\uFE00|\u2AAF\u0338|\u2AB0\u0338|\u2AC5\u0338|\u2AC6\u0338|\u2ACB\uFE00|\u2ACC\uFE00|\u2AFD\u20E5|[\xA0-\u0113\u0116-\u0122\u0124-\u012B\u012E-\u014D\u0150-\u017E\u0192\u01B5\u01F5\u0237\u02C6\u02C7\u02D8-\u02DD\u0311\u0391-\u03A1\u03A3-\u03A9\u03B1-\u03C9\u03D1\u03D2\u03D5\u03D6\u03DC\u03DD\u03F0\u03F1\u03F5\u03F6\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E\u045F\u2002-\u2005\u2007-\u2010\u2013-\u2016\u2018-\u201A\u201C-\u201E\u2020-\u2022\u2025\u2026\u2030-\u2035\u2039\u203A\u203E\u2041\u2043\u2044\u204F\u2057\u205F-\u2063\u20AC\u20DB\u20DC\u2102\u2105\u210A-\u2113\u2115-\u211E\u2122\u2124\u2127-\u2129\u212C\u212D\u212F-\u2131\u2133-\u2138\u2145-\u2148\u2153-\u215E\u2190-\u219B\u219D-\u21A7\u21A9-\u21AE\u21B0-\u21B3\u21B5-\u21B7\u21BA-\u21DB\u21DD\u21E4\u21E5\u21F5\u21FD-\u2205\u2207-\u2209\u220B\u220C\u220F-\u2214\u2216-\u2218\u221A\u221D-\u2238\u223A-\u2257\u2259\u225A\u225C\u225F-\u2262\u2264-\u228B\u228D-\u229B\u229D-\u22A5\u22A7-\u22B0\u22B2-\u22BB\u22BD-\u22DB\u22DE-\u22E3\u22E6-\u22F7\u22F9-\u22FE\u2305\u2306\u2308-\u2310\u2312\u2313\u2315\u2316\u231C-\u231F\u2322\u2323\u232D\u232E\u2336\u233D\u233F\u237C\u23B0\u23B1\u23B4-\u23B6\u23DC-\u23DF\u23E2\u23E7\u2423\u24C8\u2500\u2502\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C\u2550-\u256C\u2580\u2584\u2588\u2591-\u2593\u25A1\u25AA\u25AB\u25AD\u25AE\u25B1\u25B3-\u25B5\u25B8\u25B9\u25BD-\u25BF\u25C2\u25C3\u25CA\u25CB\u25EC\u25EF\u25F8-\u25FC\u2605\u2606\u260E\u2640\u2642\u2660\u2663\u2665\u2666\u266A\u266D-\u266F\u2713\u2717\u2720\u2736\u2758\u2772\u2773\u27C8\u27C9\u27E6-\u27ED\u27F5-\u27FA\u27FC\u27FF\u2902-\u2905\u290C-\u2913\u2916\u2919-\u2920\u2923-\u292A\u2933\u2935-\u2939\u293C\u293D\u2945\u2948-\u294B\u294E-\u2976\u2978\u2979\u297B-\u297F\u2985\u2986\u298B-\u2996\u299A\u299C\u299D\u29A4-\u29B7\u29B9\u29BB\u29BC\u29BE-\u29C5\u29C9\u29CD-\u29D0\u29DC-\u29DE\u29E3-\u29E5\u29EB\u29F4\u29F6\u2A00-\u2A02\u2A04\u2A06\u2A0C\u2A0D\u2A10-\u2A17\u2A22-\u2A27\u2A29\u2A2A\u2A2D-\u2A31\u2A33-\u2A3C\u2A3F\u2A40\u2A42-\u2A4D\u2A50\u2A53-\u2A58\u2A5A-\u2A5D\u2A5F\u2A66\u2A6A\u2A6D-\u2A75\u2A77-\u2A9A\u2A9D-\u2AA2\u2AA4-\u2AB0\u2AB3-\u2AC8\u2ACB\u2ACC\u2ACF-\u2ADB\u2AE4\u2AE6-\u2AE9\u2AEB-\u2AF3\u2AFD\uFB00-\uFB04]|\uD835[\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDCCF\uDD04\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDD6B]/g; - var encodeMap = {'\xAD':'shy','\u200C':'zwnj','\u200D':'zwj','\u200E':'lrm','\u2063':'ic','\u2062':'it','\u2061':'af','\u200F':'rlm','\u200B':'ZeroWidthSpace','\u2060':'NoBreak','\u0311':'DownBreve','\u20DB':'tdot','\u20DC':'DotDot','\t':'Tab','\n':'NewLine','\u2008':'puncsp','\u205F':'MediumSpace','\u2009':'thinsp','\u200A':'hairsp','\u2004':'emsp13','\u2002':'ensp','\u2005':'emsp14','\u2003':'emsp','\u2007':'numsp','\xA0':'nbsp','\u205F\u200A':'ThickSpace','\u203E':'oline','_':'lowbar','\u2010':'dash','\u2013':'ndash','\u2014':'mdash','\u2015':'horbar',',':'comma',';':'semi','\u204F':'bsemi',':':'colon','\u2A74':'Colone','!':'excl','\xA1':'iexcl','?':'quest','\xBF':'iquest','.':'period','\u2025':'nldr','\u2026':'mldr','\xB7':'middot','\'':'apos','\u2018':'lsquo','\u2019':'rsquo','\u201A':'sbquo','\u2039':'lsaquo','\u203A':'rsaquo','"':'quot','\u201C':'ldquo','\u201D':'rdquo','\u201E':'bdquo','\xAB':'laquo','\xBB':'raquo','(':'lpar',')':'rpar','[':'lsqb',']':'rsqb','{':'lcub','}':'rcub','\u2308':'lceil','\u2309':'rceil','\u230A':'lfloor','\u230B':'rfloor','\u2985':'lopar','\u2986':'ropar','\u298B':'lbrke','\u298C':'rbrke','\u298D':'lbrkslu','\u298E':'rbrksld','\u298F':'lbrksld','\u2990':'rbrkslu','\u2991':'langd','\u2992':'rangd','\u2993':'lparlt','\u2994':'rpargt','\u2995':'gtlPar','\u2996':'ltrPar','\u27E6':'lobrk','\u27E7':'robrk','\u27E8':'lang','\u27E9':'rang','\u27EA':'Lang','\u27EB':'Rang','\u27EC':'loang','\u27ED':'roang','\u2772':'lbbrk','\u2773':'rbbrk','\u2016':'Vert','\xA7':'sect','\xB6':'para','@':'commat','*':'ast','/':'sol','undefined':null,'&':'amp','#':'num','%':'percnt','\u2030':'permil','\u2031':'pertenk','\u2020':'dagger','\u2021':'Dagger','\u2022':'bull','\u2043':'hybull','\u2032':'prime','\u2033':'Prime','\u2034':'tprime','\u2057':'qprime','\u2035':'bprime','\u2041':'caret','`':'grave','\xB4':'acute','\u02DC':'tilde','^':'Hat','\xAF':'macr','\u02D8':'breve','\u02D9':'dot','\xA8':'die','\u02DA':'ring','\u02DD':'dblac','\xB8':'cedil','\u02DB':'ogon','\u02C6':'circ','\u02C7':'caron','\xB0':'deg','\xA9':'copy','\xAE':'reg','\u2117':'copysr','\u2118':'wp','\u211E':'rx','\u2127':'mho','\u2129':'iiota','\u2190':'larr','\u219A':'nlarr','\u2192':'rarr','\u219B':'nrarr','\u2191':'uarr','\u2193':'darr','\u2194':'harr','\u21AE':'nharr','\u2195':'varr','\u2196':'nwarr','\u2197':'nearr','\u2198':'searr','\u2199':'swarr','\u219D':'rarrw','\u219D\u0338':'nrarrw','\u219E':'Larr','\u219F':'Uarr','\u21A0':'Rarr','\u21A1':'Darr','\u21A2':'larrtl','\u21A3':'rarrtl','\u21A4':'mapstoleft','\u21A5':'mapstoup','\u21A6':'map','\u21A7':'mapstodown','\u21A9':'larrhk','\u21AA':'rarrhk','\u21AB':'larrlp','\u21AC':'rarrlp','\u21AD':'harrw','\u21B0':'lsh','\u21B1':'rsh','\u21B2':'ldsh','\u21B3':'rdsh','\u21B5':'crarr','\u21B6':'cularr','\u21B7':'curarr','\u21BA':'olarr','\u21BB':'orarr','\u21BC':'lharu','\u21BD':'lhard','\u21BE':'uharr','\u21BF':'uharl','\u21C0':'rharu','\u21C1':'rhard','\u21C2':'dharr','\u21C3':'dharl','\u21C4':'rlarr','\u21C5':'udarr','\u21C6':'lrarr','\u21C7':'llarr','\u21C8':'uuarr','\u21C9':'rrarr','\u21CA':'ddarr','\u21CB':'lrhar','\u21CC':'rlhar','\u21D0':'lArr','\u21CD':'nlArr','\u21D1':'uArr','\u21D2':'rArr','\u21CF':'nrArr','\u21D3':'dArr','\u21D4':'iff','\u21CE':'nhArr','\u21D5':'vArr','\u21D6':'nwArr','\u21D7':'neArr','\u21D8':'seArr','\u21D9':'swArr','\u21DA':'lAarr','\u21DB':'rAarr','\u21DD':'zigrarr','\u21E4':'larrb','\u21E5':'rarrb','\u21F5':'duarr','\u21FD':'loarr','\u21FE':'roarr','\u21FF':'hoarr','\u2200':'forall','\u2201':'comp','\u2202':'part','\u2202\u0338':'npart','\u2203':'exist','\u2204':'nexist','\u2205':'empty','\u2207':'Del','\u2208':'in','\u2209':'notin','\u220B':'ni','\u220C':'notni','\u03F6':'bepsi','\u220F':'prod','\u2210':'coprod','\u2211':'sum','+':'plus','\xB1':'pm','\xF7':'div','\xD7':'times','<':'lt','\u226E':'nlt','<\u20D2':'nvlt','=':'equals','\u2260':'ne','=\u20E5':'bne','\u2A75':'Equal','>':'gt','\u226F':'ngt','>\u20D2':'nvgt','\xAC':'not','|':'vert','\xA6':'brvbar','\u2212':'minus','\u2213':'mp','\u2214':'plusdo','\u2044':'frasl','\u2216':'setmn','\u2217':'lowast','\u2218':'compfn','\u221A':'Sqrt','\u221D':'prop','\u221E':'infin','\u221F':'angrt','\u2220':'ang','\u2220\u20D2':'nang','\u2221':'angmsd','\u2222':'angsph','\u2223':'mid','\u2224':'nmid','\u2225':'par','\u2226':'npar','\u2227':'and','\u2228':'or','\u2229':'cap','\u2229\uFE00':'caps','\u222A':'cup','\u222A\uFE00':'cups','\u222B':'int','\u222C':'Int','\u222D':'tint','\u2A0C':'qint','\u222E':'oint','\u222F':'Conint','\u2230':'Cconint','\u2231':'cwint','\u2232':'cwconint','\u2233':'awconint','\u2234':'there4','\u2235':'becaus','\u2236':'ratio','\u2237':'Colon','\u2238':'minusd','\u223A':'mDDot','\u223B':'homtht','\u223C':'sim','\u2241':'nsim','\u223C\u20D2':'nvsim','\u223D':'bsim','\u223D\u0331':'race','\u223E':'ac','\u223E\u0333':'acE','\u223F':'acd','\u2240':'wr','\u2242':'esim','\u2242\u0338':'nesim','\u2243':'sime','\u2244':'nsime','\u2245':'cong','\u2247':'ncong','\u2246':'simne','\u2248':'ap','\u2249':'nap','\u224A':'ape','\u224B':'apid','\u224B\u0338':'napid','\u224C':'bcong','\u224D':'CupCap','\u226D':'NotCupCap','\u224D\u20D2':'nvap','\u224E':'bump','\u224E\u0338':'nbump','\u224F':'bumpe','\u224F\u0338':'nbumpe','\u2250':'doteq','\u2250\u0338':'nedot','\u2251':'eDot','\u2252':'efDot','\u2253':'erDot','\u2254':'colone','\u2255':'ecolon','\u2256':'ecir','\u2257':'cire','\u2259':'wedgeq','\u225A':'veeeq','\u225C':'trie','\u225F':'equest','\u2261':'equiv','\u2262':'nequiv','\u2261\u20E5':'bnequiv','\u2264':'le','\u2270':'nle','\u2264\u20D2':'nvle','\u2265':'ge','\u2271':'nge','\u2265\u20D2':'nvge','\u2266':'lE','\u2266\u0338':'nlE','\u2267':'gE','\u2267\u0338':'ngE','\u2268\uFE00':'lvnE','\u2268':'lnE','\u2269':'gnE','\u2269\uFE00':'gvnE','\u226A':'ll','\u226A\u0338':'nLtv','\u226A\u20D2':'nLt','\u226B':'gg','\u226B\u0338':'nGtv','\u226B\u20D2':'nGt','\u226C':'twixt','\u2272':'lsim','\u2274':'nlsim','\u2273':'gsim','\u2275':'ngsim','\u2276':'lg','\u2278':'ntlg','\u2277':'gl','\u2279':'ntgl','\u227A':'pr','\u2280':'npr','\u227B':'sc','\u2281':'nsc','\u227C':'prcue','\u22E0':'nprcue','\u227D':'sccue','\u22E1':'nsccue','\u227E':'prsim','\u227F':'scsim','\u227F\u0338':'NotSucceedsTilde','\u2282':'sub','\u2284':'nsub','\u2282\u20D2':'vnsub','\u2283':'sup','\u2285':'nsup','\u2283\u20D2':'vnsup','\u2286':'sube','\u2288':'nsube','\u2287':'supe','\u2289':'nsupe','\u228A\uFE00':'vsubne','\u228A':'subne','\u228B\uFE00':'vsupne','\u228B':'supne','\u228D':'cupdot','\u228E':'uplus','\u228F':'sqsub','\u228F\u0338':'NotSquareSubset','\u2290':'sqsup','\u2290\u0338':'NotSquareSuperset','\u2291':'sqsube','\u22E2':'nsqsube','\u2292':'sqsupe','\u22E3':'nsqsupe','\u2293':'sqcap','\u2293\uFE00':'sqcaps','\u2294':'sqcup','\u2294\uFE00':'sqcups','\u2295':'oplus','\u2296':'ominus','\u2297':'otimes','\u2298':'osol','\u2299':'odot','\u229A':'ocir','\u229B':'oast','\u229D':'odash','\u229E':'plusb','\u229F':'minusb','\u22A0':'timesb','\u22A1':'sdotb','\u22A2':'vdash','\u22AC':'nvdash','\u22A3':'dashv','\u22A4':'top','\u22A5':'bot','\u22A7':'models','\u22A8':'vDash','\u22AD':'nvDash','\u22A9':'Vdash','\u22AE':'nVdash','\u22AA':'Vvdash','\u22AB':'VDash','\u22AF':'nVDash','\u22B0':'prurel','\u22B2':'vltri','\u22EA':'nltri','\u22B3':'vrtri','\u22EB':'nrtri','\u22B4':'ltrie','\u22EC':'nltrie','\u22B4\u20D2':'nvltrie','\u22B5':'rtrie','\u22ED':'nrtrie','\u22B5\u20D2':'nvrtrie','\u22B6':'origof','\u22B7':'imof','\u22B8':'mumap','\u22B9':'hercon','\u22BA':'intcal','\u22BB':'veebar','\u22BD':'barvee','\u22BE':'angrtvb','\u22BF':'lrtri','\u22C0':'Wedge','\u22C1':'Vee','\u22C2':'xcap','\u22C3':'xcup','\u22C4':'diam','\u22C5':'sdot','\u22C6':'Star','\u22C7':'divonx','\u22C8':'bowtie','\u22C9':'ltimes','\u22CA':'rtimes','\u22CB':'lthree','\u22CC':'rthree','\u22CD':'bsime','\u22CE':'cuvee','\u22CF':'cuwed','\u22D0':'Sub','\u22D1':'Sup','\u22D2':'Cap','\u22D3':'Cup','\u22D4':'fork','\u22D5':'epar','\u22D6':'ltdot','\u22D7':'gtdot','\u22D8':'Ll','\u22D8\u0338':'nLl','\u22D9':'Gg','\u22D9\u0338':'nGg','\u22DA\uFE00':'lesg','\u22DA':'leg','\u22DB':'gel','\u22DB\uFE00':'gesl','\u22DE':'cuepr','\u22DF':'cuesc','\u22E6':'lnsim','\u22E7':'gnsim','\u22E8':'prnsim','\u22E9':'scnsim','\u22EE':'vellip','\u22EF':'ctdot','\u22F0':'utdot','\u22F1':'dtdot','\u22F2':'disin','\u22F3':'isinsv','\u22F4':'isins','\u22F5':'isindot','\u22F5\u0338':'notindot','\u22F6':'notinvc','\u22F7':'notinvb','\u22F9':'isinE','\u22F9\u0338':'notinE','\u22FA':'nisd','\u22FB':'xnis','\u22FC':'nis','\u22FD':'notnivc','\u22FE':'notnivb','\u2305':'barwed','\u2306':'Barwed','\u230C':'drcrop','\u230D':'dlcrop','\u230E':'urcrop','\u230F':'ulcrop','\u2310':'bnot','\u2312':'profline','\u2313':'profsurf','\u2315':'telrec','\u2316':'target','\u231C':'ulcorn','\u231D':'urcorn','\u231E':'dlcorn','\u231F':'drcorn','\u2322':'frown','\u2323':'smile','\u232D':'cylcty','\u232E':'profalar','\u2336':'topbot','\u233D':'ovbar','\u233F':'solbar','\u237C':'angzarr','\u23B0':'lmoust','\u23B1':'rmoust','\u23B4':'tbrk','\u23B5':'bbrk','\u23B6':'bbrktbrk','\u23DC':'OverParenthesis','\u23DD':'UnderParenthesis','\u23DE':'OverBrace','\u23DF':'UnderBrace','\u23E2':'trpezium','\u23E7':'elinters','\u2423':'blank','\u2500':'boxh','\u2502':'boxv','\u250C':'boxdr','\u2510':'boxdl','\u2514':'boxur','\u2518':'boxul','\u251C':'boxvr','\u2524':'boxvl','\u252C':'boxhd','\u2534':'boxhu','\u253C':'boxvh','\u2550':'boxH','\u2551':'boxV','\u2552':'boxdR','\u2553':'boxDr','\u2554':'boxDR','\u2555':'boxdL','\u2556':'boxDl','\u2557':'boxDL','\u2558':'boxuR','\u2559':'boxUr','\u255A':'boxUR','\u255B':'boxuL','\u255C':'boxUl','\u255D':'boxUL','\u255E':'boxvR','\u255F':'boxVr','\u2560':'boxVR','\u2561':'boxvL','\u2562':'boxVl','\u2563':'boxVL','\u2564':'boxHd','\u2565':'boxhD','\u2566':'boxHD','\u2567':'boxHu','\u2568':'boxhU','\u2569':'boxHU','\u256A':'boxvH','\u256B':'boxVh','\u256C':'boxVH','\u2580':'uhblk','\u2584':'lhblk','\u2588':'block','\u2591':'blk14','\u2592':'blk12','\u2593':'blk34','\u25A1':'squ','\u25AA':'squf','\u25AB':'EmptyVerySmallSquare','\u25AD':'rect','\u25AE':'marker','\u25B1':'fltns','\u25B3':'xutri','\u25B4':'utrif','\u25B5':'utri','\u25B8':'rtrif','\u25B9':'rtri','\u25BD':'xdtri','\u25BE':'dtrif','\u25BF':'dtri','\u25C2':'ltrif','\u25C3':'ltri','\u25CA':'loz','\u25CB':'cir','\u25EC':'tridot','\u25EF':'xcirc','\u25F8':'ultri','\u25F9':'urtri','\u25FA':'lltri','\u25FB':'EmptySmallSquare','\u25FC':'FilledSmallSquare','\u2605':'starf','\u2606':'star','\u260E':'phone','\u2640':'female','\u2642':'male','\u2660':'spades','\u2663':'clubs','\u2665':'hearts','\u2666':'diams','\u266A':'sung','\u2713':'check','\u2717':'cross','\u2720':'malt','\u2736':'sext','\u2758':'VerticalSeparator','\u27C8':'bsolhsub','\u27C9':'suphsol','\u27F5':'xlarr','\u27F6':'xrarr','\u27F7':'xharr','\u27F8':'xlArr','\u27F9':'xrArr','\u27FA':'xhArr','\u27FC':'xmap','\u27FF':'dzigrarr','\u2902':'nvlArr','\u2903':'nvrArr','\u2904':'nvHarr','\u2905':'Map','\u290C':'lbarr','\u290D':'rbarr','\u290E':'lBarr','\u290F':'rBarr','\u2910':'RBarr','\u2911':'DDotrahd','\u2912':'UpArrowBar','\u2913':'DownArrowBar','\u2916':'Rarrtl','\u2919':'latail','\u291A':'ratail','\u291B':'lAtail','\u291C':'rAtail','\u291D':'larrfs','\u291E':'rarrfs','\u291F':'larrbfs','\u2920':'rarrbfs','\u2923':'nwarhk','\u2924':'nearhk','\u2925':'searhk','\u2926':'swarhk','\u2927':'nwnear','\u2928':'toea','\u2929':'tosa','\u292A':'swnwar','\u2933':'rarrc','\u2933\u0338':'nrarrc','\u2935':'cudarrr','\u2936':'ldca','\u2937':'rdca','\u2938':'cudarrl','\u2939':'larrpl','\u293C':'curarrm','\u293D':'cularrp','\u2945':'rarrpl','\u2948':'harrcir','\u2949':'Uarrocir','\u294A':'lurdshar','\u294B':'ldrushar','\u294E':'LeftRightVector','\u294F':'RightUpDownVector','\u2950':'DownLeftRightVector','\u2951':'LeftUpDownVector','\u2952':'LeftVectorBar','\u2953':'RightVectorBar','\u2954':'RightUpVectorBar','\u2955':'RightDownVectorBar','\u2956':'DownLeftVectorBar','\u2957':'DownRightVectorBar','\u2958':'LeftUpVectorBar','\u2959':'LeftDownVectorBar','\u295A':'LeftTeeVector','\u295B':'RightTeeVector','\u295C':'RightUpTeeVector','\u295D':'RightDownTeeVector','\u295E':'DownLeftTeeVector','\u295F':'DownRightTeeVector','\u2960':'LeftUpTeeVector','\u2961':'LeftDownTeeVector','\u2962':'lHar','\u2963':'uHar','\u2964':'rHar','\u2965':'dHar','\u2966':'luruhar','\u2967':'ldrdhar','\u2968':'ruluhar','\u2969':'rdldhar','\u296A':'lharul','\u296B':'llhard','\u296C':'rharul','\u296D':'lrhard','\u296E':'udhar','\u296F':'duhar','\u2970':'RoundImplies','\u2971':'erarr','\u2972':'simrarr','\u2973':'larrsim','\u2974':'rarrsim','\u2975':'rarrap','\u2976':'ltlarr','\u2978':'gtrarr','\u2979':'subrarr','\u297B':'suplarr','\u297C':'lfisht','\u297D':'rfisht','\u297E':'ufisht','\u297F':'dfisht','\u299A':'vzigzag','\u299C':'vangrt','\u299D':'angrtvbd','\u29A4':'ange','\u29A5':'range','\u29A6':'dwangle','\u29A7':'uwangle','\u29A8':'angmsdaa','\u29A9':'angmsdab','\u29AA':'angmsdac','\u29AB':'angmsdad','\u29AC':'angmsdae','\u29AD':'angmsdaf','\u29AE':'angmsdag','\u29AF':'angmsdah','\u29B0':'bemptyv','\u29B1':'demptyv','\u29B2':'cemptyv','\u29B3':'raemptyv','\u29B4':'laemptyv','\u29B5':'ohbar','\u29B6':'omid','\u29B7':'opar','\u29B9':'operp','\u29BB':'olcross','\u29BC':'odsold','\u29BE':'olcir','\u29BF':'ofcir','\u29C0':'olt','\u29C1':'ogt','\u29C2':'cirscir','\u29C3':'cirE','\u29C4':'solb','\u29C5':'bsolb','\u29C9':'boxbox','\u29CD':'trisb','\u29CE':'rtriltri','\u29CF':'LeftTriangleBar','\u29CF\u0338':'NotLeftTriangleBar','\u29D0':'RightTriangleBar','\u29D0\u0338':'NotRightTriangleBar','\u29DC':'iinfin','\u29DD':'infintie','\u29DE':'nvinfin','\u29E3':'eparsl','\u29E4':'smeparsl','\u29E5':'eqvparsl','\u29EB':'lozf','\u29F4':'RuleDelayed','\u29F6':'dsol','\u2A00':'xodot','\u2A01':'xoplus','\u2A02':'xotime','\u2A04':'xuplus','\u2A06':'xsqcup','\u2A0D':'fpartint','\u2A10':'cirfnint','\u2A11':'awint','\u2A12':'rppolint','\u2A13':'scpolint','\u2A14':'npolint','\u2A15':'pointint','\u2A16':'quatint','\u2A17':'intlarhk','\u2A22':'pluscir','\u2A23':'plusacir','\u2A24':'simplus','\u2A25':'plusdu','\u2A26':'plussim','\u2A27':'plustwo','\u2A29':'mcomma','\u2A2A':'minusdu','\u2A2D':'loplus','\u2A2E':'roplus','\u2A2F':'Cross','\u2A30':'timesd','\u2A31':'timesbar','\u2A33':'smashp','\u2A34':'lotimes','\u2A35':'rotimes','\u2A36':'otimesas','\u2A37':'Otimes','\u2A38':'odiv','\u2A39':'triplus','\u2A3A':'triminus','\u2A3B':'tritime','\u2A3C':'iprod','\u2A3F':'amalg','\u2A40':'capdot','\u2A42':'ncup','\u2A43':'ncap','\u2A44':'capand','\u2A45':'cupor','\u2A46':'cupcap','\u2A47':'capcup','\u2A48':'cupbrcap','\u2A49':'capbrcup','\u2A4A':'cupcup','\u2A4B':'capcap','\u2A4C':'ccups','\u2A4D':'ccaps','\u2A50':'ccupssm','\u2A53':'And','\u2A54':'Or','\u2A55':'andand','\u2A56':'oror','\u2A57':'orslope','\u2A58':'andslope','\u2A5A':'andv','\u2A5B':'orv','\u2A5C':'andd','\u2A5D':'ord','\u2A5F':'wedbar','\u2A66':'sdote','\u2A6A':'simdot','\u2A6D':'congdot','\u2A6D\u0338':'ncongdot','\u2A6E':'easter','\u2A6F':'apacir','\u2A70':'apE','\u2A70\u0338':'napE','\u2A71':'eplus','\u2A72':'pluse','\u2A73':'Esim','\u2A77':'eDDot','\u2A78':'equivDD','\u2A79':'ltcir','\u2A7A':'gtcir','\u2A7B':'ltquest','\u2A7C':'gtquest','\u2A7D':'les','\u2A7D\u0338':'nles','\u2A7E':'ges','\u2A7E\u0338':'nges','\u2A7F':'lesdot','\u2A80':'gesdot','\u2A81':'lesdoto','\u2A82':'gesdoto','\u2A83':'lesdotor','\u2A84':'gesdotol','\u2A85':'lap','\u2A86':'gap','\u2A87':'lne','\u2A88':'gne','\u2A89':'lnap','\u2A8A':'gnap','\u2A8B':'lEg','\u2A8C':'gEl','\u2A8D':'lsime','\u2A8E':'gsime','\u2A8F':'lsimg','\u2A90':'gsiml','\u2A91':'lgE','\u2A92':'glE','\u2A93':'lesges','\u2A94':'gesles','\u2A95':'els','\u2A96':'egs','\u2A97':'elsdot','\u2A98':'egsdot','\u2A99':'el','\u2A9A':'eg','\u2A9D':'siml','\u2A9E':'simg','\u2A9F':'simlE','\u2AA0':'simgE','\u2AA1':'LessLess','\u2AA1\u0338':'NotNestedLessLess','\u2AA2':'GreaterGreater','\u2AA2\u0338':'NotNestedGreaterGreater','\u2AA4':'glj','\u2AA5':'gla','\u2AA6':'ltcc','\u2AA7':'gtcc','\u2AA8':'lescc','\u2AA9':'gescc','\u2AAA':'smt','\u2AAB':'lat','\u2AAC':'smte','\u2AAC\uFE00':'smtes','\u2AAD':'late','\u2AAD\uFE00':'lates','\u2AAE':'bumpE','\u2AAF':'pre','\u2AAF\u0338':'npre','\u2AB0':'sce','\u2AB0\u0338':'nsce','\u2AB3':'prE','\u2AB4':'scE','\u2AB5':'prnE','\u2AB6':'scnE','\u2AB7':'prap','\u2AB8':'scap','\u2AB9':'prnap','\u2ABA':'scnap','\u2ABB':'Pr','\u2ABC':'Sc','\u2ABD':'subdot','\u2ABE':'supdot','\u2ABF':'subplus','\u2AC0':'supplus','\u2AC1':'submult','\u2AC2':'supmult','\u2AC3':'subedot','\u2AC4':'supedot','\u2AC5':'subE','\u2AC5\u0338':'nsubE','\u2AC6':'supE','\u2AC6\u0338':'nsupE','\u2AC7':'subsim','\u2AC8':'supsim','\u2ACB\uFE00':'vsubnE','\u2ACB':'subnE','\u2ACC\uFE00':'vsupnE','\u2ACC':'supnE','\u2ACF':'csub','\u2AD0':'csup','\u2AD1':'csube','\u2AD2':'csupe','\u2AD3':'subsup','\u2AD4':'supsub','\u2AD5':'subsub','\u2AD6':'supsup','\u2AD7':'suphsub','\u2AD8':'supdsub','\u2AD9':'forkv','\u2ADA':'topfork','\u2ADB':'mlcp','\u2AE4':'Dashv','\u2AE6':'Vdashl','\u2AE7':'Barv','\u2AE8':'vBar','\u2AE9':'vBarv','\u2AEB':'Vbar','\u2AEC':'Not','\u2AED':'bNot','\u2AEE':'rnmid','\u2AEF':'cirmid','\u2AF0':'midcir','\u2AF1':'topcir','\u2AF2':'nhpar','\u2AF3':'parsim','\u2AFD':'parsl','\u2AFD\u20E5':'nparsl','\u266D':'flat','\u266E':'natur','\u266F':'sharp','\xA4':'curren','\xA2':'cent','$':'dollar','\xA3':'pound','\xA5':'yen','\u20AC':'euro','\xB9':'sup1','\xBD':'half','\u2153':'frac13','\xBC':'frac14','\u2155':'frac15','\u2159':'frac16','\u215B':'frac18','\xB2':'sup2','\u2154':'frac23','\u2156':'frac25','\xB3':'sup3','\xBE':'frac34','\u2157':'frac35','\u215C':'frac38','\u2158':'frac45','\u215A':'frac56','\u215D':'frac58','\u215E':'frac78','\uD835\uDCB6':'ascr','\uD835\uDD52':'aopf','\uD835\uDD1E':'afr','\uD835\uDD38':'Aopf','\uD835\uDD04':'Afr','\uD835\uDC9C':'Ascr','\xAA':'ordf','\xE1':'aacute','\xC1':'Aacute','\xE0':'agrave','\xC0':'Agrave','\u0103':'abreve','\u0102':'Abreve','\xE2':'acirc','\xC2':'Acirc','\xE5':'aring','\xC5':'angst','\xE4':'auml','\xC4':'Auml','\xE3':'atilde','\xC3':'Atilde','\u0105':'aogon','\u0104':'Aogon','\u0101':'amacr','\u0100':'Amacr','\xE6':'aelig','\xC6':'AElig','\uD835\uDCB7':'bscr','\uD835\uDD53':'bopf','\uD835\uDD1F':'bfr','\uD835\uDD39':'Bopf','\u212C':'Bscr','\uD835\uDD05':'Bfr','\uD835\uDD20':'cfr','\uD835\uDCB8':'cscr','\uD835\uDD54':'copf','\u212D':'Cfr','\uD835\uDC9E':'Cscr','\u2102':'Copf','\u0107':'cacute','\u0106':'Cacute','\u0109':'ccirc','\u0108':'Ccirc','\u010D':'ccaron','\u010C':'Ccaron','\u010B':'cdot','\u010A':'Cdot','\xE7':'ccedil','\xC7':'Ccedil','\u2105':'incare','\uD835\uDD21':'dfr','\u2146':'dd','\uD835\uDD55':'dopf','\uD835\uDCB9':'dscr','\uD835\uDC9F':'Dscr','\uD835\uDD07':'Dfr','\u2145':'DD','\uD835\uDD3B':'Dopf','\u010F':'dcaron','\u010E':'Dcaron','\u0111':'dstrok','\u0110':'Dstrok','\xF0':'eth','\xD0':'ETH','\u2147':'ee','\u212F':'escr','\uD835\uDD22':'efr','\uD835\uDD56':'eopf','\u2130':'Escr','\uD835\uDD08':'Efr','\uD835\uDD3C':'Eopf','\xE9':'eacute','\xC9':'Eacute','\xE8':'egrave','\xC8':'Egrave','\xEA':'ecirc','\xCA':'Ecirc','\u011B':'ecaron','\u011A':'Ecaron','\xEB':'euml','\xCB':'Euml','\u0117':'edot','\u0116':'Edot','\u0119':'eogon','\u0118':'Eogon','\u0113':'emacr','\u0112':'Emacr','\uD835\uDD23':'ffr','\uD835\uDD57':'fopf','\uD835\uDCBB':'fscr','\uD835\uDD09':'Ffr','\uD835\uDD3D':'Fopf','\u2131':'Fscr','\uFB00':'fflig','\uFB03':'ffilig','\uFB04':'ffllig','\uFB01':'filig','fj':'fjlig','\uFB02':'fllig','\u0192':'fnof','\u210A':'gscr','\uD835\uDD58':'gopf','\uD835\uDD24':'gfr','\uD835\uDCA2':'Gscr','\uD835\uDD3E':'Gopf','\uD835\uDD0A':'Gfr','\u01F5':'gacute','\u011F':'gbreve','\u011E':'Gbreve','\u011D':'gcirc','\u011C':'Gcirc','\u0121':'gdot','\u0120':'Gdot','\u0122':'Gcedil','\uD835\uDD25':'hfr','\u210E':'planckh','\uD835\uDCBD':'hscr','\uD835\uDD59':'hopf','\u210B':'Hscr','\u210C':'Hfr','\u210D':'Hopf','\u0125':'hcirc','\u0124':'Hcirc','\u210F':'hbar','\u0127':'hstrok','\u0126':'Hstrok','\uD835\uDD5A':'iopf','\uD835\uDD26':'ifr','\uD835\uDCBE':'iscr','\u2148':'ii','\uD835\uDD40':'Iopf','\u2110':'Iscr','\u2111':'Im','\xED':'iacute','\xCD':'Iacute','\xEC':'igrave','\xCC':'Igrave','\xEE':'icirc','\xCE':'Icirc','\xEF':'iuml','\xCF':'Iuml','\u0129':'itilde','\u0128':'Itilde','\u0130':'Idot','\u012F':'iogon','\u012E':'Iogon','\u012B':'imacr','\u012A':'Imacr','\u0133':'ijlig','\u0132':'IJlig','\u0131':'imath','\uD835\uDCBF':'jscr','\uD835\uDD5B':'jopf','\uD835\uDD27':'jfr','\uD835\uDCA5':'Jscr','\uD835\uDD0D':'Jfr','\uD835\uDD41':'Jopf','\u0135':'jcirc','\u0134':'Jcirc','\u0237':'jmath','\uD835\uDD5C':'kopf','\uD835\uDCC0':'kscr','\uD835\uDD28':'kfr','\uD835\uDCA6':'Kscr','\uD835\uDD42':'Kopf','\uD835\uDD0E':'Kfr','\u0137':'kcedil','\u0136':'Kcedil','\uD835\uDD29':'lfr','\uD835\uDCC1':'lscr','\u2113':'ell','\uD835\uDD5D':'lopf','\u2112':'Lscr','\uD835\uDD0F':'Lfr','\uD835\uDD43':'Lopf','\u013A':'lacute','\u0139':'Lacute','\u013E':'lcaron','\u013D':'Lcaron','\u013C':'lcedil','\u013B':'Lcedil','\u0142':'lstrok','\u0141':'Lstrok','\u0140':'lmidot','\u013F':'Lmidot','\uD835\uDD2A':'mfr','\uD835\uDD5E':'mopf','\uD835\uDCC2':'mscr','\uD835\uDD10':'Mfr','\uD835\uDD44':'Mopf','\u2133':'Mscr','\uD835\uDD2B':'nfr','\uD835\uDD5F':'nopf','\uD835\uDCC3':'nscr','\u2115':'Nopf','\uD835\uDCA9':'Nscr','\uD835\uDD11':'Nfr','\u0144':'nacute','\u0143':'Nacute','\u0148':'ncaron','\u0147':'Ncaron','\xF1':'ntilde','\xD1':'Ntilde','\u0146':'ncedil','\u0145':'Ncedil','\u2116':'numero','\u014B':'eng','\u014A':'ENG','\uD835\uDD60':'oopf','\uD835\uDD2C':'ofr','\u2134':'oscr','\uD835\uDCAA':'Oscr','\uD835\uDD12':'Ofr','\uD835\uDD46':'Oopf','\xBA':'ordm','\xF3':'oacute','\xD3':'Oacute','\xF2':'ograve','\xD2':'Ograve','\xF4':'ocirc','\xD4':'Ocirc','\xF6':'ouml','\xD6':'Ouml','\u0151':'odblac','\u0150':'Odblac','\xF5':'otilde','\xD5':'Otilde','\xF8':'oslash','\xD8':'Oslash','\u014D':'omacr','\u014C':'Omacr','\u0153':'oelig','\u0152':'OElig','\uD835\uDD2D':'pfr','\uD835\uDCC5':'pscr','\uD835\uDD61':'popf','\u2119':'Popf','\uD835\uDD13':'Pfr','\uD835\uDCAB':'Pscr','\uD835\uDD62':'qopf','\uD835\uDD2E':'qfr','\uD835\uDCC6':'qscr','\uD835\uDCAC':'Qscr','\uD835\uDD14':'Qfr','\u211A':'Qopf','\u0138':'kgreen','\uD835\uDD2F':'rfr','\uD835\uDD63':'ropf','\uD835\uDCC7':'rscr','\u211B':'Rscr','\u211C':'Re','\u211D':'Ropf','\u0155':'racute','\u0154':'Racute','\u0159':'rcaron','\u0158':'Rcaron','\u0157':'rcedil','\u0156':'Rcedil','\uD835\uDD64':'sopf','\uD835\uDCC8':'sscr','\uD835\uDD30':'sfr','\uD835\uDD4A':'Sopf','\uD835\uDD16':'Sfr','\uD835\uDCAE':'Sscr','\u24C8':'oS','\u015B':'sacute','\u015A':'Sacute','\u015D':'scirc','\u015C':'Scirc','\u0161':'scaron','\u0160':'Scaron','\u015F':'scedil','\u015E':'Scedil','\xDF':'szlig','\uD835\uDD31':'tfr','\uD835\uDCC9':'tscr','\uD835\uDD65':'topf','\uD835\uDCAF':'Tscr','\uD835\uDD17':'Tfr','\uD835\uDD4B':'Topf','\u0165':'tcaron','\u0164':'Tcaron','\u0163':'tcedil','\u0162':'Tcedil','\u2122':'trade','\u0167':'tstrok','\u0166':'Tstrok','\uD835\uDCCA':'uscr','\uD835\uDD66':'uopf','\uD835\uDD32':'ufr','\uD835\uDD4C':'Uopf','\uD835\uDD18':'Ufr','\uD835\uDCB0':'Uscr','\xFA':'uacute','\xDA':'Uacute','\xF9':'ugrave','\xD9':'Ugrave','\u016D':'ubreve','\u016C':'Ubreve','\xFB':'ucirc','\xDB':'Ucirc','\u016F':'uring','\u016E':'Uring','\xFC':'uuml','\xDC':'Uuml','\u0171':'udblac','\u0170':'Udblac','\u0169':'utilde','\u0168':'Utilde','\u0173':'uogon','\u0172':'Uogon','\u016B':'umacr','\u016A':'Umacr','\uD835\uDD33':'vfr','\uD835\uDD67':'vopf','\uD835\uDCCB':'vscr','\uD835\uDD19':'Vfr','\uD835\uDD4D':'Vopf','\uD835\uDCB1':'Vscr','\uD835\uDD68':'wopf','\uD835\uDCCC':'wscr','\uD835\uDD34':'wfr','\uD835\uDCB2':'Wscr','\uD835\uDD4E':'Wopf','\uD835\uDD1A':'Wfr','\u0175':'wcirc','\u0174':'Wcirc','\uD835\uDD35':'xfr','\uD835\uDCCD':'xscr','\uD835\uDD69':'xopf','\uD835\uDD4F':'Xopf','\uD835\uDD1B':'Xfr','\uD835\uDCB3':'Xscr','\uD835\uDD36':'yfr','\uD835\uDCCE':'yscr','\uD835\uDD6A':'yopf','\uD835\uDCB4':'Yscr','\uD835\uDD1C':'Yfr','\uD835\uDD50':'Yopf','\xFD':'yacute','\xDD':'Yacute','\u0177':'ycirc','\u0176':'Ycirc','\xFF':'yuml','\u0178':'Yuml','\uD835\uDCCF':'zscr','\uD835\uDD37':'zfr','\uD835\uDD6B':'zopf','\u2128':'Zfr','\u2124':'Zopf','\uD835\uDCB5':'Zscr','\u017A':'zacute','\u0179':'Zacute','\u017E':'zcaron','\u017D':'Zcaron','\u017C':'zdot','\u017B':'Zdot','\u01B5':'imped','\xFE':'thorn','\xDE':'THORN','\u0149':'napos','\u03B1':'alpha','\u0391':'Alpha','\u03B2':'beta','\u0392':'Beta','\u03B3':'gamma','\u0393':'Gamma','\u03B4':'delta','\u0394':'Delta','\u03B5':'epsi','\u03F5':'epsiv','\u0395':'Epsilon','\u03DD':'gammad','\u03DC':'Gammad','\u03B6':'zeta','\u0396':'Zeta','\u03B7':'eta','\u0397':'Eta','\u03B8':'theta','\u03D1':'thetav','\u0398':'Theta','\u03B9':'iota','\u0399':'Iota','\u03BA':'kappa','\u03F0':'kappav','\u039A':'Kappa','\u03BB':'lambda','\u039B':'Lambda','\u03BC':'mu','\xB5':'micro','\u039C':'Mu','\u03BD':'nu','\u039D':'Nu','\u03BE':'xi','\u039E':'Xi','\u03BF':'omicron','\u039F':'Omicron','\u03C0':'pi','\u03D6':'piv','\u03A0':'Pi','\u03C1':'rho','\u03F1':'rhov','\u03A1':'Rho','\u03C3':'sigma','\u03A3':'Sigma','\u03C2':'sigmaf','\u03C4':'tau','\u03A4':'Tau','\u03C5':'upsi','\u03A5':'Upsilon','\u03D2':'Upsi','\u03C6':'phi','\u03D5':'phiv','\u03A6':'Phi','\u03C7':'chi','\u03A7':'Chi','\u03C8':'psi','\u03A8':'Psi','\u03C9':'omega','\u03A9':'ohm','\u0430':'acy','\u0410':'Acy','\u0431':'bcy','\u0411':'Bcy','\u0432':'vcy','\u0412':'Vcy','\u0433':'gcy','\u0413':'Gcy','\u0453':'gjcy','\u0403':'GJcy','\u0434':'dcy','\u0414':'Dcy','\u0452':'djcy','\u0402':'DJcy','\u0435':'iecy','\u0415':'IEcy','\u0451':'iocy','\u0401':'IOcy','\u0454':'jukcy','\u0404':'Jukcy','\u0436':'zhcy','\u0416':'ZHcy','\u0437':'zcy','\u0417':'Zcy','\u0455':'dscy','\u0405':'DScy','\u0438':'icy','\u0418':'Icy','\u0456':'iukcy','\u0406':'Iukcy','\u0457':'yicy','\u0407':'YIcy','\u0439':'jcy','\u0419':'Jcy','\u0458':'jsercy','\u0408':'Jsercy','\u043A':'kcy','\u041A':'Kcy','\u045C':'kjcy','\u040C':'KJcy','\u043B':'lcy','\u041B':'Lcy','\u0459':'ljcy','\u0409':'LJcy','\u043C':'mcy','\u041C':'Mcy','\u043D':'ncy','\u041D':'Ncy','\u045A':'njcy','\u040A':'NJcy','\u043E':'ocy','\u041E':'Ocy','\u043F':'pcy','\u041F':'Pcy','\u0440':'rcy','\u0420':'Rcy','\u0441':'scy','\u0421':'Scy','\u0442':'tcy','\u0422':'Tcy','\u045B':'tshcy','\u040B':'TSHcy','\u0443':'ucy','\u0423':'Ucy','\u045E':'ubrcy','\u040E':'Ubrcy','\u0444':'fcy','\u0424':'Fcy','\u0445':'khcy','\u0425':'KHcy','\u0446':'tscy','\u0426':'TScy','\u0447':'chcy','\u0427':'CHcy','\u045F':'dzcy','\u040F':'DZcy','\u0448':'shcy','\u0428':'SHcy','\u0449':'shchcy','\u0429':'SHCHcy','\u044A':'hardcy','\u042A':'HARDcy','\u044B':'ycy','\u042B':'Ycy','\u044C':'softcy','\u042C':'SOFTcy','\u044D':'ecy','\u042D':'Ecy','\u044E':'yucy','\u042E':'YUcy','\u044F':'yacy','\u042F':'YAcy','\u2135':'aleph','\u2136':'beth','\u2137':'gimel','\u2138':'daleth'}; - - var regexEscape = /["&'<>`]/g; - var escapeMap = { - '"': '"', - '&': '&', - '\'': ''', - '<': '<', - // See https://mathiasbynens.be/notes/ambiguous-ampersands: in HTML, the - // following is not strictly necessary unless it’s part of a tag or an - // unquoted attribute value. We’re only escaping it to support those - // situations, and for XML support. - '>': '>', - // In Internet Explorer ≤ 8, the backtick character can be used - // to break out of (un)quoted attribute values or HTML comments. - // See http://html5sec.org/#102, http://html5sec.org/#108, and - // http://html5sec.org/#133. - '`': '`' - }; - - var regexInvalidEntity = /&#(?:[xX][^a-fA-F0-9]|[^0-9xX])/; - var regexInvalidRawCodePoint = /[\0-\x08\x0B\x0E-\x1F\x7F-\x9F\uFDD0-\uFDEF\uFFFE\uFFFF]|[\uD83F\uD87F\uD8BF\uD8FF\uD93F\uD97F\uD9BF\uD9FF\uDA3F\uDA7F\uDABF\uDAFF\uDB3F\uDB7F\uDBBF\uDBFF][\uDFFE\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; - var regexDecode = /&(CounterClockwiseContourIntegral|DoubleLongLeftRightArrow|ClockwiseContourIntegral|NotNestedGreaterGreater|NotSquareSupersetEqual|DiacriticalDoubleAcute|NotRightTriangleEqual|NotSucceedsSlantEqual|NotPrecedesSlantEqual|CloseCurlyDoubleQuote|NegativeVeryThinSpace|DoubleContourIntegral|FilledVerySmallSquare|CapitalDifferentialD|OpenCurlyDoubleQuote|EmptyVerySmallSquare|NestedGreaterGreater|DoubleLongRightArrow|NotLeftTriangleEqual|NotGreaterSlantEqual|ReverseUpEquilibrium|DoubleLeftRightArrow|NotSquareSubsetEqual|NotDoubleVerticalBar|RightArrowLeftArrow|NotGreaterFullEqual|NotRightTriangleBar|SquareSupersetEqual|DownLeftRightVector|DoubleLongLeftArrow|leftrightsquigarrow|LeftArrowRightArrow|NegativeMediumSpace|blacktriangleright|RightDownVectorBar|PrecedesSlantEqual|RightDoubleBracket|SucceedsSlantEqual|NotLeftTriangleBar|RightTriangleEqual|SquareIntersection|RightDownTeeVector|ReverseEquilibrium|NegativeThickSpace|longleftrightarrow|Longleftrightarrow|LongLeftRightArrow|DownRightTeeVector|DownRightVectorBar|GreaterSlantEqual|SquareSubsetEqual|LeftDownVectorBar|LeftDoubleBracket|VerticalSeparator|rightleftharpoons|NotGreaterGreater|NotSquareSuperset|blacktriangleleft|blacktriangledown|NegativeThinSpace|LeftDownTeeVector|NotLessSlantEqual|leftrightharpoons|DoubleUpDownArrow|DoubleVerticalBar|LeftTriangleEqual|FilledSmallSquare|twoheadrightarrow|NotNestedLessLess|DownLeftTeeVector|DownLeftVectorBar|RightAngleBracket|NotTildeFullEqual|NotReverseElement|RightUpDownVector|DiacriticalTilde|NotSucceedsTilde|circlearrowright|NotPrecedesEqual|rightharpoondown|DoubleRightArrow|NotSucceedsEqual|NonBreakingSpace|NotRightTriangle|LessEqualGreater|RightUpTeeVector|LeftAngleBracket|GreaterFullEqual|DownArrowUpArrow|RightUpVectorBar|twoheadleftarrow|GreaterEqualLess|downharpoonright|RightTriangleBar|ntrianglerighteq|NotSupersetEqual|LeftUpDownVector|DiacriticalAcute|rightrightarrows|vartriangleright|UpArrowDownArrow|DiacriticalGrave|UnderParenthesis|EmptySmallSquare|LeftUpVectorBar|leftrightarrows|DownRightVector|downharpoonleft|trianglerighteq|ShortRightArrow|OverParenthesis|DoubleLeftArrow|DoubleDownArrow|NotSquareSubset|bigtriangledown|ntrianglelefteq|UpperRightArrow|curvearrowright|vartriangleleft|NotLeftTriangle|nleftrightarrow|LowerRightArrow|NotHumpDownHump|NotGreaterTilde|rightthreetimes|LeftUpTeeVector|NotGreaterEqual|straightepsilon|LeftTriangleBar|rightsquigarrow|ContourIntegral|rightleftarrows|CloseCurlyQuote|RightDownVector|LeftRightVector|nLeftrightarrow|leftharpoondown|circlearrowleft|SquareSuperset|OpenCurlyQuote|hookrightarrow|HorizontalLine|DiacriticalDot|NotLessGreater|ntriangleright|DoubleRightTee|InvisibleComma|InvisibleTimes|LowerLeftArrow|DownLeftVector|NotSubsetEqual|curvearrowleft|trianglelefteq|NotVerticalBar|TildeFullEqual|downdownarrows|NotGreaterLess|RightTeeVector|ZeroWidthSpace|looparrowright|LongRightArrow|doublebarwedge|ShortLeftArrow|ShortDownArrow|RightVectorBar|GreaterGreater|ReverseElement|rightharpoonup|LessSlantEqual|leftthreetimes|upharpoonright|rightarrowtail|LeftDownVector|Longrightarrow|NestedLessLess|UpperLeftArrow|nshortparallel|leftleftarrows|leftrightarrow|Leftrightarrow|LeftRightArrow|longrightarrow|upharpoonleft|RightArrowBar|ApplyFunction|LeftTeeVector|leftarrowtail|NotEqualTilde|varsubsetneqq|varsupsetneqq|RightTeeArrow|SucceedsEqual|SucceedsTilde|LeftVectorBar|SupersetEqual|hookleftarrow|DifferentialD|VerticalTilde|VeryThinSpace|blacktriangle|bigtriangleup|LessFullEqual|divideontimes|leftharpoonup|UpEquilibrium|ntriangleleft|RightTriangle|measuredangle|shortparallel|longleftarrow|Longleftarrow|LongLeftArrow|DoubleLeftTee|Poincareplane|PrecedesEqual|triangleright|DoubleUpArrow|RightUpVector|fallingdotseq|looparrowleft|PrecedesTilde|NotTildeEqual|NotTildeTilde|smallsetminus|Proportional|triangleleft|triangledown|UnderBracket|NotHumpEqual|exponentiale|ExponentialE|NotLessTilde|HilbertSpace|RightCeiling|blacklozenge|varsupsetneq|HumpDownHump|GreaterEqual|VerticalLine|LeftTeeArrow|NotLessEqual|DownTeeArrow|LeftTriangle|varsubsetneq|Intersection|NotCongruent|DownArrowBar|LeftUpVector|LeftArrowBar|risingdotseq|GreaterTilde|RoundImplies|SquareSubset|ShortUpArrow|NotSuperset|quaternions|precnapprox|backepsilon|preccurlyeq|OverBracket|blacksquare|MediumSpace|VerticalBar|circledcirc|circleddash|CircleMinus|CircleTimes|LessGreater|curlyeqprec|curlyeqsucc|diamondsuit|UpDownArrow|Updownarrow|RuleDelayed|Rrightarrow|updownarrow|RightVector|nRightarrow|nrightarrow|eqslantless|LeftCeiling|Equilibrium|SmallCircle|expectation|NotSucceeds|thickapprox|GreaterLess|SquareUnion|NotPrecedes|NotLessLess|straightphi|succnapprox|succcurlyeq|SubsetEqual|sqsupseteq|Proportion|Laplacetrf|ImaginaryI|supsetneqq|NotGreater|gtreqqless|NotElement|ThickSpace|TildeEqual|TildeTilde|Fouriertrf|rmoustache|EqualTilde|eqslantgtr|UnderBrace|LeftVector|UpArrowBar|nLeftarrow|nsubseteqq|subsetneqq|nsupseteqq|nleftarrow|succapprox|lessapprox|UpTeeArrow|upuparrows|curlywedge|lesseqqgtr|varepsilon|varnothing|RightFloor|complement|CirclePlus|sqsubseteq|Lleftarrow|circledast|RightArrow|Rightarrow|rightarrow|lmoustache|Bernoullis|precapprox|mapstoleft|mapstodown|longmapsto|dotsquare|downarrow|DoubleDot|nsubseteq|supsetneq|leftarrow|nsupseteq|subsetneq|ThinSpace|ngeqslant|subseteqq|HumpEqual|NotSubset|triangleq|NotCupCap|lesseqgtr|heartsuit|TripleDot|Leftarrow|Coproduct|Congruent|varpropto|complexes|gvertneqq|LeftArrow|LessTilde|supseteqq|MinusPlus|CircleDot|nleqslant|NotExists|gtreqless|nparallel|UnionPlus|LeftFloor|checkmark|CenterDot|centerdot|Mellintrf|gtrapprox|bigotimes|OverBrace|spadesuit|therefore|pitchfork|rationals|PlusMinus|Backslash|Therefore|DownBreve|backsimeq|backprime|DownArrow|nshortmid|Downarrow|lvertneqq|eqvparsl|imagline|imagpart|infintie|integers|Integral|intercal|LessLess|Uarrocir|intlarhk|sqsupset|angmsdaf|sqsubset|llcorner|vartheta|cupbrcap|lnapprox|Superset|SuchThat|succnsim|succneqq|angmsdag|biguplus|curlyvee|trpezium|Succeeds|NotTilde|bigwedge|angmsdah|angrtvbd|triminus|cwconint|fpartint|lrcorner|smeparsl|subseteq|urcorner|lurdshar|laemptyv|DDotrahd|approxeq|ldrushar|awconint|mapstoup|backcong|shortmid|triangle|geqslant|gesdotol|timesbar|circledR|circledS|setminus|multimap|naturals|scpolint|ncongdot|RightTee|boxminus|gnapprox|boxtimes|andslope|thicksim|angmsdaa|varsigma|cirfnint|rtriltri|angmsdab|rppolint|angmsdac|barwedge|drbkarow|clubsuit|thetasym|bsolhsub|capbrcup|dzigrarr|doteqdot|DotEqual|dotminus|UnderBar|NotEqual|realpart|otimesas|ulcorner|hksearow|hkswarow|parallel|PartialD|elinters|emptyset|plusacir|bbrktbrk|angmsdad|pointint|bigoplus|angmsdae|Precedes|bigsqcup|varkappa|notindot|supseteq|precneqq|precnsim|profalar|profline|profsurf|leqslant|lesdotor|raemptyv|subplus|notnivb|notnivc|subrarr|zigrarr|vzigzag|submult|subedot|Element|between|cirscir|larrbfs|larrsim|lotimes|lbrksld|lbrkslu|lozenge|ldrdhar|dbkarow|bigcirc|epsilon|simrarr|simplus|ltquest|Epsilon|luruhar|gtquest|maltese|npolint|eqcolon|npreceq|bigodot|ddagger|gtrless|bnequiv|harrcir|ddotseq|equivDD|backsim|demptyv|nsqsube|nsqsupe|Upsilon|nsubset|upsilon|minusdu|nsucceq|swarrow|nsupset|coloneq|searrow|boxplus|napprox|natural|asympeq|alefsym|congdot|nearrow|bigstar|diamond|supplus|tritime|LeftTee|nvinfin|triplus|NewLine|nvltrie|nvrtrie|nwarrow|nexists|Diamond|ruluhar|Implies|supmult|angzarr|suplarr|suphsub|questeq|because|digamma|Because|olcross|bemptyv|omicron|Omicron|rotimes|NoBreak|intprod|angrtvb|orderof|uwangle|suphsol|lesdoto|orslope|DownTee|realine|cudarrl|rdldhar|OverBar|supedot|lessdot|supdsub|topfork|succsim|rbrkslu|rbrksld|pertenk|cudarrr|isindot|planckh|lessgtr|pluscir|gesdoto|plussim|plustwo|lesssim|cularrp|rarrsim|Cayleys|notinva|notinvb|notinvc|UpArrow|Uparrow|uparrow|NotLess|dwangle|precsim|Product|curarrm|Cconint|dotplus|rarrbfs|ccupssm|Cedilla|cemptyv|notniva|quatint|frac35|frac38|frac45|frac56|frac58|frac78|tridot|xoplus|gacute|gammad|Gammad|lfisht|lfloor|bigcup|sqsupe|gbreve|Gbreve|lharul|sqsube|sqcups|Gcedil|apacir|llhard|lmidot|Lmidot|lmoust|andand|sqcaps|approx|Abreve|spades|circeq|tprime|divide|topcir|Assign|topbot|gesdot|divonx|xuplus|timesd|gesles|atilde|solbar|SOFTcy|loplus|timesb|lowast|lowbar|dlcorn|dlcrop|softcy|dollar|lparlt|thksim|lrhard|Atilde|lsaquo|smashp|bigvee|thinsp|wreath|bkarow|lsquor|lstrok|Lstrok|lthree|ltimes|ltlarr|DotDot|simdot|ltrPar|weierp|xsqcup|angmsd|sigmav|sigmaf|zeetrf|Zcaron|zcaron|mapsto|vsupne|thetav|cirmid|marker|mcomma|Zacute|vsubnE|there4|gtlPar|vsubne|bottom|gtrarr|SHCHcy|shchcy|midast|midcir|middot|minusb|minusd|gtrdot|bowtie|sfrown|mnplus|models|colone|seswar|Colone|mstpos|searhk|gtrsim|nacute|Nacute|boxbox|telrec|hairsp|Tcedil|nbumpe|scnsim|ncaron|Ncaron|ncedil|Ncedil|hamilt|Scedil|nearhk|hardcy|HARDcy|tcedil|Tcaron|commat|nequiv|nesear|tcaron|target|hearts|nexist|varrho|scedil|Scaron|scaron|hellip|Sacute|sacute|hercon|swnwar|compfn|rtimes|rthree|rsquor|rsaquo|zacute|wedgeq|homtht|barvee|barwed|Barwed|rpargt|horbar|conint|swarhk|roplus|nltrie|hslash|hstrok|Hstrok|rmoust|Conint|bprime|hybull|hyphen|iacute|Iacute|supsup|supsub|supsim|varphi|coprod|brvbar|agrave|Supset|supset|igrave|Igrave|notinE|Agrave|iiiint|iinfin|copysr|wedbar|Verbar|vangrt|becaus|incare|verbar|inodot|bullet|drcorn|intcal|drcrop|cularr|vellip|Utilde|bumpeq|cupcap|dstrok|Dstrok|CupCap|cupcup|cupdot|eacute|Eacute|supdot|iquest|easter|ecaron|Ecaron|ecolon|isinsv|utilde|itilde|Itilde|curarr|succeq|Bumpeq|cacute|ulcrop|nparsl|Cacute|nprcue|egrave|Egrave|nrarrc|nrarrw|subsup|subsub|nrtrie|jsercy|nsccue|Jsercy|kappav|kcedil|Kcedil|subsim|ulcorn|nsimeq|egsdot|veebar|kgreen|capand|elsdot|Subset|subset|curren|aacute|lacute|Lacute|emptyv|ntilde|Ntilde|lagran|lambda|Lambda|capcap|Ugrave|langle|subdot|emsp13|numero|emsp14|nvdash|nvDash|nVdash|nVDash|ugrave|ufisht|nvHarr|larrfs|nvlArr|larrhk|larrlp|larrpl|nvrArr|Udblac|nwarhk|larrtl|nwnear|oacute|Oacute|latail|lAtail|sstarf|lbrace|odblac|Odblac|lbrack|udblac|odsold|eparsl|lcaron|Lcaron|ograve|Ograve|lcedil|Lcedil|Aacute|ssmile|ssetmn|squarf|ldquor|capcup|ominus|cylcty|rharul|eqcirc|dagger|rfloor|rfisht|Dagger|daleth|equals|origof|capdot|equest|dcaron|Dcaron|rdquor|oslash|Oslash|otilde|Otilde|otimes|Otimes|urcrop|Ubreve|ubreve|Yacute|Uacute|uacute|Rcedil|rcedil|urcorn|parsim|Rcaron|Vdashl|rcaron|Tstrok|percnt|period|permil|Exists|yacute|rbrack|rbrace|phmmat|ccaron|Ccaron|planck|ccedil|plankv|tstrok|female|plusdo|plusdu|ffilig|plusmn|ffllig|Ccedil|rAtail|dfisht|bernou|ratail|Rarrtl|rarrtl|angsph|rarrpl|rarrlp|rarrhk|xwedge|xotime|forall|ForAll|Vvdash|vsupnE|preceq|bigcap|frac12|frac13|frac14|primes|rarrfs|prnsim|frac15|Square|frac16|square|lesdot|frac18|frac23|propto|prurel|rarrap|rangle|puncsp|frac25|Racute|qprime|racute|lesges|frac34|abreve|AElig|eqsim|utdot|setmn|urtri|Equal|Uring|seArr|uring|searr|dashv|Dashv|mumap|nabla|iogon|Iogon|sdote|sdotb|scsim|napid|napos|equiv|natur|Acirc|dblac|erarr|nbump|iprod|erDot|ucirc|awint|esdot|angrt|ncong|isinE|scnap|Scirc|scirc|ndash|isins|Ubrcy|nearr|neArr|isinv|nedot|ubrcy|acute|Ycirc|iukcy|Iukcy|xutri|nesim|caret|jcirc|Jcirc|caron|twixt|ddarr|sccue|exist|jmath|sbquo|ngeqq|angst|ccaps|lceil|ngsim|UpTee|delta|Delta|rtrif|nharr|nhArr|nhpar|rtrie|jukcy|Jukcy|kappa|rsquo|Kappa|nlarr|nlArr|TSHcy|rrarr|aogon|Aogon|fflig|xrarr|tshcy|ccirc|nleqq|filig|upsih|nless|dharl|nlsim|fjlig|ropar|nltri|dharr|robrk|roarr|fllig|fltns|roang|rnmid|subnE|subne|lAarr|trisb|Ccirc|acirc|ccups|blank|VDash|forkv|Vdash|langd|cedil|blk12|blk14|laquo|strns|diams|notin|vDash|larrb|blk34|block|disin|uplus|vdash|vBarv|aelig|starf|Wedge|check|xrArr|lates|lbarr|lBarr|notni|lbbrk|bcong|frasl|lbrke|frown|vrtri|vprop|vnsup|gamma|Gamma|wedge|xodot|bdquo|srarr|doteq|ldquo|boxdl|boxdL|gcirc|Gcirc|boxDl|boxDL|boxdr|boxdR|boxDr|TRADE|trade|rlhar|boxDR|vnsub|npart|vltri|rlarr|boxhd|boxhD|nprec|gescc|nrarr|nrArr|boxHd|boxHD|boxhu|boxhU|nrtri|boxHu|clubs|boxHU|times|colon|Colon|gimel|xlArr|Tilde|nsime|tilde|nsmid|nspar|THORN|thorn|xlarr|nsube|nsubE|thkap|xhArr|comma|nsucc|boxul|boxuL|nsupe|nsupE|gneqq|gnsim|boxUl|boxUL|grave|boxur|boxuR|boxUr|boxUR|lescc|angle|bepsi|boxvh|varpi|boxvH|numsp|Theta|gsime|gsiml|theta|boxVh|boxVH|boxvl|gtcir|gtdot|boxvL|boxVl|boxVL|crarr|cross|Cross|nvsim|boxvr|nwarr|nwArr|sqsup|dtdot|Uogon|lhard|lharu|dtrif|ocirc|Ocirc|lhblk|duarr|odash|sqsub|Hacek|sqcup|llarr|duhar|oelig|OElig|ofcir|boxvR|uogon|lltri|boxVr|csube|uuarr|ohbar|csupe|ctdot|olarr|olcir|harrw|oline|sqcap|omacr|Omacr|omega|Omega|boxVR|aleph|lneqq|lnsim|loang|loarr|rharu|lobrk|hcirc|operp|oplus|rhard|Hcirc|orarr|Union|order|ecirc|Ecirc|cuepr|szlig|cuesc|breve|reals|eDDot|Breve|hoarr|lopar|utrif|rdquo|Umacr|umacr|efDot|swArr|ultri|alpha|rceil|ovbar|swarr|Wcirc|wcirc|smtes|smile|bsemi|lrarr|aring|parsl|lrhar|bsime|uhblk|lrtri|cupor|Aring|uharr|uharl|slarr|rbrke|bsolb|lsime|rbbrk|RBarr|lsimg|phone|rBarr|rbarr|icirc|lsquo|Icirc|emacr|Emacr|ratio|simne|plusb|simlE|simgE|simeq|pluse|ltcir|ltdot|empty|xharr|xdtri|iexcl|Alpha|ltrie|rarrw|pound|ltrif|xcirc|bumpe|prcue|bumpE|asymp|amacr|cuvee|Sigma|sigma|iiint|udhar|iiota|ijlig|IJlig|supnE|imacr|Imacr|prime|Prime|image|prnap|eogon|Eogon|rarrc|mdash|mDDot|cuwed|imath|supne|imped|Amacr|udarr|prsim|micro|rarrb|cwint|raquo|infin|eplus|range|rangd|Ucirc|radic|minus|amalg|veeeq|rAarr|epsiv|ycirc|quest|sharp|quot|zwnj|Qscr|race|qscr|Qopf|qopf|qint|rang|Rang|Zscr|zscr|Zopf|zopf|rarr|rArr|Rarr|Pscr|pscr|prop|prod|prnE|prec|ZHcy|zhcy|prap|Zeta|zeta|Popf|popf|Zdot|plus|zdot|Yuml|yuml|phiv|YUcy|yucy|Yscr|yscr|perp|Yopf|yopf|part|para|YIcy|Ouml|rcub|yicy|YAcy|rdca|ouml|osol|Oscr|rdsh|yacy|real|oscr|xvee|andd|rect|andv|Xscr|oror|ordm|ordf|xscr|ange|aopf|Aopf|rHar|Xopf|opar|Oopf|xopf|xnis|rhov|oopf|omid|xmap|oint|apid|apos|ogon|ascr|Ascr|odot|odiv|xcup|xcap|ocir|oast|nvlt|nvle|nvgt|nvge|nvap|Wscr|wscr|auml|ntlg|ntgl|nsup|nsub|nsim|Nscr|nscr|nsce|Wopf|ring|npre|wopf|npar|Auml|Barv|bbrk|Nopf|nopf|nmid|nLtv|beta|ropf|Ropf|Beta|beth|nles|rpar|nleq|bnot|bNot|nldr|NJcy|rscr|Rscr|Vscr|vscr|rsqb|njcy|bopf|nisd|Bopf|rtri|Vopf|nGtv|ngtr|vopf|boxh|boxH|boxv|nges|ngeq|boxV|bscr|scap|Bscr|bsim|Vert|vert|bsol|bull|bump|caps|cdot|ncup|scnE|ncap|nbsp|napE|Cdot|cent|sdot|Vbar|nang|vBar|chcy|Mscr|mscr|sect|semi|CHcy|Mopf|mopf|sext|circ|cire|mldr|mlcp|cirE|comp|shcy|SHcy|vArr|varr|cong|copf|Copf|copy|COPY|malt|male|macr|lvnE|cscr|ltri|sime|ltcc|simg|Cscr|siml|csub|Uuml|lsqb|lsim|uuml|csup|Lscr|lscr|utri|smid|lpar|cups|smte|lozf|darr|Lopf|Uscr|solb|lopf|sopf|Sopf|lneq|uscr|spar|dArr|lnap|Darr|dash|Sqrt|LJcy|ljcy|lHar|dHar|Upsi|upsi|diam|lesg|djcy|DJcy|leqq|dopf|Dopf|dscr|Dscr|dscy|ldsh|ldca|squf|DScy|sscr|Sscr|dsol|lcub|late|star|Star|Uopf|Larr|lArr|larr|uopf|dtri|dzcy|sube|subE|Lang|lang|Kscr|kscr|Kopf|kopf|KJcy|kjcy|KHcy|khcy|DZcy|ecir|edot|eDot|Jscr|jscr|succ|Jopf|jopf|Edot|uHar|emsp|ensp|Iuml|iuml|eopf|isin|Iscr|iscr|Eopf|epar|sung|epsi|escr|sup1|sup2|sup3|Iota|iota|supe|supE|Iopf|iopf|IOcy|iocy|Escr|esim|Esim|imof|Uarr|QUOT|uArr|uarr|euml|IEcy|iecy|Idot|Euml|euro|excl|Hscr|hscr|Hopf|hopf|TScy|tscy|Tscr|hbar|tscr|flat|tbrk|fnof|hArr|harr|half|fopf|Fopf|tdot|gvnE|fork|trie|gtcc|fscr|Fscr|gdot|gsim|Gscr|gscr|Gopf|gopf|gneq|Gdot|tosa|gnap|Topf|topf|geqq|toea|GJcy|gjcy|tint|gesl|mid|Sfr|ggg|top|ges|gla|glE|glj|geq|gne|gEl|gel|gnE|Gcy|gcy|gap|Tfr|tfr|Tcy|tcy|Hat|Tau|Ffr|tau|Tab|hfr|Hfr|ffr|Fcy|fcy|icy|Icy|iff|ETH|eth|ifr|Ifr|Eta|eta|int|Int|Sup|sup|ucy|Ucy|Sum|sum|jcy|ENG|ufr|Ufr|eng|Jcy|jfr|els|ell|egs|Efr|efr|Jfr|uml|kcy|Kcy|Ecy|ecy|kfr|Kfr|lap|Sub|sub|lat|lcy|Lcy|leg|Dot|dot|lEg|leq|les|squ|div|die|lfr|Lfr|lgE|Dfr|dfr|Del|deg|Dcy|dcy|lne|lnE|sol|loz|smt|Cup|lrm|cup|lsh|Lsh|sim|shy|map|Map|mcy|Mcy|mfr|Mfr|mho|gfr|Gfr|sfr|cir|Chi|chi|nap|Cfr|vcy|Vcy|cfr|Scy|scy|ncy|Ncy|vee|Vee|Cap|cap|nfr|scE|sce|Nfr|nge|ngE|nGg|vfr|Vfr|ngt|bot|nGt|nis|niv|Rsh|rsh|nle|nlE|bne|Bfr|bfr|nLl|nlt|nLt|Bcy|bcy|not|Not|rlm|wfr|Wfr|npr|nsc|num|ocy|ast|Ocy|ofr|xfr|Xfr|Ofr|ogt|ohm|apE|olt|Rho|ape|rho|Rfr|rfr|ord|REG|ang|reg|orv|And|and|AMP|Rcy|amp|Afr|ycy|Ycy|yen|yfr|Yfr|rcy|par|pcy|Pcy|pfr|Pfr|phi|Phi|afr|Acy|acy|zcy|Zcy|piv|acE|acd|zfr|Zfr|pre|prE|psi|Psi|qfr|Qfr|zwj|Or|ge|Gg|gt|gg|el|oS|lt|Lt|LT|Re|lg|gl|eg|ne|Im|it|le|DD|wp|wr|nu|Nu|dd|lE|Sc|sc|pi|Pi|ee|af|ll|Ll|rx|gE|xi|pm|Xi|ic|pr|Pr|in|ni|mp|mu|ac|Mu|or|ap|Gt|GT|ii);|&(Aacute|Agrave|Atilde|Ccedil|Eacute|Egrave|Iacute|Igrave|Ntilde|Oacute|Ograve|Oslash|Otilde|Uacute|Ugrave|Yacute|aacute|agrave|atilde|brvbar|ccedil|curren|divide|eacute|egrave|frac12|frac14|frac34|iacute|igrave|iquest|middot|ntilde|oacute|ograve|oslash|otilde|plusmn|uacute|ugrave|yacute|AElig|Acirc|Aring|Ecirc|Icirc|Ocirc|THORN|Ucirc|acirc|acute|aelig|aring|cedil|ecirc|icirc|iexcl|laquo|micro|ocirc|pound|raquo|szlig|thorn|times|ucirc|Auml|COPY|Euml|Iuml|Ouml|QUOT|Uuml|auml|cent|copy|euml|iuml|macr|nbsp|ordf|ordm|ouml|para|quot|sect|sup1|sup2|sup3|uuml|yuml|AMP|ETH|REG|amp|deg|eth|not|reg|shy|uml|yen|GT|LT|gt|lt)(?!;)([=a-zA-Z0-9]?)|&#([0-9]+)(;?)|&#[xX]([a-fA-F0-9]+)(;?)|&([0-9a-zA-Z]+)/g; - var decodeMap = {'aacute':'\xE1','Aacute':'\xC1','abreve':'\u0103','Abreve':'\u0102','ac':'\u223E','acd':'\u223F','acE':'\u223E\u0333','acirc':'\xE2','Acirc':'\xC2','acute':'\xB4','acy':'\u0430','Acy':'\u0410','aelig':'\xE6','AElig':'\xC6','af':'\u2061','afr':'\uD835\uDD1E','Afr':'\uD835\uDD04','agrave':'\xE0','Agrave':'\xC0','alefsym':'\u2135','aleph':'\u2135','alpha':'\u03B1','Alpha':'\u0391','amacr':'\u0101','Amacr':'\u0100','amalg':'\u2A3F','amp':'&','AMP':'&','and':'\u2227','And':'\u2A53','andand':'\u2A55','andd':'\u2A5C','andslope':'\u2A58','andv':'\u2A5A','ang':'\u2220','ange':'\u29A4','angle':'\u2220','angmsd':'\u2221','angmsdaa':'\u29A8','angmsdab':'\u29A9','angmsdac':'\u29AA','angmsdad':'\u29AB','angmsdae':'\u29AC','angmsdaf':'\u29AD','angmsdag':'\u29AE','angmsdah':'\u29AF','angrt':'\u221F','angrtvb':'\u22BE','angrtvbd':'\u299D','angsph':'\u2222','angst':'\xC5','angzarr':'\u237C','aogon':'\u0105','Aogon':'\u0104','aopf':'\uD835\uDD52','Aopf':'\uD835\uDD38','ap':'\u2248','apacir':'\u2A6F','ape':'\u224A','apE':'\u2A70','apid':'\u224B','apos':'\'','ApplyFunction':'\u2061','approx':'\u2248','approxeq':'\u224A','aring':'\xE5','Aring':'\xC5','ascr':'\uD835\uDCB6','Ascr':'\uD835\uDC9C','Assign':'\u2254','ast':'*','asymp':'\u2248','asympeq':'\u224D','atilde':'\xE3','Atilde':'\xC3','auml':'\xE4','Auml':'\xC4','awconint':'\u2233','awint':'\u2A11','backcong':'\u224C','backepsilon':'\u03F6','backprime':'\u2035','backsim':'\u223D','backsimeq':'\u22CD','Backslash':'\u2216','Barv':'\u2AE7','barvee':'\u22BD','barwed':'\u2305','Barwed':'\u2306','barwedge':'\u2305','bbrk':'\u23B5','bbrktbrk':'\u23B6','bcong':'\u224C','bcy':'\u0431','Bcy':'\u0411','bdquo':'\u201E','becaus':'\u2235','because':'\u2235','Because':'\u2235','bemptyv':'\u29B0','bepsi':'\u03F6','bernou':'\u212C','Bernoullis':'\u212C','beta':'\u03B2','Beta':'\u0392','beth':'\u2136','between':'\u226C','bfr':'\uD835\uDD1F','Bfr':'\uD835\uDD05','bigcap':'\u22C2','bigcirc':'\u25EF','bigcup':'\u22C3','bigodot':'\u2A00','bigoplus':'\u2A01','bigotimes':'\u2A02','bigsqcup':'\u2A06','bigstar':'\u2605','bigtriangledown':'\u25BD','bigtriangleup':'\u25B3','biguplus':'\u2A04','bigvee':'\u22C1','bigwedge':'\u22C0','bkarow':'\u290D','blacklozenge':'\u29EB','blacksquare':'\u25AA','blacktriangle':'\u25B4','blacktriangledown':'\u25BE','blacktriangleleft':'\u25C2','blacktriangleright':'\u25B8','blank':'\u2423','blk12':'\u2592','blk14':'\u2591','blk34':'\u2593','block':'\u2588','bne':'=\u20E5','bnequiv':'\u2261\u20E5','bnot':'\u2310','bNot':'\u2AED','bopf':'\uD835\uDD53','Bopf':'\uD835\uDD39','bot':'\u22A5','bottom':'\u22A5','bowtie':'\u22C8','boxbox':'\u29C9','boxdl':'\u2510','boxdL':'\u2555','boxDl':'\u2556','boxDL':'\u2557','boxdr':'\u250C','boxdR':'\u2552','boxDr':'\u2553','boxDR':'\u2554','boxh':'\u2500','boxH':'\u2550','boxhd':'\u252C','boxhD':'\u2565','boxHd':'\u2564','boxHD':'\u2566','boxhu':'\u2534','boxhU':'\u2568','boxHu':'\u2567','boxHU':'\u2569','boxminus':'\u229F','boxplus':'\u229E','boxtimes':'\u22A0','boxul':'\u2518','boxuL':'\u255B','boxUl':'\u255C','boxUL':'\u255D','boxur':'\u2514','boxuR':'\u2558','boxUr':'\u2559','boxUR':'\u255A','boxv':'\u2502','boxV':'\u2551','boxvh':'\u253C','boxvH':'\u256A','boxVh':'\u256B','boxVH':'\u256C','boxvl':'\u2524','boxvL':'\u2561','boxVl':'\u2562','boxVL':'\u2563','boxvr':'\u251C','boxvR':'\u255E','boxVr':'\u255F','boxVR':'\u2560','bprime':'\u2035','breve':'\u02D8','Breve':'\u02D8','brvbar':'\xA6','bscr':'\uD835\uDCB7','Bscr':'\u212C','bsemi':'\u204F','bsim':'\u223D','bsime':'\u22CD','bsol':'\\','bsolb':'\u29C5','bsolhsub':'\u27C8','bull':'\u2022','bullet':'\u2022','bump':'\u224E','bumpe':'\u224F','bumpE':'\u2AAE','bumpeq':'\u224F','Bumpeq':'\u224E','cacute':'\u0107','Cacute':'\u0106','cap':'\u2229','Cap':'\u22D2','capand':'\u2A44','capbrcup':'\u2A49','capcap':'\u2A4B','capcup':'\u2A47','capdot':'\u2A40','CapitalDifferentialD':'\u2145','caps':'\u2229\uFE00','caret':'\u2041','caron':'\u02C7','Cayleys':'\u212D','ccaps':'\u2A4D','ccaron':'\u010D','Ccaron':'\u010C','ccedil':'\xE7','Ccedil':'\xC7','ccirc':'\u0109','Ccirc':'\u0108','Cconint':'\u2230','ccups':'\u2A4C','ccupssm':'\u2A50','cdot':'\u010B','Cdot':'\u010A','cedil':'\xB8','Cedilla':'\xB8','cemptyv':'\u29B2','cent':'\xA2','centerdot':'\xB7','CenterDot':'\xB7','cfr':'\uD835\uDD20','Cfr':'\u212D','chcy':'\u0447','CHcy':'\u0427','check':'\u2713','checkmark':'\u2713','chi':'\u03C7','Chi':'\u03A7','cir':'\u25CB','circ':'\u02C6','circeq':'\u2257','circlearrowleft':'\u21BA','circlearrowright':'\u21BB','circledast':'\u229B','circledcirc':'\u229A','circleddash':'\u229D','CircleDot':'\u2299','circledR':'\xAE','circledS':'\u24C8','CircleMinus':'\u2296','CirclePlus':'\u2295','CircleTimes':'\u2297','cire':'\u2257','cirE':'\u29C3','cirfnint':'\u2A10','cirmid':'\u2AEF','cirscir':'\u29C2','ClockwiseContourIntegral':'\u2232','CloseCurlyDoubleQuote':'\u201D','CloseCurlyQuote':'\u2019','clubs':'\u2663','clubsuit':'\u2663','colon':':','Colon':'\u2237','colone':'\u2254','Colone':'\u2A74','coloneq':'\u2254','comma':',','commat':'@','comp':'\u2201','compfn':'\u2218','complement':'\u2201','complexes':'\u2102','cong':'\u2245','congdot':'\u2A6D','Congruent':'\u2261','conint':'\u222E','Conint':'\u222F','ContourIntegral':'\u222E','copf':'\uD835\uDD54','Copf':'\u2102','coprod':'\u2210','Coproduct':'\u2210','copy':'\xA9','COPY':'\xA9','copysr':'\u2117','CounterClockwiseContourIntegral':'\u2233','crarr':'\u21B5','cross':'\u2717','Cross':'\u2A2F','cscr':'\uD835\uDCB8','Cscr':'\uD835\uDC9E','csub':'\u2ACF','csube':'\u2AD1','csup':'\u2AD0','csupe':'\u2AD2','ctdot':'\u22EF','cudarrl':'\u2938','cudarrr':'\u2935','cuepr':'\u22DE','cuesc':'\u22DF','cularr':'\u21B6','cularrp':'\u293D','cup':'\u222A','Cup':'\u22D3','cupbrcap':'\u2A48','cupcap':'\u2A46','CupCap':'\u224D','cupcup':'\u2A4A','cupdot':'\u228D','cupor':'\u2A45','cups':'\u222A\uFE00','curarr':'\u21B7','curarrm':'\u293C','curlyeqprec':'\u22DE','curlyeqsucc':'\u22DF','curlyvee':'\u22CE','curlywedge':'\u22CF','curren':'\xA4','curvearrowleft':'\u21B6','curvearrowright':'\u21B7','cuvee':'\u22CE','cuwed':'\u22CF','cwconint':'\u2232','cwint':'\u2231','cylcty':'\u232D','dagger':'\u2020','Dagger':'\u2021','daleth':'\u2138','darr':'\u2193','dArr':'\u21D3','Darr':'\u21A1','dash':'\u2010','dashv':'\u22A3','Dashv':'\u2AE4','dbkarow':'\u290F','dblac':'\u02DD','dcaron':'\u010F','Dcaron':'\u010E','dcy':'\u0434','Dcy':'\u0414','dd':'\u2146','DD':'\u2145','ddagger':'\u2021','ddarr':'\u21CA','DDotrahd':'\u2911','ddotseq':'\u2A77','deg':'\xB0','Del':'\u2207','delta':'\u03B4','Delta':'\u0394','demptyv':'\u29B1','dfisht':'\u297F','dfr':'\uD835\uDD21','Dfr':'\uD835\uDD07','dHar':'\u2965','dharl':'\u21C3','dharr':'\u21C2','DiacriticalAcute':'\xB4','DiacriticalDot':'\u02D9','DiacriticalDoubleAcute':'\u02DD','DiacriticalGrave':'`','DiacriticalTilde':'\u02DC','diam':'\u22C4','diamond':'\u22C4','Diamond':'\u22C4','diamondsuit':'\u2666','diams':'\u2666','die':'\xA8','DifferentialD':'\u2146','digamma':'\u03DD','disin':'\u22F2','div':'\xF7','divide':'\xF7','divideontimes':'\u22C7','divonx':'\u22C7','djcy':'\u0452','DJcy':'\u0402','dlcorn':'\u231E','dlcrop':'\u230D','dollar':'$','dopf':'\uD835\uDD55','Dopf':'\uD835\uDD3B','dot':'\u02D9','Dot':'\xA8','DotDot':'\u20DC','doteq':'\u2250','doteqdot':'\u2251','DotEqual':'\u2250','dotminus':'\u2238','dotplus':'\u2214','dotsquare':'\u22A1','doublebarwedge':'\u2306','DoubleContourIntegral':'\u222F','DoubleDot':'\xA8','DoubleDownArrow':'\u21D3','DoubleLeftArrow':'\u21D0','DoubleLeftRightArrow':'\u21D4','DoubleLeftTee':'\u2AE4','DoubleLongLeftArrow':'\u27F8','DoubleLongLeftRightArrow':'\u27FA','DoubleLongRightArrow':'\u27F9','DoubleRightArrow':'\u21D2','DoubleRightTee':'\u22A8','DoubleUpArrow':'\u21D1','DoubleUpDownArrow':'\u21D5','DoubleVerticalBar':'\u2225','downarrow':'\u2193','Downarrow':'\u21D3','DownArrow':'\u2193','DownArrowBar':'\u2913','DownArrowUpArrow':'\u21F5','DownBreve':'\u0311','downdownarrows':'\u21CA','downharpoonleft':'\u21C3','downharpoonright':'\u21C2','DownLeftRightVector':'\u2950','DownLeftTeeVector':'\u295E','DownLeftVector':'\u21BD','DownLeftVectorBar':'\u2956','DownRightTeeVector':'\u295F','DownRightVector':'\u21C1','DownRightVectorBar':'\u2957','DownTee':'\u22A4','DownTeeArrow':'\u21A7','drbkarow':'\u2910','drcorn':'\u231F','drcrop':'\u230C','dscr':'\uD835\uDCB9','Dscr':'\uD835\uDC9F','dscy':'\u0455','DScy':'\u0405','dsol':'\u29F6','dstrok':'\u0111','Dstrok':'\u0110','dtdot':'\u22F1','dtri':'\u25BF','dtrif':'\u25BE','duarr':'\u21F5','duhar':'\u296F','dwangle':'\u29A6','dzcy':'\u045F','DZcy':'\u040F','dzigrarr':'\u27FF','eacute':'\xE9','Eacute':'\xC9','easter':'\u2A6E','ecaron':'\u011B','Ecaron':'\u011A','ecir':'\u2256','ecirc':'\xEA','Ecirc':'\xCA','ecolon':'\u2255','ecy':'\u044D','Ecy':'\u042D','eDDot':'\u2A77','edot':'\u0117','eDot':'\u2251','Edot':'\u0116','ee':'\u2147','efDot':'\u2252','efr':'\uD835\uDD22','Efr':'\uD835\uDD08','eg':'\u2A9A','egrave':'\xE8','Egrave':'\xC8','egs':'\u2A96','egsdot':'\u2A98','el':'\u2A99','Element':'\u2208','elinters':'\u23E7','ell':'\u2113','els':'\u2A95','elsdot':'\u2A97','emacr':'\u0113','Emacr':'\u0112','empty':'\u2205','emptyset':'\u2205','EmptySmallSquare':'\u25FB','emptyv':'\u2205','EmptyVerySmallSquare':'\u25AB','emsp':'\u2003','emsp13':'\u2004','emsp14':'\u2005','eng':'\u014B','ENG':'\u014A','ensp':'\u2002','eogon':'\u0119','Eogon':'\u0118','eopf':'\uD835\uDD56','Eopf':'\uD835\uDD3C','epar':'\u22D5','eparsl':'\u29E3','eplus':'\u2A71','epsi':'\u03B5','epsilon':'\u03B5','Epsilon':'\u0395','epsiv':'\u03F5','eqcirc':'\u2256','eqcolon':'\u2255','eqsim':'\u2242','eqslantgtr':'\u2A96','eqslantless':'\u2A95','Equal':'\u2A75','equals':'=','EqualTilde':'\u2242','equest':'\u225F','Equilibrium':'\u21CC','equiv':'\u2261','equivDD':'\u2A78','eqvparsl':'\u29E5','erarr':'\u2971','erDot':'\u2253','escr':'\u212F','Escr':'\u2130','esdot':'\u2250','esim':'\u2242','Esim':'\u2A73','eta':'\u03B7','Eta':'\u0397','eth':'\xF0','ETH':'\xD0','euml':'\xEB','Euml':'\xCB','euro':'\u20AC','excl':'!','exist':'\u2203','Exists':'\u2203','expectation':'\u2130','exponentiale':'\u2147','ExponentialE':'\u2147','fallingdotseq':'\u2252','fcy':'\u0444','Fcy':'\u0424','female':'\u2640','ffilig':'\uFB03','fflig':'\uFB00','ffllig':'\uFB04','ffr':'\uD835\uDD23','Ffr':'\uD835\uDD09','filig':'\uFB01','FilledSmallSquare':'\u25FC','FilledVerySmallSquare':'\u25AA','fjlig':'fj','flat':'\u266D','fllig':'\uFB02','fltns':'\u25B1','fnof':'\u0192','fopf':'\uD835\uDD57','Fopf':'\uD835\uDD3D','forall':'\u2200','ForAll':'\u2200','fork':'\u22D4','forkv':'\u2AD9','Fouriertrf':'\u2131','fpartint':'\u2A0D','frac12':'\xBD','frac13':'\u2153','frac14':'\xBC','frac15':'\u2155','frac16':'\u2159','frac18':'\u215B','frac23':'\u2154','frac25':'\u2156','frac34':'\xBE','frac35':'\u2157','frac38':'\u215C','frac45':'\u2158','frac56':'\u215A','frac58':'\u215D','frac78':'\u215E','frasl':'\u2044','frown':'\u2322','fscr':'\uD835\uDCBB','Fscr':'\u2131','gacute':'\u01F5','gamma':'\u03B3','Gamma':'\u0393','gammad':'\u03DD','Gammad':'\u03DC','gap':'\u2A86','gbreve':'\u011F','Gbreve':'\u011E','Gcedil':'\u0122','gcirc':'\u011D','Gcirc':'\u011C','gcy':'\u0433','Gcy':'\u0413','gdot':'\u0121','Gdot':'\u0120','ge':'\u2265','gE':'\u2267','gel':'\u22DB','gEl':'\u2A8C','geq':'\u2265','geqq':'\u2267','geqslant':'\u2A7E','ges':'\u2A7E','gescc':'\u2AA9','gesdot':'\u2A80','gesdoto':'\u2A82','gesdotol':'\u2A84','gesl':'\u22DB\uFE00','gesles':'\u2A94','gfr':'\uD835\uDD24','Gfr':'\uD835\uDD0A','gg':'\u226B','Gg':'\u22D9','ggg':'\u22D9','gimel':'\u2137','gjcy':'\u0453','GJcy':'\u0403','gl':'\u2277','gla':'\u2AA5','glE':'\u2A92','glj':'\u2AA4','gnap':'\u2A8A','gnapprox':'\u2A8A','gne':'\u2A88','gnE':'\u2269','gneq':'\u2A88','gneqq':'\u2269','gnsim':'\u22E7','gopf':'\uD835\uDD58','Gopf':'\uD835\uDD3E','grave':'`','GreaterEqual':'\u2265','GreaterEqualLess':'\u22DB','GreaterFullEqual':'\u2267','GreaterGreater':'\u2AA2','GreaterLess':'\u2277','GreaterSlantEqual':'\u2A7E','GreaterTilde':'\u2273','gscr':'\u210A','Gscr':'\uD835\uDCA2','gsim':'\u2273','gsime':'\u2A8E','gsiml':'\u2A90','gt':'>','Gt':'\u226B','GT':'>','gtcc':'\u2AA7','gtcir':'\u2A7A','gtdot':'\u22D7','gtlPar':'\u2995','gtquest':'\u2A7C','gtrapprox':'\u2A86','gtrarr':'\u2978','gtrdot':'\u22D7','gtreqless':'\u22DB','gtreqqless':'\u2A8C','gtrless':'\u2277','gtrsim':'\u2273','gvertneqq':'\u2269\uFE00','gvnE':'\u2269\uFE00','Hacek':'\u02C7','hairsp':'\u200A','half':'\xBD','hamilt':'\u210B','hardcy':'\u044A','HARDcy':'\u042A','harr':'\u2194','hArr':'\u21D4','harrcir':'\u2948','harrw':'\u21AD','Hat':'^','hbar':'\u210F','hcirc':'\u0125','Hcirc':'\u0124','hearts':'\u2665','heartsuit':'\u2665','hellip':'\u2026','hercon':'\u22B9','hfr':'\uD835\uDD25','Hfr':'\u210C','HilbertSpace':'\u210B','hksearow':'\u2925','hkswarow':'\u2926','hoarr':'\u21FF','homtht':'\u223B','hookleftarrow':'\u21A9','hookrightarrow':'\u21AA','hopf':'\uD835\uDD59','Hopf':'\u210D','horbar':'\u2015','HorizontalLine':'\u2500','hscr':'\uD835\uDCBD','Hscr':'\u210B','hslash':'\u210F','hstrok':'\u0127','Hstrok':'\u0126','HumpDownHump':'\u224E','HumpEqual':'\u224F','hybull':'\u2043','hyphen':'\u2010','iacute':'\xED','Iacute':'\xCD','ic':'\u2063','icirc':'\xEE','Icirc':'\xCE','icy':'\u0438','Icy':'\u0418','Idot':'\u0130','iecy':'\u0435','IEcy':'\u0415','iexcl':'\xA1','iff':'\u21D4','ifr':'\uD835\uDD26','Ifr':'\u2111','igrave':'\xEC','Igrave':'\xCC','ii':'\u2148','iiiint':'\u2A0C','iiint':'\u222D','iinfin':'\u29DC','iiota':'\u2129','ijlig':'\u0133','IJlig':'\u0132','Im':'\u2111','imacr':'\u012B','Imacr':'\u012A','image':'\u2111','ImaginaryI':'\u2148','imagline':'\u2110','imagpart':'\u2111','imath':'\u0131','imof':'\u22B7','imped':'\u01B5','Implies':'\u21D2','in':'\u2208','incare':'\u2105','infin':'\u221E','infintie':'\u29DD','inodot':'\u0131','int':'\u222B','Int':'\u222C','intcal':'\u22BA','integers':'\u2124','Integral':'\u222B','intercal':'\u22BA','Intersection':'\u22C2','intlarhk':'\u2A17','intprod':'\u2A3C','InvisibleComma':'\u2063','InvisibleTimes':'\u2062','iocy':'\u0451','IOcy':'\u0401','iogon':'\u012F','Iogon':'\u012E','iopf':'\uD835\uDD5A','Iopf':'\uD835\uDD40','iota':'\u03B9','Iota':'\u0399','iprod':'\u2A3C','iquest':'\xBF','iscr':'\uD835\uDCBE','Iscr':'\u2110','isin':'\u2208','isindot':'\u22F5','isinE':'\u22F9','isins':'\u22F4','isinsv':'\u22F3','isinv':'\u2208','it':'\u2062','itilde':'\u0129','Itilde':'\u0128','iukcy':'\u0456','Iukcy':'\u0406','iuml':'\xEF','Iuml':'\xCF','jcirc':'\u0135','Jcirc':'\u0134','jcy':'\u0439','Jcy':'\u0419','jfr':'\uD835\uDD27','Jfr':'\uD835\uDD0D','jmath':'\u0237','jopf':'\uD835\uDD5B','Jopf':'\uD835\uDD41','jscr':'\uD835\uDCBF','Jscr':'\uD835\uDCA5','jsercy':'\u0458','Jsercy':'\u0408','jukcy':'\u0454','Jukcy':'\u0404','kappa':'\u03BA','Kappa':'\u039A','kappav':'\u03F0','kcedil':'\u0137','Kcedil':'\u0136','kcy':'\u043A','Kcy':'\u041A','kfr':'\uD835\uDD28','Kfr':'\uD835\uDD0E','kgreen':'\u0138','khcy':'\u0445','KHcy':'\u0425','kjcy':'\u045C','KJcy':'\u040C','kopf':'\uD835\uDD5C','Kopf':'\uD835\uDD42','kscr':'\uD835\uDCC0','Kscr':'\uD835\uDCA6','lAarr':'\u21DA','lacute':'\u013A','Lacute':'\u0139','laemptyv':'\u29B4','lagran':'\u2112','lambda':'\u03BB','Lambda':'\u039B','lang':'\u27E8','Lang':'\u27EA','langd':'\u2991','langle':'\u27E8','lap':'\u2A85','Laplacetrf':'\u2112','laquo':'\xAB','larr':'\u2190','lArr':'\u21D0','Larr':'\u219E','larrb':'\u21E4','larrbfs':'\u291F','larrfs':'\u291D','larrhk':'\u21A9','larrlp':'\u21AB','larrpl':'\u2939','larrsim':'\u2973','larrtl':'\u21A2','lat':'\u2AAB','latail':'\u2919','lAtail':'\u291B','late':'\u2AAD','lates':'\u2AAD\uFE00','lbarr':'\u290C','lBarr':'\u290E','lbbrk':'\u2772','lbrace':'{','lbrack':'[','lbrke':'\u298B','lbrksld':'\u298F','lbrkslu':'\u298D','lcaron':'\u013E','Lcaron':'\u013D','lcedil':'\u013C','Lcedil':'\u013B','lceil':'\u2308','lcub':'{','lcy':'\u043B','Lcy':'\u041B','ldca':'\u2936','ldquo':'\u201C','ldquor':'\u201E','ldrdhar':'\u2967','ldrushar':'\u294B','ldsh':'\u21B2','le':'\u2264','lE':'\u2266','LeftAngleBracket':'\u27E8','leftarrow':'\u2190','Leftarrow':'\u21D0','LeftArrow':'\u2190','LeftArrowBar':'\u21E4','LeftArrowRightArrow':'\u21C6','leftarrowtail':'\u21A2','LeftCeiling':'\u2308','LeftDoubleBracket':'\u27E6','LeftDownTeeVector':'\u2961','LeftDownVector':'\u21C3','LeftDownVectorBar':'\u2959','LeftFloor':'\u230A','leftharpoondown':'\u21BD','leftharpoonup':'\u21BC','leftleftarrows':'\u21C7','leftrightarrow':'\u2194','Leftrightarrow':'\u21D4','LeftRightArrow':'\u2194','leftrightarrows':'\u21C6','leftrightharpoons':'\u21CB','leftrightsquigarrow':'\u21AD','LeftRightVector':'\u294E','LeftTee':'\u22A3','LeftTeeArrow':'\u21A4','LeftTeeVector':'\u295A','leftthreetimes':'\u22CB','LeftTriangle':'\u22B2','LeftTriangleBar':'\u29CF','LeftTriangleEqual':'\u22B4','LeftUpDownVector':'\u2951','LeftUpTeeVector':'\u2960','LeftUpVector':'\u21BF','LeftUpVectorBar':'\u2958','LeftVector':'\u21BC','LeftVectorBar':'\u2952','leg':'\u22DA','lEg':'\u2A8B','leq':'\u2264','leqq':'\u2266','leqslant':'\u2A7D','les':'\u2A7D','lescc':'\u2AA8','lesdot':'\u2A7F','lesdoto':'\u2A81','lesdotor':'\u2A83','lesg':'\u22DA\uFE00','lesges':'\u2A93','lessapprox':'\u2A85','lessdot':'\u22D6','lesseqgtr':'\u22DA','lesseqqgtr':'\u2A8B','LessEqualGreater':'\u22DA','LessFullEqual':'\u2266','LessGreater':'\u2276','lessgtr':'\u2276','LessLess':'\u2AA1','lesssim':'\u2272','LessSlantEqual':'\u2A7D','LessTilde':'\u2272','lfisht':'\u297C','lfloor':'\u230A','lfr':'\uD835\uDD29','Lfr':'\uD835\uDD0F','lg':'\u2276','lgE':'\u2A91','lHar':'\u2962','lhard':'\u21BD','lharu':'\u21BC','lharul':'\u296A','lhblk':'\u2584','ljcy':'\u0459','LJcy':'\u0409','ll':'\u226A','Ll':'\u22D8','llarr':'\u21C7','llcorner':'\u231E','Lleftarrow':'\u21DA','llhard':'\u296B','lltri':'\u25FA','lmidot':'\u0140','Lmidot':'\u013F','lmoust':'\u23B0','lmoustache':'\u23B0','lnap':'\u2A89','lnapprox':'\u2A89','lne':'\u2A87','lnE':'\u2268','lneq':'\u2A87','lneqq':'\u2268','lnsim':'\u22E6','loang':'\u27EC','loarr':'\u21FD','lobrk':'\u27E6','longleftarrow':'\u27F5','Longleftarrow':'\u27F8','LongLeftArrow':'\u27F5','longleftrightarrow':'\u27F7','Longleftrightarrow':'\u27FA','LongLeftRightArrow':'\u27F7','longmapsto':'\u27FC','longrightarrow':'\u27F6','Longrightarrow':'\u27F9','LongRightArrow':'\u27F6','looparrowleft':'\u21AB','looparrowright':'\u21AC','lopar':'\u2985','lopf':'\uD835\uDD5D','Lopf':'\uD835\uDD43','loplus':'\u2A2D','lotimes':'\u2A34','lowast':'\u2217','lowbar':'_','LowerLeftArrow':'\u2199','LowerRightArrow':'\u2198','loz':'\u25CA','lozenge':'\u25CA','lozf':'\u29EB','lpar':'(','lparlt':'\u2993','lrarr':'\u21C6','lrcorner':'\u231F','lrhar':'\u21CB','lrhard':'\u296D','lrm':'\u200E','lrtri':'\u22BF','lsaquo':'\u2039','lscr':'\uD835\uDCC1','Lscr':'\u2112','lsh':'\u21B0','Lsh':'\u21B0','lsim':'\u2272','lsime':'\u2A8D','lsimg':'\u2A8F','lsqb':'[','lsquo':'\u2018','lsquor':'\u201A','lstrok':'\u0142','Lstrok':'\u0141','lt':'<','Lt':'\u226A','LT':'<','ltcc':'\u2AA6','ltcir':'\u2A79','ltdot':'\u22D6','lthree':'\u22CB','ltimes':'\u22C9','ltlarr':'\u2976','ltquest':'\u2A7B','ltri':'\u25C3','ltrie':'\u22B4','ltrif':'\u25C2','ltrPar':'\u2996','lurdshar':'\u294A','luruhar':'\u2966','lvertneqq':'\u2268\uFE00','lvnE':'\u2268\uFE00','macr':'\xAF','male':'\u2642','malt':'\u2720','maltese':'\u2720','map':'\u21A6','Map':'\u2905','mapsto':'\u21A6','mapstodown':'\u21A7','mapstoleft':'\u21A4','mapstoup':'\u21A5','marker':'\u25AE','mcomma':'\u2A29','mcy':'\u043C','Mcy':'\u041C','mdash':'\u2014','mDDot':'\u223A','measuredangle':'\u2221','MediumSpace':'\u205F','Mellintrf':'\u2133','mfr':'\uD835\uDD2A','Mfr':'\uD835\uDD10','mho':'\u2127','micro':'\xB5','mid':'\u2223','midast':'*','midcir':'\u2AF0','middot':'\xB7','minus':'\u2212','minusb':'\u229F','minusd':'\u2238','minusdu':'\u2A2A','MinusPlus':'\u2213','mlcp':'\u2ADB','mldr':'\u2026','mnplus':'\u2213','models':'\u22A7','mopf':'\uD835\uDD5E','Mopf':'\uD835\uDD44','mp':'\u2213','mscr':'\uD835\uDCC2','Mscr':'\u2133','mstpos':'\u223E','mu':'\u03BC','Mu':'\u039C','multimap':'\u22B8','mumap':'\u22B8','nabla':'\u2207','nacute':'\u0144','Nacute':'\u0143','nang':'\u2220\u20D2','nap':'\u2249','napE':'\u2A70\u0338','napid':'\u224B\u0338','napos':'\u0149','napprox':'\u2249','natur':'\u266E','natural':'\u266E','naturals':'\u2115','nbsp':'\xA0','nbump':'\u224E\u0338','nbumpe':'\u224F\u0338','ncap':'\u2A43','ncaron':'\u0148','Ncaron':'\u0147','ncedil':'\u0146','Ncedil':'\u0145','ncong':'\u2247','ncongdot':'\u2A6D\u0338','ncup':'\u2A42','ncy':'\u043D','Ncy':'\u041D','ndash':'\u2013','ne':'\u2260','nearhk':'\u2924','nearr':'\u2197','neArr':'\u21D7','nearrow':'\u2197','nedot':'\u2250\u0338','NegativeMediumSpace':'\u200B','NegativeThickSpace':'\u200B','NegativeThinSpace':'\u200B','NegativeVeryThinSpace':'\u200B','nequiv':'\u2262','nesear':'\u2928','nesim':'\u2242\u0338','NestedGreaterGreater':'\u226B','NestedLessLess':'\u226A','NewLine':'\n','nexist':'\u2204','nexists':'\u2204','nfr':'\uD835\uDD2B','Nfr':'\uD835\uDD11','nge':'\u2271','ngE':'\u2267\u0338','ngeq':'\u2271','ngeqq':'\u2267\u0338','ngeqslant':'\u2A7E\u0338','nges':'\u2A7E\u0338','nGg':'\u22D9\u0338','ngsim':'\u2275','ngt':'\u226F','nGt':'\u226B\u20D2','ngtr':'\u226F','nGtv':'\u226B\u0338','nharr':'\u21AE','nhArr':'\u21CE','nhpar':'\u2AF2','ni':'\u220B','nis':'\u22FC','nisd':'\u22FA','niv':'\u220B','njcy':'\u045A','NJcy':'\u040A','nlarr':'\u219A','nlArr':'\u21CD','nldr':'\u2025','nle':'\u2270','nlE':'\u2266\u0338','nleftarrow':'\u219A','nLeftarrow':'\u21CD','nleftrightarrow':'\u21AE','nLeftrightarrow':'\u21CE','nleq':'\u2270','nleqq':'\u2266\u0338','nleqslant':'\u2A7D\u0338','nles':'\u2A7D\u0338','nless':'\u226E','nLl':'\u22D8\u0338','nlsim':'\u2274','nlt':'\u226E','nLt':'\u226A\u20D2','nltri':'\u22EA','nltrie':'\u22EC','nLtv':'\u226A\u0338','nmid':'\u2224','NoBreak':'\u2060','NonBreakingSpace':'\xA0','nopf':'\uD835\uDD5F','Nopf':'\u2115','not':'\xAC','Not':'\u2AEC','NotCongruent':'\u2262','NotCupCap':'\u226D','NotDoubleVerticalBar':'\u2226','NotElement':'\u2209','NotEqual':'\u2260','NotEqualTilde':'\u2242\u0338','NotExists':'\u2204','NotGreater':'\u226F','NotGreaterEqual':'\u2271','NotGreaterFullEqual':'\u2267\u0338','NotGreaterGreater':'\u226B\u0338','NotGreaterLess':'\u2279','NotGreaterSlantEqual':'\u2A7E\u0338','NotGreaterTilde':'\u2275','NotHumpDownHump':'\u224E\u0338','NotHumpEqual':'\u224F\u0338','notin':'\u2209','notindot':'\u22F5\u0338','notinE':'\u22F9\u0338','notinva':'\u2209','notinvb':'\u22F7','notinvc':'\u22F6','NotLeftTriangle':'\u22EA','NotLeftTriangleBar':'\u29CF\u0338','NotLeftTriangleEqual':'\u22EC','NotLess':'\u226E','NotLessEqual':'\u2270','NotLessGreater':'\u2278','NotLessLess':'\u226A\u0338','NotLessSlantEqual':'\u2A7D\u0338','NotLessTilde':'\u2274','NotNestedGreaterGreater':'\u2AA2\u0338','NotNestedLessLess':'\u2AA1\u0338','notni':'\u220C','notniva':'\u220C','notnivb':'\u22FE','notnivc':'\u22FD','NotPrecedes':'\u2280','NotPrecedesEqual':'\u2AAF\u0338','NotPrecedesSlantEqual':'\u22E0','NotReverseElement':'\u220C','NotRightTriangle':'\u22EB','NotRightTriangleBar':'\u29D0\u0338','NotRightTriangleEqual':'\u22ED','NotSquareSubset':'\u228F\u0338','NotSquareSubsetEqual':'\u22E2','NotSquareSuperset':'\u2290\u0338','NotSquareSupersetEqual':'\u22E3','NotSubset':'\u2282\u20D2','NotSubsetEqual':'\u2288','NotSucceeds':'\u2281','NotSucceedsEqual':'\u2AB0\u0338','NotSucceedsSlantEqual':'\u22E1','NotSucceedsTilde':'\u227F\u0338','NotSuperset':'\u2283\u20D2','NotSupersetEqual':'\u2289','NotTilde':'\u2241','NotTildeEqual':'\u2244','NotTildeFullEqual':'\u2247','NotTildeTilde':'\u2249','NotVerticalBar':'\u2224','npar':'\u2226','nparallel':'\u2226','nparsl':'\u2AFD\u20E5','npart':'\u2202\u0338','npolint':'\u2A14','npr':'\u2280','nprcue':'\u22E0','npre':'\u2AAF\u0338','nprec':'\u2280','npreceq':'\u2AAF\u0338','nrarr':'\u219B','nrArr':'\u21CF','nrarrc':'\u2933\u0338','nrarrw':'\u219D\u0338','nrightarrow':'\u219B','nRightarrow':'\u21CF','nrtri':'\u22EB','nrtrie':'\u22ED','nsc':'\u2281','nsccue':'\u22E1','nsce':'\u2AB0\u0338','nscr':'\uD835\uDCC3','Nscr':'\uD835\uDCA9','nshortmid':'\u2224','nshortparallel':'\u2226','nsim':'\u2241','nsime':'\u2244','nsimeq':'\u2244','nsmid':'\u2224','nspar':'\u2226','nsqsube':'\u22E2','nsqsupe':'\u22E3','nsub':'\u2284','nsube':'\u2288','nsubE':'\u2AC5\u0338','nsubset':'\u2282\u20D2','nsubseteq':'\u2288','nsubseteqq':'\u2AC5\u0338','nsucc':'\u2281','nsucceq':'\u2AB0\u0338','nsup':'\u2285','nsupe':'\u2289','nsupE':'\u2AC6\u0338','nsupset':'\u2283\u20D2','nsupseteq':'\u2289','nsupseteqq':'\u2AC6\u0338','ntgl':'\u2279','ntilde':'\xF1','Ntilde':'\xD1','ntlg':'\u2278','ntriangleleft':'\u22EA','ntrianglelefteq':'\u22EC','ntriangleright':'\u22EB','ntrianglerighteq':'\u22ED','nu':'\u03BD','Nu':'\u039D','num':'#','numero':'\u2116','numsp':'\u2007','nvap':'\u224D\u20D2','nvdash':'\u22AC','nvDash':'\u22AD','nVdash':'\u22AE','nVDash':'\u22AF','nvge':'\u2265\u20D2','nvgt':'>\u20D2','nvHarr':'\u2904','nvinfin':'\u29DE','nvlArr':'\u2902','nvle':'\u2264\u20D2','nvlt':'<\u20D2','nvltrie':'\u22B4\u20D2','nvrArr':'\u2903','nvrtrie':'\u22B5\u20D2','nvsim':'\u223C\u20D2','nwarhk':'\u2923','nwarr':'\u2196','nwArr':'\u21D6','nwarrow':'\u2196','nwnear':'\u2927','oacute':'\xF3','Oacute':'\xD3','oast':'\u229B','ocir':'\u229A','ocirc':'\xF4','Ocirc':'\xD4','ocy':'\u043E','Ocy':'\u041E','odash':'\u229D','odblac':'\u0151','Odblac':'\u0150','odiv':'\u2A38','odot':'\u2299','odsold':'\u29BC','oelig':'\u0153','OElig':'\u0152','ofcir':'\u29BF','ofr':'\uD835\uDD2C','Ofr':'\uD835\uDD12','ogon':'\u02DB','ograve':'\xF2','Ograve':'\xD2','ogt':'\u29C1','ohbar':'\u29B5','ohm':'\u03A9','oint':'\u222E','olarr':'\u21BA','olcir':'\u29BE','olcross':'\u29BB','oline':'\u203E','olt':'\u29C0','omacr':'\u014D','Omacr':'\u014C','omega':'\u03C9','Omega':'\u03A9','omicron':'\u03BF','Omicron':'\u039F','omid':'\u29B6','ominus':'\u2296','oopf':'\uD835\uDD60','Oopf':'\uD835\uDD46','opar':'\u29B7','OpenCurlyDoubleQuote':'\u201C','OpenCurlyQuote':'\u2018','operp':'\u29B9','oplus':'\u2295','or':'\u2228','Or':'\u2A54','orarr':'\u21BB','ord':'\u2A5D','order':'\u2134','orderof':'\u2134','ordf':'\xAA','ordm':'\xBA','origof':'\u22B6','oror':'\u2A56','orslope':'\u2A57','orv':'\u2A5B','oS':'\u24C8','oscr':'\u2134','Oscr':'\uD835\uDCAA','oslash':'\xF8','Oslash':'\xD8','osol':'\u2298','otilde':'\xF5','Otilde':'\xD5','otimes':'\u2297','Otimes':'\u2A37','otimesas':'\u2A36','ouml':'\xF6','Ouml':'\xD6','ovbar':'\u233D','OverBar':'\u203E','OverBrace':'\u23DE','OverBracket':'\u23B4','OverParenthesis':'\u23DC','par':'\u2225','para':'\xB6','parallel':'\u2225','parsim':'\u2AF3','parsl':'\u2AFD','part':'\u2202','PartialD':'\u2202','pcy':'\u043F','Pcy':'\u041F','percnt':'%','period':'.','permil':'\u2030','perp':'\u22A5','pertenk':'\u2031','pfr':'\uD835\uDD2D','Pfr':'\uD835\uDD13','phi':'\u03C6','Phi':'\u03A6','phiv':'\u03D5','phmmat':'\u2133','phone':'\u260E','pi':'\u03C0','Pi':'\u03A0','pitchfork':'\u22D4','piv':'\u03D6','planck':'\u210F','planckh':'\u210E','plankv':'\u210F','plus':'+','plusacir':'\u2A23','plusb':'\u229E','pluscir':'\u2A22','plusdo':'\u2214','plusdu':'\u2A25','pluse':'\u2A72','PlusMinus':'\xB1','plusmn':'\xB1','plussim':'\u2A26','plustwo':'\u2A27','pm':'\xB1','Poincareplane':'\u210C','pointint':'\u2A15','popf':'\uD835\uDD61','Popf':'\u2119','pound':'\xA3','pr':'\u227A','Pr':'\u2ABB','prap':'\u2AB7','prcue':'\u227C','pre':'\u2AAF','prE':'\u2AB3','prec':'\u227A','precapprox':'\u2AB7','preccurlyeq':'\u227C','Precedes':'\u227A','PrecedesEqual':'\u2AAF','PrecedesSlantEqual':'\u227C','PrecedesTilde':'\u227E','preceq':'\u2AAF','precnapprox':'\u2AB9','precneqq':'\u2AB5','precnsim':'\u22E8','precsim':'\u227E','prime':'\u2032','Prime':'\u2033','primes':'\u2119','prnap':'\u2AB9','prnE':'\u2AB5','prnsim':'\u22E8','prod':'\u220F','Product':'\u220F','profalar':'\u232E','profline':'\u2312','profsurf':'\u2313','prop':'\u221D','Proportion':'\u2237','Proportional':'\u221D','propto':'\u221D','prsim':'\u227E','prurel':'\u22B0','pscr':'\uD835\uDCC5','Pscr':'\uD835\uDCAB','psi':'\u03C8','Psi':'\u03A8','puncsp':'\u2008','qfr':'\uD835\uDD2E','Qfr':'\uD835\uDD14','qint':'\u2A0C','qopf':'\uD835\uDD62','Qopf':'\u211A','qprime':'\u2057','qscr':'\uD835\uDCC6','Qscr':'\uD835\uDCAC','quaternions':'\u210D','quatint':'\u2A16','quest':'?','questeq':'\u225F','quot':'"','QUOT':'"','rAarr':'\u21DB','race':'\u223D\u0331','racute':'\u0155','Racute':'\u0154','radic':'\u221A','raemptyv':'\u29B3','rang':'\u27E9','Rang':'\u27EB','rangd':'\u2992','range':'\u29A5','rangle':'\u27E9','raquo':'\xBB','rarr':'\u2192','rArr':'\u21D2','Rarr':'\u21A0','rarrap':'\u2975','rarrb':'\u21E5','rarrbfs':'\u2920','rarrc':'\u2933','rarrfs':'\u291E','rarrhk':'\u21AA','rarrlp':'\u21AC','rarrpl':'\u2945','rarrsim':'\u2974','rarrtl':'\u21A3','Rarrtl':'\u2916','rarrw':'\u219D','ratail':'\u291A','rAtail':'\u291C','ratio':'\u2236','rationals':'\u211A','rbarr':'\u290D','rBarr':'\u290F','RBarr':'\u2910','rbbrk':'\u2773','rbrace':'}','rbrack':']','rbrke':'\u298C','rbrksld':'\u298E','rbrkslu':'\u2990','rcaron':'\u0159','Rcaron':'\u0158','rcedil':'\u0157','Rcedil':'\u0156','rceil':'\u2309','rcub':'}','rcy':'\u0440','Rcy':'\u0420','rdca':'\u2937','rdldhar':'\u2969','rdquo':'\u201D','rdquor':'\u201D','rdsh':'\u21B3','Re':'\u211C','real':'\u211C','realine':'\u211B','realpart':'\u211C','reals':'\u211D','rect':'\u25AD','reg':'\xAE','REG':'\xAE','ReverseElement':'\u220B','ReverseEquilibrium':'\u21CB','ReverseUpEquilibrium':'\u296F','rfisht':'\u297D','rfloor':'\u230B','rfr':'\uD835\uDD2F','Rfr':'\u211C','rHar':'\u2964','rhard':'\u21C1','rharu':'\u21C0','rharul':'\u296C','rho':'\u03C1','Rho':'\u03A1','rhov':'\u03F1','RightAngleBracket':'\u27E9','rightarrow':'\u2192','Rightarrow':'\u21D2','RightArrow':'\u2192','RightArrowBar':'\u21E5','RightArrowLeftArrow':'\u21C4','rightarrowtail':'\u21A3','RightCeiling':'\u2309','RightDoubleBracket':'\u27E7','RightDownTeeVector':'\u295D','RightDownVector':'\u21C2','RightDownVectorBar':'\u2955','RightFloor':'\u230B','rightharpoondown':'\u21C1','rightharpoonup':'\u21C0','rightleftarrows':'\u21C4','rightleftharpoons':'\u21CC','rightrightarrows':'\u21C9','rightsquigarrow':'\u219D','RightTee':'\u22A2','RightTeeArrow':'\u21A6','RightTeeVector':'\u295B','rightthreetimes':'\u22CC','RightTriangle':'\u22B3','RightTriangleBar':'\u29D0','RightTriangleEqual':'\u22B5','RightUpDownVector':'\u294F','RightUpTeeVector':'\u295C','RightUpVector':'\u21BE','RightUpVectorBar':'\u2954','RightVector':'\u21C0','RightVectorBar':'\u2953','ring':'\u02DA','risingdotseq':'\u2253','rlarr':'\u21C4','rlhar':'\u21CC','rlm':'\u200F','rmoust':'\u23B1','rmoustache':'\u23B1','rnmid':'\u2AEE','roang':'\u27ED','roarr':'\u21FE','robrk':'\u27E7','ropar':'\u2986','ropf':'\uD835\uDD63','Ropf':'\u211D','roplus':'\u2A2E','rotimes':'\u2A35','RoundImplies':'\u2970','rpar':')','rpargt':'\u2994','rppolint':'\u2A12','rrarr':'\u21C9','Rrightarrow':'\u21DB','rsaquo':'\u203A','rscr':'\uD835\uDCC7','Rscr':'\u211B','rsh':'\u21B1','Rsh':'\u21B1','rsqb':']','rsquo':'\u2019','rsquor':'\u2019','rthree':'\u22CC','rtimes':'\u22CA','rtri':'\u25B9','rtrie':'\u22B5','rtrif':'\u25B8','rtriltri':'\u29CE','RuleDelayed':'\u29F4','ruluhar':'\u2968','rx':'\u211E','sacute':'\u015B','Sacute':'\u015A','sbquo':'\u201A','sc':'\u227B','Sc':'\u2ABC','scap':'\u2AB8','scaron':'\u0161','Scaron':'\u0160','sccue':'\u227D','sce':'\u2AB0','scE':'\u2AB4','scedil':'\u015F','Scedil':'\u015E','scirc':'\u015D','Scirc':'\u015C','scnap':'\u2ABA','scnE':'\u2AB6','scnsim':'\u22E9','scpolint':'\u2A13','scsim':'\u227F','scy':'\u0441','Scy':'\u0421','sdot':'\u22C5','sdotb':'\u22A1','sdote':'\u2A66','searhk':'\u2925','searr':'\u2198','seArr':'\u21D8','searrow':'\u2198','sect':'\xA7','semi':';','seswar':'\u2929','setminus':'\u2216','setmn':'\u2216','sext':'\u2736','sfr':'\uD835\uDD30','Sfr':'\uD835\uDD16','sfrown':'\u2322','sharp':'\u266F','shchcy':'\u0449','SHCHcy':'\u0429','shcy':'\u0448','SHcy':'\u0428','ShortDownArrow':'\u2193','ShortLeftArrow':'\u2190','shortmid':'\u2223','shortparallel':'\u2225','ShortRightArrow':'\u2192','ShortUpArrow':'\u2191','shy':'\xAD','sigma':'\u03C3','Sigma':'\u03A3','sigmaf':'\u03C2','sigmav':'\u03C2','sim':'\u223C','simdot':'\u2A6A','sime':'\u2243','simeq':'\u2243','simg':'\u2A9E','simgE':'\u2AA0','siml':'\u2A9D','simlE':'\u2A9F','simne':'\u2246','simplus':'\u2A24','simrarr':'\u2972','slarr':'\u2190','SmallCircle':'\u2218','smallsetminus':'\u2216','smashp':'\u2A33','smeparsl':'\u29E4','smid':'\u2223','smile':'\u2323','smt':'\u2AAA','smte':'\u2AAC','smtes':'\u2AAC\uFE00','softcy':'\u044C','SOFTcy':'\u042C','sol':'/','solb':'\u29C4','solbar':'\u233F','sopf':'\uD835\uDD64','Sopf':'\uD835\uDD4A','spades':'\u2660','spadesuit':'\u2660','spar':'\u2225','sqcap':'\u2293','sqcaps':'\u2293\uFE00','sqcup':'\u2294','sqcups':'\u2294\uFE00','Sqrt':'\u221A','sqsub':'\u228F','sqsube':'\u2291','sqsubset':'\u228F','sqsubseteq':'\u2291','sqsup':'\u2290','sqsupe':'\u2292','sqsupset':'\u2290','sqsupseteq':'\u2292','squ':'\u25A1','square':'\u25A1','Square':'\u25A1','SquareIntersection':'\u2293','SquareSubset':'\u228F','SquareSubsetEqual':'\u2291','SquareSuperset':'\u2290','SquareSupersetEqual':'\u2292','SquareUnion':'\u2294','squarf':'\u25AA','squf':'\u25AA','srarr':'\u2192','sscr':'\uD835\uDCC8','Sscr':'\uD835\uDCAE','ssetmn':'\u2216','ssmile':'\u2323','sstarf':'\u22C6','star':'\u2606','Star':'\u22C6','starf':'\u2605','straightepsilon':'\u03F5','straightphi':'\u03D5','strns':'\xAF','sub':'\u2282','Sub':'\u22D0','subdot':'\u2ABD','sube':'\u2286','subE':'\u2AC5','subedot':'\u2AC3','submult':'\u2AC1','subne':'\u228A','subnE':'\u2ACB','subplus':'\u2ABF','subrarr':'\u2979','subset':'\u2282','Subset':'\u22D0','subseteq':'\u2286','subseteqq':'\u2AC5','SubsetEqual':'\u2286','subsetneq':'\u228A','subsetneqq':'\u2ACB','subsim':'\u2AC7','subsub':'\u2AD5','subsup':'\u2AD3','succ':'\u227B','succapprox':'\u2AB8','succcurlyeq':'\u227D','Succeeds':'\u227B','SucceedsEqual':'\u2AB0','SucceedsSlantEqual':'\u227D','SucceedsTilde':'\u227F','succeq':'\u2AB0','succnapprox':'\u2ABA','succneqq':'\u2AB6','succnsim':'\u22E9','succsim':'\u227F','SuchThat':'\u220B','sum':'\u2211','Sum':'\u2211','sung':'\u266A','sup':'\u2283','Sup':'\u22D1','sup1':'\xB9','sup2':'\xB2','sup3':'\xB3','supdot':'\u2ABE','supdsub':'\u2AD8','supe':'\u2287','supE':'\u2AC6','supedot':'\u2AC4','Superset':'\u2283','SupersetEqual':'\u2287','suphsol':'\u27C9','suphsub':'\u2AD7','suplarr':'\u297B','supmult':'\u2AC2','supne':'\u228B','supnE':'\u2ACC','supplus':'\u2AC0','supset':'\u2283','Supset':'\u22D1','supseteq':'\u2287','supseteqq':'\u2AC6','supsetneq':'\u228B','supsetneqq':'\u2ACC','supsim':'\u2AC8','supsub':'\u2AD4','supsup':'\u2AD6','swarhk':'\u2926','swarr':'\u2199','swArr':'\u21D9','swarrow':'\u2199','swnwar':'\u292A','szlig':'\xDF','Tab':'\t','target':'\u2316','tau':'\u03C4','Tau':'\u03A4','tbrk':'\u23B4','tcaron':'\u0165','Tcaron':'\u0164','tcedil':'\u0163','Tcedil':'\u0162','tcy':'\u0442','Tcy':'\u0422','tdot':'\u20DB','telrec':'\u2315','tfr':'\uD835\uDD31','Tfr':'\uD835\uDD17','there4':'\u2234','therefore':'\u2234','Therefore':'\u2234','theta':'\u03B8','Theta':'\u0398','thetasym':'\u03D1','thetav':'\u03D1','thickapprox':'\u2248','thicksim':'\u223C','ThickSpace':'\u205F\u200A','thinsp':'\u2009','ThinSpace':'\u2009','thkap':'\u2248','thksim':'\u223C','thorn':'\xFE','THORN':'\xDE','tilde':'\u02DC','Tilde':'\u223C','TildeEqual':'\u2243','TildeFullEqual':'\u2245','TildeTilde':'\u2248','times':'\xD7','timesb':'\u22A0','timesbar':'\u2A31','timesd':'\u2A30','tint':'\u222D','toea':'\u2928','top':'\u22A4','topbot':'\u2336','topcir':'\u2AF1','topf':'\uD835\uDD65','Topf':'\uD835\uDD4B','topfork':'\u2ADA','tosa':'\u2929','tprime':'\u2034','trade':'\u2122','TRADE':'\u2122','triangle':'\u25B5','triangledown':'\u25BF','triangleleft':'\u25C3','trianglelefteq':'\u22B4','triangleq':'\u225C','triangleright':'\u25B9','trianglerighteq':'\u22B5','tridot':'\u25EC','trie':'\u225C','triminus':'\u2A3A','TripleDot':'\u20DB','triplus':'\u2A39','trisb':'\u29CD','tritime':'\u2A3B','trpezium':'\u23E2','tscr':'\uD835\uDCC9','Tscr':'\uD835\uDCAF','tscy':'\u0446','TScy':'\u0426','tshcy':'\u045B','TSHcy':'\u040B','tstrok':'\u0167','Tstrok':'\u0166','twixt':'\u226C','twoheadleftarrow':'\u219E','twoheadrightarrow':'\u21A0','uacute':'\xFA','Uacute':'\xDA','uarr':'\u2191','uArr':'\u21D1','Uarr':'\u219F','Uarrocir':'\u2949','ubrcy':'\u045E','Ubrcy':'\u040E','ubreve':'\u016D','Ubreve':'\u016C','ucirc':'\xFB','Ucirc':'\xDB','ucy':'\u0443','Ucy':'\u0423','udarr':'\u21C5','udblac':'\u0171','Udblac':'\u0170','udhar':'\u296E','ufisht':'\u297E','ufr':'\uD835\uDD32','Ufr':'\uD835\uDD18','ugrave':'\xF9','Ugrave':'\xD9','uHar':'\u2963','uharl':'\u21BF','uharr':'\u21BE','uhblk':'\u2580','ulcorn':'\u231C','ulcorner':'\u231C','ulcrop':'\u230F','ultri':'\u25F8','umacr':'\u016B','Umacr':'\u016A','uml':'\xA8','UnderBar':'_','UnderBrace':'\u23DF','UnderBracket':'\u23B5','UnderParenthesis':'\u23DD','Union':'\u22C3','UnionPlus':'\u228E','uogon':'\u0173','Uogon':'\u0172','uopf':'\uD835\uDD66','Uopf':'\uD835\uDD4C','uparrow':'\u2191','Uparrow':'\u21D1','UpArrow':'\u2191','UpArrowBar':'\u2912','UpArrowDownArrow':'\u21C5','updownarrow':'\u2195','Updownarrow':'\u21D5','UpDownArrow':'\u2195','UpEquilibrium':'\u296E','upharpoonleft':'\u21BF','upharpoonright':'\u21BE','uplus':'\u228E','UpperLeftArrow':'\u2196','UpperRightArrow':'\u2197','upsi':'\u03C5','Upsi':'\u03D2','upsih':'\u03D2','upsilon':'\u03C5','Upsilon':'\u03A5','UpTee':'\u22A5','UpTeeArrow':'\u21A5','upuparrows':'\u21C8','urcorn':'\u231D','urcorner':'\u231D','urcrop':'\u230E','uring':'\u016F','Uring':'\u016E','urtri':'\u25F9','uscr':'\uD835\uDCCA','Uscr':'\uD835\uDCB0','utdot':'\u22F0','utilde':'\u0169','Utilde':'\u0168','utri':'\u25B5','utrif':'\u25B4','uuarr':'\u21C8','uuml':'\xFC','Uuml':'\xDC','uwangle':'\u29A7','vangrt':'\u299C','varepsilon':'\u03F5','varkappa':'\u03F0','varnothing':'\u2205','varphi':'\u03D5','varpi':'\u03D6','varpropto':'\u221D','varr':'\u2195','vArr':'\u21D5','varrho':'\u03F1','varsigma':'\u03C2','varsubsetneq':'\u228A\uFE00','varsubsetneqq':'\u2ACB\uFE00','varsupsetneq':'\u228B\uFE00','varsupsetneqq':'\u2ACC\uFE00','vartheta':'\u03D1','vartriangleleft':'\u22B2','vartriangleright':'\u22B3','vBar':'\u2AE8','Vbar':'\u2AEB','vBarv':'\u2AE9','vcy':'\u0432','Vcy':'\u0412','vdash':'\u22A2','vDash':'\u22A8','Vdash':'\u22A9','VDash':'\u22AB','Vdashl':'\u2AE6','vee':'\u2228','Vee':'\u22C1','veebar':'\u22BB','veeeq':'\u225A','vellip':'\u22EE','verbar':'|','Verbar':'\u2016','vert':'|','Vert':'\u2016','VerticalBar':'\u2223','VerticalLine':'|','VerticalSeparator':'\u2758','VerticalTilde':'\u2240','VeryThinSpace':'\u200A','vfr':'\uD835\uDD33','Vfr':'\uD835\uDD19','vltri':'\u22B2','vnsub':'\u2282\u20D2','vnsup':'\u2283\u20D2','vopf':'\uD835\uDD67','Vopf':'\uD835\uDD4D','vprop':'\u221D','vrtri':'\u22B3','vscr':'\uD835\uDCCB','Vscr':'\uD835\uDCB1','vsubne':'\u228A\uFE00','vsubnE':'\u2ACB\uFE00','vsupne':'\u228B\uFE00','vsupnE':'\u2ACC\uFE00','Vvdash':'\u22AA','vzigzag':'\u299A','wcirc':'\u0175','Wcirc':'\u0174','wedbar':'\u2A5F','wedge':'\u2227','Wedge':'\u22C0','wedgeq':'\u2259','weierp':'\u2118','wfr':'\uD835\uDD34','Wfr':'\uD835\uDD1A','wopf':'\uD835\uDD68','Wopf':'\uD835\uDD4E','wp':'\u2118','wr':'\u2240','wreath':'\u2240','wscr':'\uD835\uDCCC','Wscr':'\uD835\uDCB2','xcap':'\u22C2','xcirc':'\u25EF','xcup':'\u22C3','xdtri':'\u25BD','xfr':'\uD835\uDD35','Xfr':'\uD835\uDD1B','xharr':'\u27F7','xhArr':'\u27FA','xi':'\u03BE','Xi':'\u039E','xlarr':'\u27F5','xlArr':'\u27F8','xmap':'\u27FC','xnis':'\u22FB','xodot':'\u2A00','xopf':'\uD835\uDD69','Xopf':'\uD835\uDD4F','xoplus':'\u2A01','xotime':'\u2A02','xrarr':'\u27F6','xrArr':'\u27F9','xscr':'\uD835\uDCCD','Xscr':'\uD835\uDCB3','xsqcup':'\u2A06','xuplus':'\u2A04','xutri':'\u25B3','xvee':'\u22C1','xwedge':'\u22C0','yacute':'\xFD','Yacute':'\xDD','yacy':'\u044F','YAcy':'\u042F','ycirc':'\u0177','Ycirc':'\u0176','ycy':'\u044B','Ycy':'\u042B','yen':'\xA5','yfr':'\uD835\uDD36','Yfr':'\uD835\uDD1C','yicy':'\u0457','YIcy':'\u0407','yopf':'\uD835\uDD6A','Yopf':'\uD835\uDD50','yscr':'\uD835\uDCCE','Yscr':'\uD835\uDCB4','yucy':'\u044E','YUcy':'\u042E','yuml':'\xFF','Yuml':'\u0178','zacute':'\u017A','Zacute':'\u0179','zcaron':'\u017E','Zcaron':'\u017D','zcy':'\u0437','Zcy':'\u0417','zdot':'\u017C','Zdot':'\u017B','zeetrf':'\u2128','ZeroWidthSpace':'\u200B','zeta':'\u03B6','Zeta':'\u0396','zfr':'\uD835\uDD37','Zfr':'\u2128','zhcy':'\u0436','ZHcy':'\u0416','zigrarr':'\u21DD','zopf':'\uD835\uDD6B','Zopf':'\u2124','zscr':'\uD835\uDCCF','Zscr':'\uD835\uDCB5','zwj':'\u200D','zwnj':'\u200C'}; - var decodeMapLegacy = {'aacute':'\xE1','Aacute':'\xC1','acirc':'\xE2','Acirc':'\xC2','acute':'\xB4','aelig':'\xE6','AElig':'\xC6','agrave':'\xE0','Agrave':'\xC0','amp':'&','AMP':'&','aring':'\xE5','Aring':'\xC5','atilde':'\xE3','Atilde':'\xC3','auml':'\xE4','Auml':'\xC4','brvbar':'\xA6','ccedil':'\xE7','Ccedil':'\xC7','cedil':'\xB8','cent':'\xA2','copy':'\xA9','COPY':'\xA9','curren':'\xA4','deg':'\xB0','divide':'\xF7','eacute':'\xE9','Eacute':'\xC9','ecirc':'\xEA','Ecirc':'\xCA','egrave':'\xE8','Egrave':'\xC8','eth':'\xF0','ETH':'\xD0','euml':'\xEB','Euml':'\xCB','frac12':'\xBD','frac14':'\xBC','frac34':'\xBE','gt':'>','GT':'>','iacute':'\xED','Iacute':'\xCD','icirc':'\xEE','Icirc':'\xCE','iexcl':'\xA1','igrave':'\xEC','Igrave':'\xCC','iquest':'\xBF','iuml':'\xEF','Iuml':'\xCF','laquo':'\xAB','lt':'<','LT':'<','macr':'\xAF','micro':'\xB5','middot':'\xB7','nbsp':'\xA0','not':'\xAC','ntilde':'\xF1','Ntilde':'\xD1','oacute':'\xF3','Oacute':'\xD3','ocirc':'\xF4','Ocirc':'\xD4','ograve':'\xF2','Ograve':'\xD2','ordf':'\xAA','ordm':'\xBA','oslash':'\xF8','Oslash':'\xD8','otilde':'\xF5','Otilde':'\xD5','ouml':'\xF6','Ouml':'\xD6','para':'\xB6','plusmn':'\xB1','pound':'\xA3','quot':'"','QUOT':'"','raquo':'\xBB','reg':'\xAE','REG':'\xAE','sect':'\xA7','shy':'\xAD','sup1':'\xB9','sup2':'\xB2','sup3':'\xB3','szlig':'\xDF','thorn':'\xFE','THORN':'\xDE','times':'\xD7','uacute':'\xFA','Uacute':'\xDA','ucirc':'\xFB','Ucirc':'\xDB','ugrave':'\xF9','Ugrave':'\xD9','uml':'\xA8','uuml':'\xFC','Uuml':'\xDC','yacute':'\xFD','Yacute':'\xDD','yen':'\xA5','yuml':'\xFF'}; - var decodeMapNumeric = {'0':'\uFFFD','128':'\u20AC','130':'\u201A','131':'\u0192','132':'\u201E','133':'\u2026','134':'\u2020','135':'\u2021','136':'\u02C6','137':'\u2030','138':'\u0160','139':'\u2039','140':'\u0152','142':'\u017D','145':'\u2018','146':'\u2019','147':'\u201C','148':'\u201D','149':'\u2022','150':'\u2013','151':'\u2014','152':'\u02DC','153':'\u2122','154':'\u0161','155':'\u203A','156':'\u0153','158':'\u017E','159':'\u0178'}; - var invalidReferenceCodePoints = [1,2,3,4,5,6,7,8,11,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,64976,64977,64978,64979,64980,64981,64982,64983,64984,64985,64986,64987,64988,64989,64990,64991,64992,64993,64994,64995,64996,64997,64998,64999,65000,65001,65002,65003,65004,65005,65006,65007,65534,65535,131070,131071,196606,196607,262142,262143,327678,327679,393214,393215,458750,458751,524286,524287,589822,589823,655358,655359,720894,720895,786430,786431,851966,851967,917502,917503,983038,983039,1048574,1048575,1114110,1114111]; - - /*--------------------------------------------------------------------------*/ - - var stringFromCharCode = String.fromCharCode; - - var object = {}; - var hasOwnProperty = object.hasOwnProperty; - var has = function(object, propertyName) { - return hasOwnProperty.call(object, propertyName); - }; - - var contains = function(array, value) { - var index = -1; - var length = array.length; - while (++index < length) { - if (array[index] == value) { - return true; - } - } - return false; - }; - - var merge = function(options, defaults) { - if (!options) { - return defaults; - } - var result = {}; - var key; - for (key in defaults) { - // A `hasOwnProperty` check is not needed here, since only recognized - // option names are used anyway. Any others are ignored. - result[key] = has(options, key) ? options[key] : defaults[key]; - } - return result; - }; - - // Modified version of `ucs2encode`; see https://mths.be/punycode. - var codePointToSymbol = function(codePoint, strict) { - var output = ''; - if ((codePoint >= 0xD800 && codePoint <= 0xDFFF) || codePoint > 0x10FFFF) { - // See issue #4: - // “Otherwise, if the number is in the range 0xD800 to 0xDFFF or is - // greater than 0x10FFFF, then this is a parse error. Return a U+FFFD - // REPLACEMENT CHARACTER.” - if (strict) { - parseError('character reference outside the permissible Unicode range'); - } - return '\uFFFD'; - } - if (has(decodeMapNumeric, codePoint)) { - if (strict) { - parseError('disallowed character reference'); - } - return decodeMapNumeric[codePoint]; - } - if (strict && contains(invalidReferenceCodePoints, codePoint)) { - parseError('disallowed character reference'); - } - if (codePoint > 0xFFFF) { - codePoint -= 0x10000; - output += stringFromCharCode(codePoint >>> 10 & 0x3FF | 0xD800); - codePoint = 0xDC00 | codePoint & 0x3FF; - } - output += stringFromCharCode(codePoint); - return output; - }; - - var hexEscape = function(codePoint) { - return '&#x' + codePoint.toString(16).toUpperCase() + ';'; - }; - - var decEscape = function(codePoint) { - return '&#' + codePoint + ';'; - }; - - var parseError = function(message) { - throw Error('Parse error: ' + message); - }; - - /*--------------------------------------------------------------------------*/ - - var encode = function(string, options) { - options = merge(options, encode.options); - var strict = options.strict; - if (strict && regexInvalidRawCodePoint.test(string)) { - parseError('forbidden code point'); - } - var encodeEverything = options.encodeEverything; - var useNamedReferences = options.useNamedReferences; - var allowUnsafeSymbols = options.allowUnsafeSymbols; - var escapeCodePoint = options.decimal ? decEscape : hexEscape; - - var escapeBmpSymbol = function(symbol) { - return escapeCodePoint(symbol.charCodeAt(0)); - }; - - if (encodeEverything) { - // Encode ASCII symbols. - string = string.replace(regexAsciiWhitelist, function(symbol) { - // Use named references if requested & possible. - if (useNamedReferences && has(encodeMap, symbol)) { - return '&' + encodeMap[symbol] + ';'; - } - return escapeBmpSymbol(symbol); - }); - // Shorten a few escapes that represent two symbols, of which at least one - // is within the ASCII range. - if (useNamedReferences) { - string = string - .replace(/>\u20D2/g, '>⃒') - .replace(/<\u20D2/g, '<⃒') - .replace(/fj/g, 'fj'); - } - // Encode non-ASCII symbols. - if (useNamedReferences) { - // Encode non-ASCII symbols that can be replaced with a named reference. - string = string.replace(regexEncodeNonAscii, function(string) { - // Note: there is no need to check `has(encodeMap, string)` here. - return '&' + encodeMap[string] + ';'; - }); - } - // Note: any remaining non-ASCII symbols are handled outside of the `if`. - } else if (useNamedReferences) { - // Apply named character references. - // Encode `<>"'&` using named character references. - if (!allowUnsafeSymbols) { - string = string.replace(regexEscape, function(string) { - return '&' + encodeMap[string] + ';'; // no need to check `has()` here - }); - } - // Shorten escapes that represent two symbols, of which at least one is - // `<>"'&`. - string = string - .replace(/>\u20D2/g, '>⃒') - .replace(/<\u20D2/g, '<⃒'); - // Encode non-ASCII symbols that can be replaced with a named reference. - string = string.replace(regexEncodeNonAscii, function(string) { - // Note: there is no need to check `has(encodeMap, string)` here. - return '&' + encodeMap[string] + ';'; - }); - } else if (!allowUnsafeSymbols) { - // Encode `<>"'&` using hexadecimal escapes, now that they’re not handled - // using named character references. - string = string.replace(regexEscape, escapeBmpSymbol); - } - return string - // Encode astral symbols. - .replace(regexAstralSymbols, function($0) { - // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae - var high = $0.charCodeAt(0); - var low = $0.charCodeAt(1); - var codePoint = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000; - return escapeCodePoint(codePoint); - }) - // Encode any remaining BMP symbols that are not printable ASCII symbols - // using a hexadecimal escape. - .replace(regexBmpWhitelist, escapeBmpSymbol); - }; - // Expose default options (so they can be overridden globally). - encode.options = { - 'allowUnsafeSymbols': false, - 'encodeEverything': false, - 'strict': false, - 'useNamedReferences': false, - 'decimal' : false - }; - - var decode = function(html, options) { - options = merge(options, decode.options); - var strict = options.strict; - if (strict && regexInvalidEntity.test(html)) { - parseError('malformed character reference'); - } - return html.replace(regexDecode, function($0, $1, $2, $3, $4, $5, $6, $7, $8) { - var codePoint; - var semicolon; - var decDigits; - var hexDigits; - var reference; - var next; - - if ($1) { - reference = $1; - // Note: there is no need to check `has(decodeMap, reference)`. - return decodeMap[reference]; - } - - if ($2) { - // Decode named character references without trailing `;`, e.g. `&`. - // This is only a parse error if it gets converted to `&`, or if it is - // followed by `=` in an attribute context. - reference = $2; - next = $3; - if (next && options.isAttributeValue) { - if (strict && next == '=') { - parseError('`&` did not start a character reference'); - } - return $0; - } else { - if (strict) { - parseError( - 'named character reference was not terminated by a semicolon' - ); - } - // Note: there is no need to check `has(decodeMapLegacy, reference)`. - return decodeMapLegacy[reference] + (next || ''); - } - } - - if ($4) { - // Decode decimal escapes, e.g. `𝌆`. - decDigits = $4; - semicolon = $5; - if (strict && !semicolon) { - parseError('character reference was not terminated by a semicolon'); - } - codePoint = parseInt(decDigits, 10); - return codePointToSymbol(codePoint, strict); - } - - if ($6) { - // Decode hexadecimal escapes, e.g. `𝌆`. - hexDigits = $6; - semicolon = $7; - if (strict && !semicolon) { - parseError('character reference was not terminated by a semicolon'); - } - codePoint = parseInt(hexDigits, 16); - return codePointToSymbol(codePoint, strict); - } - - // If we’re still here, `if ($7)` is implied; it’s an ambiguous - // ampersand for sure. https://mths.be/notes/ambiguous-ampersands - if (strict) { - parseError( - 'named character reference was not terminated by a semicolon' - ); - } - return $0; - }); - }; - // Expose default options (so they can be overridden globally). - decode.options = { - 'isAttributeValue': false, - 'strict': false - }; - - var escape = function(string) { - return string.replace(regexEscape, function($0) { - // Note: there is no need to check `has(escapeMap, $0)` here. - return escapeMap[$0]; - }); - }; - - /*--------------------------------------------------------------------------*/ - - var he = { - 'version': '1.2.0', - 'encode': encode, - 'decode': decode, - 'escape': escape, - 'unescape': decode - }; - - // Some AMD build optimizers, like r.js, check for specific condition patterns - // like the following: - if ( - false - ) { - define(function() { - return he; - }); - } else if (freeExports && !freeExports.nodeType) { - if (freeModule) { // in Node.js, io.js, or RingoJS v0.8.0+ - freeModule.exports = he; - } else { // in Narwhal or RingoJS v0.7.0- - for (var key in he) { - has(he, key) && (freeExports[key] = he[key]); - } - } - } else { // in Rhino or a web browser - root.he = he; - } - -}(this)); - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],55:[function(require,module,exports){ -exports.read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var nBits = -7 - var i = isLE ? (nBytes - 1) : 0 - var d = isLE ? -1 : 1 - var s = buffer[offset + i] - - i += d - - e = s & ((1 << (-nBits)) - 1) - s >>= (-nBits) - nBits += eLen - for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - m = e & ((1 << (-nBits)) - 1) - e >>= (-nBits) - nBits += mLen - for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - if (e === 0) { - e = 1 - eBias - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen) - e = e - eBias - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) -} - -exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) - var i = isLE ? 0 : (nBytes - 1) - var d = isLE ? 1 : -1 - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 - - value = Math.abs(value) - - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0 - e = eMax - } else { - e = Math.floor(Math.log(value) / Math.LN2) - if (value * (c = Math.pow(2, -e)) < 1) { - e-- - c *= 2 - } - if (e + eBias >= 1) { - value += rt / c - } else { - value += rt * Math.pow(2, 1 - eBias) - } - if (value * c >= 2) { - e++ - c /= 2 - } - - if (e + eBias >= eMax) { - m = 0 - e = eMax - } else if (e + eBias >= 1) { - m = ((value * c) - 1) * Math.pow(2, mLen) - e = e + eBias - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) - e = 0 - } - } - - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - - e = (e << mLen) | m - eLen += mLen - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - - buffer[offset + i - d] |= s * 128 -} - -},{}],56:[function(require,module,exports){ -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }); - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } -} - -},{}],57:[function(require,module,exports){ -/*! - * Determine if an object is a Buffer - * - * @author Feross Aboukhadijeh <https://feross.org> - * @license MIT - */ - -// The _isBuffer check is for Safari 5-7 support, because it's missing -// Object.prototype.constructor. Remove this eventually -module.exports = function (obj) { - return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) -} - -function isBuffer (obj) { - return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) -} - -// For Node v0.10 support. Remove this eventually. -function isSlowBuffer (obj) { - return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) -} - -},{}],58:[function(require,module,exports){ -var toString = {}.toString; - -module.exports = Array.isArray || function (arr) { - return toString.call(arr) == '[object Array]'; -}; - -},{}],59:[function(require,module,exports){ -var path = require('path'); -var fs = require('fs'); -var _0777 = parseInt('0777', 8); - -module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; - -function mkdirP (p, opts, f, made) { - if (typeof opts === 'function') { - f = opts; - opts = {}; - } - else if (!opts || typeof opts !== 'object') { - opts = { mode: opts }; - } - - var mode = opts.mode; - var xfs = opts.fs || fs; - - if (mode === undefined) { - mode = _0777 - } - if (!made) made = null; - - var cb = f || function () {}; - p = path.resolve(p); - - xfs.mkdir(p, mode, function (er) { - if (!er) { - made = made || p; - return cb(null, made); - } - switch (er.code) { - case 'ENOENT': - if (path.dirname(p) === p) return cb(er); - mkdirP(path.dirname(p), opts, function (er, made) { - if (er) cb(er, made); - else mkdirP(p, opts, cb, made); - }); - break; - - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - xfs.stat(p, function (er2, stat) { - // if the stat fails, then that's super weird. - // let the original error be the failure reason. - if (er2 || !stat.isDirectory()) cb(er, made) - else cb(null, made); - }); - break; - } - }); -} - -mkdirP.sync = function sync (p, opts, made) { - if (!opts || typeof opts !== 'object') { - opts = { mode: opts }; - } - - var mode = opts.mode; - var xfs = opts.fs || fs; - - if (mode === undefined) { - mode = _0777 - } - if (!made) made = null; - - p = path.resolve(p); - - try { - xfs.mkdirSync(p, mode); - made = made || p; - } - catch (err0) { - switch (err0.code) { - case 'ENOENT' : - made = sync(path.dirname(p), opts, made); - sync(p, opts, made); - break; - - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - var stat; - try { - stat = xfs.statSync(p); - } - catch (err1) { - throw err0; - } - if (!stat.isDirectory()) throw err0; - break; - } - } - - return made; -}; - -},{"fs":42,"path":42}],60:[function(require,module,exports){ -/** - * Helpers. - */ - -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var w = d * 7; -var y = d * 365.25; - -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public - */ - -module.exports = function(val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); - } - throw new Error( - 'val is not a non-empty string or a valid number. val=' + - JSON.stringify(val) - ); -}; - -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - -function parse(str) { - str = String(str); - if (str.length > 100) { - return; - } - var match = /^((?:\d+)?\-?\d?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( - str - ); - if (!match) { - return; - } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'weeks': - case 'week': - case 'w': - return n * w; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; - } -} - -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtShort(ms) { - var msAbs = Math.abs(ms); - if (msAbs >= d) { - return Math.round(ms / d) + 'd'; - } - if (msAbs >= h) { - return Math.round(ms / h) + 'h'; - } - if (msAbs >= m) { - return Math.round(ms / m) + 'm'; - } - if (msAbs >= s) { - return Math.round(ms / s) + 's'; - } - return ms + 'ms'; -} - -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtLong(ms) { - var msAbs = Math.abs(ms); - if (msAbs >= d) { - return plural(ms, msAbs, d, 'day'); - } - if (msAbs >= h) { - return plural(ms, msAbs, h, 'hour'); - } - if (msAbs >= m) { - return plural(ms, msAbs, m, 'minute'); - } - if (msAbs >= s) { - return plural(ms, msAbs, s, 'second'); - } - return ms + ' ms'; -} - -/** - * Pluralization helper. - */ - -function plural(ms, msAbs, n, name) { - var isPlural = msAbs >= n * 1.5; - return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); -} - -},{}],61:[function(require,module,exports){ -'use strict'; - -var keysShim; -if (!Object.keys) { - // modified from https://github.com/es-shims/es5-shim - var has = Object.prototype.hasOwnProperty; - var toStr = Object.prototype.toString; - var isArgs = require('./isArguments'); // eslint-disable-line global-require - var isEnumerable = Object.prototype.propertyIsEnumerable; - var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); - var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); - var dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ]; - var equalsConstructorPrototype = function (o) { - var ctor = o.constructor; - return ctor && ctor.prototype === o; - }; - var excludedKeys = { - $applicationCache: true, - $console: true, - $external: true, - $frame: true, - $frameElement: true, - $frames: true, - $innerHeight: true, - $innerWidth: true, - $outerHeight: true, - $outerWidth: true, - $pageXOffset: true, - $pageYOffset: true, - $parent: true, - $scrollLeft: true, - $scrollTop: true, - $scrollX: true, - $scrollY: true, - $self: true, - $webkitIndexedDB: true, - $webkitStorageInfo: true, - $window: true - }; - var hasAutomationEqualityBug = (function () { - /* global window */ - if (typeof window === 'undefined') { return false; } - for (var k in window) { - try { - if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { - try { - equalsConstructorPrototype(window[k]); - } catch (e) { - return true; - } - } - } catch (e) { - return true; - } - } - return false; - }()); - var equalsConstructorPrototypeIfNotBuggy = function (o) { - /* global window */ - if (typeof window === 'undefined' || !hasAutomationEqualityBug) { - return equalsConstructorPrototype(o); - } - try { - return equalsConstructorPrototype(o); - } catch (e) { - return false; - } - }; - - keysShim = function keys(object) { - var isObject = object !== null && typeof object === 'object'; - var isFunction = toStr.call(object) === '[object Function]'; - var isArguments = isArgs(object); - var isString = isObject && toStr.call(object) === '[object String]'; - var theKeys = []; - - if (!isObject && !isFunction && !isArguments) { - throw new TypeError('Object.keys called on a non-object'); - } - - var skipProto = hasProtoEnumBug && isFunction; - if (isString && object.length > 0 && !has.call(object, 0)) { - for (var i = 0; i < object.length; ++i) { - theKeys.push(String(i)); - } - } - - if (isArguments && object.length > 0) { - for (var j = 0; j < object.length; ++j) { - theKeys.push(String(j)); - } - } else { - for (var name in object) { - if (!(skipProto && name === 'prototype') && has.call(object, name)) { - theKeys.push(String(name)); - } - } - } - - if (hasDontEnumBug) { - var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); - - for (var k = 0; k < dontEnums.length; ++k) { - if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { - theKeys.push(dontEnums[k]); - } - } - } - return theKeys; - }; -} -module.exports = keysShim; - -},{"./isArguments":63}],62:[function(require,module,exports){ -'use strict'; - -var slice = Array.prototype.slice; -var isArgs = require('./isArguments'); - -var origKeys = Object.keys; -var keysShim = origKeys ? function keys(o) { return origKeys(o); } : require('./implementation'); - -var originalKeys = Object.keys; - -keysShim.shim = function shimObjectKeys() { - if (Object.keys) { - var keysWorksWithArguments = (function () { - // Safari 5.0 bug - var args = Object.keys(arguments); - return args && args.length === arguments.length; - }(1, 2)); - if (!keysWorksWithArguments) { - Object.keys = function keys(object) { // eslint-disable-line func-name-matching - if (isArgs(object)) { - return originalKeys(slice.call(object)); - } - return originalKeys(object); - }; - } - } else { - Object.keys = keysShim; - } - return Object.keys || keysShim; -}; - -module.exports = keysShim; - -},{"./implementation":61,"./isArguments":63}],63:[function(require,module,exports){ -'use strict'; - -var toStr = Object.prototype.toString; - -module.exports = function isArguments(value) { - var str = toStr.call(value); - var isArgs = str === '[object Arguments]'; - if (!isArgs) { - isArgs = str !== '[object Array]' && - value !== null && - typeof value === 'object' && - typeof value.length === 'number' && - value.length >= 0 && - toStr.call(value.callee) === '[object Function]'; - } - return isArgs; -}; - -},{}],64:[function(require,module,exports){ -'use strict'; - -// modified from https://github.com/es-shims/es6-shim -var keys = require('object-keys'); -var bind = require('function-bind'); -var canBeObject = function (obj) { - return typeof obj !== 'undefined' && obj !== null; -}; -var hasSymbols = require('has-symbols/shams')(); -var toObject = Object; -var push = bind.call(Function.call, Array.prototype.push); -var propIsEnumerable = bind.call(Function.call, Object.prototype.propertyIsEnumerable); -var originalGetSymbols = hasSymbols ? Object.getOwnPropertySymbols : null; - -module.exports = function assign(target, source1) { - if (!canBeObject(target)) { throw new TypeError('target must be an object'); } - var objTarget = toObject(target); - var s, source, i, props, syms, value, key; - for (s = 1; s < arguments.length; ++s) { - source = toObject(arguments[s]); - props = keys(source); - var getSymbols = hasSymbols && (Object.getOwnPropertySymbols || originalGetSymbols); - if (getSymbols) { - syms = getSymbols(source); - for (i = 0; i < syms.length; ++i) { - key = syms[i]; - if (propIsEnumerable(source, key)) { - push(props, key); - } - } - } - for (i = 0; i < props.length; ++i) { - key = props[i]; - value = source[key]; - if (propIsEnumerable(source, key)) { - objTarget[key] = value; - } - } - } - return objTarget; -}; - -},{"function-bind":52,"has-symbols/shams":53,"object-keys":62}],65:[function(require,module,exports){ -'use strict'; - -var defineProperties = require('define-properties'); - -var implementation = require('./implementation'); -var getPolyfill = require('./polyfill'); -var shim = require('./shim'); - -var polyfill = getPolyfill(); - -defineProperties(polyfill, { - getPolyfill: getPolyfill, - implementation: implementation, - shim: shim -}); - -module.exports = polyfill; - -},{"./implementation":64,"./polyfill":66,"./shim":67,"define-properties":47}],66:[function(require,module,exports){ -'use strict'; - -var implementation = require('./implementation'); - -var lacksProperEnumerationOrder = function () { - if (!Object.assign) { - return false; - } - // v8, specifically in node 4.x, has a bug with incorrect property enumeration order - // note: this does not detect the bug unless there's 20 characters - var str = 'abcdefghijklmnopqrst'; - var letters = str.split(''); - var map = {}; - for (var i = 0; i < letters.length; ++i) { - map[letters[i]] = letters[i]; - } - var obj = Object.assign({}, map); - var actual = ''; - for (var k in obj) { - actual += k; - } - return str !== actual; -}; - -var assignHasPendingExceptions = function () { - if (!Object.assign || !Object.preventExtensions) { - return false; - } - // Firefox 37 still has "pending exception" logic in its Object.assign implementation, - // which is 72% slower than our shim, and Firefox 40's native implementation. - var thrower = Object.preventExtensions({ 1: 2 }); - try { - Object.assign(thrower, 'xy'); - } catch (e) { - return thrower[1] === 'y'; - } - return false; -}; - -module.exports = function getPolyfill() { - if (!Object.assign) { - return implementation; - } - if (lacksProperEnumerationOrder()) { - return implementation; - } - if (assignHasPendingExceptions()) { - return implementation; - } - return Object.assign; -}; - -},{"./implementation":64}],67:[function(require,module,exports){ -'use strict'; - -var define = require('define-properties'); -var getPolyfill = require('./polyfill'); - -module.exports = function shimAssign() { - var polyfill = getPolyfill(); - define( - Object, - { assign: polyfill }, - { assign: function () { return Object.assign !== polyfill; } } - ); - return polyfill; -}; - -},{"./polyfill":66,"define-properties":47}],68:[function(require,module,exports){ -(function (process){ -'use strict'; - -if (!process.version || - process.version.indexOf('v0.') === 0 || - process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { - module.exports = { nextTick: nextTick }; -} else { - module.exports = process -} - -function nextTick(fn, arg1, arg2, arg3) { - if (typeof fn !== 'function') { - throw new TypeError('"callback" argument must be a function'); - } - var len = arguments.length; - var args, i; - switch (len) { - case 0: - case 1: - return process.nextTick(fn); - case 2: - return process.nextTick(function afterTickOne() { - fn.call(null, arg1); - }); - case 3: - return process.nextTick(function afterTickTwo() { - fn.call(null, arg1, arg2); - }); - case 4: - return process.nextTick(function afterTickThree() { - fn.call(null, arg1, arg2, arg3); - }); - default: - args = new Array(len - 1); - i = 0; - while (i < args.length) { - args[i++] = arguments[i]; - } - return process.nextTick(function afterTick() { - fn.apply(null, args); - }); - } -} - - -}).call(this,require('_process')) -},{"_process":69}],69:[function(require,module,exports){ -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; - -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} - -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],70:[function(require,module,exports){ -module.exports = require('./lib/_stream_duplex.js'); - -},{"./lib/_stream_duplex.js":71}],71:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a duplex stream is just a stream that is both readable and writable. -// Since JS doesn't have multiple prototypal inheritance, this class -// prototypally inherits from Readable, and then parasitically from -// Writable. - -'use strict'; - -/*<replacement>*/ - -var pna = require('process-nextick-args'); -/*</replacement>*/ - -/*<replacement>*/ -var objectKeys = Object.keys || function (obj) { - var keys = []; - for (var key in obj) { - keys.push(key); - }return keys; -}; -/*</replacement>*/ - -module.exports = Duplex; - -/*<replacement>*/ -var util = require('core-util-is'); -util.inherits = require('inherits'); -/*</replacement>*/ - -var Readable = require('./_stream_readable'); -var Writable = require('./_stream_writable'); - -util.inherits(Duplex, Readable); - -{ - // avoid scope creep, the keys array can then be collected - var keys = objectKeys(Writable.prototype); - for (var v = 0; v < keys.length; v++) { - var method = keys[v]; - if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; - } -} - -function Duplex(options) { - if (!(this instanceof Duplex)) return new Duplex(options); - - Readable.call(this, options); - Writable.call(this, options); - - if (options && options.readable === false) this.readable = false; - - if (options && options.writable === false) this.writable = false; - - this.allowHalfOpen = true; - if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; - - this.once('end', onend); -} - -Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function () { - return this._writableState.highWaterMark; - } -}); - -// the no-half-open enforcer -function onend() { - // if we allow half-open state, or if the writable side ended, - // then we're ok. - if (this.allowHalfOpen || this._writableState.ended) return; - - // no more data can be written. - // But allow more writes to happen in this tick. - pna.nextTick(onEndNT, this); -} - -function onEndNT(self) { - self.end(); -} - -Object.defineProperty(Duplex.prototype, 'destroyed', { - get: function () { - if (this._readableState === undefined || this._writableState === undefined) { - return false; - } - return this._readableState.destroyed && this._writableState.destroyed; - }, - set: function (value) { - // we ignore the value if the stream - // has not been initialized yet - if (this._readableState === undefined || this._writableState === undefined) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } -}); - -Duplex.prototype._destroy = function (err, cb) { - this.push(null); - this.end(); - - pna.nextTick(cb, err); -}; -},{"./_stream_readable":73,"./_stream_writable":75,"core-util-is":44,"inherits":56,"process-nextick-args":68}],72:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a passthrough stream. -// basically just the most minimal sort of Transform stream. -// Every written chunk gets output as-is. - -'use strict'; - -module.exports = PassThrough; - -var Transform = require('./_stream_transform'); - -/*<replacement>*/ -var util = require('core-util-is'); -util.inherits = require('inherits'); -/*</replacement>*/ - -util.inherits(PassThrough, Transform); - -function PassThrough(options) { - if (!(this instanceof PassThrough)) return new PassThrough(options); - - Transform.call(this, options); -} - -PassThrough.prototype._transform = function (chunk, encoding, cb) { - cb(null, chunk); -}; -},{"./_stream_transform":74,"core-util-is":44,"inherits":56}],73:[function(require,module,exports){ -(function (process,global){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -/*<replacement>*/ - -var pna = require('process-nextick-args'); -/*</replacement>*/ - -module.exports = Readable; - -/*<replacement>*/ -var isArray = require('isarray'); -/*</replacement>*/ - -/*<replacement>*/ -var Duplex; -/*</replacement>*/ - -Readable.ReadableState = ReadableState; - -/*<replacement>*/ -var EE = require('events').EventEmitter; - -var EElistenerCount = function (emitter, type) { - return emitter.listeners(type).length; -}; -/*</replacement>*/ - -/*<replacement>*/ -var Stream = require('./internal/streams/stream'); -/*</replacement>*/ - -/*<replacement>*/ - -var Buffer = require('safe-buffer').Buffer; -var OurUint8Array = global.Uint8Array || function () {}; -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -/*</replacement>*/ - -/*<replacement>*/ -var util = require('core-util-is'); -util.inherits = require('inherits'); -/*</replacement>*/ - -/*<replacement>*/ -var debugUtil = require('util'); -var debug = void 0; -if (debugUtil && debugUtil.debuglog) { - debug = debugUtil.debuglog('stream'); -} else { - debug = function () {}; -} -/*</replacement>*/ - -var BufferList = require('./internal/streams/BufferList'); -var destroyImpl = require('./internal/streams/destroy'); -var StringDecoder; - -util.inherits(Readable, Stream); - -var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; - -function prependListener(emitter, event, fn) { - // Sadly this is not cacheable as some libraries bundle their own - // event emitter implementation with them. - if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); - - // This is a hack to make sure that our error handler is attached before any - // userland ones. NEVER DO THIS. This is here only because this code needs - // to continue to work with older versions of Node.js that do not include - // the prependListener() method. The goal is to eventually remove this hack. - if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; -} - -function ReadableState(options, stream) { - Duplex = Duplex || require('./_stream_duplex'); - - options = options || {}; - - // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Duplex; - - // object stream flag. Used to make read(n) ignore n and to - // make all the buffer merging and length checks go away - this.objectMode = !!options.objectMode; - - if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; - - // the point at which it stops calling _read() to fill the buffer - // Note: 0 is a valid value, means "don't call _read preemptively ever" - var hwm = options.highWaterMark; - var readableHwm = options.readableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; - - if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (readableHwm || readableHwm === 0)) this.highWaterMark = readableHwm;else this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); - - // A linked list is used to store data chunks instead of an array because the - // linked list can remove elements from the beginning faster than - // array.shift() - this.buffer = new BufferList(); - this.length = 0; - this.pipes = null; - this.pipesCount = 0; - this.flowing = null; - this.ended = false; - this.endEmitted = false; - this.reading = false; - - // a flag to be able to tell if the event 'readable'/'data' is emitted - // immediately, or on a later tick. We set this to true at first, because - // any actions that shouldn't happen until "later" should generally also - // not happen before the first read call. - this.sync = true; - - // whenever we return null, then we set a flag to say - // that we're awaiting a 'readable' event emission. - this.needReadable = false; - this.emittedReadable = false; - this.readableListening = false; - this.resumeScheduled = false; - - // has it been destroyed - this.destroyed = false; - - // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - this.defaultEncoding = options.defaultEncoding || 'utf8'; - - // the number of writers that are awaiting a drain event in .pipe()s - this.awaitDrain = 0; - - // if true, a maybeReadMore has been scheduled - this.readingMore = false; - - this.decoder = null; - this.encoding = null; - if (options.encoding) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } -} - -function Readable(options) { - Duplex = Duplex || require('./_stream_duplex'); - - if (!(this instanceof Readable)) return new Readable(options); - - this._readableState = new ReadableState(options, this); - - // legacy - this.readable = true; - - if (options) { - if (typeof options.read === 'function') this._read = options.read; - - if (typeof options.destroy === 'function') this._destroy = options.destroy; - } - - Stream.call(this); -} - -Object.defineProperty(Readable.prototype, 'destroyed', { - get: function () { - if (this._readableState === undefined) { - return false; - } - return this._readableState.destroyed; - }, - set: function (value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._readableState) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._readableState.destroyed = value; - } -}); - -Readable.prototype.destroy = destroyImpl.destroy; -Readable.prototype._undestroy = destroyImpl.undestroy; -Readable.prototype._destroy = function (err, cb) { - this.push(null); - cb(err); -}; - -// Manually shove something into the read() buffer. -// This returns true if the highWaterMark has not been hit yet, -// similar to how Writable.write() returns true if you should -// write() some more. -Readable.prototype.push = function (chunk, encoding) { - var state = this._readableState; - var skipChunkCheck; - - if (!state.objectMode) { - if (typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; - } - skipChunkCheck = true; - } - } else { - skipChunkCheck = true; - } - - return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); -}; - -// Unshift should *always* be something directly out of read() -Readable.prototype.unshift = function (chunk) { - return readableAddChunk(this, chunk, null, true, false); -}; - -function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { - var state = stream._readableState; - if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else { - var er; - if (!skipChunkCheck) er = chunkInvalid(state, chunk); - if (er) { - stream.emit('error', er); - } else if (state.objectMode || chunk && chunk.length > 0) { - if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (addToFront) { - if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true); - } else if (state.ended) { - stream.emit('error', new Error('stream.push() after EOF')); - } else { - state.reading = false; - if (state.decoder && !encoding) { - chunk = state.decoder.write(chunk); - if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); - } else { - addChunk(stream, state, chunk, false); - } - } - } else if (!addToFront) { - state.reading = false; - } - } - - return needMoreData(state); -} - -function addChunk(stream, state, chunk, addToFront) { - if (state.flowing && state.length === 0 && !state.sync) { - stream.emit('data', chunk); - stream.read(0); - } else { - // update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); - - if (state.needReadable) emitReadable(stream); - } - maybeReadMore(stream, state); -} - -function chunkInvalid(state, chunk) { - var er; - if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); - } - return er; -} - -// if it's past the high water mark, we can push in some more. -// Also, if we have no data yet, we can stand some -// more bytes. This is to work around cases where hwm=0, -// such as the repl. Also, if the push() triggered a -// readable event, and the user called read(largeNumber) such that -// needReadable was set, then we ought to push more, so that another -// 'readable' event will be triggered. -function needMoreData(state) { - return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0); -} - -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; - -// backwards compatibility. -Readable.prototype.setEncoding = function (enc) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this._readableState.decoder = new StringDecoder(enc); - this._readableState.encoding = enc; - return this; -}; - -// Don't raise the hwm > 8MB -var MAX_HWM = 0x800000; -function computeNewHighWaterMark(n) { - if (n >= MAX_HWM) { - n = MAX_HWM; - } else { - // Get the next highest power of 2 to prevent increasing hwm excessively in - // tiny amounts - n--; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n++; - } - return n; -} - -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function howMuchToRead(n, state) { - if (n <= 0 || state.length === 0 && state.ended) return 0; - if (state.objectMode) return 1; - if (n !== n) { - // Only flow one buffer at a time - if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; - } - // If we're asking for more than the current hwm, then raise the hwm. - if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); - if (n <= state.length) return n; - // Don't have enough - if (!state.ended) { - state.needReadable = true; - return 0; - } - return state.length; -} - -// you can override either this method, or the async _read(n) below. -Readable.prototype.read = function (n) { - debug('read', n); - n = parseInt(n, 10); - var state = this._readableState; - var nOrig = n; - - if (n !== 0) state.emittedReadable = false; - - // if we're doing read(0) to trigger a readable event, but we - // already have a bunch of data in the buffer, then just trigger - // the 'readable' event and move on. - if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) { - debug('read: emitReadable', state.length, state.ended); - if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); - return null; - } - - n = howMuchToRead(n, state); - - // if we've ended, and we're now clear, then finish it up. - if (n === 0 && state.ended) { - if (state.length === 0) endReadable(this); - return null; - } - - // All the actual chunk generation logic needs to be - // *below* the call to _read. The reason is that in certain - // synthetic stream cases, such as passthrough streams, _read - // may be a completely synchronous operation which may change - // the state of the read buffer, providing enough data when - // before there was *not* enough. - // - // So, the steps are: - // 1. Figure out what the state of things will be after we do - // a read from the buffer. - // - // 2. If that resulting state will trigger a _read, then call _read. - // Note that this may be asynchronous, or synchronous. Yes, it is - // deeply ugly to write APIs this way, but that still doesn't mean - // that the Readable class should behave improperly, as streams are - // designed to be sync/async agnostic. - // Take note if the _read call is sync or async (ie, if the read call - // has returned yet), so that we know whether or not it's safe to emit - // 'readable' etc. - // - // 3. Actually pull the requested chunks out of the buffer and return. - - // if we need a readable event, then we need to do some reading. - var doRead = state.needReadable; - debug('need readable', doRead); - - // if we currently have less than the highWaterMark, then also read some - if (state.length === 0 || state.length - n < state.highWaterMark) { - doRead = true; - debug('length less than watermark', doRead); - } - - // however, if we've ended, then there's no point, and if we're already - // reading, then it's unnecessary. - if (state.ended || state.reading) { - doRead = false; - debug('reading or ended', doRead); - } else if (doRead) { - debug('do read'); - state.reading = true; - state.sync = true; - // if the length is currently zero, then we *need* a readable event. - if (state.length === 0) state.needReadable = true; - // call internal read method - this._read(state.highWaterMark); - state.sync = false; - // If _read pushed data synchronously, then `reading` will be false, - // and we need to re-evaluate how much data we can return to the user. - if (!state.reading) n = howMuchToRead(nOrig, state); - } - - var ret; - if (n > 0) ret = fromList(n, state);else ret = null; - - if (ret === null) { - state.needReadable = true; - n = 0; - } else { - state.length -= n; - } - - if (state.length === 0) { - // If we have nothing in the buffer, then we want to know - // as soon as we *do* get something into the buffer. - if (!state.ended) state.needReadable = true; - - // If we tried to read() past the EOF, then emit end on the next tick. - if (nOrig !== n && state.ended) endReadable(this); - } - - if (ret !== null) this.emit('data', ret); - - return ret; -}; - -function onEofChunk(stream, state) { - if (state.ended) return; - if (state.decoder) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) { - state.buffer.push(chunk); - state.length += state.objectMode ? 1 : chunk.length; - } - } - state.ended = true; - - // emit 'readable' now to make sure it gets picked up. - emitReadable(stream); -} - -// Don't emit readable right away in sync mode, because this can trigger -// another read() call => stack overflow. This way, it might trigger -// a nextTick recursion warning, but that's not so bad. -function emitReadable(stream) { - var state = stream._readableState; - state.needReadable = false; - if (!state.emittedReadable) { - debug('emitReadable', state.flowing); - state.emittedReadable = true; - if (state.sync) pna.nextTick(emitReadable_, stream);else emitReadable_(stream); - } -} - -function emitReadable_(stream) { - debug('emit readable'); - stream.emit('readable'); - flow(stream); -} - -// at this point, the user has presumably seen the 'readable' event, -// and called read() to consume some data. that may have triggered -// in turn another _read(n) call, in which case reading = true if -// it's in progress. -// However, if we're not ended, or reading, and the length < hwm, -// then go ahead and try to read some more preemptively. -function maybeReadMore(stream, state) { - if (!state.readingMore) { - state.readingMore = true; - pna.nextTick(maybeReadMore_, stream, state); - } -} - -function maybeReadMore_(stream, state) { - var len = state.length; - while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) { - debug('maybeReadMore read 0'); - stream.read(0); - if (len === state.length) - // didn't get any data, stop spinning. - break;else len = state.length; - } - state.readingMore = false; -} - -// abstract method. to be overridden in specific implementation classes. -// call cb(er, data) where data is <= n in length. -// for virtual (non-string, non-buffer) streams, "length" is somewhat -// arbitrary, and perhaps not very meaningful. -Readable.prototype._read = function (n) { - this.emit('error', new Error('_read() is not implemented')); -}; - -Readable.prototype.pipe = function (dest, pipeOpts) { - var src = this; - var state = this._readableState; - - switch (state.pipesCount) { - case 0: - state.pipes = dest; - break; - case 1: - state.pipes = [state.pipes, dest]; - break; - default: - state.pipes.push(dest); - break; - } - state.pipesCount += 1; - debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); - - var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; - - var endFn = doEnd ? onend : unpipe; - if (state.endEmitted) pna.nextTick(endFn);else src.once('end', endFn); - - dest.on('unpipe', onunpipe); - function onunpipe(readable, unpipeInfo) { - debug('onunpipe'); - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); - } - } - } - - function onend() { - debug('onend'); - dest.end(); - } - - // when the dest drains, it reduces the awaitDrain counter - // on the source. This would be more elegant with a .once() - // handler in flow(), but adding and removing repeatedly is - // too slow. - var ondrain = pipeOnDrain(src); - dest.on('drain', ondrain); - - var cleanedUp = false; - function cleanup() { - debug('cleanup'); - // cleanup event handlers once the pipe is broken - dest.removeListener('close', onclose); - dest.removeListener('finish', onfinish); - dest.removeListener('drain', ondrain); - dest.removeListener('error', onerror); - dest.removeListener('unpipe', onunpipe); - src.removeListener('end', onend); - src.removeListener('end', unpipe); - src.removeListener('data', ondata); - - cleanedUp = true; - - // if the reader is waiting for a drain event from this - // specific writer, then it would cause it to never start - // flowing again. - // So, if this is awaiting a drain, then we just call it now. - // If we don't know, then assume that we are waiting for one. - if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); - } - - // If the user pushes more data while we're writing to dest then we'll end up - // in ondata again. However, we only want to increase awaitDrain once because - // dest will only emit one 'drain' event for the multiple writes. - // => Introduce a guard on increasing awaitDrain. - var increasedAwaitDrain = false; - src.on('data', ondata); - function ondata(chunk) { - debug('ondata'); - increasedAwaitDrain = false; - var ret = dest.write(chunk); - if (false === ret && !increasedAwaitDrain) { - // If the user unpiped during `dest.write()`, it is possible - // to get stuck in a permanently paused state if that write - // also returned false. - // => Check whether `dest` is still a piping destination. - if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { - debug('false write response, pause', src._readableState.awaitDrain); - src._readableState.awaitDrain++; - increasedAwaitDrain = true; - } - src.pause(); - } - } - - // if the dest has an error, then stop piping into it. - // however, don't suppress the throwing behavior for this. - function onerror(er) { - debug('onerror', er); - unpipe(); - dest.removeListener('error', onerror); - if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er); - } - - // Make sure our error handler is attached before userland ones. - prependListener(dest, 'error', onerror); - - // Both close and finish should trigger unpipe, but only once. - function onclose() { - dest.removeListener('finish', onfinish); - unpipe(); - } - dest.once('close', onclose); - function onfinish() { - debug('onfinish'); - dest.removeListener('close', onclose); - unpipe(); - } - dest.once('finish', onfinish); - - function unpipe() { - debug('unpipe'); - src.unpipe(dest); - } - - // tell the dest that it's being piped to - dest.emit('pipe', src); - - // start the flow if it hasn't been started already. - if (!state.flowing) { - debug('pipe resume'); - src.resume(); - } - - return dest; -}; - -function pipeOnDrain(src) { - return function () { - var state = src._readableState; - debug('pipeOnDrain', state.awaitDrain); - if (state.awaitDrain) state.awaitDrain--; - if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { - state.flowing = true; - flow(src); - } - }; -} - -Readable.prototype.unpipe = function (dest) { - var state = this._readableState; - var unpipeInfo = { hasUnpiped: false }; - - // if we're not piping anywhere, then do nothing. - if (state.pipesCount === 0) return this; - - // just one destination. most common case. - if (state.pipesCount === 1) { - // passed in one, but it's not the right one. - if (dest && dest !== state.pipes) return this; - - if (!dest) dest = state.pipes; - - // got a match. - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - if (dest) dest.emit('unpipe', this, unpipeInfo); - return this; - } - - // slow case. multiple pipe destinations. - - if (!dest) { - // remove all. - var dests = state.pipes; - var len = state.pipesCount; - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - - for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this, unpipeInfo); - }return this; - } - - // try to find the right one. - var index = indexOf(state.pipes, dest); - if (index === -1) return this; - - state.pipes.splice(index, 1); - state.pipesCount -= 1; - if (state.pipesCount === 1) state.pipes = state.pipes[0]; - - dest.emit('unpipe', this, unpipeInfo); - - return this; -}; - -// set up data events if they are asked for -// Ensure readable listeners eventually get something -Readable.prototype.on = function (ev, fn) { - var res = Stream.prototype.on.call(this, ev, fn); - - if (ev === 'data') { - // Start flowing on next tick if stream isn't explicitly paused - if (this._readableState.flowing !== false) this.resume(); - } else if (ev === 'readable') { - var state = this._readableState; - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.emittedReadable = false; - if (!state.reading) { - pna.nextTick(nReadingNextTick, this); - } else if (state.length) { - emitReadable(this); - } - } - } - - return res; -}; -Readable.prototype.addListener = Readable.prototype.on; - -function nReadingNextTick(self) { - debug('readable nexttick read 0'); - self.read(0); -} - -// pause() and resume() are remnants of the legacy readable stream API -// If the user uses them, then switch into old mode. -Readable.prototype.resume = function () { - var state = this._readableState; - if (!state.flowing) { - debug('resume'); - state.flowing = true; - resume(this, state); - } - return this; -}; - -function resume(stream, state) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - pna.nextTick(resume_, stream, state); - } -} - -function resume_(stream, state) { - if (!state.reading) { - debug('resume read 0'); - stream.read(0); - } - - state.resumeScheduled = false; - state.awaitDrain = 0; - stream.emit('resume'); - flow(stream); - if (state.flowing && !state.reading) stream.read(0); -} - -Readable.prototype.pause = function () { - debug('call pause flowing=%j', this._readableState.flowing); - if (false !== this._readableState.flowing) { - debug('pause'); - this._readableState.flowing = false; - this.emit('pause'); - } - return this; -}; - -function flow(stream) { - var state = stream._readableState; - debug('flow', state.flowing); - while (state.flowing && stream.read() !== null) {} -} - -// wrap an old-style stream as the async data source. -// This is *not* part of the readable stream interface. -// It is an ugly unfortunate mess of history. -Readable.prototype.wrap = function (stream) { - var _this = this; - - var state = this._readableState; - var paused = false; - - stream.on('end', function () { - debug('wrapped end'); - if (state.decoder && !state.ended) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) _this.push(chunk); - } - - _this.push(null); - }); - - stream.on('data', function (chunk) { - debug('wrapped data'); - if (state.decoder) chunk = state.decoder.write(chunk); - - // don't skip over falsy values in objectMode - if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; - - var ret = _this.push(chunk); - if (!ret) { - paused = true; - stream.pause(); - } - }); - - // proxy all the other methods. - // important when wrapping filters and duplexes. - for (var i in stream) { - if (this[i] === undefined && typeof stream[i] === 'function') { - this[i] = function (method) { - return function () { - return stream[method].apply(stream, arguments); - }; - }(i); - } - } - - // proxy certain important events. - for (var n = 0; n < kProxyEvents.length; n++) { - stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); - } - - // when we try to consume some more bytes, simply unpause the - // underlying stream. - this._read = function (n) { - debug('wrapped _read', n); - if (paused) { - paused = false; - stream.resume(); - } - }; - - return this; -}; - -Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function () { - return this._readableState.highWaterMark; - } -}); - -// exposed for testing purposes only. -Readable._fromList = fromList; - -// Pluck off n bytes from an array of buffers. -// Length is the combined lengths of all the buffers in the list. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function fromList(n, state) { - // nothing buffered - if (state.length === 0) return null; - - var ret; - if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { - // read it all, truncate the list - if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length); - state.buffer.clear(); - } else { - // read part of list - ret = fromListPartial(n, state.buffer, state.decoder); - } - - return ret; -} - -// Extracts only enough buffered data to satisfy the amount requested. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function fromListPartial(n, list, hasStrings) { - var ret; - if (n < list.head.data.length) { - // slice is the same for buffers and strings - ret = list.head.data.slice(0, n); - list.head.data = list.head.data.slice(n); - } else if (n === list.head.data.length) { - // first chunk is a perfect match - ret = list.shift(); - } else { - // result spans more than one buffer - ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list); - } - return ret; -} - -// Copies a specified amount of characters from the list of buffered data -// chunks. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function copyFromBufferString(n, list) { - var p = list.head; - var c = 1; - var ret = p.data; - n -= ret.length; - while (p = p.next) { - var str = p.data; - var nb = n > str.length ? str.length : n; - if (nb === str.length) ret += str;else ret += str.slice(0, n); - n -= nb; - if (n === 0) { - if (nb === str.length) { - ++c; - if (p.next) list.head = p.next;else list.head = list.tail = null; - } else { - list.head = p; - p.data = str.slice(nb); - } - break; - } - ++c; - } - list.length -= c; - return ret; -} - -// Copies a specified amount of bytes from the list of buffered data chunks. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function copyFromBuffer(n, list) { - var ret = Buffer.allocUnsafe(n); - var p = list.head; - var c = 1; - p.data.copy(ret); - n -= p.data.length; - while (p = p.next) { - var buf = p.data; - var nb = n > buf.length ? buf.length : n; - buf.copy(ret, ret.length - n, 0, nb); - n -= nb; - if (n === 0) { - if (nb === buf.length) { - ++c; - if (p.next) list.head = p.next;else list.head = list.tail = null; - } else { - list.head = p; - p.data = buf.slice(nb); - } - break; - } - ++c; - } - list.length -= c; - return ret; -} - -function endReadable(stream) { - var state = stream._readableState; - - // If we get here before consuming all the bytes, then that is a - // bug in node. Should never happen. - if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); - - if (!state.endEmitted) { - state.ended = true; - pna.nextTick(endReadableNT, state, stream); - } -} - -function endReadableNT(state, stream) { - // Check that we didn't get one last unshift. - if (!state.endEmitted && state.length === 0) { - state.endEmitted = true; - stream.readable = false; - stream.emit('end'); - } -} - -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - return -1; -} -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./_stream_duplex":71,"./internal/streams/BufferList":76,"./internal/streams/destroy":77,"./internal/streams/stream":78,"_process":69,"core-util-is":44,"events":50,"inherits":56,"isarray":58,"process-nextick-args":68,"safe-buffer":83,"string_decoder/":85,"util":40}],74:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a transform stream is a readable/writable stream where you do -// something with the data. Sometimes it's called a "filter", -// but that's not a great name for it, since that implies a thing where -// some bits pass through, and others are simply ignored. (That would -// be a valid example of a transform, of course.) -// -// While the output is causally related to the input, it's not a -// necessarily symmetric or synchronous transformation. For example, -// a zlib stream might take multiple plain-text writes(), and then -// emit a single compressed chunk some time in the future. -// -// Here's how this works: -// -// The Transform stream has all the aspects of the readable and writable -// stream classes. When you write(chunk), that calls _write(chunk,cb) -// internally, and returns false if there's a lot of pending writes -// buffered up. When you call read(), that calls _read(n) until -// there's enough pending readable data buffered up. -// -// In a transform stream, the written data is placed in a buffer. When -// _read(n) is called, it transforms the queued up data, calling the -// buffered _write cb's as it consumes chunks. If consuming a single -// written chunk would result in multiple output chunks, then the first -// outputted bit calls the readcb, and subsequent chunks just go into -// the read buffer, and will cause it to emit 'readable' if necessary. -// -// This way, back-pressure is actually determined by the reading side, -// since _read has to be called to start processing a new chunk. However, -// a pathological inflate type of transform can cause excessive buffering -// here. For example, imagine a stream where every byte of input is -// interpreted as an integer from 0-255, and then results in that many -// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in -// 1kb of data being output. In this case, you could write a very small -// amount of input, and end up with a very large amount of output. In -// such a pathological inflating mechanism, there'd be no way to tell -// the system to stop doing the transform. A single 4MB write could -// cause the system to run out of memory. -// -// However, even in such a pathological case, only a single written chunk -// would be consumed, and then the rest would wait (un-transformed) until -// the results of the previous transformed chunk were consumed. - -'use strict'; - -module.exports = Transform; - -var Duplex = require('./_stream_duplex'); - -/*<replacement>*/ -var util = require('core-util-is'); -util.inherits = require('inherits'); -/*</replacement>*/ - -util.inherits(Transform, Duplex); - -function afterTransform(er, data) { - var ts = this._transformState; - ts.transforming = false; - - var cb = ts.writecb; - - if (!cb) { - return this.emit('error', new Error('write callback called multiple times')); - } - - ts.writechunk = null; - ts.writecb = null; - - if (data != null) // single equals check for both `null` and `undefined` - this.push(data); - - cb(er); - - var rs = this._readableState; - rs.reading = false; - if (rs.needReadable || rs.length < rs.highWaterMark) { - this._read(rs.highWaterMark); - } -} - -function Transform(options) { - if (!(this instanceof Transform)) return new Transform(options); - - Duplex.call(this, options); - - this._transformState = { - afterTransform: afterTransform.bind(this), - needTransform: false, - transforming: false, - writecb: null, - writechunk: null, - writeencoding: null - }; - - // start out asking for a readable event once data is transformed. - this._readableState.needReadable = true; - - // we have implemented the _read method, and done the other things - // that Readable wants before the first _read call, so unset the - // sync guard flag. - this._readableState.sync = false; - - if (options) { - if (typeof options.transform === 'function') this._transform = options.transform; - - if (typeof options.flush === 'function') this._flush = options.flush; - } - - // When the writable side finishes, then flush out anything remaining. - this.on('prefinish', prefinish); -} - -function prefinish() { - var _this = this; - - if (typeof this._flush === 'function') { - this._flush(function (er, data) { - done(_this, er, data); - }); - } else { - done(this, null, null); - } -} - -Transform.prototype.push = function (chunk, encoding) { - this._transformState.needTransform = false; - return Duplex.prototype.push.call(this, chunk, encoding); -}; - -// This is the part where you do stuff! -// override this function in implementation classes. -// 'chunk' is an input chunk. -// -// Call `push(newChunk)` to pass along transformed output -// to the readable side. You may call 'push' zero or more times. -// -// Call `cb(err)` when you are done with this chunk. If you pass -// an error, then that'll put the hurt on the whole operation. If you -// never call cb(), then you'll never get another chunk. -Transform.prototype._transform = function (chunk, encoding, cb) { - throw new Error('_transform() is not implemented'); -}; - -Transform.prototype._write = function (chunk, encoding, cb) { - var ts = this._transformState; - ts.writecb = cb; - ts.writechunk = chunk; - ts.writeencoding = encoding; - if (!ts.transforming) { - var rs = this._readableState; - if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); - } -}; - -// Doesn't matter what the args are here. -// _transform does all the work. -// That we got here means that the readable side wants more data. -Transform.prototype._read = function (n) { - var ts = this._transformState; - - if (ts.writechunk !== null && ts.writecb && !ts.transforming) { - ts.transforming = true; - this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); - } else { - // mark that we need a transform, so that any data that comes in - // will get processed, now that we've asked for it. - ts.needTransform = true; - } -}; - -Transform.prototype._destroy = function (err, cb) { - var _this2 = this; - - Duplex.prototype._destroy.call(this, err, function (err2) { - cb(err2); - _this2.emit('close'); - }); -}; - -function done(stream, er, data) { - if (er) return stream.emit('error', er); - - if (data != null) // single equals check for both `null` and `undefined` - stream.push(data); - - // if there's nothing in the write buffer, then that means - // that nothing more will ever be provided - if (stream._writableState.length) throw new Error('Calling transform done when ws.length != 0'); - - if (stream._transformState.transforming) throw new Error('Calling transform done when still transforming'); - - return stream.push(null); -} -},{"./_stream_duplex":71,"core-util-is":44,"inherits":56}],75:[function(require,module,exports){ -(function (process,global,setImmediate){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. - -'use strict'; - -/*<replacement>*/ - -var pna = require('process-nextick-args'); -/*</replacement>*/ - -module.exports = Writable; - -/* <replacement> */ -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} - -// It seems a linked list but it is not -// there will be only 2 of these for each stream -function CorkedRequest(state) { - var _this = this; - - this.next = null; - this.entry = null; - this.finish = function () { - onCorkedFinish(_this, state); - }; -} -/* </replacement> */ - -/*<replacement>*/ -var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick; -/*</replacement>*/ - -/*<replacement>*/ -var Duplex; -/*</replacement>*/ - -Writable.WritableState = WritableState; - -/*<replacement>*/ -var util = require('core-util-is'); -util.inherits = require('inherits'); -/*</replacement>*/ - -/*<replacement>*/ -var internalUtil = { - deprecate: require('util-deprecate') -}; -/*</replacement>*/ - -/*<replacement>*/ -var Stream = require('./internal/streams/stream'); -/*</replacement>*/ - -/*<replacement>*/ - -var Buffer = require('safe-buffer').Buffer; -var OurUint8Array = global.Uint8Array || function () {}; -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -/*</replacement>*/ - -var destroyImpl = require('./internal/streams/destroy'); - -util.inherits(Writable, Stream); - -function nop() {} - -function WritableState(options, stream) { - Duplex = Duplex || require('./_stream_duplex'); - - options = options || {}; - - // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Duplex; - - // object stream flag to indicate whether or not this stream - // contains buffers or objects. - this.objectMode = !!options.objectMode; - - if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; - - // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - var hwm = options.highWaterMark; - var writableHwm = options.writableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; - - if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (writableHwm || writableHwm === 0)) this.highWaterMark = writableHwm;else this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); - - // if _final has been called - this.finalCalled = false; - - // drain event flag. - this.needDrain = false; - // at the start of calling end() - this.ending = false; - // when end() has been called, and returned - this.ended = false; - // when 'finish' is emitted - this.finished = false; - - // has it been destroyed - this.destroyed = false; - - // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; - - // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - this.defaultEncoding = options.defaultEncoding || 'utf8'; - - // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. - this.length = 0; - - // a flag to see when we're in the middle of a write. - this.writing = false; - - // when true all writes will be buffered until .uncork() call - this.corked = 0; - - // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. - this.sync = true; - - // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. - this.bufferProcessing = false; - - // the callback that's passed to _write(chunk,cb) - this.onwrite = function (er) { - onwrite(stream, er); - }; - - // the callback that the user supplies to write(chunk,encoding,cb) - this.writecb = null; - - // the amount that is being written when _write is called. - this.writelen = 0; - - this.bufferedRequest = null; - this.lastBufferedRequest = null; - - // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - this.pendingcb = 0; - - // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - this.prefinished = false; - - // True if the error was already emitted and should not be thrown again - this.errorEmitted = false; - - // count buffered requests - this.bufferedRequestCount = 0; - - // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - this.corkedRequestsFree = new CorkedRequest(this); -} - -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - while (current) { - out.push(current); - current = current.next; - } - return out; -}; - -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function () { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') - }); - } catch (_) {} -})(); - -// Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. -var realHasInstance; -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function (object) { - if (realHasInstance.call(this, object)) return true; - if (this !== Writable) return false; - - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function (object) { - return object instanceof this; - }; -} - -function Writable(options) { - Duplex = Duplex || require('./_stream_duplex'); - - // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. - - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { - return new Writable(options); - } - - this._writableState = new WritableState(options, this); - - // legacy. - this.writable = true; - - if (options) { - if (typeof options.write === 'function') this._write = options.write; - - if (typeof options.writev === 'function') this._writev = options.writev; - - if (typeof options.destroy === 'function') this._destroy = options.destroy; - - if (typeof options.final === 'function') this._final = options.final; - } - - Stream.call(this); -} - -// Otherwise people can pipe Writable streams, which is just wrong. -Writable.prototype.pipe = function () { - this.emit('error', new Error('Cannot pipe, not readable')); -}; - -function writeAfterEnd(stream, cb) { - var er = new Error('write after end'); - // TODO: defer error events consistently everywhere, not just the cb - stream.emit('error', er); - pna.nextTick(cb, er); -} - -// Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. -function validChunk(stream, state, chunk, cb) { - var valid = true; - var er = false; - - if (chunk === null) { - er = new TypeError('May not write null values to stream'); - } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); - } - if (er) { - stream.emit('error', er); - pna.nextTick(cb, er); - valid = false; - } - return valid; -} - -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - var isBuf = !state.objectMode && _isUint8Array(chunk); - - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - - if (typeof cb !== 'function') cb = nop; - - if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } - - return ret; -}; - -Writable.prototype.cork = function () { - var state = this._writableState; - - state.corked++; -}; - -Writable.prototype.uncork = function () { - var state = this._writableState; - - if (state.corked) { - state.corked--; - - if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } -}; - -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; - -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - return chunk; -} - -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function () { - return this._writableState.highWaterMark; - } -}); - -// if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - var newChunk = decodeChunk(state, chunk, encoding); - if (chunk !== newChunk) { - isBuf = true; - encoding = 'buffer'; - chunk = newChunk; - } - } - var len = state.objectMode ? 1 : chunk.length; - - state.length += len; - - var ret = state.length < state.highWaterMark; - // we must ensure that previous needDrain will not be reset to false. - if (!ret) state.needDrain = true; - - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null - }; - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } - - return ret; -} - -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; -} - -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; - - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - pna.nextTick(cb, er); - // this can emit finish, and it will always happen - // after error - pna.nextTick(finishMaybe, stream, state); - stream._writableState.errorEmitted = true; - stream.emit('error', er); - } else { - // the caller expect this to happen before if - // it is async - cb(er); - stream._writableState.errorEmitted = true; - stream.emit('error', er); - // this can emit finish, but finish must - // always follow error - finishMaybe(stream, state); - } -} - -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} - -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; - - onwriteStateUpdate(state); - - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state); - - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } - - if (sync) { - /*<replacement>*/ - asyncWrite(afterWrite, stream, state, finished, cb); - /*</replacement>*/ - } else { - afterWrite(stream, state, finished, cb); - } - } -} - -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} - -// Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); - } -} - -// if there's something in the buffer waiting, then process it -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; - - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; - - var count = 0; - var allBuffers = true; - while (entry) { - buffer[count] = entry; - if (!entry.isBuf) allBuffers = false; - entry = entry.next; - count += 1; - } - buffer.allBuffers = allBuffers; - - doWrite(stream, state, true, state.length, buffer, '', holder.finish); - - // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite - state.pendingcb++; - state.lastBufferedRequest = null; - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } - state.bufferedRequestCount = 0; - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; - - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - state.bufferedRequestCount--; - // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. - if (state.writing) { - break; - } - } - - if (entry === null) state.lastBufferedRequest = null; - } - - state.bufferedRequest = entry; - state.bufferProcessing = false; -} - -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new Error('_write() is not implemented')); -}; - -Writable.prototype._writev = null; - -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; - - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); - - // .end() fully uncorks - if (state.corked) { - state.corked = 1; - this.uncork(); - } - - // ignore unnecessary end() calls. - if (!state.ending && !state.finished) endWritable(this, state, cb); -}; - -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} -function callFinal(stream, state) { - stream._final(function (err) { - state.pendingcb--; - if (err) { - stream.emit('error', err); - } - state.prefinished = true; - stream.emit('prefinish'); - finishMaybe(stream, state); - }); -} -function prefinish(stream, state) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === 'function') { - state.pendingcb++; - state.finalCalled = true; - pna.nextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); - } - } -} - -function finishMaybe(stream, state) { - var need = needFinish(state); - if (need) { - prefinish(stream, state); - if (state.pendingcb === 0) { - state.finished = true; - stream.emit('finish'); - } - } - return need; -} - -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - if (cb) { - if (state.finished) pna.nextTick(cb);else stream.once('finish', cb); - } - state.ended = true; - stream.writable = false; -} - -function onCorkedFinish(corkReq, state, err) { - var entry = corkReq.entry; - corkReq.entry = null; - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } - if (state.corkedRequestsFree) { - state.corkedRequestsFree.next = corkReq; - } else { - state.corkedRequestsFree = corkReq; - } -} - -Object.defineProperty(Writable.prototype, 'destroyed', { - get: function () { - if (this._writableState === undefined) { - return false; - } - return this._writableState.destroyed; - }, - set: function (value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._writableState) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._writableState.destroyed = value; - } -}); - -Writable.prototype.destroy = destroyImpl.destroy; -Writable.prototype._undestroy = destroyImpl.undestroy; -Writable.prototype._destroy = function (err, cb) { - this.end(); - cb(err); -}; -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate) -},{"./_stream_duplex":71,"./internal/streams/destroy":77,"./internal/streams/stream":78,"_process":69,"core-util-is":44,"inherits":56,"process-nextick-args":68,"safe-buffer":83,"timers":86,"util-deprecate":87}],76:[function(require,module,exports){ -'use strict'; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var Buffer = require('safe-buffer').Buffer; -var util = require('util'); - -function copyBuffer(src, target, offset) { - src.copy(target, offset); -} - -module.exports = function () { - function BufferList() { - _classCallCheck(this, BufferList); - - this.head = null; - this.tail = null; - this.length = 0; - } - - BufferList.prototype.push = function push(v) { - var entry = { data: v, next: null }; - if (this.length > 0) this.tail.next = entry;else this.head = entry; - this.tail = entry; - ++this.length; - }; - - BufferList.prototype.unshift = function unshift(v) { - var entry = { data: v, next: this.head }; - if (this.length === 0) this.tail = entry; - this.head = entry; - ++this.length; - }; - - BufferList.prototype.shift = function shift() { - if (this.length === 0) return; - var ret = this.head.data; - if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; - --this.length; - return ret; - }; - - BufferList.prototype.clear = function clear() { - this.head = this.tail = null; - this.length = 0; - }; - - BufferList.prototype.join = function join(s) { - if (this.length === 0) return ''; - var p = this.head; - var ret = '' + p.data; - while (p = p.next) { - ret += s + p.data; - }return ret; - }; - - BufferList.prototype.concat = function concat(n) { - if (this.length === 0) return Buffer.alloc(0); - if (this.length === 1) return this.head.data; - var ret = Buffer.allocUnsafe(n >>> 0); - var p = this.head; - var i = 0; - while (p) { - copyBuffer(p.data, ret, i); - i += p.data.length; - p = p.next; - } - return ret; - }; - - return BufferList; -}(); - -if (util && util.inspect && util.inspect.custom) { - module.exports.prototype[util.inspect.custom] = function () { - var obj = util.inspect({ length: this.length }); - return this.constructor.name + ' ' + obj; - }; -} -},{"safe-buffer":83,"util":40}],77:[function(require,module,exports){ -'use strict'; - -/*<replacement>*/ - -var pna = require('process-nextick-args'); -/*</replacement>*/ - -// undocumented cb() API, needed for core, not for public API -function destroy(err, cb) { - var _this = this; - - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; - - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { - pna.nextTick(emitErrorNT, this, err); - } - return this; - } - - // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks - - if (this._readableState) { - this._readableState.destroyed = true; - } - - // if this is a duplex stream mark the writable part as destroyed as well - if (this._writableState) { - this._writableState.destroyed = true; - } - - this._destroy(err || null, function (err) { - if (!cb && err) { - pna.nextTick(emitErrorNT, _this, err); - if (_this._writableState) { - _this._writableState.errorEmitted = true; - } - } else if (cb) { - cb(err); - } - }); - - return this; -} - -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; - } - - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; - } -} - -function emitErrorNT(self, err) { - self.emit('error', err); -} - -module.exports = { - destroy: destroy, - undestroy: undestroy -}; -},{"process-nextick-args":68}],78:[function(require,module,exports){ -module.exports = require('events').EventEmitter; - -},{"events":50}],79:[function(require,module,exports){ -module.exports = require('./readable').PassThrough - -},{"./readable":80}],80:[function(require,module,exports){ -exports = module.exports = require('./lib/_stream_readable.js'); -exports.Stream = exports; -exports.Readable = exports; -exports.Writable = require('./lib/_stream_writable.js'); -exports.Duplex = require('./lib/_stream_duplex.js'); -exports.Transform = require('./lib/_stream_transform.js'); -exports.PassThrough = require('./lib/_stream_passthrough.js'); - -},{"./lib/_stream_duplex.js":71,"./lib/_stream_passthrough.js":72,"./lib/_stream_readable.js":73,"./lib/_stream_transform.js":74,"./lib/_stream_writable.js":75}],81:[function(require,module,exports){ -module.exports = require('./readable').Transform - -},{"./readable":80}],82:[function(require,module,exports){ -module.exports = require('./lib/_stream_writable.js'); - -},{"./lib/_stream_writable.js":75}],83:[function(require,module,exports){ -/* eslint-disable node/no-deprecated-api */ -var buffer = require('buffer') -var Buffer = buffer.Buffer - -// alternative to using Object.keys for old browsers -function copyProps (src, dst) { - for (var key in src) { - dst[key] = src[key] - } -} -if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { - module.exports = buffer -} else { - // Copy properties from require('buffer') - copyProps(buffer, exports) - exports.Buffer = SafeBuffer -} - -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) -} - -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) - -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') - } - return Buffer(arg, encodingOrOffset, length) -} - -SafeBuffer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - var buf = Buffer(size) - if (fill !== undefined) { - if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - } else { - buf.fill(0) - } - return buf -} - -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return Buffer(size) -} - -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return buffer.SlowBuffer(size) -} - -},{"buffer":43}],84:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -module.exports = Stream; - -var EE = require('events').EventEmitter; -var inherits = require('inherits'); - -inherits(Stream, EE); -Stream.Readable = require('readable-stream/readable.js'); -Stream.Writable = require('readable-stream/writable.js'); -Stream.Duplex = require('readable-stream/duplex.js'); -Stream.Transform = require('readable-stream/transform.js'); -Stream.PassThrough = require('readable-stream/passthrough.js'); - -// Backwards-compat with node 0.4.x -Stream.Stream = Stream; - - - -// old-style streams. Note that the pipe method (the only relevant -// part of this class) is overridden in the Readable class. - -function Stream() { - EE.call(this); -} - -Stream.prototype.pipe = function(dest, options) { - var source = this; - - function ondata(chunk) { - if (dest.writable) { - if (false === dest.write(chunk) && source.pause) { - source.pause(); - } - } - } - - source.on('data', ondata); - - function ondrain() { - if (source.readable && source.resume) { - source.resume(); - } - } - - dest.on('drain', ondrain); - - // If the 'end' option is not supplied, dest.end() will be called when - // source gets the 'end' or 'close' events. Only dest.end() once. - if (!dest._isStdio && (!options || options.end !== false)) { - source.on('end', onend); - source.on('close', onclose); - } - - var didOnEnd = false; - function onend() { - if (didOnEnd) return; - didOnEnd = true; - - dest.end(); - } - - - function onclose() { - if (didOnEnd) return; - didOnEnd = true; - - if (typeof dest.destroy === 'function') dest.destroy(); - } - - // don't leave dangling pipes when there are errors. - function onerror(er) { - cleanup(); - if (EE.listenerCount(this, 'error') === 0) { - throw er; // Unhandled stream error in pipe. - } - } - - source.on('error', onerror); - dest.on('error', onerror); - - // remove all the event listeners that were added. - function cleanup() { - source.removeListener('data', ondata); - dest.removeListener('drain', ondrain); - - source.removeListener('end', onend); - source.removeListener('close', onclose); - - source.removeListener('error', onerror); - dest.removeListener('error', onerror); - - source.removeListener('end', cleanup); - source.removeListener('close', cleanup); - - dest.removeListener('close', cleanup); - } - - source.on('end', cleanup); - source.on('close', cleanup); - - dest.on('close', cleanup); - - dest.emit('pipe', source); - - // Allow for unix-like usage: A.pipe(B).pipe(C) - return dest; -}; - -},{"events":50,"inherits":56,"readable-stream/duplex.js":70,"readable-stream/passthrough.js":79,"readable-stream/readable.js":80,"readable-stream/transform.js":81,"readable-stream/writable.js":82}],85:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -/*<replacement>*/ - -var Buffer = require('safe-buffer').Buffer; -/*</replacement>*/ - -var isEncoding = Buffer.isEncoding || function (encoding) { - encoding = '' + encoding; - switch (encoding && encoding.toLowerCase()) { - case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': - return true; - default: - return false; - } -}; - -function _normalizeEncoding(enc) { - if (!enc) return 'utf8'; - var retried; - while (true) { - switch (enc) { - case 'utf8': - case 'utf-8': - return 'utf8'; - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return 'utf16le'; - case 'latin1': - case 'binary': - return 'latin1'; - case 'base64': - case 'ascii': - case 'hex': - return enc; - default: - if (retried) return; // undefined - enc = ('' + enc).toLowerCase(); - retried = true; - } - } -}; - -// Do not cache `Buffer.isEncoding` when checking encoding names as some -// modules monkey-patch it to support additional encodings -function normalizeEncoding(enc) { - var nenc = _normalizeEncoding(enc); - if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); - return nenc || enc; -} - -// StringDecoder provides an interface for efficiently splitting a series of -// buffers into a series of JS strings without breaking apart multi-byte -// characters. -exports.StringDecoder = StringDecoder; -function StringDecoder(encoding) { - this.encoding = normalizeEncoding(encoding); - var nb; - switch (this.encoding) { - case 'utf16le': - this.text = utf16Text; - this.end = utf16End; - nb = 4; - break; - case 'utf8': - this.fillLast = utf8FillLast; - nb = 4; - break; - case 'base64': - this.text = base64Text; - this.end = base64End; - nb = 3; - break; - default: - this.write = simpleWrite; - this.end = simpleEnd; - return; - } - this.lastNeed = 0; - this.lastTotal = 0; - this.lastChar = Buffer.allocUnsafe(nb); -} - -StringDecoder.prototype.write = function (buf) { - if (buf.length === 0) return ''; - var r; - var i; - if (this.lastNeed) { - r = this.fillLast(buf); - if (r === undefined) return ''; - i = this.lastNeed; - this.lastNeed = 0; - } else { - i = 0; - } - if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); - return r || ''; -}; - -StringDecoder.prototype.end = utf8End; - -// Returns only complete characters in a Buffer -StringDecoder.prototype.text = utf8Text; - -// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer -StringDecoder.prototype.fillLast = function (buf) { - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); - this.lastNeed -= buf.length; -}; - -// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a -// continuation byte. If an invalid byte is detected, -2 is returned. -function utf8CheckByte(byte) { - if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; - return byte >> 6 === 0x02 ? -1 : -2; -} - -// Checks at most 3 bytes at the end of a Buffer in order to detect an -// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) -// needed to complete the UTF-8 character (if applicable) are returned. -function utf8CheckIncomplete(self, buf, i) { - var j = buf.length - 1; - if (j < i) return 0; - var nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 1; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 2; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) { - if (nb === 2) nb = 0;else self.lastNeed = nb - 3; - } - return nb; - } - return 0; -} - -// Validates as many continuation bytes for a multi-byte UTF-8 character as -// needed or are available. If we see a non-continuation byte where we expect -// one, we "replace" the validated continuation bytes we've seen so far with -// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding -// behavior. The continuation byte check is included three times in the case -// where all of the continuation bytes for a character exist in the same buffer. -// It is also done this way as a slight performance increase instead of using a -// loop. -function utf8CheckExtraBytes(self, buf, p) { - if ((buf[0] & 0xC0) !== 0x80) { - self.lastNeed = 0; - return '\ufffd'; - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xC0) !== 0x80) { - self.lastNeed = 1; - return '\ufffd'; - } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xC0) !== 0x80) { - self.lastNeed = 2; - return '\ufffd'; - } - } - } -} - -// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. -function utf8FillLast(buf) { - var p = this.lastTotal - this.lastNeed; - var r = utf8CheckExtraBytes(this, buf, p); - if (r !== undefined) return r; - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, p, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, p, 0, buf.length); - this.lastNeed -= buf.length; -} - -// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a -// partial character, the character's bytes are buffered until the required -// number of bytes are available. -function utf8Text(buf, i) { - var total = utf8CheckIncomplete(this, buf, i); - if (!this.lastNeed) return buf.toString('utf8', i); - this.lastTotal = total; - var end = buf.length - (total - this.lastNeed); - buf.copy(this.lastChar, 0, end); - return buf.toString('utf8', i, end); -} - -// For UTF-8, a replacement character is added when ending on a partial -// character. -function utf8End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + '\ufffd'; - return r; -} - -// UTF-16LE typically needs two bytes per character, but even if we have an even -// number of bytes available, we need to check if we end on a leading/high -// surrogate. In that case, we need to wait for the next two bytes in order to -// decode the last character properly. -function utf16Text(buf, i) { - if ((buf.length - i) % 2 === 0) { - var r = buf.toString('utf16le', i); - if (r) { - var c = r.charCodeAt(r.length - 1); - if (c >= 0xD800 && c <= 0xDBFF) { - this.lastNeed = 2; - this.lastTotal = 4; - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - return r.slice(0, -1); - } - } - return r; - } - this.lastNeed = 1; - this.lastTotal = 2; - this.lastChar[0] = buf[buf.length - 1]; - return buf.toString('utf16le', i, buf.length - 1); -} - -// For UTF-16LE we do not explicitly append special replacement characters if we -// end on a partial character, we simply let v8 handle that. -function utf16End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) { - var end = this.lastTotal - this.lastNeed; - return r + this.lastChar.toString('utf16le', 0, end); - } - return r; -} - -function base64Text(buf, i) { - var n = (buf.length - i) % 3; - if (n === 0) return buf.toString('base64', i); - this.lastNeed = 3 - n; - this.lastTotal = 3; - if (n === 1) { - this.lastChar[0] = buf[buf.length - 1]; - } else { - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - } - return buf.toString('base64', i, buf.length - n); -} - -function base64End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); - return r; -} - -// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) -function simpleWrite(buf) { - return buf.toString(this.encoding); -} - -function simpleEnd(buf) { - return buf && buf.length ? this.write(buf) : ''; -} -},{"safe-buffer":83}],86:[function(require,module,exports){ -(function (setImmediate,clearImmediate){ -var nextTick = require('process/browser.js').nextTick; -var apply = Function.prototype.apply; -var slice = Array.prototype.slice; -var immediateIds = {}; -var nextImmediateId = 0; - -// DOM APIs, for completeness - -exports.setTimeout = function() { - return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); -}; -exports.setInterval = function() { - return new Timeout(apply.call(setInterval, window, arguments), clearInterval); -}; -exports.clearTimeout = -exports.clearInterval = function(timeout) { timeout.close(); }; - -function Timeout(id, clearFn) { - this._id = id; - this._clearFn = clearFn; -} -Timeout.prototype.unref = Timeout.prototype.ref = function() {}; -Timeout.prototype.close = function() { - this._clearFn.call(window, this._id); -}; - -// Does not start the time, just sets up the members needed. -exports.enroll = function(item, msecs) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = msecs; -}; - -exports.unenroll = function(item) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = -1; -}; - -exports._unrefActive = exports.active = function(item) { - clearTimeout(item._idleTimeoutId); - - var msecs = item._idleTimeout; - if (msecs >= 0) { - item._idleTimeoutId = setTimeout(function onTimeout() { - if (item._onTimeout) - item._onTimeout(); - }, msecs); - } -}; - -// That's not how node.js implements it but the exposed api is the same. -exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { - var id = nextImmediateId++; - var args = arguments.length < 2 ? false : slice.call(arguments, 1); - - immediateIds[id] = true; - - nextTick(function onNextTick() { - if (immediateIds[id]) { - // fn.call() is faster so we optimize for the common use-case - // @see http://jsperf.com/call-apply-segu - if (args) { - fn.apply(null, args); - } else { - fn.call(null); - } - // Prevent ids from leaking - exports.clearImmediate(id); - } - }); - - return id; -}; - -exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { - delete immediateIds[id]; -}; -}).call(this,require("timers").setImmediate,require("timers").clearImmediate) -},{"process/browser.js":69,"timers":86}],87:[function(require,module,exports){ -(function (global){ - -/** - * Module exports. - */ - -module.exports = deprecate; - -/** - * Mark that a method should not be used. - * Returns a modified function which warns once by default. - * - * If `localStorage.noDeprecation = true` is set, then it is a no-op. - * - * If `localStorage.throwDeprecation = true` is set, then deprecated functions - * will throw an Error when invoked. - * - * If `localStorage.traceDeprecation = true` is set, then deprecated functions - * will invoke `console.trace()` instead of `console.error()`. - * - * @param {Function} fn - the function to deprecate - * @param {String} msg - the string to print to the console when `fn` is invoked - * @returns {Function} a new "deprecated" version of `fn` - * @api public - */ - -function deprecate (fn, msg) { - if (config('noDeprecation')) { - return fn; - } - - var warned = false; - function deprecated() { - if (!warned) { - if (config('throwDeprecation')) { - throw new Error(msg); - } else if (config('traceDeprecation')) { - console.trace(msg); - } else { - console.warn(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } - - return deprecated; -} - -/** - * Checks `localStorage` for boolean values for the given `name`. - * - * @param {String} name - * @returns {Boolean} - * @api private - */ - -function config (name) { - // accessing global.localStorage can trigger a DOMException in sandboxed iframes - try { - if (!global.localStorage) return false; - } catch (_) { - return false; - } - var val = global.localStorage[name]; - if (null == val) return false; - return String(val).toLowerCase() === 'true'; -} - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],88:[function(require,module,exports){ -module.exports = function isBuffer(arg) { - return arg && typeof arg === 'object' - && typeof arg.copy === 'function' - && typeof arg.fill === 'function' - && typeof arg.readUInt8 === 'function'; -} -},{}],89:[function(require,module,exports){ -(function (process,global){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var formatRegExp = /%[sdj%]/g; -exports.format = function(f) { - if (!isString(f)) { - var objects = []; - for (var i = 0; i < arguments.length; i++) { - objects.push(inspect(arguments[i])); - } - return objects.join(' '); - } - - var i = 1; - var args = arguments; - var len = args.length; - var str = String(f).replace(formatRegExp, function(x) { - if (x === '%%') return '%'; - if (i >= len) return x; - switch (x) { - case '%s': return String(args[i++]); - case '%d': return Number(args[i++]); - case '%j': - try { - return JSON.stringify(args[i++]); - } catch (_) { - return '[Circular]'; - } - default: - return x; - } - }); - for (var x = args[i]; i < len; x = args[++i]) { - if (isNull(x) || !isObject(x)) { - str += ' ' + x; - } else { - str += ' ' + inspect(x); - } - } - return str; -}; - - -// Mark that a method should not be used. -// Returns a modified function which warns once by default. -// If --no-deprecation is set, then it is a no-op. -exports.deprecate = function(fn, msg) { - // Allow for deprecating things in the process of starting up. - if (isUndefined(global.process)) { - return function() { - return exports.deprecate(fn, msg).apply(this, arguments); - }; - } - - if (process.noDeprecation === true) { - return fn; - } - - var warned = false; - function deprecated() { - if (!warned) { - if (process.throwDeprecation) { - throw new Error(msg); - } else if (process.traceDeprecation) { - console.trace(msg); - } else { - console.error(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } - - return deprecated; -}; - - -var debugs = {}; -var debugEnviron; -exports.debuglog = function(set) { - if (isUndefined(debugEnviron)) - debugEnviron = process.env.NODE_DEBUG || ''; - set = set.toUpperCase(); - if (!debugs[set]) { - if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { - var pid = process.pid; - debugs[set] = function() { - var msg = exports.format.apply(exports, arguments); - console.error('%s %d: %s', set, pid, msg); - }; - } else { - debugs[set] = function() {}; - } - } - return debugs[set]; -}; - - -/** - * Echos the value of a value. Trys to print the value out - * in the best way possible given the different types. - * - * @param {Object} obj The object to print out. - * @param {Object} opts Optional options object that alters the output. - */ -/* legacy: obj, showHidden, depth, colors*/ -function inspect(obj, opts) { - // default options - var ctx = { - seen: [], - stylize: stylizeNoColor - }; - // legacy... - if (arguments.length >= 3) ctx.depth = arguments[2]; - if (arguments.length >= 4) ctx.colors = arguments[3]; - if (isBoolean(opts)) { - // legacy... - ctx.showHidden = opts; - } else if (opts) { - // got an "options" object - exports._extend(ctx, opts); - } - // set default options - if (isUndefined(ctx.showHidden)) ctx.showHidden = false; - if (isUndefined(ctx.depth)) ctx.depth = 2; - if (isUndefined(ctx.colors)) ctx.colors = false; - if (isUndefined(ctx.customInspect)) ctx.customInspect = true; - if (ctx.colors) ctx.stylize = stylizeWithColor; - return formatValue(ctx, obj, ctx.depth); -} -exports.inspect = inspect; - - -// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics -inspect.colors = { - 'bold' : [1, 22], - 'italic' : [3, 23], - 'underline' : [4, 24], - 'inverse' : [7, 27], - 'white' : [37, 39], - 'grey' : [90, 39], - 'black' : [30, 39], - 'blue' : [34, 39], - 'cyan' : [36, 39], - 'green' : [32, 39], - 'magenta' : [35, 39], - 'red' : [31, 39], - 'yellow' : [33, 39] -}; - -// Don't use 'blue' not visible on cmd.exe -inspect.styles = { - 'special': 'cyan', - 'number': 'yellow', - 'boolean': 'yellow', - 'undefined': 'grey', - 'null': 'bold', - 'string': 'green', - 'date': 'magenta', - // "name": intentionally not styling - 'regexp': 'red' -}; - - -function stylizeWithColor(str, styleType) { - var style = inspect.styles[styleType]; - - if (style) { - return '\u001b[' + inspect.colors[style][0] + 'm' + str + - '\u001b[' + inspect.colors[style][1] + 'm'; - } else { - return str; - } -} - - -function stylizeNoColor(str, styleType) { - return str; -} - - -function arrayToHash(array) { - var hash = {}; - - array.forEach(function(val, idx) { - hash[val] = true; - }); - - return hash; -} - - -function formatValue(ctx, value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (ctx.customInspect && - value && - isFunction(value.inspect) && - // Filter out the util module, it's inspect function is special - value.inspect !== exports.inspect && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes, ctx); - if (!isString(ret)) { - ret = formatValue(ctx, ret, recurseTimes); - } - return ret; - } - - // Primitive types cannot have properties - var primitive = formatPrimitive(ctx, value); - if (primitive) { - return primitive; - } - - // Look up the keys of the object. - var keys = Object.keys(value); - var visibleKeys = arrayToHash(keys); - - if (ctx.showHidden) { - keys = Object.getOwnPropertyNames(value); - } - - // IE doesn't make error fields non-enumerable - // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx - if (isError(value) - && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { - return formatError(value); - } - - // Some type of object without properties can be shortcutted. - if (keys.length === 0) { - if (isFunction(value)) { - var name = value.name ? ': ' + value.name : ''; - return ctx.stylize('[Function' + name + ']', 'special'); - } - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } - if (isDate(value)) { - return ctx.stylize(Date.prototype.toString.call(value), 'date'); - } - if (isError(value)) { - return formatError(value); - } - } - - var base = '', array = false, braces = ['{', '}']; - - // Make Array say that they are Array - if (isArray(value)) { - array = true; - braces = ['[', ']']; - } - - // Make functions say that they are functions - if (isFunction(value)) { - var n = value.name ? ': ' + value.name : ''; - base = ' [Function' + n + ']'; - } - - // Make RegExps say that they are RegExps - if (isRegExp(value)) { - base = ' ' + RegExp.prototype.toString.call(value); - } - - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + Date.prototype.toUTCString.call(value); - } - - // Make error with message first say the error - if (isError(value)) { - base = ' ' + formatError(value); - } - - if (keys.length === 0 && (!array || value.length == 0)) { - return braces[0] + base + braces[1]; - } - - if (recurseTimes < 0) { - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } else { - return ctx.stylize('[Object]', 'special'); - } - } - - ctx.seen.push(value); - - var output; - if (array) { - output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); - } else { - output = keys.map(function(key) { - return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); - }); - } - - ctx.seen.pop(); - - return reduceToSingleString(output, base, braces); -} - - -function formatPrimitive(ctx, value) { - if (isUndefined(value)) - return ctx.stylize('undefined', 'undefined'); - if (isString(value)) { - var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return ctx.stylize(simple, 'string'); - } - if (isNumber(value)) - return ctx.stylize('' + value, 'number'); - if (isBoolean(value)) - return ctx.stylize('' + value, 'boolean'); - // For some reason typeof null is "object", so special case here. - if (isNull(value)) - return ctx.stylize('null', 'null'); -} - - -function formatError(value) { - return '[' + Error.prototype.toString.call(value) + ']'; -} - - -function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - for (var i = 0, l = value.length; i < l; ++i) { - if (hasOwnProperty(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); - } - } - keys.forEach(function(key) { - if (!key.match(/^\d+$/)) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, true)); - } - }); - return output; -} - - -function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str, desc; - desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; - if (desc.get) { - if (desc.set) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } - } else { - if (desc.set) { - str = ctx.stylize('[Setter]', 'special'); - } - } - if (!hasOwnProperty(visibleKeys, key)) { - name = '[' + key + ']'; - } - if (!str) { - if (ctx.seen.indexOf(desc.value) < 0) { - if (isNull(recurseTimes)) { - str = formatValue(ctx, desc.value, null); - } else { - str = formatValue(ctx, desc.value, recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (array) { - str = str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n').substr(2); - } else { - str = '\n' + str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n'); - } - } - } else { - str = ctx.stylize('[Circular]', 'special'); - } - } - if (isUndefined(name)) { - if (array && key.match(/^\d+$/)) { - return str; - } - name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.substr(1, name.length - 2); - name = ctx.stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = ctx.stylize(name, 'string'); - } - } - - return name + ': ' + str; -} - - -function reduceToSingleString(output, base, braces) { - var numLinesEst = 0; - var length = output.reduce(function(prev, cur) { - numLinesEst++; - if (cur.indexOf('\n') >= 0) numLinesEst++; - return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; - }, 0); - - if (length > 60) { - return braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; - } - - return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; -} - - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. -function isArray(ar) { - return Array.isArray(ar); -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return isObject(re) && objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return isObject(d) && objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; - -function isError(e) { - return isObject(e) && - (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = require('./support/isBuffer'); - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - - -function pad(n) { - return n < 10 ? '0' + n.toString(10) : n.toString(10); -} - - -var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; - -// 26 Feb 16:19:34 -function timestamp() { - var d = new Date(); - var time = [pad(d.getHours()), - pad(d.getMinutes()), - pad(d.getSeconds())].join(':'); - return [d.getDate(), months[d.getMonth()], time].join(' '); -} - - -// log is just a thin wrapper to console.log that prepends a timestamp -exports.log = function() { - console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); -}; - - -/** - * Inherit the prototype methods from one constructor into another. - * - * The Function.prototype.inherits from lang.js rewritten as a standalone - * function (not on Function.prototype). NOTE: If this file is to be loaded - * during bootstrapping this function needs to be rewritten using some native - * functions as prototype setup using normal JavaScript does not work as - * expected during bootstrapping (see mirror.js in r114903). - * - * @param {function} ctor Constructor function which needs to inherit the - * prototype. - * @param {function} superCtor Constructor function to inherit prototype from. - */ -exports.inherits = require('inherits'); - -exports._extend = function(origin, add) { - // Don't do anything if add isn't an object - if (!add || !isObject(add)) return origin; - - var keys = Object.keys(add); - var i = keys.length; - while (i--) { - origin[keys[i]] = add[keys[i]]; - } - return origin; -}; - -function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); -} - -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./support/isBuffer":88,"_process":69,"inherits":56}],90:[function(require,module,exports){ -module.exports={ - "name": "mocha", - "version": "7.1.2", - "homepage": "https://mochajs.org/", - "notifyLogo": "https://ibin.co/4QuRuGjXvl36.png" -} -},{}]},{},[1]); diff --git a/src/tests/frontend/lib/sendkeys.js b/src/tests/frontend/lib/sendkeys.js deleted file mode 100644 index 91433f96482..00000000000 --- a/src/tests/frontend/lib/sendkeys.js +++ /dev/null @@ -1,467 +0,0 @@ -// Cross-broswer implementation of text ranges and selections -// documentation: http://bililite.com/blog/2011/01/11/cross-browser-.and-selections/ -// Version: 1.1 -// Copyright (c) 2010 Daniel Wachsstock -// MIT license: -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: - -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. - -(function($){ - -bililiteRange = function(el, debug){ - var ret; - if (debug){ - ret = new NothingRange(); // Easier to force it to use the no-selection type than to try to find an old browser - }else if (document.selection && !document.addEventListener){ - // Internet Explorer 8 and lower - ret = new IERange(); - }else if (window.getSelection && el.setSelectionRange){ - // Standards. Element is an input or textarea - ret = new InputRange(); - }else if (window.getSelection){ - // Standards, with any other kind of element - ret = new W3CRange() - }else{ - // doesn't support selection - ret = new NothingRange(); - } - ret._el = el; - ret._doc = el.ownerDocument; - ret._win = 'defaultView' in ret._doc ? ret._doc.defaultView : ret._doc.parentWindow; - ret._textProp = textProp(el); - ret._bounds = [0, ret.length()]; - return ret; -} - -function textProp(el){ - // returns the property that contains the text of the element - if (typeof el.value != 'undefined') return 'value'; - if (typeof el.text != 'undefined') return 'text'; - if (typeof el.textContent != 'undefined') return 'textContent'; - return 'innerText'; -} - -// base class -function Range(){} -Range.prototype = { - length: function() { - return this._el[this._textProp].replace(/\r/g, '').length; // need to correct for IE's CrLf weirdness - }, - bounds: function(s){ - if (s === 'all'){ - this._bounds = [0, this.length()]; - }else if (s === 'start'){ - this._bounds = [0, 0]; - }else if (s === 'end'){ - this._bounds = [this.length(), this.length()]; - }else if (s === 'selection'){ - this.bounds ('all'); // first select the whole thing for constraining - this._bounds = this._nativeSelection(); - }else if (s){ - this._bounds = s; // don't error check now; the element may change at any moment, so constrain it when we need it. - }else{ - var b = [ - Math.max(0, Math.min (this.length(), this._bounds[0])), - Math.max(0, Math.min (this.length(), this._bounds[1])) - ]; - return b; // need to constrain it to fit - } - return this; // allow for chaining - }, - select: function(){ - this._nativeSelect(this._nativeRange(this.bounds())); - return this; // allow for chaining - }, - text: function(text, select){ - if (arguments.length){ - this._nativeSetText(text, this._nativeRange(this.bounds())); - if (select == 'start'){ - this.bounds ([this._bounds[0], this._bounds[0]]); - this.select(); - }else if (select == 'end'){ - this.bounds ([this._bounds[0]+text.length, this._bounds[0]+text.length]); - this.select(); - }else if (select == 'all'){ - this.bounds ([this._bounds[0], this._bounds[0]+text.length]); - this.select(); - } - return this; // allow for chaining - }else{ - return this._nativeGetText(this._nativeRange(this.bounds())); - } - }, - insertEOL: function (){ - this._nativeEOL(); - this._bounds = [this._bounds[0]+1, this._bounds[0]+1]; // move past the EOL marker - return this; - } -}; - - -function IERange(){} -IERange.prototype = new Range(); -IERange.prototype._nativeRange = function (bounds){ - var rng; - if (this._el.tagName == 'INPUT'){ - // IE 8 is very inconsistent; textareas have createTextRange but it doesn't work - rng = this._el.createTextRange(); - }else{ - rng = this._doc.body.createTextRange (); - rng.moveToElementText(this._el); - } - if (bounds){ - if (bounds[1] < 0) bounds[1] = 0; // IE tends to run elements out of bounds - if (bounds[0] > this.length()) bounds[0] = this.length(); - if (bounds[1] < rng.text.replace(/\r/g, '').length){ // correct for IE's CrLf wierdness - // block-display elements have an invisible, uncounted end of element marker, so we move an extra one and use the current length of the range - rng.moveEnd ('character', -1); - rng.moveEnd ('character', bounds[1]-rng.text.replace(/\r/g, '').length); - } - if (bounds[0] > 0) rng.moveStart('character', bounds[0]); - } - return rng; -}; -IERange.prototype._nativeSelect = function (rng){ - rng.select(); -}; -IERange.prototype._nativeSelection = function (){ - // returns [start, end] for the selection constrained to be in element - var rng = this._nativeRange(); // range of the element to constrain to - var len = this.length(); - if (this._doc.selection.type != 'Text') return [0,0]; // append to the end - var sel = this._doc.selection.createRange(); - try{ - return [ - iestart(sel, rng), - ieend (sel, rng) - ]; - }catch (e){ - // IE gets upset sometimes about comparing text to input elements, but the selections cannot overlap, so make a best guess - return (sel.parentElement().sourceIndex < this._el.sourceIndex) ? [0,0] : [len, len]; - } -}; -IERange.prototype._nativeGetText = function (rng){ - return rng.text.replace(/\r/g, ''); // correct for IE's CrLf weirdness -}; -IERange.prototype._nativeSetText = function (text, rng){ - rng.text = text; -}; -IERange.prototype._nativeEOL = function(){ - if (typeof this._el.value != 'undefined'){ - this.text('\n'); // for input and textarea, insert it straight - }else{ - this._nativeRange(this.bounds()).pasteHTML('<br/>'); - } -}; -// IE internals -function iestart(rng, constraint){ - // returns the position (in character) of the start of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after - var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness - if (rng.compareEndPoints ('StartToStart', constraint) <= 0) return 0; // at or before the beginning - if (rng.compareEndPoints ('StartToEnd', constraint) >= 0) return len; - for (var i = 0; rng.compareEndPoints ('StartToStart', constraint) > 0; ++i, rng.moveStart('character', -1)); - return i; -} -function ieend (rng, constraint){ - // returns the position (in character) of the end of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after - var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness - if (rng.compareEndPoints ('EndToEnd', constraint) >= 0) return len; // at or after the end - if (rng.compareEndPoints ('EndToStart', constraint) <= 0) return 0; - for (var i = 0; rng.compareEndPoints ('EndToStart', constraint) > 0; ++i, rng.moveEnd('character', -1)); - return i; -} - -// an input element in a standards document. "Native Range" is just the bounds array -function InputRange(){} -InputRange.prototype = new Range(); -InputRange.prototype._nativeRange = function(bounds) { - return bounds || [0, this.length()]; -}; -InputRange.prototype._nativeSelect = function (rng){ - this._el.setSelectionRange(rng[0], rng[1]); -}; -InputRange.prototype._nativeSelection = function(){ - return [this._el.selectionStart, this._el.selectionEnd]; -}; -InputRange.prototype._nativeGetText = function(rng){ - return this._el.value.substring(rng[0], rng[1]); -}; -InputRange.prototype._nativeSetText = function(text, rng){ - var val = this._el.value; - this._el.value = val.substring(0, rng[0]) + text + val.substring(rng[1]); -}; -InputRange.prototype._nativeEOL = function(){ - this.text('\n'); -}; - -function W3CRange(){} -W3CRange.prototype = new Range(); -W3CRange.prototype._nativeRange = function (bounds){ - var rng = this._doc.createRange(); - rng.selectNodeContents(this._el); - if (bounds){ - w3cmoveBoundary (rng, bounds[0], true, this._el); - rng.collapse (true); - w3cmoveBoundary (rng, bounds[1]-bounds[0], false, this._el); - } - return rng; -}; -W3CRange.prototype._nativeSelect = function (rng){ - this._win.getSelection().removeAllRanges(); - this._win.getSelection().addRange (rng); -}; -W3CRange.prototype._nativeSelection = function (){ - // returns [start, end] for the selection constrained to be in element - var rng = this._nativeRange(); // range of the element to constrain to - if (this._win.getSelection().rangeCount == 0) return [this.length(), this.length()]; // append to the end - var sel = this._win.getSelection().getRangeAt(0); - return [ - w3cstart(sel, rng), - w3cend (sel, rng) - ]; - } -W3CRange.prototype._nativeGetText = function (rng){ - return rng.toString(); -}; -W3CRange.prototype._nativeSetText = function (text, rng){ - rng.deleteContents(); - rng.insertNode (this._doc.createTextNode(text)); - this._el.normalize(); // merge the text with the surrounding text -}; -W3CRange.prototype._nativeEOL = function(){ - var rng = this._nativeRange(this.bounds()); - rng.deleteContents(); - var br = this._doc.createElement('br'); - br.setAttribute ('_moz_dirty', ''); // for Firefox - rng.insertNode (br); - rng.insertNode (this._doc.createTextNode('\n')); - rng.collapse (false); -}; -// W3C internals -function nextnode (node, root){ - // in-order traversal - // we've already visited node, so get kids then siblings - if (node.firstChild) return node.firstChild; - if (node.nextSibling) return node.nextSibling; - if (node===root) return null; - while (node.parentNode){ - // get uncles - node = node.parentNode; - if (node == root) return null; - if (node.nextSibling) return node.nextSibling; - } - return null; -} -function w3cmoveBoundary (rng, n, bStart, el){ - // move the boundary (bStart == true ? start : end) n characters forward, up to the end of element el. Forward only! - // if the start is moved after the end, then an exception is raised - if (n <= 0) return; - var node = rng[bStart ? 'startContainer' : 'endContainer']; - if (node.nodeType == 3){ - // we may be starting somewhere into the text - n += rng[bStart ? 'startOffset' : 'endOffset']; - } - while (node){ - if (node.nodeType == 3){ - if (n <= node.nodeValue.length){ - rng[bStart ? 'setStart' : 'setEnd'](node, n); - // special case: if we end next to a <br>, include that node. - if (n == node.nodeValue.length){ - // skip past zero-length text nodes - for (var next = nextnode (node, el); next && next.nodeType==3 && next.nodeValue.length == 0; next = nextnode(next, el)){ - rng[bStart ? 'setStartAfter' : 'setEndAfter'](next); - } - if (next && next.nodeType == 1 && next.nodeName == "BR") rng[bStart ? 'setStartAfter' : 'setEndAfter'](next); - } - return; - }else{ - rng[bStart ? 'setStartAfter' : 'setEndAfter'](node); // skip past this one - n -= node.nodeValue.length; // and eat these characters - } - } - node = nextnode (node, el); - } -} -var START_TO_START = 0; // from the w3c definitions -var START_TO_END = 1; -var END_TO_END = 2; -var END_TO_START = 3; -// from the Mozilla documentation, for range.compareBoundaryPoints(how, sourceRange) -// -1, 0, or 1, indicating whether the corresponding boundary-point of range is respectively before, equal to, or after the corresponding boundary-point of sourceRange. - // * Range.END_TO_END compares the end boundary-point of sourceRange to the end boundary-point of range. - // * Range.END_TO_START compares the end boundary-point of sourceRange to the start boundary-point of range. - // * Range.START_TO_END compares the start boundary-point of sourceRange to the end boundary-point of range. - // * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range. -function w3cstart(rng, constraint){ - if (rng.compareBoundaryPoints (START_TO_START, constraint) <= 0) return 0; // at or before the beginning - if (rng.compareBoundaryPoints (END_TO_START, constraint) >= 0) return constraint.toString().length; - rng = rng.cloneRange(); // don't change the original - rng.setEnd (constraint.endContainer, constraint.endOffset); // they now end at the same place - return constraint.toString().length - rng.toString().length; -} -function w3cend (rng, constraint){ - if (rng.compareBoundaryPoints (END_TO_END, constraint) >= 0) return constraint.toString().length; // at or after the end - if (rng.compareBoundaryPoints (START_TO_END, constraint) <= 0) return 0; - rng = rng.cloneRange(); // don't change the original - rng.setStart (constraint.startContainer, constraint.startOffset); // they now start at the same place - return rng.toString().length; -} - -function NothingRange(){} -NothingRange.prototype = new Range(); -NothingRange.prototype._nativeRange = function(bounds) { - return bounds || [0,this.length()]; -}; -NothingRange.prototype._nativeSelect = function (rng){ // do nothing -}; -NothingRange.prototype._nativeSelection = function(){ - return [0,0]; -}; -NothingRange.prototype._nativeGetText = function (rng){ - return this._el[this._textProp].substring(rng[0], rng[1]); -}; -NothingRange.prototype._nativeSetText = function (text, rng){ - var val = this._el[this._textProp]; - this._el[this._textProp] = val.substring(0, rng[0]) + text + val.substring(rng[1]); -}; -NothingRange.prototype._nativeEOL = function(){ - this.text('\n'); -}; - -})(jQuery); - -// insert characters in a textarea or text input field -// special characters are enclosed in {}; use {{} for the { character itself -// documentation: http://bililite.com/blog/2008/08/20/the-fnsendkeys-plugin/ -// Version: 2.0 -// Copyright (c) 2010 Daniel Wachsstock -// MIT license: -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: - -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. - -(function($){ - -$.fn.sendkeys = function (x, opts){ - return this.each( function(){ - var localkeys = $.extend({}, opts, $(this).data('sendkeys')); // allow for element-specific key functions - // most elements to not keep track of their selection when they lose focus, so we have to do it for them - var rng = $.data (this, 'sendkeys.selection'); - if (!rng){ - rng = bililiteRange(this).bounds('selection'); - $.data(this, 'sendkeys.selection', rng); - $(this).bind('mouseup.sendkeys', function(){ - // we have to update the saved range. The routines here update the bounds with each press, but actual keypresses and mouseclicks do not - $.data(this, 'sendkeys.selection').bounds('selection'); - }).bind('keyup.sendkeys', function(evt){ - // restore the selection if we got here with a tab (a click should select what was clicked on) - if (evt.which == 9){ - // there's a flash of selection when we restore the focus, but I don't know how to avoid that - $.data(this, 'sendkeys.selection').select(); - }else{ - $.data(this, 'sendkeys.selection').bounds('selection'); - } - }); - } - this.focus(); - if (typeof x === 'undefined') return; // no string, so we just set up the event handlers - $.data(this, 'sendkeys.originalText', rng.text()); - x.replace(/\n/g, '{enter}'). // turn line feeds into explicit break insertions - replace(/{[^}]*}|[^{]+/g, function(s){ - (localkeys[s] || $.fn.sendkeys.defaults[s] || $.fn.sendkeys.defaults.simplechar)(rng, s); - }); - $(this).trigger({type: 'sendkeys', which: x}); - }); -}; // sendkeys - - -// add the functions publicly so they can be overridden -$.fn.sendkeys.defaults = { - simplechar: function (rng, s){ - rng.text(s, 'end'); - for (var i =0; i < s.length; ++i){ - var x = s.charCodeAt(i); - // a bit of cheating: rng._el is the element associated with rng. - $(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x}); - } - }, - '{{}': function (rng){ - $.fn.sendkeys.defaults.simplechar (rng, '{') - }, - '{enter}': function (rng){ - rng.insertEOL(); - rng.select(); - $(rng._el).trigger( - {type: 'keypress', keyCode: 13, which: 13, charCode: 13, code: 'Enter', key: 'Enter'}); - }, - '{backspace}': function (rng){ - var b = rng.bounds(); - if (b[0] == b[1]) rng.bounds([b[0]-1, b[0]]); // no characters selected; it's just an insertion point. Remove the previous character - rng.text('', 'end'); // delete the characters and update the selection - }, - '{del}': function (rng){ - var b = rng.bounds(); - if (b[0] == b[1]) rng.bounds([b[0], b[0]+1]); // no characters selected; it's just an insertion point. Remove the next character - rng.text('', 'end'); // delete the characters and update the selection - }, - '{rightarrow}': function (rng){ - var b = rng.bounds(); - if (b[0] == b[1]) ++b[1]; // no characters selected; it's just an insertion point. Move to the right - rng.bounds([b[1], b[1]]).select(); - }, - '{leftarrow}': function (rng){ - var b = rng.bounds(); - if (b[0] == b[1]) --b[0]; // no characters selected; it's just an insertion point. Move to the left - rng.bounds([b[0], b[0]]).select(); - }, - '{selectall}' : function (rng){ - rng.bounds('all').select(); - }, - '{selection}': function (rng){ - $.fn.sendkeys.defaults.simplechar(rng, $.data(rng._el, 'sendkeys.originalText')); - }, - '{mark}' : function (rng){ - var bounds = rng.bounds(); - $(rng._el).one('sendkeys', function(){ - // set up the event listener to change the selection after the sendkeys is done - rng.bounds(bounds).select(); - }); - } -}; - -})(jQuery) diff --git a/src/tests/frontend/lib/underscore.js b/src/tests/frontend/lib/underscore.js deleted file mode 100644 index 1ebe2671b96..00000000000 --- a/src/tests/frontend/lib/underscore.js +++ /dev/null @@ -1,1200 +0,0 @@ -// Underscore.js 1.4.2 -// http://underscorejs.org -// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Underscore may be freely distributed under the MIT license. - -(function() { - - // Baseline setup - // -------------- - - // Establish the root object, `window` in the browser, or `global` on the server. - var root = this; - - // Save the previous value of the `_` variable. - var previousUnderscore = root._; - - // Establish the object that gets returned to break out of a loop iteration. - var breaker = {}; - - // Save bytes in the minified (but not gzipped) version: - var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; - - // Create quick reference variables for speed access to core prototypes. - var push = ArrayProto.push, - slice = ArrayProto.slice, - concat = ArrayProto.concat, - unshift = ArrayProto.unshift, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; - - // All **ECMAScript 5** native function implementations that we hope to use - // are declared here. - var - nativeForEach = ArrayProto.forEach, - nativeMap = ArrayProto.map, - nativeReduce = ArrayProto.reduce, - nativeReduceRight = ArrayProto.reduceRight, - nativeFilter = ArrayProto.filter, - nativeEvery = ArrayProto.every, - nativeSome = ArrayProto.some, - nativeIndexOf = ArrayProto.indexOf, - nativeLastIndexOf = ArrayProto.lastIndexOf, - nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeBind = FuncProto.bind; - - // Create a safe reference to the Underscore object for use below. - var _ = function(obj) { - if (obj instanceof _) return obj; - if (!(this instanceof _)) return new _(obj); - this._wrapped = obj; - }; - - // Export the Underscore object for **Node.js**, with - // backwards-compatibility for the old `require()` API. If we're in - // the browser, add `_` as a global object via a string identifier, - // for Closure Compiler "advanced" mode. - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = _; - } - exports._ = _; - } else { - root['_'] = _; - } - - // Current version. - _.VERSION = '1.4.2'; - - // Collection Functions - // -------------------- - - // The cornerstone, an `each` implementation, aka `forEach`. - // Handles objects with the built-in `forEach`, arrays, and raw objects. - // Delegates to **ECMAScript 5**'s native `forEach` if available. - var each = _.each = _.forEach = function(obj, iterator, context) { - if (obj == null) return; - if (nativeForEach && obj.forEach === nativeForEach) { - obj.forEach(iterator, context); - } else if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { - if (iterator.call(context, obj[i], i, obj) === breaker) return; - } - } else { - for (var key in obj) { - if (_.has(obj, key)) { - if (iterator.call(context, obj[key], key, obj) === breaker) return; - } - } - } - }; - - // Return the results of applying the iterator to each element. - // Delegates to **ECMAScript 5**'s native `map` if available. - _.map = _.collect = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); - each(obj, function(value, index, list) { - results[results.length] = iterator.call(context, value, index, list); - }); - return results; - }; - - // **Reduce** builds up a single result from a list of values, aka `inject`, - // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. - _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduce && obj.reduce === nativeReduce) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); - } - each(obj, function(value, index, list) { - if (!initial) { - memo = value; - initial = true; - } else { - memo = iterator.call(context, memo, value, index, list); - } - }); - if (!initial) throw new TypeError('Reduce of empty array with no initial value'); - return memo; - }; - - // The right-associative version of reduce, also known as `foldr`. - // Delegates to **ECMAScript 5**'s native `reduceRight` if available. - _.reduceRight = _.foldr = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { - if (context) iterator = _.bind(iterator, context); - return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); - } - var length = obj.length; - if (length !== +length) { - var keys = _.keys(obj); - length = keys.length; - } - each(obj, function(value, index, list) { - index = keys ? keys[--length] : --length; - if (!initial) { - memo = obj[index]; - initial = true; - } else { - memo = iterator.call(context, memo, obj[index], index, list); - } - }); - if (!initial) throw new TypeError('Reduce of empty array with no initial value'); - return memo; - }; - - // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, iterator, context) { - var result; - any(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) { - result = value; - return true; - } - }); - return result; - }; - - // Return all the elements that pass a truth test. - // Delegates to **ECMAScript 5**'s native `filter` if available. - // Aliased as `select`. - _.filter = _.select = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); - each(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) results[results.length] = value; - }); - return results; - }; - - // Return all the elements for which a truth test fails. - _.reject = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - each(obj, function(value, index, list) { - if (!iterator.call(context, value, index, list)) results[results.length] = value; - }); - return results; - }; - - // Determine whether all of the elements match a truth test. - // Delegates to **ECMAScript 5**'s native `every` if available. - // Aliased as `all`. - _.every = _.all = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = true; - if (obj == null) return result; - if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); - each(obj, function(value, index, list) { - if (!(result = result && iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; - - // Determine if at least one element in the object matches a truth test. - // Delegates to **ECMAScript 5**'s native `some` if available. - // Aliased as `any`. - var any = _.some = _.any = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = false; - if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); - each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; - - // Determine if the array or object contains a given value (using `===`). - // Aliased as `include`. - _.contains = _.include = function(obj, target) { - var found = false; - if (obj == null) return found; - if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; - found = any(obj, function(value) { - return value === target; - }); - return found; - }; - - // Invoke a method (with arguments) on every item in a collection. - _.invoke = function(obj, method) { - var args = slice.call(arguments, 2); - return _.map(obj, function(value) { - return (_.isFunction(method) ? method : value[method]).apply(value, args); - }); - }; - - // Convenience version of a common use case of `map`: fetching a property. - _.pluck = function(obj, key) { - return _.map(obj, function(value){ return value[key]; }); - }; - - // Convenience version of a common use case of `filter`: selecting only objects - // with specific `key:value` pairs. - _.where = function(obj, attrs) { - if (_.isEmpty(attrs)) return []; - return _.filter(obj, function(value) { - for (var key in attrs) { - if (attrs[key] !== value[key]) return false; - } - return true; - }); - }; - - // Return the maximum element or (element-based computation). - // Can't optimize arrays of integers longer than 65,535 elements. - // See: https://bugs.webkit.org/show_bug.cgi?id=80797 - _.max = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { - return Math.max.apply(Math, obj); - } - if (!iterator && _.isEmpty(obj)) return -Infinity; - var result = {computed : -Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed >= result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; - - // Return the minimum element (or element-based computation). - _.min = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { - return Math.min.apply(Math, obj); - } - if (!iterator && _.isEmpty(obj)) return Infinity; - var result = {computed : Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed < result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; - - // Shuffle an array. - _.shuffle = function(obj) { - var rand; - var index = 0; - var shuffled = []; - each(obj, function(value) { - rand = _.random(index++); - shuffled[index - 1] = shuffled[rand]; - shuffled[rand] = value; - }); - return shuffled; - }; - - // An internal function to generate lookup iterators. - var lookupIterator = function(value) { - return _.isFunction(value) ? value : function(obj){ return obj[value]; }; - }; - - // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, value, context) { - var iterator = lookupIterator(value); - return _.pluck(_.map(obj, function(value, index, list) { - return { - value : value, - index : index, - criteria : iterator.call(context, value, index, list) - }; - }).sort(function(left, right) { - var a = left.criteria; - var b = right.criteria; - if (a !== b) { - if (a > b || a === void 0) return 1; - if (a < b || b === void 0) return -1; - } - return left.index < right.index ? -1 : 1; - }), 'value'); - }; - - // An internal function used for aggregate "group by" operations. - var group = function(obj, value, context, behavior) { - var result = {}; - var iterator = lookupIterator(value); - each(obj, function(value, index) { - var key = iterator.call(context, value, index, obj); - behavior(result, key, value); - }); - return result; - }; - - // Groups the object's values by a criterion. Pass either a string attribute - // to group by, or a function that returns the criterion. - _.groupBy = function(obj, value, context) { - return group(obj, value, context, function(result, key, value) { - (_.has(result, key) ? result[key] : (result[key] = [])).push(value); - }); - }; - - // Counts instances of an object that group by a certain criterion. Pass - // either a string attribute to count by, or a function that returns the - // criterion. - _.countBy = function(obj, value, context) { - return group(obj, value, context, function(result, key, value) { - if (!_.has(result, key)) result[key] = 0; - result[key]++; - }); - }; - - // Use a comparator function to figure out the smallest index at which - // an object should be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iterator, context) { - iterator = iterator == null ? _.identity : lookupIterator(iterator); - var value = iterator.call(context, obj); - var low = 0, high = array.length; - while (low < high) { - var mid = (low + high) >>> 1; - iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; - } - return low; - }; - - // Safely convert anything iterable into a real, live array. - _.toArray = function(obj) { - if (!obj) return []; - if (obj.length === +obj.length) return slice.call(obj); - return _.values(obj); - }; - - // Return the number of elements in an object. - _.size = function(obj) { - return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; - }; - - // Array Functions - // --------------- - - // Get the first element of an array. Passing **n** will return the first N - // values in the array. Aliased as `head` and `take`. The **guard** check - // allows it to work with `_.map`. - _.first = _.head = _.take = function(array, n, guard) { - return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; - }; - - // Returns everything but the last entry of the array. Especially useful on - // the arguments object. Passing **n** will return all the values in - // the array, excluding the last N. The **guard** check allows it to work with - // `_.map`. - _.initial = function(array, n, guard) { - return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); - }; - - // Get the last element of an array. Passing **n** will return the last N - // values in the array. The **guard** check allows it to work with `_.map`. - _.last = function(array, n, guard) { - if ((n != null) && !guard) { - return slice.call(array, Math.max(array.length - n, 0)); - } else { - return array[array.length - 1]; - } - }; - - // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. - // Especially useful on the arguments object. Passing an **n** will return - // the rest N values in the array. The **guard** - // check allows it to work with `_.map`. - _.rest = _.tail = _.drop = function(array, n, guard) { - return slice.call(array, (n == null) || guard ? 1 : n); - }; - - // Trim out all falsy values from an array. - _.compact = function(array) { - return _.filter(array, function(value){ return !!value; }); - }; - - // Internal implementation of a recursive `flatten` function. - var flatten = function(input, shallow, output) { - each(input, function(value) { - if (_.isArray(value)) { - shallow ? push.apply(output, value) : flatten(value, shallow, output); - } else { - output.push(value); - } - }); - return output; - }; - - // Return a completely flattened version of an array. - _.flatten = function(array, shallow) { - return flatten(array, shallow, []); - }; - - // Return a version of the array that does not contain the specified value(s). - _.without = function(array) { - return _.difference(array, slice.call(arguments, 1)); - }; - - // Produce a duplicate-free version of the array. If the array has already - // been sorted, you have the option of using a faster algorithm. - // Aliased as `unique`. - _.uniq = _.unique = function(array, isSorted, iterator, context) { - var initial = iterator ? _.map(array, iterator, context) : array; - var results = []; - var seen = []; - each(initial, function(value, index) { - if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { - seen.push(value); - results.push(array[index]); - } - }); - return results; - }; - - // Produce an array that contains the union: each distinct element from all of - // the passed-in arrays. - _.union = function() { - return _.uniq(concat.apply(ArrayProto, arguments)); - }; - - // Produce an array that contains every item shared between all the - // passed-in arrays. - _.intersection = function(array) { - var rest = slice.call(arguments, 1); - return _.filter(_.uniq(array), function(item) { - return _.every(rest, function(other) { - return _.indexOf(other, item) >= 0; - }); - }); - }; - - // Take the difference between one array and a number of other arrays. - // Only the elements present in just the first array will remain. - _.difference = function(array) { - var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); - return _.filter(array, function(value){ return !_.contains(rest, value); }); - }; - - // Zip together multiple lists into a single array -- elements that share - // an index go together. - _.zip = function() { - var args = slice.call(arguments); - var length = _.max(_.pluck(args, 'length')); - var results = new Array(length); - for (var i = 0; i < length; i++) { - results[i] = _.pluck(args, "" + i); - } - return results; - }; - - // Converts lists into objects. Pass either a single array of `[key, value]` - // pairs, or two parallel arrays of the same length -- one of keys, and one of - // the corresponding values. - _.object = function(list, values) { - var result = {}; - for (var i = 0, l = list.length; i < l; i++) { - if (values) { - result[list[i]] = values[i]; - } else { - result[list[i][0]] = list[i][1]; - } - } - return result; - }; - - // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), - // we need this function. Return the position of the first occurrence of an - // item in an array, or -1 if the item is not included in the array. - // Delegates to **ECMAScript 5**'s native `indexOf` if available. - // If the array is large and already in sort order, pass `true` - // for **isSorted** to use binary search. - _.indexOf = function(array, item, isSorted) { - if (array == null) return -1; - var i = 0, l = array.length; - if (isSorted) { - if (typeof isSorted == 'number') { - i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); - } else { - i = _.sortedIndex(array, item); - return array[i] === item ? i : -1; - } - } - if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); - for (; i < l; i++) if (array[i] === item) return i; - return -1; - }; - - // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. - _.lastIndexOf = function(array, item, from) { - if (array == null) return -1; - var hasIndex = from != null; - if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { - return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); - } - var i = (hasIndex ? from : array.length); - while (i--) if (array[i] === item) return i; - return -1; - }; - - // Generate an integer Array containing an arithmetic progression. A port of - // the native Python `range()` function. See - // [the Python documentation](http://docs.python.org/library/functions.html#range). - _.range = function(start, stop, step) { - if (arguments.length <= 1) { - stop = start || 0; - start = 0; - } - step = arguments[2] || 1; - - var len = Math.max(Math.ceil((stop - start) / step), 0); - var idx = 0; - var range = new Array(len); - - while(idx < len) { - range[idx++] = start; - start += step; - } - - return range; - }; - - // Function (ahem) Functions - // ------------------ - - // Reusable constructor function for prototype setting. - var ctor = function(){}; - - // Create a function bound to a given object (assigning `this`, and arguments, - // optionally). Binding with arguments is also known as `curry`. - // Delegates to **ECMAScript 5**'s native `Function.bind` if available. - // We check for `func.bind` first, to fail fast when `func` is undefined. - _.bind = function bind(func, context) { - var bound, args; - if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - if (!_.isFunction(func)) throw new TypeError; - args = slice.call(arguments, 2); - return bound = function() { - if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); - ctor.prototype = func.prototype; - var self = new ctor; - var result = func.apply(self, args.concat(slice.call(arguments))); - if (Object(result) === result) return result; - return self; - }; - }; - - // Bind all of an object's methods to that object. Useful for ensuring that - // all callbacks defined on an object belong to it. - _.bindAll = function(obj) { - var funcs = slice.call(arguments, 1); - if (funcs.length == 0) funcs = _.functions(obj); - each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); - return obj; - }; - - // Memoize an expensive function by storing its results. - _.memoize = function(func, hasher) { - var memo = {}; - hasher || (hasher = _.identity); - return function() { - var key = hasher.apply(this, arguments); - return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); - }; - }; - - // Delays a function for the given number of milliseconds, and then calls - // it with the arguments supplied. - _.delay = function(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function(){ return func.apply(null, args); }, wait); - }; - - // Defers a function, scheduling it to run after the current call stack has - // cleared. - _.defer = function(func) { - return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); - }; - - // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. - _.throttle = function(func, wait) { - var context, args, timeout, throttling, more, result; - var whenDone = _.debounce(function(){ more = throttling = false; }, wait); - return function() { - context = this; args = arguments; - var later = function() { - timeout = null; - if (more) { - result = func.apply(context, args); - } - whenDone(); - }; - if (!timeout) timeout = setTimeout(later, wait); - if (throttling) { - more = true; - } else { - throttling = true; - result = func.apply(context, args); - } - whenDone(); - return result; - }; - }; - - // 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 - // N milliseconds. If `immediate` is passed, trigger the function on the - // leading edge, instead of the trailing. - _.debounce = function(func, wait, immediate) { - var timeout, result; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - if (!immediate) result = func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) result = func.apply(context, args); - return result; - }; - }; - - // Returns a function that will be executed at most one time, no matter how - // often you call it. Useful for lazy initialization. - _.once = function(func) { - var ran = false, memo; - return function() { - if (ran) return memo; - ran = true; - memo = func.apply(this, arguments); - func = null; - return memo; - }; - }; - - // Returns the first function passed as an argument to the second, - // allowing you to adjust arguments, run code before and after, and - // conditionally execute the original function. - _.wrap = function(func, wrapper) { - return function() { - var args = [func]; - push.apply(args, arguments); - return wrapper.apply(this, args); - }; - }; - - // Returns a function that is the composition of a list of functions, each - // consuming the return value of the function that follows. - _.compose = function() { - var funcs = arguments; - return function() { - var args = arguments; - for (var i = funcs.length - 1; i >= 0; i--) { - args = [funcs[i].apply(this, args)]; - } - return args[0]; - }; - }; - - // Returns a function that will only be executed after being called N times. - _.after = function(times, func) { - if (times <= 0) return func(); - return function() { - if (--times < 1) { - return func.apply(this, arguments); - } - }; - }; - - // Object Functions - // ---------------- - - // Retrieve the names of an object's properties. - // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); - var keys = []; - for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; - return keys; - }; - - // Retrieve the values of an object's properties. - _.values = function(obj) { - var values = []; - for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); - return values; - }; - - // Convert an object into a list of `[key, value]` pairs. - _.pairs = function(obj) { - var pairs = []; - for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); - return pairs; - }; - - // Invert the keys and values of an object. The values must be serializable. - _.invert = function(obj) { - var result = {}; - for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; - return result; - }; - - // Return a sorted list of the function names available on the object. - // Aliased as `methods` - _.functions = _.methods = function(obj) { - var names = []; - for (var key in obj) { - if (_.isFunction(obj[key])) names.push(key); - } - return names.sort(); - }; - - // Extend a given object with all the properties in passed-in object(s). - _.extend = function(obj) { - each(slice.call(arguments, 1), function(source) { - for (var prop in source) { - obj[prop] = source[prop]; - } - }); - return obj; - }; - - // Return a copy of the object only containing the whitelisted properties. - _.pick = function(obj) { - var copy = {}; - var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); - each(keys, function(key) { - if (key in obj) copy[key] = obj[key]; - }); - return copy; - }; - - // Return a copy of the object without the blacklisted properties. - _.omit = function(obj) { - var copy = {}; - var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); - for (var key in obj) { - if (!_.contains(keys, key)) copy[key] = obj[key]; - } - return copy; - }; - - // Fill in a given object with default properties. - _.defaults = function(obj) { - each(slice.call(arguments, 1), function(source) { - for (var prop in source) { - if (obj[prop] == null) obj[prop] = source[prop]; - } - }); - return obj; - }; - - // Create a (shallow-cloned) duplicate of an object. - _.clone = function(obj) { - if (!_.isObject(obj)) return obj; - return _.isArray(obj) ? obj.slice() : _.extend({}, obj); - }; - - // Invokes interceptor with the obj, and then returns obj. - // The primary purpose of this method is to "tap into" a method chain, in - // order to perform operations on intermediate results within the chain. - _.tap = function(obj, interceptor) { - interceptor(obj); - return obj; - }; - - // Internal recursive comparison function for `isEqual`. - var eq = function(a, b, aStack, bStack) { - // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. - if (a === b) return a !== 0 || 1 / a == 1 / b; - // A strict comparison is necessary because `null == undefined`. - if (a == null || b == null) return a === b; - // Unwrap any wrapped objects. - if (a instanceof _) a = a._wrapped; - if (b instanceof _) b = b._wrapped; - // Compare `[[Class]]` names. - var className = toString.call(a); - if (className != toString.call(b)) return false; - switch (className) { - // Strings, numbers, dates, and booleans are compared by value. - case '[object String]': - // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is - // equivalent to `new String("5")`. - return a == String(b); - case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for - // other numeric values. - return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); - case '[object Date]': - case '[object Boolean]': - // Coerce dates and booleans to numeric primitive values. Dates are compared by their - // millisecond representations. Note that invalid dates with millisecond representations - // of `NaN` are not equivalent. - return +a == +b; - // RegExps are compared by their source patterns and flags. - case '[object RegExp]': - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; - } - if (typeof a != 'object' || typeof b != 'object') return false; - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = aStack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (aStack[length] == a) return bStack[length] == b; - } - // Add the first object to the stack of traversed objects. - aStack.push(a); - bStack.push(b); - var size = 0, result = true; - // Recursively compare objects and arrays. - if (className == '[object Array]') { - // Compare array lengths to determine if a deep comparison is necessary. - size = a.length; - result = size == b.length; - if (result) { - // Deep compare the contents, ignoring non-numeric properties. - while (size--) { - if (!(result = eq(a[size], b[size], aStack, bStack))) break; - } - } - } else { - // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && - _.isFunction(bCtor) && (bCtor instanceof bCtor))) { - return false; - } - // Deep compare objects. - for (var key in a) { - if (_.has(a, key)) { - // Count the expected number of properties. - size++; - // Deep compare each member. - if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; - } - } - // Ensure that both objects contain the same number of properties. - if (result) { - for (key in b) { - if (_.has(b, key) && !(size--)) break; - } - result = !size; - } - } - // Remove the first object from the stack of traversed objects. - aStack.pop(); - bStack.pop(); - return result; - }; - - // Perform a deep comparison to check if two objects are equal. - _.isEqual = function(a, b) { - return eq(a, b, [], []); - }; - - // Is a given array, string, or object empty? - // An "empty" object has no enumerable own-properties. - _.isEmpty = function(obj) { - if (obj == null) return true; - if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; - for (var key in obj) if (_.has(obj, key)) return false; - return true; - }; - - // Is a given value a DOM element? - _.isElement = function(obj) { - return !!(obj && obj.nodeType === 1); - }; - - // Is a given value an array? - // Delegates to ECMA5's native Array.isArray - _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) == '[object Array]'; - }; - - // Is a given variable an object? - _.isObject = function(obj) { - return obj === Object(obj); - }; - - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. - each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { - _['is' + name] = function(obj) { - return toString.call(obj) == '[object ' + name + ']'; - }; - }); - - // Define a fallback version of the method in browsers (ahem, IE), where - // there isn't any inspectable "Arguments" type. - if (!_.isArguments(arguments)) { - _.isArguments = function(obj) { - return !!(obj && _.has(obj, 'callee')); - }; - } - - // Optimize `isFunction` if appropriate. - if (typeof (/./) !== 'function') { - _.isFunction = function(obj) { - return typeof obj === 'function'; - }; - } - - // Is a given object a finite number? - _.isFinite = function(obj) { - return _.isNumber(obj) && isFinite(obj); - }; - - // Is the given value `NaN`? (NaN is the only number which does not equal itself). - _.isNaN = function(obj) { - return _.isNumber(obj) && obj != +obj; - }; - - // Is a given value a boolean? - _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; - }; - - // Is a given value equal to null? - _.isNull = function(obj) { - return obj === null; - }; - - // Is a given variable undefined? - _.isUndefined = function(obj) { - return obj === void 0; - }; - - // Shortcut function for checking if an object has a given property directly - // on itself (in other words, not on a prototype). - _.has = function(obj, key) { - return hasOwnProperty.call(obj, key); - }; - - // Utility Functions - // ----------------- - - // Run Underscore.js in *noConflict* mode, returning the `_` variable to its - // previous owner. Returns a reference to the Underscore object. - _.noConflict = function() { - root._ = previousUnderscore; - return this; - }; - - // Keep the identity function around for default iterators. - _.identity = function(value) { - return value; - }; - - // Run a function **n** times. - _.times = function(n, iterator, context) { - for (var i = 0; i < n; i++) iterator.call(context, i); - }; - - // Return a random integer between min and max (inclusive). - _.random = function(min, max) { - if (max == null) { - max = min; - min = 0; - } - return min + (0 | Math.random() * (max - min + 1)); - }; - - // List of HTML entities for escaping. - var entityMap = { - escape: { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '/': '/' - } - }; - entityMap.unescape = _.invert(entityMap.escape); - - // Regexes containing the keys and values listed immediately above. - var entityRegexes = { - escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), - unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') - }; - - // Functions for escaping and unescaping strings to/from HTML interpolation. - _.each(['escape', 'unescape'], function(method) { - _[method] = function(string) { - if (string == null) return ''; - return ('' + string).replace(entityRegexes[method], function(match) { - return entityMap[method][match]; - }); - }; - }); - - // If the value of the named property is a function then invoke it; - // otherwise, return it. - _.result = function(object, property) { - if (object == null) return null; - var value = object[property]; - return _.isFunction(value) ? value.call(object) : value; - }; - - // Add your own custom functions to the Underscore object. - _.mixin = function(obj) { - each(_.functions(obj), function(name){ - var func = _[name] = obj[name]; - _.prototype[name] = function() { - var args = [this._wrapped]; - push.apply(args, arguments); - return result.call(this, func.apply(_, args)); - }; - }); - }; - - // Generate a unique integer id (unique within the entire client session). - // Useful for temporary DOM ids. - var idCounter = 0; - _.uniqueId = function(prefix) { - var id = idCounter++; - return prefix ? prefix + id : id; - }; - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - source += - escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" : - interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" : - evaluate ? "';\n" + evaluate + "\n__p+='" : ''; - index = offset + match.length; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - var render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - // Add a "chain" function, which will delegate to the wrapper. - _.chain = function(obj) { - return _(obj).chain(); - }; - - // OOP - // --------------- - // If Underscore is called as a function, it returns a wrapped object that - // can be used OO-style. This wrapper holds altered versions of all the - // underscore functions. Wrapped objects may be chained. - - // Helper function to continue chaining intermediate results. - var result = function(obj) { - return this._chain ? _(obj).chain() : obj; - }; - - // Add all of the Underscore functions to the wrapper object. - _.mixin(_); - - // Add all mutator Array functions to the wrapper. - each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - var obj = this._wrapped; - method.apply(obj, arguments); - if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; - return result.call(this, obj); - }; - }); - - // Add all accessor Array functions to the wrapper. - each(['concat', 'join', 'slice'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - return result.call(this, method.apply(this._wrapped, arguments)); - }; - }); - - _.extend(_.prototype, { - - // Start chaining a wrapped Underscore object. - chain: function() { - this._chain = true; - return this; - }, - - // Extracts the result from a wrapped and chained object. - value: function() { - return this._wrapped; - } - - }); - -}).call(this); diff --git a/src/tests/frontend/runner.js b/src/tests/frontend/runner.js index 3e512d4d19b..b2c76d00acd 100644 --- a/src/tests/frontend/runner.js +++ b/src/tests/frontend/runner.js @@ -187,7 +187,6 @@ $(() => (async () => { // mutates the module definition function to temporarily replace Mocha's functions with // placeholders. The placeholders make it possible to defer the actual Mocha function calls until // after the modules are all loaded in parallel. require.setGlobalKeyPath() is used to coax - // require-kernel into using the wrapper define() method instead of require.define(). // Per-module log of attempted Mocha function calls. Key is module path, value is an array of // [functionName, argsArray] arrays. diff --git a/src/tests/frontend/specs/easysync-compose.js b/src/tests/frontend/specs/easysync-compose.js deleted file mode 100644 index 69757763c6c..00000000000 --- a/src/tests/frontend/specs/easysync-compose.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -const Changeset = require('../../../static/js/Changeset'); -const AttributePool = require('../../../static/js/AttributePool'); -const {randomMultiline, randomTestChangeset} = require('../easysync-helper.js'); - -describe('easysync-compose', function () { - describe('compose', function () { - const testCompose = (randomSeed) => { - it(`testCompose#${randomSeed}`, async function () { - const p = new AttributePool(); - - const startText = `${randomMultiline(10, 20)}\n`; - - const x1 = randomTestChangeset(startText); - const change1 = x1[0]; - const text1 = x1[1]; - - const x2 = randomTestChangeset(text1); - const change2 = x2[0]; - const text2 = x2[1]; - - const x3 = randomTestChangeset(text2); - const change3 = x3[0]; - const text3 = x3[1]; - - const change12 = Changeset.checkRep(Changeset.compose(change1, change2, p)); - const change23 = Changeset.checkRep(Changeset.compose(change2, change3, p)); - const change123 = Changeset.checkRep(Changeset.compose(change12, change3, p)); - const change123a = Changeset.checkRep(Changeset.compose(change1, change23, p)); - expect(change123a).to.equal(change123); - - expect(Changeset.applyToText(change12, startText)).to.equal(text2); - expect(Changeset.applyToText(change23, text1)).to.equal(text3); - expect(Changeset.applyToText(change123, startText)).to.equal(text3); - }); - }; - - for (let i = 0; i < 30; i++) testCompose(i); - }); - - describe('compose attributes', function () { - it('simpleComposeAttributesTest', async function () { - const p = new AttributePool(); - p.putAttrib(['bold', '']); - p.putAttrib(['bold', 'true']); - const cs1 = Changeset.checkRep('Z:2>1*1+1*1=1$x'); - const cs2 = Changeset.checkRep('Z:3>0*0|1=3$'); - const cs12 = Changeset.checkRep(Changeset.compose(cs1, cs2, p)); - expect(cs12).to.equal('Z:2>1+1*0|1=2$x'); - }); - }); -}); diff --git a/src/tests/frontend/specs/easysync-other.js b/src/tests/frontend/specs/easysync-other.js deleted file mode 100644 index af4580835c8..00000000000 --- a/src/tests/frontend/specs/easysync-other.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict'; - -const Changeset = require('../../../static/js/Changeset'); -const AttributePool = require('../../../static/js/AttributePool'); -const {randomMultiline, poolOrArray} = require('../easysync-helper.js'); -const {padutils} = require('../../../static/js/pad_utils'); - -describe('easysync-other', function () { - describe('filter attribute numbers', function () { - const testFilterAttribNumbers = (testId, cs, filter, correctOutput) => { - it(`testFilterAttribNumbers#${testId}`, async function () { - const str = Changeset.filterAttribNumbers(cs, filter); - expect(str).to.equal(correctOutput); - }); - }; - - testFilterAttribNumbers(1, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', - (n) => (n % 2) === 0, '*0+1+2+3+4*2+5*0*2*c+6'); - testFilterAttribNumbers(2, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', - (n) => (n % 2) === 1, '*1+1+2+3*1+4+5*1*b+6'); - }); - - describe('make attribs string', function () { - const testMakeAttribsString = (testId, pool, opcode, attribs, correctString) => { - it(`testMakeAttribsString#${testId}`, async function () { - const p = poolOrArray(pool); - padutils.warnDeprecated.disabledForTestingOnly = true; - try { - expect(Changeset.makeAttribsString(opcode, attribs, p)).to.equal(correctString); - } finally { - delete padutils.warnDeprecated.disabledForTestingOnly; - } - }); - }; - - testMakeAttribsString(1, ['bold,'], '+', [ - ['bold', ''], - ], ''); - testMakeAttribsString(2, ['abc,def', 'bold,'], '=', [ - ['bold', ''], - ], '*1'); - testMakeAttribsString(3, ['abc,def', 'bold,true'], '+', [ - ['abc', 'def'], - ['bold', 'true'], - ], '*0*1'); - testMakeAttribsString(4, ['abc,def', 'bold,true'], '+', [ - ['bold', 'true'], - ['abc', 'def'], - ], '*0*1'); - }); - - describe('other', function () { - it('testMoveOpsToNewPool', async function () { - const pool1 = new AttributePool(); - const pool2 = new AttributePool(); - - pool1.putAttrib(['baz', 'qux']); - pool1.putAttrib(['foo', 'bar']); - - pool2.putAttrib(['foo', 'bar']); - - expect(Changeset.moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2)) - .to.equal('Z:1>2*0+1*1+1$ab'); - expect(Changeset.moveOpsToNewPool('*1+1*0+1', pool1, pool2)).to.equal('*0+1*1+1'); - }); - - it('testMakeSplice', async function () { - const t = 'a\nb\nc\n'; - const t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, 'def'), t); - expect(t2).to.equal('a\nb\ncdef\n'); - }); - - it('makeSplice at the end', async function () { - const orig = '123'; - const ins = '456'; - expect(Changeset.applyToText(Changeset.makeSplice(orig, orig.length, 0, ins), orig)) - .to.equal(`${orig}${ins}`); - }); - - it('testToSplices', async function () { - const cs = Changeset.checkRep('Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk'); - const correctSplices = [ - [5, 8, '123456789'], - [9, 17, 'abcdefghijk'], - ]; - expect(Changeset.exportedForTestingOnly.toSplices(cs)).to.eql(correctSplices); - }); - - it('opAttributeValue', async function () { - const p = new AttributePool(); - p.putAttrib(['name', 'david']); - p.putAttrib(['color', 'green']); - - const stringOp = (str) => Changeset.deserializeOps(str).next().value; - - padutils.warnDeprecated.disabledForTestingOnly = true; - try { - expect(Changeset.opAttributeValue(stringOp('*0*1+1'), 'name', p)).to.equal('david'); - expect(Changeset.opAttributeValue(stringOp('*0+1'), 'name', p)).to.equal('david'); - expect(Changeset.opAttributeValue(stringOp('*1+1'), 'name', p)).to.equal(''); - expect(Changeset.opAttributeValue(stringOp('+1'), 'name', p)).to.equal(''); - expect(Changeset.opAttributeValue(stringOp('*0*1+1'), 'color', p)).to.equal('green'); - expect(Changeset.opAttributeValue(stringOp('*1+1'), 'color', p)).to.equal('green'); - expect(Changeset.opAttributeValue(stringOp('*0+1'), 'color', p)).to.equal(''); - expect(Changeset.opAttributeValue(stringOp('+1'), 'color', p)).to.equal(''); - } finally { - delete padutils.warnDeprecated.disabledForTestingOnly; - } - }); - - describe('applyToAttribution', function () { - const runApplyToAttributionTest = (testId, attribs, cs, inAttr, outCorrect) => { - it(`applyToAttribution#${testId}`, async function () { - const p = poolOrArray(attribs); - const result = Changeset.applyToAttribution(Changeset.checkRep(cs), inAttr, p); - expect(result).to.equal(outCorrect); - }); - }; - - // turn c<b>a</b>ctus\n into a<b>c</b>tusabcd\n - runApplyToAttributionTest(1, - ['bold,', 'bold,true'], 'Z:7>3-1*0=1*1=1=3+4$abcd', '+1*1+1|1+5', '+1*1+1|1+8'); - - // turn "david\ngreenspan\n" into "<b>david\ngreen</b>\n" - runApplyToAttributionTest(2, - ['bold,', 'bold,true'], 'Z:g<4*1|1=6*1=5-4$', '|2+g', '*1|1+6*1+5|1+1'); - }); - - describe('split/join attribution lines', function () { - const testSplitJoinAttributionLines = (randomSeed) => { - const stringToOps = (str) => { - const assem = Changeset.mergingOpAssembler(); - const o = new Changeset.Op('+'); - o.chars = 1; - for (let i = 0; i < str.length; i++) { - const c = str.charAt(i); - o.lines = (c === '\n' ? 1 : 0); - o.attribs = (c === 'a' || c === 'b' ? `*${c}` : ''); - assem.append(o); - } - return assem.toString(); - }; - - it(`testSplitJoinAttributionLines#${randomSeed}`, async function () { - const doc = `${randomMultiline(10, 20)}\n`; - - const theJoined = stringToOps(doc); - const theSplit = doc.match(/[^\n]*\n/g).map(stringToOps); - - expect(Changeset.splitAttributionLines(theJoined, doc)).to.eql(theSplit); - expect(Changeset.joinAttributionLines(theSplit)).to.equal(theJoined); - }); - }; - - for (let i = 0; i < 10; i++) testSplitJoinAttributionLines(i); - }); - }); -}); diff --git a/src/tests/frontend/specs/skiplist.js b/src/tests/frontend/specs/skiplist.js deleted file mode 100644 index 16b98561515..00000000000 --- a/src/tests/frontend/specs/skiplist.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict'; - -const SkipList = require('ep_etherpad-lite/static/js/skiplist'); - -describe('skiplist.js', function () { - it('rejects null keys', async function () { - const skiplist = new SkipList(); - for (const key of [undefined, null]) { - expect(() => skiplist.push({key})).to.throwError(); - } - }); - - it('rejects duplicate keys', async function () { - const skiplist = new SkipList(); - skiplist.push({key: 'foo'}); - expect(() => skiplist.push({key: 'foo'})).to.throwError(); - }); - - it('atOffset() returns last entry that touches offset', async function () { - const skiplist = new SkipList(); - const entries = []; - let nextId = 0; - const makeEntry = (width) => { - const entry = {key: `id${nextId++}`, width}; - entries.push(entry); - return entry; - }; - - skiplist.push(makeEntry(5)); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[0]); - expect(() => skiplist.atOffset(6)).to.throwError(); - - skiplist.push(makeEntry(0)); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[1]); - expect(() => skiplist.atOffset(6)).to.throwError(); - - skiplist.push(makeEntry(0)); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[2]); - expect(() => skiplist.atOffset(6)).to.throwError(); - - skiplist.splice(2, 0, [makeEntry(0)]); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[2]); - expect(() => skiplist.atOffset(6)).to.throwError(); - - skiplist.push(makeEntry(3)); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[4]); - expect(skiplist.atOffset(6)).to.be(entries[4]); - }); -}); diff --git a/src/tests/frontend/travis/runnerLoadTest.sh b/src/tests/frontend/travis/runnerLoadTest.sh index 6582b4b51e5..250d01f1977 100755 --- a/src/tests/frontend/travis/runnerLoadTest.sh +++ b/src/tests/frontend/travis/runnerLoadTest.sh @@ -24,7 +24,7 @@ s!"points":[^,]*!"points": 1000! ' settings.json.template >settings.json log "Assuming src/bin/installDeps.sh has already been run" -(cd src && npm run dev & +(cd src && pnpm run prod & ep_pid=$!) log "Waiting for Etherpad to accept connections (http://localhost:9001)..." diff --git a/src/tests/settings.json b/src/tests/settings.json index c8064176b85..9fef0fa1998 100644 --- a/src/tests/settings.json +++ b/src/tests/settings.json @@ -1,654 +1 @@ -/* - * This file must be valid JSON. But comments are allowed - * - * Please edit settings.json, not settings.json.template - * - * Please note that starting from Etherpad 1.6.0 you can store DB credentials in - * a separate file (credentials.json). - * - * - * ENVIRONMENT VARIABLE SUBSTITUTION - * ================================= - * - * All the configuration values can be read from environment variables using the - * syntax "${ENV_VAR}" or "${ENV_VAR:default_value}". - * - * This is useful, for example, when running in a Docker container. - * - * DETAILED RULES: - * - If the environment variable is set to the string "true" or "false", the - * value becomes Boolean true or false. - * - If the environment variable is set to the string "null", the value - * becomes null. - * - If the environment variable is set to the string "undefined", the setting - * is removed entirely, except when used as the member of an array in which - * case it becomes null. - * - If the environment variable is set to a string representation of a finite - * number, the string is converted to that number. - * - If the environment variable is set to any other string, including the - * empty string, the value is that string. - * - If the environment variable is unset and a default value is provided, the - * value is as if the environment variable was set to the provided default: - * - "${UNSET_VAR:}" becomes the empty string. - * - "${UNSET_VAR:foo}" becomes the string "foo". - * - "${UNSET_VAR:true}" and "${UNSET_VAR:false}" become true and false. - * - "${UNSET_VAR:null}" becomes null. - * - "${UNSET_VAR:undefined}" causes the setting to be removed (or be set - * to null, if used as a member of an array). - * - If the environment variable is unset and no default value is provided, - * the value becomes null. THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF - * ETHERPAD; if you want the default value to be null, you should explicitly - * specify "null" as the default value. - * - * EXAMPLE: - * "port": "${PORT:9001}" - * "minify": "${MINIFY}" - * "skinName": "${SKIN_NAME:colibris}" - * - * Would read the configuration values for those items from the environment - * variables PORT, MINIFY and SKIN_NAME. - * - * If PORT and SKIN_NAME variables were not defined, the default values 9001 and - * "colibris" would be used. - * The configuration value "minify", on the other hand, does not have a - * designated default value. Thus, if the environment variable MINIFY were - * undefined, "minify" would be null. - * - * REMARKS: - * 1) please note that variable substitution always needs to be quoted. - * - * "port": 9001, <-- Literal values. When not using - * "minify": false substitution, only strings must be - * "skinName": "colibris" quoted. Booleans and numbers must not. - * - * "port": "${PORT:9001}" <-- CORRECT: if you want to use a variable - * "minify": "${MINIFY:true}" substitution, put quotes around its name, - * "skinName": "${SKIN_NAME}" even if the required value is a number or - * a boolean. - * Etherpad will take care of rewriting it - * to the proper type if necessary. - * - * "port": ${PORT:9001} <-- ERROR: this is not valid json. Quotes - * "minify": ${MINIFY} around variable names are missing. - * "skinName": ${SKIN_NAME} - * - * 2) Beware of undefined variables and default values: nulls and empty strings - * are different! - * - * This is particularly important for user's passwords (see the relevant - * section): - * - * "password": "${PASSW}" // if PASSW is not defined would result in password === null - * "password": "${PASSW:}" // if PASSW is not defined would result in password === '' - * - * If you want to use an empty value (null) as default value for a variable, - * simply do not set it, without putting any colons: "${ABIWORD}". - * - * 3) if you want to use newlines in the default value of a string parameter, - * use "\n" as usual. - * - * "defaultPadText" : "${DEFAULT_PAD_TEXT}Line 1\nLine 2" - */ -{ - /* - * Name your instance! - */ - "title": "Etherpad", - - /* - * Pathname of the favicon you want to use. If null, the skin's favicon is - * used if one is provided by the skin, otherwise the default Etherpad favicon - * is used. If this is a relative path it is interpreted as relative to the - * Etherpad root directory. - */ - "favicon": null, - - /* - * Skin name. - * - * Its value has to be an existing directory under src/static/skins. - * You can write your own, or use one of the included ones: - * - * - "no-skin": an empty skin (default). This yields the unmodified, - * traditional Etherpad theme. - * - "colibris": the new experimental skin (since Etherpad 1.8), candidate to - * become the default in Etherpad 2.0 - */ - "skinName": "colibris", - - /* - * Skin Variants - * - * Use the UI skin variants builder at /p/test#skinvariantsbuilder - * - * For the colibris skin only, you can choose how to render the three main - * containers: - * - toolbar (top menu with icons) - * - editor (containing the text of the pad) - * - background (area outside of editor, mostly visible when using page style) - * - * For each of the 3 containers you can choose 4 color combinations: - * super-light, light, dark, super-dark. - * - * For example, to make the toolbar dark, you will include "dark-toolbar" into - * skinVariants. - * - * You can provide multiple skin variants separated by spaces. Default - * skinVariant is "super-light-toolbar super-light-editor light-background". - * - * For the editor container, you can also make it full width by adding - * "full-width-editor" variant (by default editor is rendered as a page, with - * a max-width of 900px). - */ - "skinVariants": "super-light-toolbar super-light-editor light-background", - - /* - * IP and port which Etherpad should bind at. - * - * Binding to a Unix socket is also supported: just use an empty string for - * the ip, and put the full path to the socket in the port parameter. - * - * EXAMPLE USING UNIX SOCKET: - * "ip": "", // <-- has to be an empty string - * "port" : "/somepath/etherpad.socket", // <-- path to a Unix socket - */ - "ip": "0.0.0.0", - "port": 9001, - - /* - * Option to hide/show the settings.json in admin page. - * - * Default option is set to true - */ - "showSettingsInAdminPage": true, - - /* - * Node native SSL support - * - * This is disabled by default. - * Make sure to have the minimum and correct file access permissions set so - * that the Etherpad server can access them - */ - - /* - "ssl" : { - "key" : "/path-to-your/epl-server.key", - "cert" : "/path-to-your/epl-server.crt", - "ca": ["/path-to-your/epl-intermediate-cert1.crt", "/path-to-your/epl-intermediate-cert2.crt"] - }, - */ - - /* - * The type of the database. - * - * You can choose between many DB drivers, for example: dirty, postgres, - * sqlite, mysql. - * - * You shouldn't use "dirty" for for anything else than testing or - * development. - * - * - * Database specific settings are dependent on dbType, and go in dbSettings. - * Remember that since Etherpad 1.6.0 you can also store this information in - * credentials.json. - * - * For a complete list of the supported drivers, please refer to: - * https://www.npmjs.com/package/ueberdb2 - */ - - "dbType": "dirty", - "dbSettings": { - "filename": "var/dirty.db" - }, - - /* - * An Example of MySQL Configuration (commented out). - * - * See: https://github.com/ether/etherpad-lite/wiki/How-to-use-Etherpad-Lite-with-MySQL - */ - - /* - "dbType" : "mysql", - "dbSettings" : { - "user": "etherpaduser", - "host": "localhost", - "port": 3306, - "password": "PASSWORD", - "database": "etherpad_lite_db", - "charset": "utf8mb4" - }, - */ - - /* - * The default text of a pad - */ - "defaultPadText" : "Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nGet involved with Etherpad at https:\/\/etherpad.org\n", - - /* - * Default Pad behavior. - * - * Change them if you want to override. - */ - "padOptions": { - "noColors": false, - "showControls": true, - "showChat": true, - "showLineNumbers": true, - "useMonospaceFont": false, - "userName": null, - "userColor": null, - "rtl": false, - "alwaysShowChat": false, - "chatAndUsers": false, - "lang": null - }, - - /* - * Pad Shortcut Keys - */ - "padShortcutEnabled" : { - "altF9": true, /* focus on the File Menu and/or editbar */ - "altC": true, /* focus on the Chat window */ - "cmdShift2": true, /* shows a gritter popup showing a line author */ - "delete": true, - "return": true, - "esc": true, /* in mozilla versions 14-19 avoid reconnecting pad */ - "cmdS": true, /* save a revision */ - "tab": true, /* indent */ - "cmdZ": true, /* undo/redo */ - "cmdY": true, /* redo */ - "cmdI": true, /* italic */ - "cmdB": true, /* bold */ - "cmdU": true, /* underline */ - "cmd5": true, /* strike through */ - "cmdShiftL": true, /* unordered list */ - "cmdShiftN": true, /* ordered list */ - "cmdShift1": true, /* ordered list */ - "cmdShiftC": true, /* clear authorship */ - "cmdH": true, /* backspace */ - "ctrlHome": true, /* scroll to top of pad */ - "pageUp": true, - "pageDown": true - }, - - /* - * Should we suppress errors from being visible in the default Pad Text? - */ - "suppressErrorsInPadText": false, - - /* - * If this option is enabled, a user must have a session to access pads. - * This effectively allows only group pads to be accessed. - */ - "requireSession": false, - - /* - * Users may edit pads but not create new ones. - * - * Pad creation is only via the API. - * This applies both to group pads and regular pads. - */ - "editOnly": false, - - /* - * If true, all css & js will be minified before sending to the client. - * - * This will improve the loading performance massively, but makes it difficult - * to debug the javascript/css - */ - "minify": true, - - /* - * How long may clients use served javascript code (in seconds)? - * - * Not setting this may cause problems during deployment. - * Set to 0 to disable caching. - */ - "maxAge": 21600, // 60 * 60 * 6 = 6 hours - - /* - * Absolute path to the Abiword executable. - * - * Abiword is needed to get advanced import/export features of pads. Setting - * it to null disables Abiword and will only allow plain text and HTML - * import/exports. - */ - "abiword": null, - - /* - * This is the absolute path to the soffice executable. - * - * LibreOffice can be used in lieu of Abiword to export pads. - * Setting it to null disables LibreOffice exporting. - */ - "soffice": null, - - /* - * Allow import of file types other than the supported ones: - * txt, doc, docx, rtf, odt, html & htm - */ - "allowUnknownFileEnds": true, - - /* - * This setting is used if you require authentication of all users. - * - * Note: "/admin" always requires authentication. - */ - "requireAuthentication": false, - - /* - * Require authorization by a module, or a user with is_admin set, see below. - */ - "requireAuthorization": false, - - /* - * When you use NGINX or another proxy/load-balancer set this to true. - * - * This is especially necessary when the reverse proxy performs SSL - * termination, otherwise the cookies will not have the "secure" flag. - * - * The other effect will be that the logs will contain the real client's IP, - * instead of the reverse proxy's IP. - */ - "trustProxy": false, - - /* - * Settings controlling the session cookie issued by Etherpad. - */ - "cookie": { - /* - * How often (in milliseconds) the key used to sign the express_sid cookie - * should be rotated. Long rotation intervals reduce signature verification - * overhead (because there are fewer historical keys to check) and database - * load (fewer historical keys to store, and less frequent queries to - * get/update the keys). Short rotation intervals are slightly more secure. - * - * Multiple Etherpad processes sharing the same database (table) is - * supported as long as the clock sync error is significantly less than this - * value. - * - * Key rotation can be disabled (not recommended) by setting this to 0 or - * null, or by disabling session expiration (see sessionLifetime). - */ - "keyRotationInterval": 86400000, // = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s - - /* - * Value of the SameSite cookie property. "Lax" is recommended unless - * Etherpad will be embedded in an iframe from another site, in which case - * this must be set to "None". Note: "None" will not work (the browser will - * not send the cookie to Etherpad) unless https is used to access Etherpad - * (either directly or via a reverse proxy with "trustProxy" set to true). - * - * "Strict" is not recommended because it has few security benefits but - * significant usability drawbacks vs. "Lax". See - * https://stackoverflow.com/q/41841880 for discussion. - */ - "sameSite": "Lax", - - /* - * How long (in milliseconds) after navigating away from Etherpad before the - * user is required to log in again. (The express_sid cookie is set to - * expire at time now + sessionLifetime when first created, and its - * expiration time is periodically refreshed to a new now + sessionLifetime - * value.) If requireAuthentication is false then this value does not really - * matter. - * - * The "best" value depends on your users' usage patterns and the amount of - * convenience you desire. A long lifetime is more convenient (users won't - * have to log back in as often) but has some drawbacks: - * - It increases the amount of state kept in the database. - * - It might weaken security somewhat: The cookie expiration is refreshed - * indefinitely without consulting authentication or authorization - * hooks, so once a user has accessed a pad, the user can continue to - * use the pad until the user leaves for longer than sessionLifetime. - * - More historical keys (sessionLifetime / keyRotationInterval) must be - * checked when verifying signatures. - * - * Session lifetime can be set to infinity (not recommended) by setting this - * to null or 0. Note that if the session does not expire, most browsers - * will delete the cookie when the browser exits, but a session record is - * kept in the database forever. - */ - "sessionLifetime": 864000000, // = 10d * 24h/d * 60m/h * 60s/m * 1000ms/s - - /* - * How long (in milliseconds) before the expiration time of an active user's - * session is refreshed (to now + sessionLifetime). This setting affects the - * following: - * - How often a new session expiration time will be written to the - * database. - * - How often each user's browser will ping the Etherpad server to - * refresh the expiration time of the session cookie. - * - * High values reduce the load on the database and the load from browsers, - * but can shorten the effective session lifetime if Etherpad is restarted - * or the user navigates away. - * - * Automatic session refreshes can be disabled (not recommended) by setting - * this to null. - */ - "sessionRefreshInterval": 86400000 // = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s - }, - - /* - * Privacy: disable IP logging - */ - "disableIPlogging": false, - - /* - * Time (in seconds) to automatically reconnect pad when a "Force reconnect" - * message is shown to user. - * - * Set to 0 to disable automatic reconnection. - */ - "automaticReconnectionTimeout": 0, - - /* - * By default, when caret is moved out of viewport, it scrolls the minimum - * height needed to make this line visible. - */ - "scrollWhenFocusLineIsOutOfViewport": { - - /* - * Percentage of viewport height to be additionally scrolled. - * - * E.g.: use "percentage.editionAboveViewport": 0.5, to place caret line in - * the middle of viewport, when user edits a line above of the - * viewport - * - * Set to 0 to disable extra scrolling - */ - "percentage": { - "editionAboveViewport": 0, - "editionBelowViewport": 0 - }, - - /* - * Time (in milliseconds) used to animate the scroll transition. - * Set to 0 to disable animation - */ - "duration": 0, - - /* - * Flag to control if it should scroll when user places the caret in the - * last line of the viewport - */ - "scrollWhenCaretIsInTheLastLineOfViewport": false, - - /* - * Percentage of viewport height to be additionally scrolled when user - * presses arrow up in the line of the top of the viewport. - * - * Set to 0 to let the scroll to be handled as default by Etherpad - */ - "percentageToScrollWhenUserPressesArrowUp": 0 - }, - - /* - * User accounts. These accounts are used by: - * - default HTTP basic authentication if no plugin handles authentication - * - some but not all authentication plugins - * - some but not all authorization plugins - * - * User properties: - * - password: The user's password. Some authentication plugins will ignore - * this. - * - is_admin: true gives access to /admin. Defaults to false. If you do not - * uncomment this, /admin will not be available! - * - readOnly: If true, this user will not be able to create new pads or - * modify existing pads. Defaults to false. - * - canCreate: If this is true and readOnly is false, this user can create - * new pads. Defaults to true. - * - * Authentication and authorization plugins may define additional properties. - * - * WARNING: passwords should not be stored in plaintext in this file. - * If you want to mitigate this, please install ep_hash_auth and - * follow the section "secure your installation" in README.md - */ - - - "users": { - "admin": { - // 1) "password" can be replaced with "hash" if you install ep_hash_auth - // 2) please note that if password is null, the user will not be created - "password": "changeme1", - "is_admin": true - }, - "user": { - // 1) "password" can be replaced with "hash" if you install ep_hash_auth - // 2) please note that if password is null, the user will not be created - "password": "changeme1", - "is_admin": false - } - }, - - - /* - * Restrict socket.io transport methods - */ - "socketTransportProtocols" : ["websocket", "polling"], - - "socketIo": { - /* - * Maximum permitted client message size (in bytes). All messages from - * clients that are larger than this will be rejected. Large values make it - * possible to paste large amounts of text, and plugins may require a larger - * value to work properly, but increasing the value increases susceptibility - * to denial of service attacks (malicious clients can exhaust memory). - */ - "maxHttpBufferSize": 1000000 - }, - - /* - * Allow Load Testing tools to hit the Etherpad Instance. - * - * WARNING: this will disable security on the instance. - */ - "loadTest": false, - - /** - * Disable dump of objects preventing a clean exit - */ - "dumpOnUncleanExit": false, - - /* - * Disable indentation on new line when previous line ends with some special - * chars (':', '[', '(', '{') - */ - - /* - "indentationOnNewLine": false, - */ - - /* - * From Etherpad 1.8.3 onwards, import and export of pads is always rate - * limited. - * - * The default is to allow at most 10 requests per IP in a 90 seconds window. - * After that the import/export request is rejected. - * - * See https://github.com/nfriedly/express-rate-limit for more options - */ - "importExportRateLimiting": { - // duration of the rate limit window (milliseconds) - "windowMs": 90000, - - // maximum number of requests per IP to allow during the rate limit window - "max": 10 - }, - - /* - * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported - * file is always bounded. - * - * File size is specified in bytes. Default is 50 MB. - */ - "importMaxFileSize": 52428800, // 50 * 1024 * 1024 - - /* - * From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited - * - * The default is to allow at most 10 changes per IP in a 1 second window. - * After that the change is rejected. - * - * See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options - */ - "commitRateLimiting": { - // duration of the rate limit window (seconds) - "duration": 1, - - // maximum number of changes per IP to allow during the rate limit window - "points": 10 - }, - - /* - * Toolbar buttons configuration. - * - * Uncomment to customize. - */ - - /* - "toolbar": { - "left": [ - ["bold", "italic", "underline", "strikethrough"], - ["orderedlist", "unorderedlist", "indent", "outdent"], - ["undo", "redo"], - ["clearauthorship"] - ], - "right": [ - ["importexport", "timeslider", "savedrevision"], - ["settings", "embed"], - ["showusers"] - ], - "timeslider": [ - ["timeslider_export", "timeslider_returnToPad"] - ] - }, - */ - - /* - * Expose Etherpad version in the web interface and in the Server http header. - * - * Do not enable on production machines. - */ - "exposeVersion": false, - - /* - * The log level we are using. - * - * Valid values: DEBUG, INFO, WARN, ERROR - */ - "loglevel": "INFO", - - /* Override any strings found in locale directories */ - "customLocaleStrings": {}, - - /* Disable Admin UI tests */ - "enableAdminUITests": true, - - /* - * Enable/Disable case-insensitive pad names. - */ - "lowerCasePadIds": false -} +{"title":"Etherpad","favicon":null,"skinName":"colibris","skinVariants":"super-light-toolbar super-light-editor light-background","ip":"0.0.0.0","port":9001,"showSettingsInAdminPage":true,"dbType":"dirty","dbSettings":{"filename":"var/dirty.db"},"defaultPadText":"Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nGet involved with Etherpad at https://etherpad.org\n","padOptions":{"noColors":false,"showControls":true,"showChat":true,"showLineNumbers":true,"useMonospaceFont":false,"userName":null,"userColor":null,"rtl":false,"alwaysShowChat":false,"chatAndUsers":false,"lang":null},"padShortcutEnabled":{"altF9":true,"altC":true,"cmdShift2":true,"delete":true,"return":true,"esc":true,"cmdS":true,"tab":true,"cmdZ":true,"cmdY":true,"cmdI":true,"cmdB":true,"cmdU":true,"cmd5":true,"cmdShiftL":true,"cmdShiftN":true,"cmdShift1":true,"cmdShiftC":true,"cmdH":true,"ctrlHome":true,"pageUp":true,"pageDown":true},"suppressErrorsInPadText":false,"requireSession":false,"editOnly":false,"minify":true,"maxAge":21600,"abiword":null,"soffice":null,"allowUnknownFileEnds":true,"requireAuthentication":false,"requireAuthorization":false,"trustProxy":false,"cookie":{"keyRotationInterval":86400000,"sameSite":"Lax","sessionLifetime":864000000,"sessionRefreshInterval":86400000},"disableIPlogging":false,"automaticReconnectionTimeout":0,"scrollWhenFocusLineIsOutOfViewport":{"percentage":{"editionAboveViewport":0,"editionBelowViewport":0},"duration":0,"scrollWhenCaretIsInTheLastLineOfViewport":false,"percentageToScrollWhenUserPressesArrowUp":0},"users":{"admin":{"password":"changeme1","is_admin":true},"user":{"password":"changeme1","is_admin":false}},"socketTransportProtocols":["websocket","polling"],"socketIo":{"maxHttpBufferSize":1000000},"loadTest":false,"dumpOnUncleanExit":false,"importExportRateLimiting":{"windowMs":90000,"max":10},"importMaxFileSize":52428800,"commitRateLimiting":{"duration":1,"points":10},"exposeVersion":false,"loglevel":"INFO","customLocaleStrings":{},"enableAdminUITests":true,"lowerCasePadIds":false,"sso":{"issuer":"${SSO_ISSUER:http://localhost:9001}","clients":[{"client_id":"${ADMIN_CLIENT:admin_client}","client_secret":"${ADMIN_SECRET:admin}","grant_types":["authorization_code"],"response_types":["code"],"redirect_uris":["${ADMIN_REDIRECT:http://localhost:9001/admin/}","https://oauth.pstmn.io/v1/callback"]},{"client_id":"${USER_CLIENT:user_client}","client_secret":"${USER_SECRET:user}","grant_types":["authorization_code"],"response_types":["code"],"redirect_uris":["${USER_REDIRECT:http://localhost:9001/}"]}]}} \ No newline at end of file diff --git a/src/vitest.config.ts b/src/vitest.config.ts new file mode 100644 index 00000000000..c47c424cca2 --- /dev/null +++ b/src/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ["tests/backend-new/specs/**/*.ts"], + }, +}) diff --git a/ui/.gitignore b/ui/.gitignore new file mode 100644 index 00000000000..a547bf36d8d --- /dev/null +++ b/ui/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/ui/consent.html b/ui/consent.html new file mode 100644 index 00000000000..502b95a2e09 --- /dev/null +++ b/ui/consent.html @@ -0,0 +1,24 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <link rel="icon" type="image/svg+xml" href="/favicon.ico" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Consent Etherpad</title> +</head> +<body> +<div id="app"> + <div class="login-background login-page"> + <div class="login-box login-form"> + <h1 class="login-title">Etherpad <span id="client"></span></h1> + <form class="login-inner-box input-control" method="post"> + <input type="hidden" name="prompt" value="consent"/> + <input type="submit" value="Login" class="login-button"/> + <div id="error"></div> + </form> + </div> + </div> +</div> +<script type="module" src="/src/consent.ts"></script> +</body> +</html> diff --git a/ui/login.html b/ui/login.html new file mode 100644 index 00000000000..0ff588363a4 --- /dev/null +++ b/ui/login.html @@ -0,0 +1,38 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8"/> + <link rel="icon" type="image/svg+xml" href="/favicon.ico"/> + <meta name="viewport" content="width=device-width, initial-scale=1.0"/> + <title>SSO Etherpad</title> +</head> +<body> +<div id="app"> + <div class="login-background login-page"> + <div class="login-box login-form"> + <h1 class="login-title">Etherpad <span id="client"></span></h1> + <form class="login-inner-box input-control"> + <label> + <input class="login-textinput input-control" required type="text" name="login" placeholder="Username"/> + </label> + <div class="icon-input"> + <label class="password-label"> + <input class="login-textinput" type="password" required name="password" placeholder="Password"/> + <svg id="eye-visible" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 toggle-password-visibility"> + <path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" /> + <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /> + </svg> + <svg id="eye-hide" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> + <path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" /> + </svg> + </label> + </div> + <input type="submit" value="Login" class="login-button"/> + <div id="error"></div> + </form> + </div> + </div> +</div> +<script type="module" src="/src/main.ts"></script> +</body> +</html> diff --git a/ui/package.json b/ui/package.json new file mode 100644 index 00000000000..8d4a6816c21 --- /dev/null +++ b/ui/package.json @@ -0,0 +1,17 @@ +{ + "name": "ui", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "build-copy": "tsc && vite build --outDir ../src/static/oidc --emptyOutDir" + }, + "devDependencies": { + "ep_etherpad-lite": "workspace:../src", + "typescript": "^5.5.4", + "vite": "^5.4.2" + } +} diff --git a/ui/pad.html b/ui/pad.html new file mode 100644 index 00000000000..6b34d7e9a55 --- /dev/null +++ b/ui/pad.html @@ -0,0 +1,686 @@ +<!doctype html> +<html translate="no" class="pad super-light-toolbar super-light-editor light-background"> +<head> + + + <title>Etherpad</title> + <link rel="manifest" href="/manifest.json" /> + <script> + /* + |@licstart The following is the entire license notice for the + JavaScript code in this page.| + + Copyright 2011 Peter Martischka, Primary Technology. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + |@licend The above is the entire license notice + for the JavaScript code in this page.| + */ + </script> + + <meta charset="utf-8"> + <meta name="robots" content="noindex, nofollow"> + <meta name="referrer" content="no-referrer"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> + <link rel="shortcut icon" href="../favicon.ico"> + + + <link href="../static/css/pad.css?v=5ba315cd" rel="stylesheet"> + + + <link href="../static/skins/colibris/pad.css?v=5ba315cd" rel="stylesheet"> + + + <style title="dynamicsyntax"></style> + + + <link rel="localizations" type="application/l10n+json" href="../locales.json" /> +</head> +<body> + + +<!-----------------------------> +<!--------- TOOLBAR -----------> +<!-----------------------------> +<div id="editbar" class="toolbar"> + <div id="toolbar-overlay"></div> + + <ul class="menu_left" role="toolbar"> + + <li data-type="button" data-key="bold"><a class="grouped-left" data-l10n-id="pad.toolbar.bold.title"><button class=" buttonicon buttonicon-bold" data-l10n-id="pad.toolbar.bold.title"></button></a></li> + <li data-type="button" data-key="italic"><a class="grouped-middle" data-l10n-id="pad.toolbar.italic.title"><button class=" buttonicon buttonicon-italic" data-l10n-id="pad.toolbar.italic.title"></button></a></li> + <li data-type="button" data-key="underline"><a class="grouped-middle" data-l10n-id="pad.toolbar.underline.title"><button class=" buttonicon buttonicon-underline" data-l10n-id="pad.toolbar.underline.title"></button></a></li> + <li data-type="button" data-key="strikethrough"><a class="grouped-right" data-l10n-id="pad.toolbar.strikethrough.title"><button class=" buttonicon buttonicon-strikethrough" data-l10n-id="pad.toolbar.strikethrough.title"></button + ></a></li><li class="separator"></li><li data-type="button" data-key="insertorderedlist"><a class="grouped-left" data-l10n-id="pad.toolbar.ol.title"><button class=" buttonicon buttonicon-insertorderedlist" data-l10n-id="pad.toolbar.ol.title"></button></a></li> + <li data-type="button" data-key="insertunorderedlist"><a class="grouped-middle" data-l10n-id="pad.toolbar.ul.title"><button class=" buttonicon buttonicon-insertunorderedlist" data-l10n-id="pad.toolbar.ul.title"></button></a></li> + <li data-type="button" data-key="indent"><a class="grouped-middle" data-l10n-id="pad.toolbar.indent.title"><button class=" buttonicon buttonicon-indent" data-l10n-id="pad.toolbar.indent.title"></button></a></li> + <li data-type="button" data-key="outdent"><a class="grouped-right" data-l10n-id="pad.toolbar.unindent.title"><button class=" buttonicon buttonicon-outdent" data-l10n-id="pad.toolbar.unindent.title"></button></a></li><li class="separator"></li><li data-type="button" data-key="undo"><a class="grouped-left" data-l10n-id="pad.toolbar.undo.title"><button class=" buttonicon buttonicon-undo" data-l10n-id="pad.toolbar.undo.title"></button></a></li> + <li data-type="button" data-key="redo"><a class="grouped-right" data-l10n-id="pad.toolbar.redo.title"><button class=" buttonicon buttonicon-redo" data-l10n-id="pad.toolbar.redo.title"></button></a></li><li class="separator"></li + ><li data-type="button" data-key="clearauthorship"><a class="" data-l10n-id="pad.toolbar.clearAuthorship.title"><button class=" buttonicon buttonicon-clearauthorship" data-l10n-id="pad.toolbar.clearAuthorship.title"></button></a></li> + + </ul> + <ul class="menu_right" role="toolbar"> + + <li data-type="button" data-key="import_export"><a class="grouped-left" data-l10n-id="pad.toolbar.import_export.title"><button class=" buttonicon buttonicon-import_export" data-l10n-id="pad.toolbar.import_export.title"></button></a></li> + <li data-type="button" data-key="showTimeSlider"><a class="grouped-middle" data-l10n-id="pad.toolbar.timeslider.title"><button class=" buttonicon buttonicon-history" data-l10n-id="pad.toolbar.timeslider.title"></button></a></li> + <li data-type="button" data-key="savedRevision"><a class="grouped-right" data-l10n-id="pad.toolbar.savedRevision.title"><button class=" buttonicon buttonicon-savedRevision" data-l10n-id="pad.toolbar.savedRevision.title"></button + ></a></li><li class="separator"></li><li data-type="button" data-key="settings"><a class="grouped-left" data-l10n-id="pad.toolbar.settings.title"><button class=" buttonicon buttonicon-settings" data-l10n-id="pad.toolbar.settings.title"></button></a></li> + <li data-type="button" data-key="embed"><a class="grouped-right" data-l10n-id="pad.toolbar.embed.title"><button class=" buttonicon buttonicon-embed" data-l10n-id="pad.toolbar.embed.title"></button></a></li><li class="separator"></li><li data-type="button" data-key="showusers"><a class="" data-l10n-id="pad.toolbar.showusers.title"><button class=" buttonicon buttonicon-showusers" data-l10n-id="pad.toolbar.showusers.title"></button></a></li> + + </ul> + <span class="show-more-icon-btn"></span> <!-- use on small screen to display hidden toolbar buttons --> +</div> + + + +<div id="editorcontainerbox" class="flex-layout"> + + + + <!-----------------------------> + <!--- PAD EDITOR (in iframe) --> + <!-----------------------------> + + <div id="editorcontainer" class="editorcontainer"></div> + + <div id="editorloadingbox"> + + <div id="permissionDenied"> + <p data-l10n-id="pad.permissionDenied" class="editorloadingbox-message"> + You do not have permission to access this pad + </p> + </div> + + + <p data-l10n-id="pad.loading" id="loading" class="editorloadingbox-message"> + <img src='../static/img/brand.svg' class='etherpadBrand'><br/> + Loading... + </p> + + <noscript> + <p class="editorloadingbox-message"> + <strong> + Sorry, you have to enable Javascript in order to use this. + </strong> + </p> + </noscript> + </div> + + + <!-------------------------------------------------------------> + <!-- SETTINGS POPUP (change font, language, chat parameters) --> + <!-------------------------------------------------------------> + + <div id="settings" class="popup"><div class="popup-content"> + <h1 data-l10n-id="pad.settings.padSettings"></h1> + + <h2 data-l10n-id="pad.settings.myView"></h2> + <p class="hide-for-mobile"> + <input type="checkbox" id="options-stickychat"> + <label for="options-stickychat" data-l10n-id="pad.settings.stickychat"></label> + </p> + <p class="hide-for-mobile"> + <input type="checkbox" id="options-chatandusers" onClick="chat.chatAndUsers();"> + <label for="options-chatandusers" data-l10n-id="pad.settings.chatandusers"></label> + </p> + <p> + <input type="checkbox" id="options-colorscheck"> + <label for="options-colorscheck" data-l10n-id="pad.settings.colorcheck"></label> + </p> + <p> + <input type="checkbox" id="options-linenoscheck" checked> + <label for="options-linenoscheck" data-l10n-id="pad.settings.linenocheck"></label> + </p> + <p> + <input type="checkbox" id="options-rtlcheck"> + <label for="options-rtlcheck" data-l10n-id="pad.settings.rtlcheck"></label> + </p> + + + <div class="dropdowns-container"> + + <p class="dropdown-line"> + <label for="viewfontmenu" data-l10n-id="pad.settings.fontType">Font type:</label> + <select id="viewfontmenu"> + <option value="" data-l10n-id="pad.settings.fontType.normal">Normal</option> + Quicksand,Roboto,Alegreya,PlayfairDisplay,Montserrat,OpenDyslexic,RobotoMono + + <option value="Quicksand">Quicksand</option> + + <option value="Roboto">Roboto</option> + + <option value="Alegreya">Alegreya</option> + + <option value="PlayfairDisplay">PlayfairDisplay</option> + + <option value="Montserrat">Montserrat</option> + + <option value="OpenDyslexic">OpenDyslexic</option> + + <option value="RobotoMono">RobotoMono</option> + + </select> + </p> + + <p class="dropdown-line"> + <label for="languagemenu" data-l10n-id="pad.settings.language">Language:</label> + <select id="languagemenu"> + + <option value="af">Afrikaans</option> + + <option value="ar">العربية</option> + + <option value="ast">asturianu</option> + + <option value="az">azərbaycanca</option> + + <option value="azb">تورکجه</option> + + <option value="bcc">بلوچی مکرانی</option> + + <option value="be-tarask">беларуская (тарашкевіца)</option> + + <option value="bg">български</option> + + <option value="bn">বাংলা</option> + + <option value="br">brezhoneg</option> + + <option value="bs">bosanski</option> + + <option value="ca">català</option> + + <option value="cs">česky</option> + + <option value="da">dansk</option> + + <option value="de">Deutsch</option> + + <option value="diq">Zazaki</option> + + <option value="dsb">dolnoserbski</option> + + <option value="el">Ελληνικά</option> + + <option value="en-gb">British English</option> + + <option value="en">English</option> + + <option value="eo">Esperanto</option> + + <option value="es">español</option> + + <option value="et">eesti</option> + + <option value="eu">euskara</option> + + <option value="fa">فارسی</option> + + <option value="ff">Fulfulde</option> + + <option value="fi">suomi</option> + + <option value="fo">føroyskt</option> + + <option value="fr">français</option> + + <option value="fy">Frysk</option> + + <option value="gl">galego</option> + + <option value="gu">ગુજરાતી</option> + + <option value="he">עברית</option> + + <option value="hi">हिन्दी</option> + + <option value="hr">hrvatski</option> + + <option value="hsb">hornjoserbsce</option> + + <option value="hu">magyar</option> + + <option value="hy">Հայերեն</option> + + <option value="ia">interlingua</option> + + <option value="id">Bahasa Indonesia</option> + + <option value="is">íslenska</option> + + <option value="it">italiano</option> + + <option value="ja">日本語</option> + + <option value="kab">Taqbaylit</option> + + <option value="km">ភាសាខ្មែរ</option> + + <option value="kn">ಕನ್ನಡ</option> + + <option value="ko">한국어</option> + + <option value="krc">къарачай-малкъар</option> + + <option value="ksh">Ripoarisch</option> + + <option value="ku-latn">Kurdî (latînî)</option> + + <option value="lb">Lëtzebuergesch</option> + + <option value="lt">lietuvių</option> + + <option value="lv">latviešu</option> + + <option value="map-bms">Basa Banyumasan</option> + + <option value="mg">Malagasy</option> + + <option value="mk">македонски</option> + + <option value="ml">മലയാളം</option> + + <option value="mn">монгол</option> + + <option value="mnw">ဘာသာ မန်</option> + + <option value="mr">मराठी</option> + + <option value="ms">Bahasa Melayu</option> + + <option value="my">မြန်မာဘာသာ</option> + + <option value="nah">Nāhuatl</option> + + <option value="nap">Nnapulitano</option> + + <option value="nb">norsk (bokmål)</option> + + <option value="nds">Plattdüütsch</option> + + <option value="ne">नेपाली</option> + + <option value="nl">Nederlands</option> + + <option value="nn">norsk (nynorsk)</option> + + <option value="oc">occitan</option> + + <option value="os">Ирон</option> + + <option value="pa">ਪੰਜਾਬੀ</option> + + <option value="pl">polski</option> + + <option value="pms">Piemontèis</option> + + <option value="ps">پښتو</option> + + <option value="pt-br">português do Brasil</option> + + <option value="pt">português</option> + + <option value="qqq">Message documentation</option> + + <option value="ro">română</option> + + <option value="ru">русский</option> + + <option value="sc">sardu</option> + + <option value="sco">Scots</option> + + <option value="sd">سنڌي</option> + + <option value="sh">srpskohrvatski / српскохрватски</option> + + <option value="shn">လိၵ်ႈတႆး</option> + + <option value="sk">slovenčina</option> + + <option value="sl">slovenščina</option> + + <option value="sq">shqip</option> + + <option value="sr-ec">српски (ћирилица)</option> + + <option value="sr-el">srpski (latinica)</option> + + <option value="sv">svenska</option> + + <option value="sw">Kiswahili</option> + + <option value="ta">தமிழ்</option> + + <option value="tcy">ತುಳು</option> + + <option value="te">తెలుగు</option> + + <option value="th">ไทย</option> + + <option value="tr">Türkçe</option> + + <option value="uk">українська</option> + + <option value="vec">vèneto</option> + + <option value="vi">Tiếng Việt</option> + + <option value="zh-hans">中文(简体)</option> + + <option value="zh-hant">中文(繁體)</option> + + </select> + </p> + + </div> + + <h2 data-l10n-id="pad.settings.about">About</h2> + <span data-l10n-id="pad.settings.poweredBy">Powered by</span> + <a href="https://etherpad.org">Etherpad</a> + + </div></div> + + + <!-------------------------> + <!-- IMPORT EXPORT POPUP --> + <!-------------------------> + + <div id="import_export" class="popup"><div class="popup-content"> + <h1 data-l10n-id="pad.importExport.import_export"></h1> + <div class="acl-write"> + + <h2 data-l10n-id="pad.importExport.import"></h2> + <div class="importmessage" id="importmessageabiword" data-l10n-id="pad.importExport.abiword.innerHTML"></div><br> + <form id="importform" method="post" action="" target="importiframe" enctype="multipart/form-data"> + <div class="importformdiv" id="importformfilediv"> + <input type="file" name="file" size="10" id="importfileinput"> + <div class="importmessage" id="importmessagefail"></div> + </div> + <div id="import"></div> + <div class="importmessage" id="importmessagesuccess" data-l10n-id="pad.importExport.importSuccessful"></div> + <div class="importformdiv" id="importformsubmitdiv"> + <span class="nowrap"> + <input type="submit" class="btn btn-primary" name="submit" value="Import Now" disabled="disabled" id="importsubmitinput"> + <div alt="" id="importstatusball" class="loadingAnimation" align="top"></div> + </span> + </div> + </form> + + </div> + <div id="exportColumn"> + <h2 data-l10n-id="pad.importExport.export"></h2> + + <a id="exportetherpada" target="_blank" class="exportlink"> + <span class="exporttype buttonicon buttonicon-file-powerpoint" id="exportetherpad" data-l10n-id="pad.importExport.exportetherpad"></span> + </a> + <a id="exporthtmla" target="_blank" class="exportlink"> + <span class="exporttype buttonicon buttonicon-file-code" id="exporthtml" data-l10n-id="pad.importExport.exporthtml"></span> + </a> + <a id="exportplaina" target="_blank" class="exportlink"> + <span class="exporttype buttonicon buttonicon-file" id="exportplain" data-l10n-id="pad.importExport.exportplain"></span> + </a> + <a id="exportworda" target="_blank" class="exportlink"> + <span class="exporttype buttonicon buttonicon-file-word" id="exportword" data-l10n-id="pad.importExport.exportword"></span> + </a> + <a id="exportpdfa" target="_blank" class="exportlink"> + <span class="exporttype buttonicon buttonicon-file-pdf" id="exportpdf" data-l10n-id="pad.importExport.exportpdf"></span> + </a> + <a id="exportopena" target="_blank" class="exportlink"> + <span class="exporttype buttonicon buttonicon-file-alt" id="exportopen" data-l10n-id="pad.importExport.exportopen"></span> + </a> + + </div> + </div></div> + + + <!----------------------------------------------------> + <!-- CONNECTIVITY POPUP (when you get disconnected) --> + <!----------------------------------------------------> + + <div id="connectivity" class="popup"><div class="popup-content"> + + <div class="connected visible"> + <h2 data-l10n-id="pad.modals.connected"></h2> + </div> + <div class="reconnecting"> + <h1 data-l10n-id="pad.modals.reconnecting"></h1> + <i class='buttonicon buttonicon-spin5 icon-spin'> + <img src='../static/img/brand.svg' class='etherpadBrand'><br/> + </i> + </div> + <div class="userdup"> + <h1 data-l10n-id="pad.modals.userdup"></h1> + <h2 data-l10n-id="pad.modals.userdup.explanation"></h2> + <p id="defaulttext" data-l10n-id="pad.modals.userdup.advice"></p> + <button id="forcereconnect" class="btn btn-primary" data-l10n-id="pad.modals.forcereconnect"></button> + </div> + <div class="unauth"> + <h1 data-l10n-id="pad.modals.unauth"></h1> + <p id="defaulttext" data-l10n-id="pad.modals.unauth.explanation"></p> + <button id="forcereconnect" class="btn btn-primary" data-l10n-id="pad.modals.forcereconnect"></button> + </div> + <div class="looping"> + <h1 data-l10n-id="pad.modals.disconnected"></h1> + <h2 data-l10n-id="pad.modals.looping.explanation"></h2> + <p data-l10n-id="pad.modals.looping.cause"></p> + </div> + <div class="initsocketfail"> + <h1 data-l10n-id="pad.modals.initsocketfail"></h1> + <h2 data-l10n-id="pad.modals.initsocketfail.explanation"></h2> + <p data-l10n-id="pad.modals.initsocketfail.cause"></p> + </div> + <div class="slowcommit with_reconnect_timer"> + <h1 data-l10n-id="pad.modals.disconnected"></h1> + <h2 data-l10n-id="pad.modals.slowcommit.explanation"></h2> + <p id="defaulttext" data-l10n-id="pad.modals.slowcommit.cause"></p> + <button id="forcereconnect" class="btn btn-primary" data-l10n-id="pad.modals.forcereconnect"></button> + </div> + <div class="badChangeset with_reconnect_timer"> + <h1 data-l10n-id="pad.modals.disconnected"></h1> + <h2 data-l10n-id="pad.modals.badChangeset.explanation"></h2> + <p id="defaulttext" data-l10n-id="pad.modals.badChangeset.cause"></p> + <button id="forcereconnect" class="btn btn-primary" data-l10n-id="pad.modals.forcereconnect"></button> + </div> + <div class="corruptPad"> + <h1 data-l10n-id="pad.modals.disconnected"></h1> + <h2 data-l10n-id="pad.modals.corruptPad.explanation"></h2> + <p data-l10n-id="pad.modals.corruptPad.cause"></p> + </div> + <div class="deleted"> + <h1 data-l10n-id="pad.modals.deleted"></h1> + <p data-l10n-id="pad.modals.deleted.explanation"></p> + </div> + <div class="rateLimited"> + <h1 data-l10n-id="pad.modals.rateLimited"></h1> + <p data-l10n-id="pad.modals.rateLimited.explanation"></p> + </div> + <div class="rejected"> + <h1 data-l10n-id="pad.modals.disconnected"></h1> + <h2 data-l10n-id="pad.modals.rejected.explanation"></h2> + <p data-l10n-id="pad.modals.rejected.cause"></p> + </div> + <div class="disconnected with_reconnect_timer"> + + <h1 data-l10n-id="pad.modals.disconnected"></h1> + <h2 data-l10n-id="pad.modals.disconnected.explanation"></h2> + <p id="defaulttext" data-l10n-id="pad.modals.disconnected.cause"></p> + <button id="forcereconnect" class="btn btn-primary" data-l10n-id="pad.modals.forcereconnect"></button> + + </div> + <form id="reconnectform" method="post" action="/ep/pad/reconnect" accept-charset="UTF-8" style="display: none;"> + <input type="hidden" class="padId" name="padId"> + <input type="hidden" class="diagnosticInfo" name="diagnosticInfo"> + <input type="hidden" class="missedChanges" name="missedChanges"> + </form> + + </div></div> + + + <!--------------------------------> + <!-- EMBED POPUP (Share, embed) --> + <!--------------------------------> + + <div id="embed" class="popup"><div class="popup-content"> + + <h1 data-l10n-id="pad.share"></h1> + <div id="embedreadonly" class="acl-write"> + <input type="checkbox" id="readonlyinput"> + <label for="readonlyinput" data-l10n-id="pad.share.readonly"></label> + </div> + <div id="linkcode"> + <h2 data-l10n-id="pad.share.link"></h2> + <input id="linkinput" type="text" value="" onclick="this.select()"> + </div> + <div id="embedcode"> + <h2 data-l10n-id="pad.share.emebdcode"></h2> + <input id="embedinput" type="text" value="" onclick="this.select()"> + </div> + + </div></div> + + <div class="sticky-container"> + + <!----------------------------------------------------------------------> + <!-- USERS POPUP (set username, color, see other users names & color) --> + <!----------------------------------------------------------------------> + + <div id="users" class="popup"><div class="popup-content"> + + <div id="connectionstatus"></div> + <div id="myuser"> + <div id="mycolorpicker" class="popup"><div class="popup-content"> + <div id="colorpicker"></div> + <div class="btn-container"> + <button id="mycolorpickersave" data-l10n-id="pad.colorpicker.save" class="btn btn-primary"></button> + <button id="mycolorpickercancel" data-l10n-id="pad.colorpicker.cancel" class="btn btn-default"></button> + <span id="mycolorpickerpreview" class="myswatchboxhoverable"></span> + </div> + </div></div> + <div id="myswatchbox"><div id="myswatch"></div></div> + <div id="myusernameform"> + <input type="text" id="myusernameedit" disabled="disabled" data-l10n-id="pad.userlist.entername"> + </div> + </div> + <div id="otherusers" aria-role="document"> + <table id="otheruserstable" cellspacing="0" cellpadding="0" border="0"> + <tr><td></td></tr> + </table> + </div> + <div id="userlistbuttonarea"></div> + + </div></div> + + + <!-----------------------------> + <!----------- CHAT ------------> + <!-----------------------------> + + <div id="chaticon" class="visible" onclick="chat.show();return false;" title="Chat (Alt C)"> + <span id="chatlabel" data-l10n-id="pad.chat"></span> + <span class="buttonicon buttonicon-chat"></span> + <span id="chatcounter">0</span> + </div> + + <div id="chatbox"> + <div class="chat-content"> + <div id="titlebar"> + <h1 id ="titlelabel" data-l10n-id="pad.chat"></h1> + <a id="titlecross" class="hide-reduce-btn" onClick="chat.hide();return false;">- </a> + <a id="titlesticky" class="stick-to-screen-btn" onClick="chat.stickToScreen(true);return false;" data-l10n-id="pad.chat.stick.title">█ </a> + </div> + <div id="chattext" class="thin-scrollbar" aria-live="polite" aria-relevant="additions removals text" role="log" aria-atomic="false"> + <div alt="loading.." id="chatloadmessagesball" class="chatloadmessages loadingAnimation" align="top"></div> + <button id="chatloadmessagesbutton" class="chatloadmessages" data-l10n-id="pad.chat.loadmessages"></button> + </div> + <div id="chatinputbox"> + <form> + <textarea id="chatinput" maxlength="999" data-l10n-id="pad.chat.writeMessage.placeholder"></textarea> + </form> + </div> + </div> + </div> + </div> + + <!------------------------------------------------------------------> + <!-- SKIN VARIANTS BUILDER (Customize rendering, only for admins) --> + <!------------------------------------------------------------------> + + <div id="skin-variants" class="popup"><div class="popup-content"> + <h1>Skin Builder</h1> + + <div class="dropdowns-container"> + + + <p class="dropdown-line"> + <label class="skin-variant-container">toolbar</label> + <select class="skin-variant skin-variant-color" data-container="toolbar"> + <option value="super-light">Super Light</option> + <option value="light">Light</option> + <option value="dark">Dark</option> + <option value="super-dark">Super Dark</option> + </select> + </p> + + <p class="dropdown-line"> + <label class="skin-variant-container">background</label> + <select class="skin-variant skin-variant-color" data-container="background"> + <option value="super-light">Super Light</option> + <option value="light">Light</option> + <option value="dark">Dark</option> + <option value="super-dark">Super Dark</option> + </select> + </p> + + <p class="dropdown-line"> + <label class="skin-variant-container">editor</label> + <select class="skin-variant skin-variant-color" data-container="editor"> + <option value="super-light">Super Light</option> + <option value="light">Light</option> + <option value="dark">Dark</option> + <option value="super-dark">Super Dark</option> + </select> + </p> + + </div> + + <p> + <input type="checkbox" id="skin-variant-full-width" class="skin-variant"/> + <label for="skin-variant-full-width">Full Width Editor</label> + </p> + + <p> + <label>Result to copy in settings.json</label> + <input id="skin-variants-result" type="text" readonly class="disabled" /> + </p> + </div></div> + + + + +</div> <!-- End of #editorcontainerbox --> + + + + +<!-----------------------------> +<!-------- JAVASCRIPT ---------> +<!-----------------------------> + +<script type="text/javascript" src="../static/skins/colibris/pad.js?v=5ba315cd"></script> + +<div style="display:none"><a href="/javascript" data-jslicense="1">JavaScript license information</a></div> +<script type="module" src="./node_modules/ep_etherpad-lite/templates/padViteBootstrap.js"></script> +</body> +</html> diff --git a/ui/src/consent.ts b/ui/src/consent.ts new file mode 100644 index 00000000000..79f6214fea7 --- /dev/null +++ b/ui/src/consent.ts @@ -0,0 +1,35 @@ +import "./style.css" +//import {MapArrayType} from "ep_etherpad-lite/node/types/MapType"; + +const form = document.querySelector('form')!; +const sessionId = new URLSearchParams(window.location.search).get('state'); + +form.action = '/interaction/' + sessionId; + +/*form.addEventListener('submit', function (event) { + event.preventDefault(); + const formData = new FormData(form); + const data: MapArrayType<any> = {}; + formData.forEach((value, key) => { + data[key] = value; + }); + const sessionId = new URLSearchParams(window.location.search).get('state'); + + fetch('/interaction/' + sessionId, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }).then(response => { + if (response.ok) { + if (response.redirected) { + window.location.href = response.url; + } + } else { + document.getElementById('error')!.innerText = "Error signing in"; + } + }).catch(error => { + document.getElementById('error')!.innerText = "Error signing in" + error; + }) +});*/ diff --git a/ui/src/main.ts b/ui/src/main.ts new file mode 100644 index 00000000000..1ff174cdb98 --- /dev/null +++ b/ui/src/main.ts @@ -0,0 +1,58 @@ +import './style.css' +import {MapArrayType} from "ep_etherpad-lite/node/types/MapType.ts"; + +const searchParams = new URLSearchParams(window.location.search); + + +document.getElementById('client')!.innerText = searchParams.get('client_id')!; + +const form = document.querySelector('form')!; +form.addEventListener('submit', function (event) { + event.preventDefault(); + const formData = new FormData(form); + const data: MapArrayType<any> = {}; + formData.forEach((value, key) => { + data[key] = value; + }); + const sessionId = new URLSearchParams(window.location.search).get('state'); + + fetch('/interaction/' + sessionId, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + redirect: 'follow', + body: JSON.stringify(data), + }).then(response => { + if (response.ok) { + if (response.redirected) { + window.location.href = response.url; + } + } else { + document.getElementById('error')!.innerText = "Error signing in"; + } + }).catch(error => { + document.getElementById('error')!.innerText = "Error signing in" + error; + }) +}); + +const hidePassword = document.querySelector('.toggle-password-visibility')! as HTMLElement +const showPassword = document.getElementById('eye-hide')! as HTMLElement +const togglePasswordVisibility = () => { + const passwordInput = document.getElementsByName('password')[0] as HTMLInputElement; + if (passwordInput.type === 'password') { + showPassword.style.display = 'block'; + hidePassword.style.display = 'none'; + passwordInput.type = 'text'; + } else { + showPassword.style.display = 'none'; + hidePassword.style.display = 'block'; + passwordInput.type = 'password'; + } +} + + +hidePassword.addEventListener('click', togglePasswordVisibility); +showPassword.addEventListener('click', togglePasswordVisibility); + + diff --git a/ui/src/style.css b/ui/src/style.css new file mode 100644 index 00000000000..2e4621d153a --- /dev/null +++ b/ui/src/style.css @@ -0,0 +1,125 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + --color-etherpad: #0f775b; +} + +body { + font-size: 16px; + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +#app { + max-width: 1280px; + margin: auto; + padding: 2rem; +} + + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} + +button:hover { + border-color: #646cff; +} + +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + +.login-box { + background-color: #f2f6f7; + padding: 40px; + border-radius: 20px; + color: #607278; +} + +body { + background: radial-gradient(100% 100% at 50% 0%, var(--color-etherpad) 0%, #003A47 100%) fixed +} + +input { + border-radius: 8px; + border: 1px solid #d1d1d1; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #f9f9f9; + transition: border-color 0.25s; +} + +.login-inner-box { + display: flex; + flex-direction: column; + gap: 10px; +} + +.login-inner-box input[type=submit] { + background-color: var(--color-etherpad); + color: white; + border: none; + cursor: pointer; + margin-top: 20px; +} + +.password-label { + position: relative; +} + +.password-label svg { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + cursor: pointer; + width: 16px; +} + +#eye-hide { + display: none; +} + +label { + display: flex; +} + +label input { + flex-grow: 1; +} diff --git a/ui/src/typescript.svg b/ui/src/typescript.svg new file mode 100644 index 00000000000..d91c910cc30 --- /dev/null +++ b/ui/src/typescript.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#007ACC" d="M0 128v128h256V0H0z"></path><path fill="#FFF" d="m56.612 128.85l-.081 10.483h33.32v94.68h23.568v-94.68h33.321v-10.28c0-5.69-.122-10.444-.284-10.566c-.122-.162-20.4-.244-44.983-.203l-44.74.122l-.121 10.443Zm149.955-10.742c6.501 1.625 11.459 4.51 16.01 9.224c2.357 2.52 5.851 7.111 6.136 8.208c.08.325-11.053 7.802-17.798 11.988c-.244.162-1.22-.894-2.317-2.52c-3.291-4.795-6.745-6.867-12.028-7.233c-7.76-.528-12.759 3.535-12.718 10.321c0 1.992.284 3.17 1.097 4.795c1.707 3.536 4.876 5.649 14.832 9.956c18.326 7.883 26.168 13.084 31.045 20.48c5.445 8.249 6.664 21.415 2.966 31.208c-4.063 10.646-14.14 17.879-28.323 20.276c-4.388.772-14.79.65-19.504-.203c-10.28-1.828-20.033-6.908-26.047-13.572c-2.357-2.6-6.949-9.387-6.664-9.874c.122-.163 1.178-.813 2.356-1.504c1.138-.65 5.446-3.129 9.509-5.485l7.355-4.267l1.544 2.276c2.154 3.29 6.867 7.801 9.712 9.305c8.167 4.307 19.383 3.698 24.909-1.26c2.357-2.153 3.332-4.388 3.332-7.68c0-2.966-.366-4.266-1.91-6.501c-1.99-2.845-6.054-5.242-17.595-10.24c-13.206-5.69-18.895-9.224-24.096-14.832c-3.007-3.25-5.852-8.452-7.03-12.8c-.975-3.617-1.22-12.678-.447-16.335c2.723-12.76 12.353-21.659 26.25-24.3c4.51-.853 14.994-.528 19.424.569Z"></path></svg> \ No newline at end of file diff --git a/ui/src/vite-env.d.ts b/ui/src/vite-env.d.ts new file mode 100644 index 00000000000..11f02fe2a00 --- /dev/null +++ b/ui/src/vite-env.d.ts @@ -0,0 +1 @@ +/// <reference types="vite/client" /> diff --git a/ui/tsconfig.json b/ui/tsconfig.json new file mode 100644 index 00000000000..75abdef2659 --- /dev/null +++ b/ui/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/ui/vite.config.ts b/ui/vite.config.ts new file mode 100644 index 00000000000..89667286b3e --- /dev/null +++ b/ui/vite.config.ts @@ -0,0 +1,47 @@ +// vite.config.js +import { resolve } from 'path' +import { defineConfig } from 'vite' + +export default defineConfig({ + base: '/views/', + build: { + commonjsOptions:{ + transformMixedEsModules: true, + }, + outDir: resolve(__dirname, '../src/static/oidc'), + rollupOptions: { + input: { + main: resolve(__dirname, 'consent.html'), + nested: resolve(__dirname, 'login.html'), + }, + }, + emptyOutDir: true, + }, + server:{ + proxy:{ + '/static':{ + target: 'http://localhost:9001', + changeOrigin: true, + secure: false, + }, + '/views/manifest.json':{ + target: 'http://localhost:9001', + changeOrigin: true, + secure: false, + rewrite: (path) => path.replace(/^\/views/, ''), + }, + '/locales.json':{ + target: 'http://localhost:9001', + changeOrigin: true, + secure: false, + rewrite: (path) => path.replace(/^\/views/, ''), + }, + '/locales':{ + target: 'http://localhost:9001', + changeOrigin: true, + secure: false, + rewrite: (path) => path.replace(/^\/views/, ''), + }, + } + } +}) diff --git a/var/js/.gitignore b/var/js/.gitignore new file mode 100644 index 00000000000..086f4e283b7 --- /dev/null +++ b/var/js/.gitignore @@ -0,0 +1,2 @@ +*.js +*.map