From 19dd0501197740fd681a73a6ebf0e4e0add6695a Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Wed, 9 Sep 2026 19:17:33 +0200 Subject: [PATCH 1/8] [BootstrapAdminUi] Add copy-to-clipboard component --- docs/SUMMARY.md | 2 + .../components/copy-to-clipboard.md | 82 +++++++++++ .../copy-to-clipboard-controller.js | 44 ++++++ .../components/copy_to_clipboard.html.twig | 44 ++++++ .../CopyToClipboardComponentTest.php | 138 ++++++++++++++++++ .../translations/messages.de.yaml | 3 + .../translations/messages.en.yaml | 3 + .../translations/messages.es.yaml | 3 + .../translations/messages.fr.yaml | 3 + .../translations/messages.pl.yaml | 3 + 10 files changed, 325 insertions(+) create mode 100644 docs/bootstrap-admin-ui/components/copy-to-clipboard.md create mode 100644 src/BootstrapAdminUi/assets/controllers/copy-to-clipboard-controller.js create mode 100644 src/BootstrapAdminUi/templates/shared/components/copy_to_clipboard.html.twig create mode 100644 src/BootstrapAdminUi/tests/Functional/CopyToClipboardComponentTest.php diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index b69a604a..84362db0 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -67,6 +67,8 @@ ## 🎨 Bootstrap Admin UI * [Getting started](bootstrap-admin-ui/getting-started.md) +* Components + * [Copy to clipboard](bootstrap-admin-ui/components/copy-to-clipboard.md) ## 🍀 Twig Extra diff --git a/docs/bootstrap-admin-ui/components/copy-to-clipboard.md b/docs/bootstrap-admin-ui/components/copy-to-clipboard.md new file mode 100644 index 00000000..333e0388 --- /dev/null +++ b/docs/bootstrap-admin-ui/components/copy-to-clipboard.md @@ -0,0 +1,82 @@ +# Copy to clipboard + +The `copy_to_clipboard` component renders a button that copies an explicit value to the clipboard. The value itself is not displayed by the component, so render it separately when it should also be visible on the page. + +## Installation + +Install Bootstrap Admin UI before using the component: + +```bash +composer require sylius/bootstrap-admin-ui +``` + +## Usage + +Pass a literal value with the `value` prop: + +```twig +ABC-123 + + +``` + +For a Twig expression, prefix the prop with `:`: + +```twig +{{ item.code }} + + +``` + +### Twig Hooks + +The component can also be rendered as a Twig Hook component. Pass the value explicitly through its props: + +```yaml +sylius_twig_hooks: + hooks: + 'app.example': + copy: + component: 'sylius_bootstrap_admin_ui:copy_to_clipboard' + props: + value: '@=_context.item.getCode()' +``` + +### Button attributes + +Additional component attributes are forwarded to the button. Default button classes are preserved when adding custom classes: + +```twig + +``` + +### Translations + +The component uses these translation keys by default: + +- `sylius.ui.copy_to_clipboard` +- `sylius.ui.copied_to_clipboard` +- `sylius.ui.unable_to_copy_to_clipboard` + +Override those keys in your application's translation files, or pass alternative translation keys with the label props: + +```twig + +``` + +## Browser requirements + +Copying uses the browser's Clipboard API. This API requires a secure context, which normally means HTTPS in production or localhost during development. diff --git a/src/BootstrapAdminUi/assets/controllers/copy-to-clipboard-controller.js b/src/BootstrapAdminUi/assets/controllers/copy-to-clipboard-controller.js new file mode 100644 index 00000000..a6eb9788 --- /dev/null +++ b/src/BootstrapAdminUi/assets/controllers/copy-to-clipboard-controller.js @@ -0,0 +1,44 @@ +import { Controller } from '@hotwired/stimulus'; + +export default class extends Controller { + static targets = ['copyIcon', 'successIcon', 'errorIcon', 'status']; + + static values = { + value: String, + copiedLabel: String, + errorLabel: String, + }; + + async copy() { + this.reset(); + + try { + await navigator.clipboard.writeText(this.valueValue); + this.showFeedback(this.successIconTarget, this.copiedLabelValue); + } catch (error) { + this.showFeedback(this.errorIconTarget, this.errorLabelValue); + } + } + + disconnect() { + clearTimeout(this.resetTimer); + } + + showFeedback(icon, message) { + this.copyIconTarget.classList.add('d-none'); + icon.classList.remove('d-none'); + this.statusTarget.textContent = message; + + this.resetTimer = setTimeout(() => this.reset(), 2000); + } + + reset() { + clearTimeout(this.resetTimer); + this.resetTimer = null; + + this.copyIconTarget.classList.remove('d-none'); + this.successIconTarget.classList.add('d-none'); + this.errorIconTarget.classList.add('d-none'); + this.statusTarget.textContent = ''; + } +} diff --git a/src/BootstrapAdminUi/templates/shared/components/copy_to_clipboard.html.twig b/src/BootstrapAdminUi/templates/shared/components/copy_to_clipboard.html.twig new file mode 100644 index 00000000..6d29d39b --- /dev/null +++ b/src/BootstrapAdminUi/templates/shared/components/copy_to_clipboard.html.twig @@ -0,0 +1,44 @@ +{% props + value, + copy_label = 'sylius.ui.copy_to_clipboard', + copied_label = 'sylius.ui.copied_to_clipboard', + error_label = 'sylius.ui.unable_to_copy_to_clipboard' +%} + + + + + + diff --git a/src/BootstrapAdminUi/tests/Functional/CopyToClipboardComponentTest.php b/src/BootstrapAdminUi/tests/Functional/CopyToClipboardComponentTest.php new file mode 100644 index 00000000..056b53dd --- /dev/null +++ b/src/BootstrapAdminUi/tests/Functional/CopyToClipboardComponentTest.php @@ -0,0 +1,138 @@ +get(Environment::class); + self::assertInstanceOf(Environment::class, $twig); + + $this->twig = $twig; + } + + public function testItRendersWithTheDefaultConfiguration(): void + { + $crawler = $this->renderComponent(<<<'TWIG' + + TWIG); + + $controller = $crawler->filter('[data-controller~="copy-to-clipboard"]'); + self::assertCount(1, $controller); + self::assertSame('customer@example.com', $controller->attr('data-copy-to-clipboard-value-value')); + self::assertSame('sylius.ui.copied_to_clipboard', $controller->attr('data-copy-to-clipboard-copied-label-value')); + self::assertSame('sylius.ui.unable_to_copy_to_clipboard', $controller->attr('data-copy-to-clipboard-error-label-value')); + + $button = $controller->filter('button[data-test-copy-to-clipboard]'); + self::assertCount(1, $button); + self::assertSame('button', $button->attr('type')); + self::assertSame('copy-to-clipboard#copy', $button->attr('data-action')); + self::assertSame('sylius.ui.copy_to_clipboard', $button->attr('aria-label')); + self::assertSame('sylius.ui.copy_to_clipboard', $button->attr('data-bs-title')); + self::assertSame('tooltip', $button->attr('data-bs-toggle')); + } + + public function testItSafelyRendersAValueContainingSpecialCharacters(): void + { + $value = 'customer+"' %} + + TWIG); + + self::assertCount(0, $crawler->filter('script')); + self::assertSame('', $crawler->filter('.d-inline-flex')->text()); + } + + public function testItRendersThroughTwigHooks(): void + { + $crawler = $this->renderComponent("{% hook 'app.copyable' with { value: 'ABC-123' } %}"); + + self::assertSame('ABC-123', $crawler->filter('.d-inline-flex')->text()); + self::assertCount(1, $crawler->filter('button')); + self::assertCount(0, $crawler->filter('[hookableMetadata]')); + } + + public function testItDisablesCopyingAnAbsentValueButPreservesCustomContent(): void + { + $crawler = $this->renderComponent(<<<'TWIG' + + Not available + + TWIG); + + self::assertSame('Not available', $crawler->filter('.d-inline-flex')->text()); + self::assertCount(1, $crawler->filter('button[disabled]')); + } + + private function renderComponent(string $template): Crawler + { + self::bootKernel(); + $twig = self::getContainer()->get(Environment::class); + self::assertInstanceOf(Environment::class, $twig); + + return new Crawler($twig->createTemplate($template)->render()); + } +} diff --git a/src/BootstrapAdminUi/tests/JavaScript/copy-to-clipboard-controller.test.mjs b/src/BootstrapAdminUi/tests/JavaScript/copy-to-clipboard-controller.test.mjs new file mode 100644 index 00000000..191b30b0 --- /dev/null +++ b/src/BootstrapAdminUi/tests/JavaScript/copy-to-clipboard-controller.test.mjs @@ -0,0 +1,211 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import CopyToClipboardController from '../../assets/controllers/copy-to-clipboard-controller.js'; + +// Test the real controller with small DOM doubles; no browser or additional dependency is required. +function element(...classes) { + const tokens = new Set(classes); + const attributes = new Map(); + + return { + disabled: false, + textContent: '', + setAttribute: (name, value) => attributes.set(name, value), + removeAttribute: name => attributes.delete(name), + getAttribute: name => attributes.get(name), + classList: { + add: token => tokens.add(token), + remove: token => tokens.delete(token), + contains: token => tokens.has(token), + toggle: (token, force) => force ? tokens.add(token) : tokens.delete(token), + }, + }; +} + +function setup(t, writeText = t.mock.fn(async () => {})) { + t.mock.timers.enable({ apis: ['setTimeout'] }); + const navigatorDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'navigator'); + Object.defineProperty(globalThis, 'navigator', { + configurable: true, + value: { clipboard: { writeText } }, + }); + t.after(() => { + if (navigatorDescriptor) { + Object.defineProperty(globalThis, 'navigator', navigatorDescriptor); + } else { + delete globalThis.navigator; + } + }); + + const controller = Object.assign(new CopyToClipboardController({}), { + buttonTarget: element(), + hasButtonTarget: true, + copyIconTarget: element(), + successIconTarget: element('d-none'), + errorIconTarget: element('d-none'), + statusTarget: element('visually-hidden'), + hasValueValue: true, + valueValue: '000123', + copiedLabelValue: 'Copied', + errorLabelValue: 'Unable to copy', + }); + controller.connect(); + t.after(() => controller.disconnect()); + + return { controller, writeText }; +} + +test('copies the explicit value and resets successful feedback after two seconds', async t => { + const { controller, writeText } = setup(t); + controller.valueValue = 'Référence 日本語\n000123'; + await controller.copy(); + + assert.deepEqual(writeText.mock.calls[0].arguments, [controller.valueValue]); + assert.equal(controller.copyIconTarget.classList.contains('d-none'), true); + assert.equal(controller.successIconTarget.classList.contains('d-none'), false); + assert.equal(controller.statusTarget.textContent, 'Copied'); + assert.equal(controller.statusTarget.classList.contains('visually-hidden'), true); + assert.equal(controller.buttonTarget.getAttribute('aria-busy'), undefined); + t.mock.timers.tick(2000); + assert.equal(controller.copyIconTarget.classList.contains('d-none'), false); + assert.equal(controller.successIconTarget.classList.contains('d-none'), true); + assert.equal(controller.statusTarget.textContent, ''); +}); + +test('makes failures visible and allows retrying', async t => { + const writeText = t.mock.fn(async () => { throw new Error('Permission denied'); }); + const { controller } = setup(t, writeText); + await controller.copy(); + + assert.equal(controller.errorIconTarget.classList.contains('d-none'), false); + assert.equal(controller.statusTarget.classList.contains('visually-hidden'), false); + assert.equal(controller.statusTarget.textContent, 'Unable to copy'); + assert.equal(controller.pendingCopy, null); + + writeText.mock.mockImplementation(async () => {}); + await controller.copy(); + assert.equal(controller.errorIconTarget.classList.contains('d-none'), true); + assert.equal(controller.successIconTarget.classList.contains('d-none'), false); + assert.equal(controller.statusTarget.classList.contains('visually-hidden'), true); +}); + +test('handles an unavailable Clipboard API', async t => { + const { controller } = setup(t); + delete navigator.clipboard; + await controller.copy(); + + assert.equal(controller.statusTarget.textContent, 'Unable to copy'); + assert.equal(controller.statusTarget.classList.contains('visually-hidden'), false); +}); + +test('ignores repeated clicks while a copy is pending without removing keyboard focus', async t => { + const deferred = Promise.withResolvers(); + const writeText = t.mock.fn(() => deferred.promise); + const { controller } = setup(t, writeText); + const pending = controller.copy(); + await controller.copy(); + + assert.equal(writeText.mock.callCount(), 1); + assert.equal(controller.buttonTarget.getAttribute('aria-busy'), 'true'); + assert.equal(controller.buttonTarget.disabled, false); + deferred.resolve(); + await pending; + assert.equal(controller.statusTarget.textContent, 'Copied'); + assert.equal(controller.buttonTarget.getAttribute('aria-busy'), undefined); +}); + +for (const outcome of ['resolve', 'reject']) { + test(`ignores a late ${outcome} after disconnect and reconnect`, async t => { + const stale = Promise.withResolvers(); + const current = Promise.withResolvers(); + const writeText = t.mock.fn(() => stale.promise); + const { controller } = setup(t, writeText); + const staleCopy = controller.copy(); + controller.disconnect(); + controller.connect(); + writeText.mock.mockImplementation(() => current.promise); + const currentCopy = controller.copy(); + stale[outcome](); + await staleCopy; + + assert.equal(controller.statusTarget.textContent, ''); + assert.equal(controller.buttonTarget.getAttribute('aria-busy'), 'true'); + t.mock.timers.tick(2000); + assert.equal(controller.statusTarget.textContent, ''); + current.resolve(); + await currentCopy; + assert.equal(controller.statusTarget.textContent, 'Copied'); + }); +} + +test('does not access removed targets or schedule feedback after disconnect', async t => { + const deferred = Promise.withResolvers(); + const { controller } = setup(t, () => deferred.promise); + const pending = controller.copy(); + controller.hasButtonTarget = false; + controller.disconnect(); + delete controller.buttonTarget; + delete controller.successIconTarget; + delete controller.errorIconTarget; + delete controller.statusTarget; + deferred.resolve(); + await pending; + t.mock.timers.tick(2000); +}); + +test('clears the feedback timer on disconnect and resets on reconnect', async t => { + const { controller } = setup(t); + await controller.copy(); + const reset = t.mock.method(controller, 'reset'); + controller.disconnect(); + t.mock.timers.tick(2000); + assert.equal(reset.mock.callCount(), 0); + controller.connect(); + assert.equal(controller.statusTarget.textContent, ''); + assert.equal(controller.successIconTarget.classList.contains('d-none'), true); +}); + +test('does not let an older timer clear newer feedback', async t => { + const { controller } = setup(t); + await controller.copy(); + t.mock.timers.tick(1500); + await controller.copy(); + t.mock.timers.tick(500); + assert.equal(controller.statusTarget.textContent, 'Copied'); + t.mock.timers.tick(1500); + assert.equal(controller.statusTarget.textContent, ''); +}); + +for (const state of [{ valueValue: '' }, { hasValueValue: false }, { disabled: true }]) { + test(`does not copy an unavailable value or disabled button: ${JSON.stringify(state)}`, async t => { + const { controller, writeText } = setup(t); + if (state.disabled) { + controller.buttonTarget.disabled = true; + } else { + Object.assign(controller, state); + } + await controller.copy(); + assert.equal(writeText.mock.callCount(), 0); + assert.equal(controller.statusTarget.textContent, ''); + }); +} + +test('copies zero and keeps multiple instances independent', async t => { + const { controller: first, writeText } = setup(t); + const second = Object.assign(new CopyToClipboardController({}), { + ...first, + buttonTarget: element(), + copyIconTarget: element(), + successIconTarget: element('d-none'), + errorIconTarget: element('d-none'), + statusTarget: element('visually-hidden'), + valueValue: '0', + }); + second.connect(); + t.after(() => second.disconnect()); + await second.copy(); + + assert.deepEqual(writeText.mock.calls[0].arguments, ['0']); + assert.equal(second.statusTarget.textContent, 'Copied'); + assert.equal(first.statusTarget.textContent, ''); +}); From b64c81e3ac5514e6a8a2cbf5d879ab6a8cfaee5e Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Thu, 10 Sep 2026 08:47:12 +0200 Subject: [PATCH 3/8] [CI] Run Bootstrap Admin UI controller tests on Node 24 --- .github/workflows/ci.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6b586415..a645ae31 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -128,6 +128,19 @@ jobs: - name: Run PHPUnit (Bootstrap Admin Ui) run: (cd src/BootstrapAdminUi/ && vendor/bin/phpunit) + - name: Setup Node.js for Bootstrap Admin UI tests + if: matrix.php == '8.3' && matrix.symfony == '^7.4' + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Run JavaScript tests (Bootstrap Admin UI) + if: matrix.php == '8.3' && matrix.symfony == '^7.4' + working-directory: src/BootstrapAdminUi + run: | + yarn install --frozen-lockfile --non-interactive + yarn test + - name: "Restrict packages' versions (Twig Hooks)" run: | (cd src/TwigHooks/ && composer config --no-plugins allow-plugins.symfony/runtime true) From 975b72b72b24a2565978044b3d626ac4a5058768 Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Thu, 10 Sep 2026 08:47:13 +0200 Subject: [PATCH 4/8] [Docs] Document copyable content and Stimulus integration --- .../components/copy-to-clipboard.md | 111 +++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/docs/bootstrap-admin-ui/components/copy-to-clipboard.md b/docs/bootstrap-admin-ui/components/copy-to-clipboard.md index 333e0388..a840d939 100644 --- a/docs/bootstrap-admin-ui/components/copy-to-clipboard.md +++ b/docs/bootstrap-admin-ui/components/copy-to-clipboard.md @@ -1,6 +1,6 @@ # Copy to clipboard -The `copy_to_clipboard` component renders a button that copies an explicit value to the clipboard. The value itself is not displayed by the component, so render it separately when it should also be visible on the page. +Use `copy_to_clipboard` for a standalone copy button, or `copyable` to display a value or custom inline content next to the button. Both copy the explicit `value` prop, never the rendered content. ## Installation @@ -10,6 +10,10 @@ Install Bootstrap Admin UI before using the component: composer require sylius/bootstrap-admin-ui ``` +The components use Symfony UX Twig Component, Stimulus, UX Icons and the Bootstrap Admin UI/Tabler styles. Bootstrap CSS alone does not provide all the default button styles. + +With the standard Bootstrap Admin UI layout and compiled assets, the controller is registered by the package's `symfony_ux` entrypoint. If your application starts its own Stimulus application, follow [JavaScript integration](#javascript-integration) below instead. + ## Usage Pass a literal value with the `value` prop: @@ -32,9 +36,33 @@ For a Twig expression, prefix the prop with `:`: /> ``` +### Display a value with its copy button + +```twig + +``` + +For a badge, a formatted ID, a truncated label or an email link, provide custom content: + +```twig + + {{ customer.email }} + +``` + +The body overrides the `content` block. The copy button remains a sibling of the content, not a child of the link. Only `customer.email` is copied, independently of the displayed text. Use inline content and do not place either component inside another button or link. + +The default displayed value is escaped and is not automatically translated. Translate or format the display in the content block when needed; explicitly pass a translated `value` if that is also what should be copied. + +### Missing values and disabling copying + +Omitting `value`, passing `null`, or passing an empty string disables the button. Custom content remains visible. Both numeric `0` and string `"0"` are copyable. Pass IDs with significant leading zeros as strings. + +Both components also accept `disabled` to explicitly disable the button. They do not read from inputs or watch your application data: update the component's props when re-rendering dynamic values. + ### Twig Hooks -The component can also be rendered as a Twig Hook component. Pass the value explicitly through its props: +Both components can be rendered directly as Twig Hook components. They consume the injected hook metadata without rendering it as an HTML attribute. Pass the value explicitly through the props: ```yaml sylius_twig_hooks: @@ -46,6 +74,8 @@ sylius_twig_hooks: value: '@=_context.item.getCode()' ``` +Use `sylius_bootstrap_admin_ui:copyable` instead to include the default text display. For custom markup, use a hook template containing the component and its body. + ### Button attributes Additional component attributes are forwarded to the button. Default button classes are preserved when adding custom classes: @@ -58,6 +88,19 @@ Additional component attributes are forwarded to the button. Default button clas /> ``` +For `copyable`, ordinary attributes apply to the outer container. Prefix attributes with `button:` to apply them to the copy button: + +```twig + +``` + +Both components accept `button_class` to **replace** the default classes, while `class` on the standalone button (or `button:class` on `copyable`) adds classes. For example, use `button_class="btn btn-sm btn-outline-secondary"` for standard Bootstrap button styling without Tabler's ghost-button classes. + ### Translations The component uses these translation keys by default: @@ -77,6 +120,70 @@ Override those keys in your application's translation files, or pass alternative /> ``` +### Feedback and dynamic content + +The button uses a native `title`, not a Bootstrap tooltip, so no tooltip initialization is needed when content is inserted dynamically. The controller connects through Stimulus as usual. + +During a copy, repeated clicks are ignored and the button exposes `aria-busy` without disabling it or taking away keyboard focus. Success changes the icon and announces the translated message through a live region. Failure also displays the translated error text. Feedback resets after two seconds; a new copy replaces previous feedback. Disconnecting cancels pending UI updates and timers, not the browser's clipboard operation itself. + +## JavaScript integration + +Register the controller **once in the application's existing Stimulus application**. Do not start a second application just for this component. Both Twig components use the identifier `copy-to-clipboard`. + +### AssetMapper + +Follow the [AssetMapper setup](../../getting-started.md#using-assetmapper) to disable the package's `symfony_ux` entrypoint when starting your own Stimulus application. Expose the controller source directory to AssetMapper: + +```yaml +# config/packages/asset_mapper.yaml +framework: + asset_mapper: + paths: + '%kernel.project_dir%/vendor/sylius/bootstrap-admin-ui/assets/controllers': 'sylius/bootstrap-admin-ui/controllers' +``` + +Add a local importmap entry (this does not install another JavaScript package): + +```bash +php bin/console importmap:require '@sylius/bootstrap-admin-ui/copy-to-clipboard' --path=./vendor/sylius/bootstrap-admin-ui/assets/controllers/copy-to-clipboard-controller.js +``` + +In the module where your application already starts Stimulus: + +```js +import { startStimulusApp } from '@symfony/stimulus-bundle'; +import CopyToClipboardController from '@sylius/bootstrap-admin-ui/copy-to-clipboard'; + +const app = startStimulusApp(); +app.register('copy-to-clipboard', CopyToClipboardController); +``` + +If `app` is already exported by another module, import and reuse it instead of calling `startStimulusApp()` again. The application's importmap must also provide `@hotwired/stimulus`, as in a normal Symfony Stimulus installation. + +### Webpack Encore / an existing Sylius Stimulus application + +Import the controller source and register it on your existing application. From a project-level `assets/bootstrap.js`, the import is: + +```js +import CopyToClipboardController from '../vendor/sylius/bootstrap-admin-ui/assets/controllers/copy-to-clipboard-controller.js'; + +app.register('copy-to-clipboard', CopyToClipboardController); +``` + +Here `app` is your existing Stimulus application. Adjust the relative import path to your entrypoint location and rebuild the application's assets. Do not import the package's entire `symfony_ux` entrypoint into an application that already starts Stimulus. + +### Sylius compatibility + +Match the Bootstrap Admin UI package version to the target Sylius version before installing it. This branch requires `sylius/twig-hooks ^0.12`, whereas the Sylius 2.1 and 2.2 AdminBundle branches require `^0.8` and `^0.9` respectively. These constraints do not overlap; controller registration alone does not solve that Composer incompatibility. + +Reusing this functionality in those versions requires a compatible backport or dependency alignment. It does not require replacing the application's admin layout. When integrating the templates selectively, also configure the anonymous component prefix, UX Icons and the translations rather than importing the whole Bootstrap Admin UI layout configuration. + ## Browser requirements Copying uses the browser's Clipboard API. This API requires a secure context, which normally means HTTPS in production or localhost during development. + +Clipboard permissions and embedding policies can still prevent copying. The components report failure rather than falling back to deprecated clipboard APIs. Values passed to the component are present in the page's HTML; do not pass secrets or data the current user is not authorized to see. + +## Tests + +From `src/BootstrapAdminUi`, run `vendor/bin/phpunit` for the rendering and Twig Hooks tests, and `yarn test` for the controller unit tests using Node.js 24 and the existing JavaScript dependencies. The latter use DOM doubles; actual browser permissions and assistive-technology behavior still require browser-level testing. From 277a43efa149623b7a4e26f6eb05e3124ba90fbc Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Wed, 23 Sep 2026 12:56:39 +0200 Subject: [PATCH 5/8] Add CSS hover fix to avoid bg miss placement --- src/BootstrapAdminUi/assets/styles/_buttons.scss | 5 +++++ src/BootstrapAdminUi/public/app.css | 2 +- src/BootstrapAdminUi/public/app.rtl.css | 2 +- src/BootstrapAdminUi/public/symfony_ux.js | 2 +- src/BootstrapAdminUi/public/symfony_ux.js.LICENSE.txt | 2 ++ 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/BootstrapAdminUi/assets/styles/_buttons.scss b/src/BootstrapAdminUi/assets/styles/_buttons.scss index ef66bfdf..0363a9b3 100644 --- a/src/BootstrapAdminUi/assets/styles/_buttons.scss +++ b/src/BootstrapAdminUi/assets/styles/_buttons.scss @@ -2,3 +2,8 @@ min-height: 44px; min-width: 44px; } + +.copy-to-clipboard-button { + // Tabler's minimum button dimensions require a length, not the unitless btn-sm line-height. + --tblr-btn-line-height: var(--tblr-btn-icon-size); +} diff --git a/src/BootstrapAdminUi/public/app.css b/src/BootstrapAdminUi/public/app.css index e192fde9..c47b628f 100644 --- a/src/BootstrapAdminUi/public/app.css +++ b/src/BootstrapAdminUi/public/app.css @@ -20,7 +20,7 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - */*{--tblr-breadcrumb-item-active-color:var(--tblr-gray-500);--tblr-breadcrumb-divider-color:var(--tblr-gray-300);--tblr-blue-rgb:17,81,141;--tblr-green-rgb:0,97,16;--tblr-pagination-border-width:0}[data-bs-theme=light]{--tblr-body-color:#212529;--tblr-code-color:#36393b;--tblr-breadcrumb-link-color:#212529}[data-bs-theme=light] body{--bs-body-bg:#f9fafb;--bs-tertiary-bg:#f9fafb;--bs-body-color:#212529}[data-bs-theme=light] [data-theme-switch=light]{display:none}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-body-bg:#0f1623;--tblr-bg-surface:#1e2433}[data-bs-theme=dark] .btn,body[data-bs-theme=dark] [data-bs-theme=light] .btn{--tblr-btn-border-color:var(--tblr-border-color)}[data-bs-theme=dark] .form-check-input,[data-bs-theme=dark] .form-control,[data-bs-theme=dark] .form-select,body[data-bs-theme=dark] [data-bs-theme=light] .form-check-input,body[data-bs-theme=dark] [data-bs-theme=light] .form-control,body[data-bs-theme=dark] [data-bs-theme=light] .form-select{border-color:var(--tblr-border-color)}[data-bs-theme=dark] .active>.page-link,[data-bs-theme=dark] .page-link.active{background-color:var(--tblr-bg-surface);border-color:var(--tblr-border-color)}[data-bs-theme=dark] [data-theme-switch=dark],body[data-bs-theme=dark] [data-bs-theme=light] [data-theme-switch=dark]{display:none}@font-face{font-family:Inter;font-weight:1 999;src:url(fonts/Inter-VariableFont_slnt,wght.853e0197.ttf) format("truetype-variations")}body{font-feature-settings:"cv03","cv04","cv11"}a{text-underline-offset:.25em}a.link-reset{text-decoration:none}.btn-collapse.collapsed .icon-chevron-right{display:inline-flex}.btn-collapse.collapsed .icon-chevron-down,.btn-collapse:not(.collapsed) .icon-chevron-right{display:none}.btn-collapse:not(.collapsed) .icon-chevron-down{display:inline-flex}.breadcrumb-item a{text-decoration:none}.breadcrumb-item a:hover{color:#22b99a}.switch-collapse{display:none}label:has(input:checked)~.switch-collapse{display:block}.btn:not(.btn-sm){min-height:44px;min-width:44px}/*! + */*{--tblr-breadcrumb-item-active-color:var(--tblr-gray-500);--tblr-breadcrumb-divider-color:var(--tblr-gray-300);--tblr-blue-rgb:17,81,141;--tblr-green-rgb:0,97,16;--tblr-pagination-border-width:0}[data-bs-theme=light]{--tblr-body-color:#212529;--tblr-code-color:#36393b;--tblr-breadcrumb-link-color:#212529}[data-bs-theme=light] body{--bs-body-bg:#f9fafb;--bs-tertiary-bg:#f9fafb;--bs-body-color:#212529}[data-bs-theme=light] [data-theme-switch=light]{display:none}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-body-bg:#0f1623;--tblr-bg-surface:#1e2433}[data-bs-theme=dark] .btn,body[data-bs-theme=dark] [data-bs-theme=light] .btn{--tblr-btn-border-color:var(--tblr-border-color)}[data-bs-theme=dark] .form-check-input,[data-bs-theme=dark] .form-control,[data-bs-theme=dark] .form-select,body[data-bs-theme=dark] [data-bs-theme=light] .form-check-input,body[data-bs-theme=dark] [data-bs-theme=light] .form-control,body[data-bs-theme=dark] [data-bs-theme=light] .form-select{border-color:var(--tblr-border-color)}[data-bs-theme=dark] .active>.page-link,[data-bs-theme=dark] .page-link.active{background-color:var(--tblr-bg-surface);border-color:var(--tblr-border-color)}[data-bs-theme=dark] [data-theme-switch=dark],body[data-bs-theme=dark] [data-bs-theme=light] [data-theme-switch=dark]{display:none}@font-face{font-family:Inter;font-weight:1 999;src:url(fonts/Inter-VariableFont_slnt,wght.853e0197.ttf) format("truetype-variations")}body{font-feature-settings:"cv03","cv04","cv11"}a{text-underline-offset:.25em}a.link-reset{text-decoration:none}.btn-collapse.collapsed .icon-chevron-right{display:inline-flex}.btn-collapse.collapsed .icon-chevron-down,.btn-collapse:not(.collapsed) .icon-chevron-right{display:none}.btn-collapse:not(.collapsed) .icon-chevron-down{display:inline-flex}.breadcrumb-item a{text-decoration:none}.breadcrumb-item a:hover{color:#22b99a}.switch-collapse{display:none}label:has(input:checked)~.switch-collapse{display:block}.btn:not(.btn-sm){min-height:44px;min-width:44px}.copy-to-clipboard-button{--tblr-btn-line-height:var(--tblr-btn-icon-size)}/*! * This file is part of the Sylius package. * * (c) Sylius Sp. z o.o. diff --git a/src/BootstrapAdminUi/public/app.rtl.css b/src/BootstrapAdminUi/public/app.rtl.css index 0868c220..afefe25f 100644 --- a/src/BootstrapAdminUi/public/app.rtl.css +++ b/src/BootstrapAdminUi/public/app.rtl.css @@ -20,7 +20,7 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. - */*{--tblr-breadcrumb-item-active-color:var(--tblr-gray-500);--tblr-breadcrumb-divider-color:var(--tblr-gray-300);--tblr-blue-rgb:17,81,141;--tblr-green-rgb:0,97,16;--tblr-pagination-border-width:0}[data-bs-theme=light]{--tblr-body-color:#212529;--tblr-code-color:#36393b;--tblr-breadcrumb-link-color:#212529}[data-bs-theme=light] body{--bs-body-bg:#f9fafb;--bs-tertiary-bg:#f9fafb;--bs-body-color:#212529}[data-bs-theme=light] [data-theme-switch=light]{display:none}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-body-bg:#0f1623;--tblr-bg-surface:#1e2433}[data-bs-theme=dark] .btn,body[data-bs-theme=dark] [data-bs-theme=light] .btn{--tblr-btn-border-color:var(--tblr-border-color)}[data-bs-theme=dark] .form-check-input,[data-bs-theme=dark] .form-control,[data-bs-theme=dark] .form-select,body[data-bs-theme=dark] [data-bs-theme=light] .form-check-input,body[data-bs-theme=dark] [data-bs-theme=light] .form-control,body[data-bs-theme=dark] [data-bs-theme=light] .form-select{border-color:var(--tblr-border-color)}[data-bs-theme=dark] .active>.page-link,[data-bs-theme=dark] .page-link.active{background-color:var(--tblr-bg-surface);border-color:var(--tblr-border-color)}[data-bs-theme=dark] [data-theme-switch=dark],body[data-bs-theme=dark] [data-bs-theme=light] [data-theme-switch=dark]{display:none}@font-face{font-family:Inter;font-weight:1 999;src:url(fonts/Inter-VariableFont_slnt,wght.853e0197.ttf) format("truetype-variations")}body{font-feature-settings:"cv03","cv04","cv11"}a{text-underline-offset:.25em}a.link-reset{text-decoration:none}.btn-collapse.collapsed .icon-chevron-right{display:inline-flex}.btn-collapse.collapsed .icon-chevron-down,.btn-collapse:not(.collapsed) .icon-chevron-right{display:none}.btn-collapse:not(.collapsed) .icon-chevron-down{display:inline-flex}.breadcrumb-item a{text-decoration:none}.breadcrumb-item a:hover{color:#22b99a}.switch-collapse{display:none}label:has(input:checked)~.switch-collapse{display:block}.btn:not(.btn-sm){min-height:44px;min-width:44px}/*! + */*{--tblr-breadcrumb-item-active-color:var(--tblr-gray-500);--tblr-breadcrumb-divider-color:var(--tblr-gray-300);--tblr-blue-rgb:17,81,141;--tblr-green-rgb:0,97,16;--tblr-pagination-border-width:0}[data-bs-theme=light]{--tblr-body-color:#212529;--tblr-code-color:#36393b;--tblr-breadcrumb-link-color:#212529}[data-bs-theme=light] body{--bs-body-bg:#f9fafb;--bs-tertiary-bg:#f9fafb;--bs-body-color:#212529}[data-bs-theme=light] [data-theme-switch=light]{display:none}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-body-bg:#0f1623;--tblr-bg-surface:#1e2433}[data-bs-theme=dark] .btn,body[data-bs-theme=dark] [data-bs-theme=light] .btn{--tblr-btn-border-color:var(--tblr-border-color)}[data-bs-theme=dark] .form-check-input,[data-bs-theme=dark] .form-control,[data-bs-theme=dark] .form-select,body[data-bs-theme=dark] [data-bs-theme=light] .form-check-input,body[data-bs-theme=dark] [data-bs-theme=light] .form-control,body[data-bs-theme=dark] [data-bs-theme=light] .form-select{border-color:var(--tblr-border-color)}[data-bs-theme=dark] .active>.page-link,[data-bs-theme=dark] .page-link.active{background-color:var(--tblr-bg-surface);border-color:var(--tblr-border-color)}[data-bs-theme=dark] [data-theme-switch=dark],body[data-bs-theme=dark] [data-bs-theme=light] [data-theme-switch=dark]{display:none}@font-face{font-family:Inter;font-weight:1 999;src:url(fonts/Inter-VariableFont_slnt,wght.853e0197.ttf) format("truetype-variations")}body{font-feature-settings:"cv03","cv04","cv11"}a{text-underline-offset:.25em}a.link-reset{text-decoration:none}.btn-collapse.collapsed .icon-chevron-right{display:inline-flex}.btn-collapse.collapsed .icon-chevron-down,.btn-collapse:not(.collapsed) .icon-chevron-right{display:none}.btn-collapse:not(.collapsed) .icon-chevron-down{display:inline-flex}.breadcrumb-item a{text-decoration:none}.breadcrumb-item a:hover{color:#22b99a}.switch-collapse{display:none}label:has(input:checked)~.switch-collapse{display:block}.btn:not(.btn-sm){min-height:44px;min-width:44px}.copy-to-clipboard-button{--tblr-btn-line-height:var(--tblr-btn-icon-size)}/*! * This file is part of the Sylius package. * * (c) Sylius Sp. z o.o. diff --git a/src/BootstrapAdminUi/public/symfony_ux.js b/src/BootstrapAdminUi/public/symfony_ux.js index f5821d96..00c6fca0 100644 --- a/src/BootstrapAdminUi/public/symfony_ux.js +++ b/src/BootstrapAdminUi/public/symfony_ux.js @@ -1,2 +1,2 @@ /*! For license information please see symfony_ux.js.LICENSE.txt */ -(()=>{var e={490:(e,t,n)=>{var s={"./theme-switcher-controller.js":431};function i(e){var t=r(e);return n(t)}function r(e){if(!n.o(s,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return s[e]}i.keys=function(){return Object.keys(s)},i.resolve=r,e.exports=i,i.id=490},891:(e,t,n)=>{"use strict";n.d(t,{lg:()=>Z,xI:()=>ce});class s{constructor(e,t,n){this.eventTarget=e,this.eventName=t,this.eventOptions=n,this.unorderedBindings=new Set}connect(){this.eventTarget.addEventListener(this.eventName,this,this.eventOptions)}disconnect(){this.eventTarget.removeEventListener(this.eventName,this,this.eventOptions)}bindingConnected(e){this.unorderedBindings.add(e)}bindingDisconnected(e){this.unorderedBindings.delete(e)}handleEvent(e){const t=function(e){if("immediatePropagationStopped"in e)return e;{const{stopImmediatePropagation:t}=e;return Object.assign(e,{immediatePropagationStopped:!1,stopImmediatePropagation(){this.immediatePropagationStopped=!0,t.call(this)}})}}(e);for(const e of this.bindings){if(t.immediatePropagationStopped)break;e.handleEvent(t)}}hasBindings(){return this.unorderedBindings.size>0}get bindings(){return Array.from(this.unorderedBindings).sort(((e,t)=>{const n=e.index,s=t.index;return ns?1:0}))}}class i{constructor(e){this.application=e,this.eventListenerMaps=new Map,this.started=!1}start(){this.started||(this.started=!0,this.eventListeners.forEach((e=>e.connect())))}stop(){this.started&&(this.started=!1,this.eventListeners.forEach((e=>e.disconnect())))}get eventListeners(){return Array.from(this.eventListenerMaps.values()).reduce(((e,t)=>e.concat(Array.from(t.values()))),[])}bindingConnected(e){this.fetchEventListenerForBinding(e).bindingConnected(e)}bindingDisconnected(e,t=!1){this.fetchEventListenerForBinding(e).bindingDisconnected(e),t&&this.clearEventListenersForBinding(e)}handleError(e,t,n={}){this.application.handleError(e,`Error ${t}`,n)}clearEventListenersForBinding(e){const t=this.fetchEventListenerForBinding(e);t.hasBindings()||(t.disconnect(),this.removeMappedEventListenerFor(e))}removeMappedEventListenerFor(e){const{eventTarget:t,eventName:n,eventOptions:s}=e,i=this.fetchEventListenerMapForEventTarget(t),r=this.cacheKey(n,s);i.delete(r),0==i.size&&this.eventListenerMaps.delete(t)}fetchEventListenerForBinding(e){const{eventTarget:t,eventName:n,eventOptions:s}=e;return this.fetchEventListener(t,n,s)}fetchEventListener(e,t,n){const s=this.fetchEventListenerMapForEventTarget(e),i=this.cacheKey(t,n);let r=s.get(i);return r||(r=this.createEventListener(e,t,n),s.set(i,r)),r}createEventListener(e,t,n){const i=new s(e,t,n);return this.started&&i.connect(),i}fetchEventListenerMapForEventTarget(e){let t=this.eventListenerMaps.get(e);return t||(t=new Map,this.eventListenerMaps.set(e,t)),t}cacheKey(e,t){const n=[e];return Object.keys(t).sort().forEach((e=>{n.push(`${t[e]?"":"!"}${e}`)})),n.join(":")}}const r={stop:({event:e,value:t})=>(t&&e.stopPropagation(),!0),prevent:({event:e,value:t})=>(t&&e.preventDefault(),!0),self:({event:e,value:t,element:n})=>!t||n===e.target},o=/^(?:(?:([^.]+?)\+)?(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;function a(e){return"window"==e?window:"document"==e?document:void 0}function l(e){return e.replace(/(?:[_-])([a-z0-9])/g,((e,t)=>t.toUpperCase()))}function c(e){return l(e.replace(/--/g,"-").replace(/__/g,"_"))}function d(e){return e.charAt(0).toUpperCase()+e.slice(1)}function u(e){return e.replace(/([A-Z])/g,((e,t)=>`-${t.toLowerCase()}`))}function h(e){return null!=e}function p(e,t){return Object.prototype.hasOwnProperty.call(e,t)}const m=["meta","ctrl","alt","shift"];class g{constructor(e,t,n,s){this.element=e,this.index=t,this.eventTarget=n.eventTarget||e,this.eventName=n.eventName||function(e){const t=e.tagName.toLowerCase();if(t in f)return f[t](e)}(e)||v("missing event name"),this.eventOptions=n.eventOptions||{},this.identifier=n.identifier||v("missing identifier"),this.methodName=n.methodName||v("missing method name"),this.keyFilter=n.keyFilter||"",this.schema=s}static forToken(e,t){return new this(e.element,e.index,function(e){const t=e.trim().match(o)||[];let n=t[2],s=t[3];return s&&!["keydown","keyup","keypress"].includes(n)&&(n+=`.${s}`,s=""),{eventTarget:a(t[4]),eventName:n,eventOptions:t[7]?(i=t[7],i.split(":").reduce(((e,t)=>Object.assign(e,{[t.replace(/^!/,"")]:!/^!/.test(t)})),{})):{},identifier:t[5],methodName:t[6],keyFilter:t[1]||s};var i}(e.content),t)}toString(){const e=this.keyFilter?`.${this.keyFilter}`:"",t=this.eventTargetName?`@${this.eventTargetName}`:"";return`${this.eventName}${e}${t}->${this.identifier}#${this.methodName}`}shouldIgnoreKeyboardEvent(e){if(!this.keyFilter)return!1;const t=this.keyFilter.split("+");if(this.keyFilterDissatisfied(e,t))return!0;const n=t.filter((e=>!m.includes(e)))[0];return!!n&&(p(this.keyMappings,n)||v(`contains unknown key filter: ${this.keyFilter}`),this.keyMappings[n].toLowerCase()!==e.key.toLowerCase())}shouldIgnoreMouseEvent(e){if(!this.keyFilter)return!1;const t=[this.keyFilter];return!!this.keyFilterDissatisfied(e,t)}get params(){const e={},t=new RegExp(`^data-${this.identifier}-(.+)-param$`,"i");for(const{name:n,value:s}of Array.from(this.element.attributes)){const i=n.match(t),r=i&&i[1];r&&(e[l(r)]=b(s))}return e}get eventTargetName(){return(e=this.eventTarget)==window?"window":e==document?"document":void 0;var e}get keyMappings(){return this.schema.keyMappings}keyFilterDissatisfied(e,t){const[n,s,i,r]=m.map((e=>t.includes(e)));return e.metaKey!==n||e.ctrlKey!==s||e.altKey!==i||e.shiftKey!==r}}const f={a:()=>"click",button:()=>"click",form:()=>"submit",details:()=>"toggle",input:e=>"submit"==e.getAttribute("type")?"click":"input",select:()=>"change",textarea:()=>"input"};function v(e){throw new Error(e)}function b(e){try{return JSON.parse(e)}catch(t){return e}}class y{constructor(e,t){this.context=e,this.action=t}get index(){return this.action.index}get eventTarget(){return this.action.eventTarget}get eventOptions(){return this.action.eventOptions}get identifier(){return this.context.identifier}handleEvent(e){const t=this.prepareActionEvent(e);this.willBeInvokedByEvent(e)&&this.applyEventModifiers(t)&&this.invokeWithEvent(t)}get eventName(){return this.action.eventName}get method(){const e=this.controller[this.methodName];if("function"==typeof e)return e;throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`)}applyEventModifiers(e){const{element:t}=this.action,{actionDescriptorFilters:n}=this.context.application,{controller:s}=this.context;let i=!0;for(const[r,o]of Object.entries(this.eventOptions))if(r in n){const a=n[r];i=i&&a({name:r,value:o,event:e,element:t,controller:s})}return i}prepareActionEvent(e){return Object.assign(e,{params:this.action.params})}invokeWithEvent(e){const{target:t,currentTarget:n}=e;try{this.method.call(this.controller,e),this.context.logDebugActivity(this.methodName,{event:e,target:t,currentTarget:n,action:this.methodName})}catch(t){const{identifier:n,controller:s,element:i,index:r}=this,o={identifier:n,controller:s,element:i,index:r,event:e};this.context.handleError(t,`invoking action "${this.action}"`,o)}}willBeInvokedByEvent(e){const t=e.target;return!(e instanceof KeyboardEvent&&this.action.shouldIgnoreKeyboardEvent(e))&&(!(e instanceof MouseEvent&&this.action.shouldIgnoreMouseEvent(e))&&(this.element===t||(t instanceof Element&&this.element.contains(t)?this.scope.containsElement(t):this.scope.containsElement(this.action.element))))}get controller(){return this.context.controller}get methodName(){return this.action.methodName}get element(){return this.scope.element}get scope(){return this.context.scope}}class O{constructor(e,t){this.mutationObserverInit={attributes:!0,childList:!0,subtree:!0},this.element=e,this.started=!1,this.delegate=t,this.elements=new Set,this.mutationObserver=new MutationObserver((e=>this.processMutations(e)))}start(){this.started||(this.started=!0,this.mutationObserver.observe(this.element,this.mutationObserverInit),this.refresh())}pause(e){this.started&&(this.mutationObserver.disconnect(),this.started=!1),e(),this.started||(this.mutationObserver.observe(this.element,this.mutationObserverInit),this.started=!0)}stop(){this.started&&(this.mutationObserver.takeRecords(),this.mutationObserver.disconnect(),this.started=!1)}refresh(){if(this.started){const e=new Set(this.matchElementsInTree());for(const t of Array.from(this.elements))e.has(t)||this.removeElement(t);for(const t of Array.from(e))this.addElement(t)}}processMutations(e){if(this.started)for(const t of e)this.processMutation(t)}processMutation(e){"attributes"==e.type?this.processAttributeChange(e.target,e.attributeName):"childList"==e.type&&(this.processRemovedNodes(e.removedNodes),this.processAddedNodes(e.addedNodes))}processAttributeChange(e,t){this.elements.has(e)?this.delegate.elementAttributeChanged&&this.matchElement(e)?this.delegate.elementAttributeChanged(e,t):this.removeElement(e):this.matchElement(e)&&this.addElement(e)}processRemovedNodes(e){for(const t of Array.from(e)){const e=this.elementFromNode(t);e&&this.processTree(e,this.removeElement)}}processAddedNodes(e){for(const t of Array.from(e)){const e=this.elementFromNode(t);e&&this.elementIsActive(e)&&this.processTree(e,this.addElement)}}matchElement(e){return this.delegate.matchElement(e)}matchElementsInTree(e=this.element){return this.delegate.matchElementsInTree(e)}processTree(e,t){for(const n of this.matchElementsInTree(e))t.call(this,n)}elementFromNode(e){if(e.nodeType==Node.ELEMENT_NODE)return e}elementIsActive(e){return e.isConnected==this.element.isConnected&&this.element.contains(e)}addElement(e){this.elements.has(e)||this.elementIsActive(e)&&(this.elements.add(e),this.delegate.elementMatched&&this.delegate.elementMatched(e))}removeElement(e){this.elements.has(e)&&(this.elements.delete(e),this.delegate.elementUnmatched&&this.delegate.elementUnmatched(e))}}class E{constructor(e,t,n){this.attributeName=t,this.delegate=n,this.elementObserver=new O(e,this)}get element(){return this.elementObserver.element}get selector(){return`[${this.attributeName}]`}start(){this.elementObserver.start()}pause(e){this.elementObserver.pause(e)}stop(){this.elementObserver.stop()}refresh(){this.elementObserver.refresh()}get started(){return this.elementObserver.started}matchElement(e){return e.hasAttribute(this.attributeName)}matchElementsInTree(e){const t=this.matchElement(e)?[e]:[],n=Array.from(e.querySelectorAll(this.selector));return t.concat(n)}elementMatched(e){this.delegate.elementMatchedAttribute&&this.delegate.elementMatchedAttribute(e,this.attributeName)}elementUnmatched(e){this.delegate.elementUnmatchedAttribute&&this.delegate.elementUnmatchedAttribute(e,this.attributeName)}elementAttributeChanged(e,t){this.delegate.elementAttributeValueChanged&&this.attributeName==t&&this.delegate.elementAttributeValueChanged(e,t)}}function w(e,t,n){k(e,t).add(n)}function A(e,t,n){k(e,t).delete(n),function(e,t){const n=e.get(t);null!=n&&0==n.size&&e.delete(t)}(e,t)}function k(e,t){let n=e.get(t);return n||(n=new Set,e.set(t,n)),n}class S{constructor(){this.valuesByKey=new Map}get keys(){return Array.from(this.valuesByKey.keys())}get values(){return Array.from(this.valuesByKey.values()).reduce(((e,t)=>e.concat(Array.from(t))),[])}get size(){return Array.from(this.valuesByKey.values()).reduce(((e,t)=>e+t.size),0)}add(e,t){w(this.valuesByKey,e,t)}delete(e,t){A(this.valuesByKey,e,t)}has(e,t){const n=this.valuesByKey.get(e);return null!=n&&n.has(t)}hasKey(e){return this.valuesByKey.has(e)}hasValue(e){return Array.from(this.valuesByKey.values()).some((t=>t.has(e)))}getValuesForKey(e){const t=this.valuesByKey.get(e);return t?Array.from(t):[]}getKeysForValue(e){return Array.from(this.valuesByKey).filter((([t,n])=>n.has(e))).map((([e,t])=>e))}}class C{constructor(e,t,n,s){this._selector=t,this.details=s,this.elementObserver=new O(e,this),this.delegate=n,this.matchesByElement=new S}get started(){return this.elementObserver.started}get selector(){return this._selector}set selector(e){this._selector=e,this.refresh()}start(){this.elementObserver.start()}pause(e){this.elementObserver.pause(e)}stop(){this.elementObserver.stop()}refresh(){this.elementObserver.refresh()}get element(){return this.elementObserver.element}matchElement(e){const{selector:t}=this;if(t){const n=e.matches(t);return this.delegate.selectorMatchElement?n&&this.delegate.selectorMatchElement(e,this.details):n}return!1}matchElementsInTree(e){const{selector:t}=this;if(t){const n=this.matchElement(e)?[e]:[],s=Array.from(e.querySelectorAll(t)).filter((e=>this.matchElement(e)));return n.concat(s)}return[]}elementMatched(e){const{selector:t}=this;t&&this.selectorMatched(e,t)}elementUnmatched(e){const t=this.matchesByElement.getKeysForValue(e);for(const n of t)this.selectorUnmatched(e,n)}elementAttributeChanged(e,t){const{selector:n}=this;if(n){const t=this.matchElement(e),s=this.matchesByElement.has(n,e);t&&!s?this.selectorMatched(e,n):!t&&s&&this.selectorUnmatched(e,n)}}selectorMatched(e,t){this.delegate.selectorMatched(e,t,this.details),this.matchesByElement.add(t,e)}selectorUnmatched(e,t){this.delegate.selectorUnmatched(e,t,this.details),this.matchesByElement.delete(t,e)}}class M{constructor(e,t){this.element=e,this.delegate=t,this.started=!1,this.stringMap=new Map,this.mutationObserver=new MutationObserver((e=>this.processMutations(e)))}start(){this.started||(this.started=!0,this.mutationObserver.observe(this.element,{attributes:!0,attributeOldValue:!0}),this.refresh())}stop(){this.started&&(this.mutationObserver.takeRecords(),this.mutationObserver.disconnect(),this.started=!1)}refresh(){if(this.started)for(const e of this.knownAttributeNames)this.refreshAttribute(e,null)}processMutations(e){if(this.started)for(const t of e)this.processMutation(t)}processMutation(e){const t=e.attributeName;t&&this.refreshAttribute(t,e.oldValue)}refreshAttribute(e,t){const n=this.delegate.getStringMapKeyForAttribute(e);if(null!=n){this.stringMap.has(e)||this.stringMapKeyAdded(n,e);const s=this.element.getAttribute(e);if(this.stringMap.get(e)!=s&&this.stringMapValueChanged(s,n,t),null==s){const t=this.stringMap.get(e);this.stringMap.delete(e),t&&this.stringMapKeyRemoved(n,e,t)}else this.stringMap.set(e,s)}}stringMapKeyAdded(e,t){this.delegate.stringMapKeyAdded&&this.delegate.stringMapKeyAdded(e,t)}stringMapValueChanged(e,t,n){this.delegate.stringMapValueChanged&&this.delegate.stringMapValueChanged(e,t,n)}stringMapKeyRemoved(e,t,n){this.delegate.stringMapKeyRemoved&&this.delegate.stringMapKeyRemoved(e,t,n)}get knownAttributeNames(){return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)))}get currentAttributeNames(){return Array.from(this.element.attributes).map((e=>e.name))}get recordedAttributeNames(){return Array.from(this.stringMap.keys())}}class T{constructor(e,t,n){this.attributeObserver=new E(e,t,this),this.delegate=n,this.tokensByElement=new S}get started(){return this.attributeObserver.started}start(){this.attributeObserver.start()}pause(e){this.attributeObserver.pause(e)}stop(){this.attributeObserver.stop()}refresh(){this.attributeObserver.refresh()}get element(){return this.attributeObserver.element}get attributeName(){return this.attributeObserver.attributeName}elementMatchedAttribute(e){this.tokensMatched(this.readTokensForElement(e))}elementAttributeValueChanged(e){const[t,n]=this.refreshTokensForElement(e);this.tokensUnmatched(t),this.tokensMatched(n)}elementUnmatchedAttribute(e){this.tokensUnmatched(this.tokensByElement.getValuesForKey(e))}tokensMatched(e){e.forEach((e=>this.tokenMatched(e)))}tokensUnmatched(e){e.forEach((e=>this.tokenUnmatched(e)))}tokenMatched(e){this.delegate.tokenMatched(e),this.tokensByElement.add(e.element,e)}tokenUnmatched(e){this.delegate.tokenUnmatched(e),this.tokensByElement.delete(e.element,e)}refreshTokensForElement(e){const t=this.tokensByElement.getValuesForKey(e),n=this.readTokensForElement(e),s=function(e,t){const n=Math.max(e.length,t.length);return Array.from({length:n},((n,s)=>[e[s],t[s]]))}(t,n).findIndex((([e,t])=>{return s=t,!((n=e)&&s&&n.index==s.index&&n.content==s.content);var n,s}));return-1==s?[[],[]]:[t.slice(s),n.slice(s)]}readTokensForElement(e){const t=this.attributeName;return function(e,t,n){return e.trim().split(/\s+/).filter((e=>e.length)).map(((e,s)=>({element:t,attributeName:n,content:e,index:s})))}(e.getAttribute(t)||"",e,t)}}class F{constructor(e,t,n){this.tokenListObserver=new T(e,t,this),this.delegate=n,this.parseResultsByToken=new WeakMap,this.valuesByTokenByElement=new WeakMap}get started(){return this.tokenListObserver.started}start(){this.tokenListObserver.start()}stop(){this.tokenListObserver.stop()}refresh(){this.tokenListObserver.refresh()}get element(){return this.tokenListObserver.element}get attributeName(){return this.tokenListObserver.attributeName}tokenMatched(e){const{element:t}=e,{value:n}=this.fetchParseResultForToken(e);n&&(this.fetchValuesByTokenForElement(t).set(e,n),this.delegate.elementMatchedValue(t,n))}tokenUnmatched(e){const{element:t}=e,{value:n}=this.fetchParseResultForToken(e);n&&(this.fetchValuesByTokenForElement(t).delete(e),this.delegate.elementUnmatchedValue(t,n))}fetchParseResultForToken(e){let t=this.parseResultsByToken.get(e);return t||(t=this.parseToken(e),this.parseResultsByToken.set(e,t)),t}fetchValuesByTokenForElement(e){let t=this.valuesByTokenByElement.get(e);return t||(t=new Map,this.valuesByTokenByElement.set(e,t)),t}parseToken(e){try{return{value:this.delegate.parseValueForToken(e)}}catch(e){return{error:e}}}}class x{constructor(e,t){this.context=e,this.delegate=t,this.bindingsByAction=new Map}start(){this.valueListObserver||(this.valueListObserver=new F(this.element,this.actionAttribute,this),this.valueListObserver.start())}stop(){this.valueListObserver&&(this.valueListObserver.stop(),delete this.valueListObserver,this.disconnectAllActions())}get element(){return this.context.element}get identifier(){return this.context.identifier}get actionAttribute(){return this.schema.actionAttribute}get schema(){return this.context.schema}get bindings(){return Array.from(this.bindingsByAction.values())}connectAction(e){const t=new y(this.context,e);this.bindingsByAction.set(e,t),this.delegate.bindingConnected(t)}disconnectAction(e){const t=this.bindingsByAction.get(e);t&&(this.bindingsByAction.delete(e),this.delegate.bindingDisconnected(t))}disconnectAllActions(){this.bindings.forEach((e=>this.delegate.bindingDisconnected(e,!0))),this.bindingsByAction.clear()}parseValueForToken(e){const t=g.forToken(e,this.schema);if(t.identifier==this.identifier)return t}elementMatchedValue(e,t){this.connectAction(t)}elementUnmatchedValue(e,t){this.disconnectAction(t)}}class I{constructor(e,t){this.context=e,this.receiver=t,this.stringMapObserver=new M(this.element,this),this.valueDescriptorMap=this.controller.valueDescriptorMap}start(){this.stringMapObserver.start(),this.invokeChangedCallbacksForDefaultValues()}stop(){this.stringMapObserver.stop()}get element(){return this.context.element}get controller(){return this.context.controller}getStringMapKeyForAttribute(e){if(e in this.valueDescriptorMap)return this.valueDescriptorMap[e].name}stringMapKeyAdded(e,t){const n=this.valueDescriptorMap[t];this.hasValue(e)||this.invokeChangedCallback(e,n.writer(this.receiver[e]),n.writer(n.defaultValue))}stringMapValueChanged(e,t,n){const s=this.valueDescriptorNameMap[t];null!==e&&(null===n&&(n=s.writer(s.defaultValue)),this.invokeChangedCallback(t,e,n))}stringMapKeyRemoved(e,t,n){const s=this.valueDescriptorNameMap[e];this.hasValue(e)?this.invokeChangedCallback(e,s.writer(this.receiver[e]),n):this.invokeChangedCallback(e,s.writer(s.defaultValue),n)}invokeChangedCallbacksForDefaultValues(){for(const{key:e,name:t,defaultValue:n,writer:s}of this.valueDescriptors)null==n||this.controller.data.has(e)||this.invokeChangedCallback(t,s(n),void 0)}invokeChangedCallback(e,t,n){const s=`${e}Changed`,i=this.receiver[s];if("function"==typeof i){const s=this.valueDescriptorNameMap[e];try{const e=s.reader(t);let r=n;n&&(r=s.reader(n)),i.call(this.receiver,e,r)}catch(e){throw e instanceof TypeError&&(e.message=`Stimulus Value "${this.context.identifier}.${s.name}" - ${e.message}`),e}}}get valueDescriptors(){const{valueDescriptorMap:e}=this;return Object.keys(e).map((t=>e[t]))}get valueDescriptorNameMap(){const e={};return Object.keys(this.valueDescriptorMap).forEach((t=>{const n=this.valueDescriptorMap[t];e[n.name]=n})),e}hasValue(e){const t=`has${d(this.valueDescriptorNameMap[e].name)}`;return this.receiver[t]}}class L{constructor(e,t){this.context=e,this.delegate=t,this.targetsByName=new S}start(){this.tokenListObserver||(this.tokenListObserver=new T(this.element,this.attributeName,this),this.tokenListObserver.start())}stop(){this.tokenListObserver&&(this.disconnectAllTargets(),this.tokenListObserver.stop(),delete this.tokenListObserver)}tokenMatched({element:e,content:t}){this.scope.containsElement(e)&&this.connectTarget(e,t)}tokenUnmatched({element:e,content:t}){this.disconnectTarget(e,t)}connectTarget(e,t){var n;this.targetsByName.has(t,e)||(this.targetsByName.add(t,e),null===(n=this.tokenListObserver)||void 0===n||n.pause((()=>this.delegate.targetConnected(e,t))))}disconnectTarget(e,t){var n;this.targetsByName.has(t,e)&&(this.targetsByName.delete(t,e),null===(n=this.tokenListObserver)||void 0===n||n.pause((()=>this.delegate.targetDisconnected(e,t))))}disconnectAllTargets(){for(const e of this.targetsByName.keys)for(const t of this.targetsByName.getValuesForKey(e))this.disconnectTarget(t,e)}get attributeName(){return`data-${this.context.identifier}-target`}get element(){return this.context.element}get scope(){return this.context.scope}}function N(e,t){const n=_(e);return Array.from(n.reduce(((e,n)=>(function(e,t){const n=e[t];return Array.isArray(n)?n:[]}(n,t).forEach((t=>e.add(t))),e)),new Set))}function P(e,t){return _(e).reduce(((e,n)=>(e.push(...function(e,t){const n=e[t];return n?Object.keys(n).map((e=>[e,n[e]])):[]}(n,t)),e)),[])}function _(e){const t=[];for(;e;)t.push(e),e=Object.getPrototypeOf(e);return t.reverse()}class ${constructor(e,t){this.started=!1,this.context=e,this.delegate=t,this.outletsByName=new S,this.outletElementsByName=new S,this.selectorObserverMap=new Map,this.attributeObserverMap=new Map}start(){this.started||(this.outletDefinitions.forEach((e=>{this.setupSelectorObserverForOutlet(e),this.setupAttributeObserverForOutlet(e)})),this.started=!0,this.dependentContexts.forEach((e=>e.refresh())))}refresh(){this.selectorObserverMap.forEach((e=>e.refresh())),this.attributeObserverMap.forEach((e=>e.refresh()))}stop(){this.started&&(this.started=!1,this.disconnectAllOutlets(),this.stopSelectorObservers(),this.stopAttributeObservers())}stopSelectorObservers(){this.selectorObserverMap.size>0&&(this.selectorObserverMap.forEach((e=>e.stop())),this.selectorObserverMap.clear())}stopAttributeObservers(){this.attributeObserverMap.size>0&&(this.attributeObserverMap.forEach((e=>e.stop())),this.attributeObserverMap.clear())}selectorMatched(e,t,{outletName:n}){const s=this.getOutlet(e,n);s&&this.connectOutlet(s,e,n)}selectorUnmatched(e,t,{outletName:n}){const s=this.getOutletFromMap(e,n);s&&this.disconnectOutlet(s,e,n)}selectorMatchElement(e,{outletName:t}){const n=this.selector(t),s=this.hasOutlet(e,t),i=e.matches(`[${this.schema.controllerAttribute}~=${t}]`);return!!n&&(s&&i&&e.matches(n))}elementMatchedAttribute(e,t){const n=this.getOutletNameFromOutletAttributeName(t);n&&this.updateSelectorObserverForOutlet(n)}elementAttributeValueChanged(e,t){const n=this.getOutletNameFromOutletAttributeName(t);n&&this.updateSelectorObserverForOutlet(n)}elementUnmatchedAttribute(e,t){const n=this.getOutletNameFromOutletAttributeName(t);n&&this.updateSelectorObserverForOutlet(n)}connectOutlet(e,t,n){var s;this.outletElementsByName.has(n,t)||(this.outletsByName.add(n,e),this.outletElementsByName.add(n,t),null===(s=this.selectorObserverMap.get(n))||void 0===s||s.pause((()=>this.delegate.outletConnected(e,t,n))))}disconnectOutlet(e,t,n){var s;this.outletElementsByName.has(n,t)&&(this.outletsByName.delete(n,e),this.outletElementsByName.delete(n,t),null===(s=this.selectorObserverMap.get(n))||void 0===s||s.pause((()=>this.delegate.outletDisconnected(e,t,n))))}disconnectAllOutlets(){for(const e of this.outletElementsByName.keys)for(const t of this.outletElementsByName.getValuesForKey(e))for(const n of this.outletsByName.getValuesForKey(e))this.disconnectOutlet(n,t,e)}updateSelectorObserverForOutlet(e){const t=this.selectorObserverMap.get(e);t&&(t.selector=this.selector(e))}setupSelectorObserverForOutlet(e){const t=this.selector(e),n=new C(document.body,t,this,{outletName:e});this.selectorObserverMap.set(e,n),n.start()}setupAttributeObserverForOutlet(e){const t=this.attributeNameForOutletName(e),n=new E(this.scope.element,t,this);this.attributeObserverMap.set(e,n),n.start()}selector(e){return this.scope.outlets.getSelectorForOutletName(e)}attributeNameForOutletName(e){return this.scope.schema.outletAttributeForScope(this.identifier,e)}getOutletNameFromOutletAttributeName(e){return this.outletDefinitions.find((t=>this.attributeNameForOutletName(t)===e))}get outletDependencies(){const e=new S;return this.router.modules.forEach((t=>{N(t.definition.controllerConstructor,"outlets").forEach((n=>e.add(n,t.identifier)))})),e}get outletDefinitions(){return this.outletDependencies.getKeysForValue(this.identifier)}get dependentControllerIdentifiers(){return this.outletDependencies.getValuesForKey(this.identifier)}get dependentContexts(){const e=this.dependentControllerIdentifiers;return this.router.contexts.filter((t=>e.includes(t.identifier)))}hasOutlet(e,t){return!!this.getOutlet(e,t)||!!this.getOutletFromMap(e,t)}getOutlet(e,t){return this.application.getControllerForElementAndIdentifier(e,t)}getOutletFromMap(e,t){return this.outletsByName.getValuesForKey(t).find((t=>t.element===e))}get scope(){return this.context.scope}get schema(){return this.context.schema}get identifier(){return this.context.identifier}get application(){return this.context.application}get router(){return this.application.router}}class D{constructor(e,t){this.logDebugActivity=(e,t={})=>{const{identifier:n,controller:s,element:i}=this;t=Object.assign({identifier:n,controller:s,element:i},t),this.application.logDebugActivity(this.identifier,e,t)},this.module=e,this.scope=t,this.controller=new e.controllerConstructor(this),this.bindingObserver=new x(this,this.dispatcher),this.valueObserver=new I(this,this.controller),this.targetObserver=new L(this,this),this.outletObserver=new $(this,this);try{this.controller.initialize(),this.logDebugActivity("initialize")}catch(e){this.handleError(e,"initializing controller")}}connect(){this.bindingObserver.start(),this.valueObserver.start(),this.targetObserver.start(),this.outletObserver.start();try{this.controller.connect(),this.logDebugActivity("connect")}catch(e){this.handleError(e,"connecting controller")}}refresh(){this.outletObserver.refresh()}disconnect(){try{this.controller.disconnect(),this.logDebugActivity("disconnect")}catch(e){this.handleError(e,"disconnecting controller")}this.outletObserver.stop(),this.targetObserver.stop(),this.valueObserver.stop(),this.bindingObserver.stop()}get application(){return this.module.application}get identifier(){return this.module.identifier}get schema(){return this.application.schema}get dispatcher(){return this.application.dispatcher}get element(){return this.scope.element}get parentElement(){return this.element.parentElement}handleError(e,t,n={}){const{identifier:s,controller:i,element:r}=this;n=Object.assign({identifier:s,controller:i,element:r},n),this.application.handleError(e,`Error ${t}`,n)}targetConnected(e,t){this.invokeControllerMethod(`${t}TargetConnected`,e)}targetDisconnected(e,t){this.invokeControllerMethod(`${t}TargetDisconnected`,e)}outletConnected(e,t,n){this.invokeControllerMethod(`${c(n)}OutletConnected`,e,t)}outletDisconnected(e,t,n){this.invokeControllerMethod(`${c(n)}OutletDisconnected`,e,t)}invokeControllerMethod(e,...t){const n=this.controller;"function"==typeof n[e]&&n[e](...t)}}function V(e){return function(e,t){const n=B(e),s=function(e,t){return j(t).reduce(((n,s)=>{const i=function(e,t,n){const s=Object.getOwnPropertyDescriptor(e,n);if(!s||!("value"in s)){const e=Object.getOwnPropertyDescriptor(t,n).value;return s&&(e.get=s.get||e.get,e.set=s.set||e.set),e}}(e,t,s);return i&&Object.assign(n,{[s]:i}),n}),{})}(e.prototype,t);return Object.defineProperties(n.prototype,s),n}(e,function(e){const t=N(e,"blessings");return t.reduce(((t,n)=>{const s=n(e);for(const e in s){const n=t[e]||{};t[e]=Object.assign(n,s[e])}return t}),{})}(e))}const j="function"==typeof Object.getOwnPropertySymbols?e=>[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)]:Object.getOwnPropertyNames,B=(()=>{function e(e){function t(){return Reflect.construct(e,arguments,new.target)}return t.prototype=Object.create(e.prototype,{constructor:{value:t}}),Reflect.setPrototypeOf(t,e),t}try{return function(){const t=e((function(){this.a.call(this)}));t.prototype.a=function(){},new t}(),e}catch(e){return e=>class extends e{}}})();class R{constructor(e,t){this.application=e,this.definition=function(e){return{identifier:e.identifier,controllerConstructor:V(e.controllerConstructor)}}(t),this.contextsByScope=new WeakMap,this.connectedContexts=new Set}get identifier(){return this.definition.identifier}get controllerConstructor(){return this.definition.controllerConstructor}get contexts(){return Array.from(this.connectedContexts)}connectContextForScope(e){const t=this.fetchContextForScope(e);this.connectedContexts.add(t),t.connect()}disconnectContextForScope(e){const t=this.contextsByScope.get(e);t&&(this.connectedContexts.delete(t),t.disconnect())}fetchContextForScope(e){let t=this.contextsByScope.get(e);return t||(t=new D(this,e),this.contextsByScope.set(e,t)),t}}class q{constructor(e){this.scope=e}has(e){return this.data.has(this.getDataKey(e))}get(e){return this.getAll(e)[0]}getAll(e){const t=this.data.get(this.getDataKey(e))||"";return t.match(/[^\s]+/g)||[]}getAttributeName(e){return this.data.getAttributeNameForKey(this.getDataKey(e))}getDataKey(e){return`${e}-class`}get data(){return this.scope.data}}class H{constructor(e){this.scope=e}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get(e){const t=this.getAttributeNameForKey(e);return this.element.getAttribute(t)}set(e,t){const n=this.getAttributeNameForKey(e);return this.element.setAttribute(n,t),this.get(e)}has(e){const t=this.getAttributeNameForKey(e);return this.element.hasAttribute(t)}delete(e){if(this.has(e)){const t=this.getAttributeNameForKey(e);return this.element.removeAttribute(t),!0}return!1}getAttributeNameForKey(e){return`data-${this.identifier}-${u(e)}`}}class U{constructor(e){this.warnedKeysByObject=new WeakMap,this.logger=e}warn(e,t,n){let s=this.warnedKeysByObject.get(e);s||(s=new Set,this.warnedKeysByObject.set(e,s)),s.has(t)||(s.add(t),this.logger.warn(n,e))}}function K(e,t){return`[${e}~="${t}"]`}class z{constructor(e){this.scope=e}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get schema(){return this.scope.schema}has(e){return null!=this.find(e)}find(...e){return e.reduce(((e,t)=>e||this.findTarget(t)||this.findLegacyTarget(t)),void 0)}findAll(...e){return e.reduce(((e,t)=>[...e,...this.findAllTargets(t),...this.findAllLegacyTargets(t)]),[])}findTarget(e){const t=this.getSelectorForTargetName(e);return this.scope.findElement(t)}findAllTargets(e){const t=this.getSelectorForTargetName(e);return this.scope.findAllElements(t)}getSelectorForTargetName(e){return K(this.schema.targetAttributeForScope(this.identifier),e)}findLegacyTarget(e){const t=this.getLegacySelectorForTargetName(e);return this.deprecate(this.scope.findElement(t),e)}findAllLegacyTargets(e){const t=this.getLegacySelectorForTargetName(e);return this.scope.findAllElements(t).map((t=>this.deprecate(t,e)))}getLegacySelectorForTargetName(e){const t=`${this.identifier}.${e}`;return K(this.schema.targetAttribute,t)}deprecate(e,t){if(e){const{identifier:n}=this,s=this.schema.targetAttribute,i=this.schema.targetAttributeForScope(n);this.guide.warn(e,`target:${t}`,`Please replace ${s}="${n}.${t}" with ${i}="${t}". The ${s} attribute is deprecated and will be removed in a future version of Stimulus.`)}return e}get guide(){return this.scope.guide}}class W{constructor(e,t){this.scope=e,this.controllerElement=t}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get schema(){return this.scope.schema}has(e){return null!=this.find(e)}find(...e){return e.reduce(((e,t)=>e||this.findOutlet(t)),void 0)}findAll(...e){return e.reduce(((e,t)=>[...e,...this.findAllOutlets(t)]),[])}getSelectorForOutletName(e){const t=this.schema.outletAttributeForScope(this.identifier,e);return this.controllerElement.getAttribute(t)}findOutlet(e){const t=this.getSelectorForOutletName(e);if(t)return this.findElement(t,e)}findAllOutlets(e){const t=this.getSelectorForOutletName(e);return t?this.findAllElements(t,e):[]}findElement(e,t){return this.scope.queryElements(e).filter((n=>this.matchesElement(n,e,t)))[0]}findAllElements(e,t){return this.scope.queryElements(e).filter((n=>this.matchesElement(n,e,t)))}matchesElement(e,t,n){const s=e.getAttribute(this.scope.schema.controllerAttribute)||"";return e.matches(t)&&s.split(" ").includes(n)}}class J{constructor(e,t,n,s){this.targets=new z(this),this.classes=new q(this),this.data=new H(this),this.containsElement=e=>e.closest(this.controllerSelector)===this.element,this.schema=e,this.element=t,this.identifier=n,this.guide=new U(s),this.outlets=new W(this.documentScope,t)}findElement(e){return this.element.matches(e)?this.element:this.queryElements(e).find(this.containsElement)}findAllElements(e){return[...this.element.matches(e)?[this.element]:[],...this.queryElements(e).filter(this.containsElement)]}queryElements(e){return Array.from(this.element.querySelectorAll(e))}get controllerSelector(){return K(this.schema.controllerAttribute,this.identifier)}get isDocumentScope(){return this.element===document.documentElement}get documentScope(){return this.isDocumentScope?this:new J(this.schema,document.documentElement,this.identifier,this.guide.logger)}}class G{constructor(e,t,n){this.element=e,this.schema=t,this.delegate=n,this.valueListObserver=new F(this.element,this.controllerAttribute,this),this.scopesByIdentifierByElement=new WeakMap,this.scopeReferenceCounts=new WeakMap}start(){this.valueListObserver.start()}stop(){this.valueListObserver.stop()}get controllerAttribute(){return this.schema.controllerAttribute}parseValueForToken(e){const{element:t,content:n}=e;return this.parseValueForElementAndIdentifier(t,n)}parseValueForElementAndIdentifier(e,t){const n=this.fetchScopesByIdentifierForElement(e);let s=n.get(t);return s||(s=this.delegate.createScopeForElementAndIdentifier(e,t),n.set(t,s)),s}elementMatchedValue(e,t){const n=(this.scopeReferenceCounts.get(t)||0)+1;this.scopeReferenceCounts.set(t,n),1==n&&this.delegate.scopeConnected(t)}elementUnmatchedValue(e,t){const n=this.scopeReferenceCounts.get(t);n&&(this.scopeReferenceCounts.set(t,n-1),1==n&&this.delegate.scopeDisconnected(t))}fetchScopesByIdentifierForElement(e){let t=this.scopesByIdentifierByElement.get(e);return t||(t=new Map,this.scopesByIdentifierByElement.set(e,t)),t}}class Q{constructor(e){this.application=e,this.scopeObserver=new G(this.element,this.schema,this),this.scopesByIdentifier=new S,this.modulesByIdentifier=new Map}get element(){return this.application.element}get schema(){return this.application.schema}get logger(){return this.application.logger}get controllerAttribute(){return this.schema.controllerAttribute}get modules(){return Array.from(this.modulesByIdentifier.values())}get contexts(){return this.modules.reduce(((e,t)=>e.concat(t.contexts)),[])}start(){this.scopeObserver.start()}stop(){this.scopeObserver.stop()}loadDefinition(e){this.unloadIdentifier(e.identifier);const t=new R(this.application,e);this.connectModule(t);const n=e.controllerConstructor.afterLoad;n&&n.call(e.controllerConstructor,e.identifier,this.application)}unloadIdentifier(e){const t=this.modulesByIdentifier.get(e);t&&this.disconnectModule(t)}getContextForElementAndIdentifier(e,t){const n=this.modulesByIdentifier.get(t);if(n)return n.contexts.find((t=>t.element==e))}proposeToConnectScopeForElementAndIdentifier(e,t){const n=this.scopeObserver.parseValueForElementAndIdentifier(e,t);n?this.scopeObserver.elementMatchedValue(n.element,n):console.error(`Couldn't find or create scope for identifier: "${t}" and element:`,e)}handleError(e,t,n){this.application.handleError(e,t,n)}createScopeForElementAndIdentifier(e,t){return new J(this.schema,e,t,this.logger)}scopeConnected(e){this.scopesByIdentifier.add(e.identifier,e);const t=this.modulesByIdentifier.get(e.identifier);t&&t.connectContextForScope(e)}scopeDisconnected(e){this.scopesByIdentifier.delete(e.identifier,e);const t=this.modulesByIdentifier.get(e.identifier);t&&t.disconnectContextForScope(e)}connectModule(e){this.modulesByIdentifier.set(e.identifier,e);this.scopesByIdentifier.getValuesForKey(e.identifier).forEach((t=>e.connectContextForScope(t)))}disconnectModule(e){this.modulesByIdentifier.delete(e.identifier);this.scopesByIdentifier.getValuesForKey(e.identifier).forEach((t=>e.disconnectContextForScope(t)))}}const X={controllerAttribute:"data-controller",actionAttribute:"data-action",targetAttribute:"data-target",targetAttributeForScope:e=>`data-${e}-target`,outletAttributeForScope:(e,t)=>`data-${e}-${t}-outlet`,keyMappings:Object.assign(Object.assign({enter:"Enter",tab:"Tab",esc:"Escape",space:" ",up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",page_up:"PageUp",page_down:"PageDown"},Y("abcdefghijklmnopqrstuvwxyz".split("").map((e=>[e,e])))),Y("0123456789".split("").map((e=>[e,e]))))};function Y(e){return e.reduce(((e,[t,n])=>Object.assign(Object.assign({},e),{[t]:n})),{})}class Z{constructor(e=document.documentElement,t=X){this.logger=console,this.debug=!1,this.logDebugActivity=(e,t,n={})=>{this.debug&&this.logFormattedMessage(e,t,n)},this.element=e,this.schema=t,this.dispatcher=new i(this),this.router=new Q(this),this.actionDescriptorFilters=Object.assign({},r)}static start(e,t){const n=new this(e,t);return n.start(),n}async start(){await new Promise((e=>{"loading"==document.readyState?document.addEventListener("DOMContentLoaded",(()=>e())):e()})),this.logDebugActivity("application","starting"),this.dispatcher.start(),this.router.start(),this.logDebugActivity("application","start")}stop(){this.logDebugActivity("application","stopping"),this.dispatcher.stop(),this.router.stop(),this.logDebugActivity("application","stop")}register(e,t){this.load({identifier:e,controllerConstructor:t})}registerActionOption(e,t){this.actionDescriptorFilters[e]=t}load(e,...t){(Array.isArray(e)?e:[e,...t]).forEach((e=>{e.controllerConstructor.shouldLoad&&this.router.loadDefinition(e)}))}unload(e,...t){(Array.isArray(e)?e:[e,...t]).forEach((e=>this.router.unloadIdentifier(e)))}get controllers(){return this.router.contexts.map((e=>e.controller))}getControllerForElementAndIdentifier(e,t){const n=this.router.getContextForElementAndIdentifier(e,t);return n?n.controller:null}handleError(e,t,n){var s;this.logger.error("%s\n\n%o\n\n%o",t,e,n),null===(s=window.onerror)||void 0===s||s.call(window,t,"",0,0,e)}logFormattedMessage(e,t,n={}){n=Object.assign({application:this},n),this.logger.groupCollapsed(`${e} #${t}`),this.logger.log("details:",Object.assign({},n)),this.logger.groupEnd()}}function ee(e,t,n){return e.application.getControllerForElementAndIdentifier(t,n)}function te(e,t,n){let s=ee(e,t,n);return s||(e.application.router.proposeToConnectScopeForElementAndIdentifier(t,n),s=ee(e,t,n),s||void 0)}function ne([e,t],n){return function(e){const{token:t,typeDefinition:n}=e,s=`${u(t)}-value`,i=function(e){const{controller:t,token:n,typeDefinition:s}=e,i={controller:t,token:n,typeObject:s},r=function(e){const{controller:t,token:n,typeObject:s}=e,i=h(s.type),r=h(s.default),o=i&&r,a=i&&!r,l=!i&&r,c=se(s.type),d=ie(e.typeObject.default);if(a)return c;if(l)return d;if(c!==d){throw new Error(`The specified default value for the Stimulus Value "${t?`${t}.${n}`:n}" must match the defined type "${c}". The provided default value of "${s.default}" is of type "${d}".`)}if(o)return c}(i),o=ie(s),a=se(s),l=r||o||a;if(l)return l;const c=t?`${t}.${s}`:n;throw new Error(`Unknown value type "${c}" for "${n}" value`)}(e);return{type:i,key:s,name:l(s),get defaultValue(){return function(e){const t=se(e);if(t)return re[t];const n=p(e,"default"),s=p(e,"type"),i=e;if(n)return i.default;if(s){const{type:e}=i,t=se(e);if(t)return re[t]}return e}(n)},get hasCustomDefaultValue(){return void 0!==ie(n)},reader:oe[i],writer:ae[i]||ae.default}}({controller:n,token:e,typeDefinition:t})}function se(e){switch(e){case Array:return"array";case Boolean:return"boolean";case Number:return"number";case Object:return"object";case String:return"string"}}function ie(e){switch(typeof e){case"boolean":return"boolean";case"number":return"number";case"string":return"string"}return Array.isArray(e)?"array":"[object Object]"===Object.prototype.toString.call(e)?"object":void 0}const re={get array(){return[]},boolean:!1,number:0,get object(){return{}},string:""},oe={array(e){const t=JSON.parse(e);if(!Array.isArray(t))throw new TypeError(`expected value of type "array" but instead got value "${e}" of type "${ie(t)}"`);return t},boolean:e=>!("0"==e||"false"==String(e).toLowerCase()),number:e=>Number(e.replace(/_/g,"")),object(e){const t=JSON.parse(e);if(null===t||"object"!=typeof t||Array.isArray(t))throw new TypeError(`expected value of type "object" but instead got value "${e}" of type "${ie(t)}"`);return t},string:e=>e},ae={default:function(e){return`${e}`},array:le,object:le};function le(e){return JSON.stringify(e)}class ce{constructor(e){this.context=e}static get shouldLoad(){return!0}static afterLoad(e,t){}get application(){return this.context.application}get scope(){return this.context.scope}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get targets(){return this.scope.targets}get outlets(){return this.scope.outlets}get classes(){return this.scope.classes}get data(){return this.scope.data}initialize(){}connect(){}disconnect(){}dispatch(e,{target:t=this.element,detail:n={},prefix:s=this.identifier,bubbles:i=!0,cancelable:r=!0}={}){const o=new CustomEvent(s?`${s}:${e}`:e,{detail:n,bubbles:i,cancelable:r});return t.dispatchEvent(o),o}}ce.blessings=[function(e){return N(e,"classes").reduce(((e,t)=>{return Object.assign(e,(n=t,{[`${n}Class`]:{get(){const{classes:e}=this;if(e.has(n))return e.get(n);{const t=e.getAttributeName(n);throw new Error(`Missing attribute "${t}"`)}}},[`${n}Classes`]:{get(){return this.classes.getAll(n)}},[`has${d(n)}Class`]:{get(){return this.classes.has(n)}}}));var n}),{})},function(e){return N(e,"targets").reduce(((e,t)=>{return Object.assign(e,(n=t,{[`${n}Target`]:{get(){const e=this.targets.find(n);if(e)return e;throw new Error(`Missing target element "${n}" for "${this.identifier}" controller`)}},[`${n}Targets`]:{get(){return this.targets.findAll(n)}},[`has${d(n)}Target`]:{get(){return this.targets.has(n)}}}));var n}),{})},function(e){const t=P(e,"values"),n={valueDescriptorMap:{get(){return t.reduce(((e,t)=>{const n=ne(t,this.identifier),s=this.data.getAttributeNameForKey(n.key);return Object.assign(e,{[s]:n})}),{})}}};return t.reduce(((e,t)=>Object.assign(e,function(e,t){const n=ne(e,t),{key:s,name:i,reader:r,writer:o}=n;return{[i]:{get(){const e=this.data.get(s);return null!==e?r(e):n.defaultValue},set(e){void 0===e?this.data.delete(s):this.data.set(s,o(e))}},[`has${d(i)}`]:{get(){return this.data.has(s)||n.hasCustomDefaultValue}}}}(t))),n)},function(e){return N(e,"outlets").reduce(((e,t)=>Object.assign(e,function(e){const t=c(e);return{[`${t}Outlet`]:{get(){const t=this.outlets.find(e),n=this.outlets.getSelectorForOutletName(e);if(t){const n=te(this,t,e);if(n)return n;throw new Error(`The provided outlet element is missing an outlet controller "${e}" instance for host controller "${this.identifier}"`)}throw new Error(`Missing outlet element "${e}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${n}".`)}},[`${t}Outlets`]:{get(){const t=this.outlets.findAll(e);return t.length>0?t.map((t=>{const n=te(this,t,e);if(n)return n;console.warn(`The provided outlet element is missing an outlet controller "${e}" instance for host controller "${this.identifier}"`,t)})).filter((e=>e)):[]}},[`${t}OutletElement`]:{get(){const t=this.outlets.find(e),n=this.outlets.getSelectorForOutletName(e);if(t)return t;throw new Error(`Missing outlet element "${e}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${n}".`)}},[`${t}OutletElements`]:{get(){return this.outlets.findAll(e)}},[`has${d(t)}Outlet`]:{get(){return this.outlets.has(e)}}}}(t))),{})}],ce.targets=[],ce.outlets=[],ce.values={}},431:(e,t,n)=>{"use strict";function s(e){return s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},s(e)}function i(e,t){for(var n=0;nd});var d=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),o(this,t,arguments)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&c(e,t)}(t,e),n=t,(s=[{key:"toggle",value:function(){var e="dark"===(document.documentElement.getAttribute("data-bs-theme")||"light")?"light":"dark";localStorage.setItem("sylius-theme",e),document.documentElement.setAttribute("data-bs-theme",e)}}])&&i(n.prototype,s),r&&i(n,r),Object.defineProperty(n,"prototype",{writable:!1}),n;var n,s,r}(n(891).xI)},371:function(e){e.exports=function(){"use strict";function e(e,t){e.split(/\s+/).forEach((e=>{t(e)}))}class t{constructor(){this._events=void 0,this._events={}}on(t,n){e(t,(e=>{const t=this._events[e]||[];t.push(n),this._events[e]=t}))}off(t,n){var s=arguments.length;0!==s?e(t,(e=>{if(1===s)return void delete this._events[e];const t=this._events[e];void 0!==t&&(t.splice(t.indexOf(n),1),this._events[e]=t)})):this._events={}}trigger(t,...n){var s=this;e(t,(e=>{const t=s._events[e];void 0!==t&&t.forEach((e=>{e.apply(s,n)}))}))}}function n(e){return e.plugins={},class extends e{constructor(...e){super(...e),this.plugins={names:[],settings:{},requested:{},loaded:{}}}static define(t,n){e.plugins[t]={name:t,fn:n}}initializePlugins(e){var t,n;const s=this,i=[];if(Array.isArray(e))e.forEach((e=>{"string"==typeof e?i.push(e):(s.plugins.settings[e.name]=e.options,i.push(e.name))}));else if(e)for(t in e)e.hasOwnProperty(t)&&(s.plugins.settings[t]=e[t],i.push(t));for(;n=i.shift();)s.require(n)}loadPlugin(t){var n=this,s=n.plugins,i=e.plugins[t];if(!e.plugins.hasOwnProperty(t))throw new Error('Unable to find "'+t+'" plugin');s.requested[t]=!0,s.loaded[t]=i.fn.apply(n,[n.plugins.settings[t]||{}]),s.names.push(t)}require(e){var t=this,n=t.plugins;if(!t.plugins.loaded.hasOwnProperty(e)){if(n.requested[e])throw new Error('Plugin has circular dependency ("'+e+'")');t.loadPlugin(e)}return n.loaded[e]}}}const s=e=>(e=e.filter(Boolean)).length<2?e[0]||"":1==l(e)?"["+e.join("")+"]":"(?:"+e.join("|")+")",i=e=>{if(!o(e))return e.join("");let t="",n=0;const s=()=>{n>1&&(t+="{"+n+"}")};return e.forEach(((i,r)=>{i!==e[r-1]?(s(),t+=i,n=1):n++})),s(),t},r=e=>{let t=d(e);return s(t)},o=e=>new Set(e).size!==e.length,a=e=>(e+"").replace(/([\$\(\)\*\+\.\?\[\]\^\{\|\}\\])/gu,"\\$1"),l=e=>e.reduce(((e,t)=>Math.max(e,c(t))),0),c=e=>d(e).length,d=e=>Array.from(e),u=e=>{if(1===e.length)return[[e]];let t=[];const n=e.substring(1);return u(n).forEach((function(n){let s=n.slice(0);s[0]=e.charAt(0)+s[0],t.push(s),s=n.slice(0),s.unshift(e.charAt(0)),t.push(s)})),t},h=[[0,65535]],p="[̀-ͯ·ʾʼ]";let m,g;const f=3,v={},b={"/":"⁄∕",0:"߀",a:"ⱥɐɑ",aa:"ꜳ",ae:"æǽǣ",ao:"ꜵ",au:"ꜷ",av:"ꜹꜻ",ay:"ꜽ",b:"ƀɓƃ",c:"ꜿƈȼↄ",d:"đɗɖᴅƌꮷԁɦ",e:"ɛǝᴇɇ",f:"ꝼƒ",g:"ǥɠꞡᵹꝿɢ",h:"ħⱨⱶɥ",i:"ɨı",j:"ɉȷ",k:"ƙⱪꝁꝃꝅꞣ",l:"łƚɫⱡꝉꝇꞁɭ",m:"ɱɯϻ",n:"ꞥƞɲꞑᴎлԉ",o:"øǿɔɵꝋꝍᴑ",oe:"œ",oi:"ƣ",oo:"ꝏ",ou:"ȣ",p:"ƥᵽꝑꝓꝕρ",q:"ꝗꝙɋ",r:"ɍɽꝛꞧꞃ",s:"ßȿꞩꞅʂ",t:"ŧƭʈⱦꞇ",th:"þ",tz:"ꜩ",u:"ʉ",v:"ʋꝟʌ",vy:"ꝡ",w:"ⱳ",y:"ƴɏỿ",z:"ƶȥɀⱬꝣ",hv:"ƕ"};for(let e in b){let t=b[e]||"";for(let n=0;n{void 0===m&&(m=C(e||h))},E=(e,t="NFKD")=>e.normalize(t),w=e=>d(e).reduce(((e,t)=>e+A(t)),""),A=e=>(e=E(e).toLowerCase().replace(y,(e=>v[e]||"")),E(e,"NFC"));function*k(e){for(const[t,n]of e)for(let e=t;e<=n;e++){let t=String.fromCharCode(e),n=w(t);n!=t.toLowerCase()&&(n.length>f||0!=n.length&&(yield{folded:n,composed:t,code_point:e}))}}const S=e=>{const t={},n=(e,n)=>{const s=t[e]||new Set,i=new RegExp("^"+r(s)+"$","iu");n.match(i)||(s.add(a(n)),t[e]=s)};for(let t of k(e))n(t.folded,t.folded),n(t.folded,t.composed);return t},C=e=>{const t=S(e),n={};let i=[];for(let e in t){let s=t[e];s&&(n[e]=r(s)),e.length>1&&i.push(a(e))}i.sort(((e,t)=>t.length-e.length));const o=s(i);return g=new RegExp("^"+o,"u"),n},M=(e,t=1)=>{let n=0;return e=e.map((e=>(m[e]&&(n+=e.length),m[e]||e))),n>=t?i(e):""},T=(e,t=1)=>(t=Math.max(t,e.length-1),s(u(e).map((e=>M(e,t))))),F=(e,t=!0)=>{let n=e.length>1?1:0;return s(e.map((e=>{let s=[];const r=t?e.length():e.length()-1;for(let t=0;t{for(const n of t){if(n.start!=e.start||n.end!=e.end)continue;if(n.substrs.join("")!==e.substrs.join(""))continue;let t=e.parts;const s=e=>{for(const n of t){if(n.start===e.start&&n.substr===e.substr)return!1;if(1!=e.length&&1!=n.length){if(e.startn.start)return!0;if(n.starte.start)return!0}}return!1};if(!(n.parts.filter(s).length>0))return!0}return!1};class I{constructor(){this.parts=[],this.substrs=[],this.start=0,this.end=0}add(e){e&&(this.parts.push(e),this.substrs.push(e.substr),this.start=Math.min(e.start,this.start),this.end=Math.max(e.end,this.end))}last(){return this.parts[this.parts.length-1]}length(){return this.parts.length}clone(e,t){let n=new I,s=JSON.parse(JSON.stringify(this.parts)),i=s.pop();for(const e of s)n.add(e);let r=t.substr.substring(0,e-i.start),o=r.length;return n.add({start:i.start,end:i.start+o,length:o,substr:r}),n}}const L=e=>{O(),e=w(e);let t="",n=[new I];for(let s=0;s0){a=a.sort(((e,t)=>e.length()-t.length()));for(let e of a)x(e,n)||n.push(e)}else if(s>0&&1==l.size&&!l.has("3")){t+=F(n,!1);let e=new I;const s=n[0];s&&e.add(s.last()),n=[e]}}return t+=F(n,!0),t},N=(e,t)=>{if(e)return e[t]},P=(e,t)=>{if(e){for(var n,s=t.split(".");(n=s.shift())&&(e=e[n]););return e}},_=(e,t,n)=>{var s,i;return e?(e+="",null==t.regex||-1===(i=e.search(t.regex))?0:(s=t.string.length/e.length,0===i&&(s+=.5),s*n)):0},$=(e,t)=>{var n=e[t];if("function"==typeof n)return n;n&&!Array.isArray(n)&&(e[t]=[n])},D=(e,t)=>{if(Array.isArray(e))e.forEach(t);else for(var n in e)e.hasOwnProperty(n)&&t(e[n],n)},V=(e,t)=>"number"==typeof e&&"number"==typeof t?e>t?1:e(t=w(t+"").toLowerCase())?1:t>e?-1:0;class j{constructor(e,t){this.items=void 0,this.settings=void 0,this.items=e,this.settings=t||{diacritics:!0}}tokenize(e,t,n){if(!e||!e.length)return[];const s=[],i=e.split(/\s+/);var r;return n&&(r=new RegExp("^("+Object.keys(n).map(a).join("|")+"):(.*)$")),i.forEach((e=>{let n,i=null,o=null;r&&(n=e.match(r))&&(i=n[1],e=n[2]),e.length>0&&(o=this.settings.diacritics?L(e)||null:a(e),o&&t&&(o="\\b"+o)),s.push({string:e,regex:o?new RegExp(o,"iu"):null,field:i})})),s}getScoreFunction(e,t){var n=this.prepareSearch(e,t);return this._getScoreFunction(n)}_getScoreFunction(e){const t=e.tokens,n=t.length;if(!n)return function(){return 0};const s=e.options.fields,i=e.weights,r=s.length,o=e.getAttrFn;if(!r)return function(){return 1};const a=1===r?function(e,t){const n=s[0].field;return _(o(t,n),e,i[n]||1)}:function(e,t){var n=0;if(e.field){const s=o(t,e.field);!e.regex&&s?n+=1/r:n+=_(s,e,1)}else D(i,((s,i)=>{n+=_(o(t,i),e,s)}));return n/r};return 1===n?function(e){return a(t[0],e)}:"and"===e.options.conjunction?function(e){var s,i=0;for(let n of t){if((s=a(n,e))<=0)return 0;i+=s}return i/n}:function(e){var s=0;return D(t,(t=>{s+=a(t,e)})),s/n}}getSortFunction(e,t){var n=this.prepareSearch(e,t);return this._getSortFunction(n)}_getSortFunction(e){var t,n=[];const s=this,i=e.options,r=!e.query&&i.sort_empty?i.sort_empty:i.sort;if("function"==typeof r)return r.bind(this);const o=function(t,n){return"$score"===t?n.score:e.getAttrFn(s.items[n.id],t)};if(r)for(let t of r)(e.query||"$score"!==t.field)&&n.push(t);if(e.query){t=!0;for(let e of n)if("$score"===e.field){t=!1;break}t&&n.unshift({field:"$score",direction:"desc"})}else n=n.filter((e=>"$score"!==e.field));return n.length?function(e,t){var s,i;for(let r of n)if(i=r.field,s=("desc"===r.direction?-1:1)*V(o(i,e),o(i,t)))return s;return 0}:null}prepareSearch(e,t){const n={};var s=Object.assign({},t);if($(s,"sort"),$(s,"sort_empty"),s.fields){$(s,"fields");const e=[];s.fields.forEach((t=>{"string"==typeof t&&(t={field:t,weight:1}),e.push(t),n[t.field]="weight"in t?t.weight:1})),s.fields=e}return{options:s,query:e.toLowerCase().trim(),tokens:this.tokenize(e,s.respect_word_boundaries,n),total:0,items:[],weights:n,getAttrFn:s.nesting?P:N}}search(e,t){var n,s,i=this;s=this.prepareSearch(e,t),t=s.options,e=s.query;const r=t.score||i._getScoreFunction(s);e.length?D(i.items,((e,i)=>{n=r(e),(!1===t.filter||n>0)&&s.items.push({score:n,id:i})})):D(i.items,((e,t)=>{s.items.push({score:1,id:t})}));const o=i._getSortFunction(s);return o&&s.items.sort(o),s.total=s.items.length,"number"==typeof t.limit&&(s.items=s.items.slice(0,t.limit)),s}}const B=(e,t)=>{if(Array.isArray(e))e.forEach(t);else for(var n in e)e.hasOwnProperty(n)&&t(e[n],n)},R=e=>{if(e.jquery)return e[0];if(e instanceof HTMLElement)return e;if(q(e)){var t=document.createElement("template");return t.innerHTML=e.trim(),t.content.firstChild}return document.querySelector(e)},q=e=>"string"==typeof e&&e.indexOf("<")>-1,H=e=>e.replace(/['"\\]/g,"\\$&"),U=(e,t)=>{var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!1),e.dispatchEvent(n)},K=(e,t)=>{Object.assign(e.style,t)},z=(e,...t)=>{var n=J(t);(e=G(e)).map((e=>{n.map((t=>{e.classList.add(t)}))}))},W=(e,...t)=>{var n=J(t);(e=G(e)).map((e=>{n.map((t=>{e.classList.remove(t)}))}))},J=e=>{var t=[];return B(e,(e=>{"string"==typeof e&&(e=e.trim().split(/[\11\12\14\15\40]/)),Array.isArray(e)&&(t=t.concat(e))})),t.filter(Boolean)},G=e=>(Array.isArray(e)||(e=[e]),e),Q=(e,t,n)=>{if(!n||n.contains(e))for(;e&&e.matches;){if(e.matches(t))return e;e=e.parentNode}},X=(e,t=0)=>t>0?e[e.length-1]:e[0],Y=e=>0===Object.keys(e).length,Z=(e,t)=>{if(!e)return-1;t=t||e.nodeName;for(var n=0;e=e.previousElementSibling;)e.matches(t)&&n++;return n},ee=(e,t)=>{B(t,((t,n)=>{null==t?e.removeAttribute(n):e.setAttribute(n,""+t)}))},te=(e,t)=>{e.parentNode&&e.parentNode.replaceChild(t,e)},ne=(e,t)=>{if(null===t)return;if("string"==typeof t){if(!t.length)return;t=new RegExp(t,"i")}const n=e=>{var n=e.data.match(t);if(n&&e.data.length>0){var s=document.createElement("span");s.className="highlight";var i=e.splitText(n.index);i.splitText(n[0].length);var r=i.cloneNode(!0);return s.appendChild(r),te(i,s),1}return 0},s=e=>{1!==e.nodeType||!e.childNodes||/(script|style)/i.test(e.tagName)||"highlight"===e.className&&"SPAN"===e.tagName||Array.from(e.childNodes).forEach((e=>{i(e)}))},i=e=>3===e.nodeType?n(e):(s(e),0);i(e)},se=e=>{var t=e.querySelectorAll("span.highlight");Array.prototype.forEach.call(t,(function(e){var t=e.parentNode;t.replaceChild(e.firstChild,e),t.normalize()}))},ie=65,re=13,oe=27,ae=37,le=38,ce=39,de=40,ue=8,he=46,pe=9,me="undefined"!=typeof navigator&&/Mac/.test(navigator.userAgent)?"metaKey":"ctrlKey";var ge={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:null,createOnBlur:!1,createFilter:null,highlight:!0,openOnFocus:!0,shouldOpen:null,maxOptions:50,maxItems:null,hideSelected:null,duplicates:!1,addPrecedence:!1,selectOnTab:!1,preload:null,allowEmptyOption:!1,refreshThrottle:300,loadThrottle:300,loadingClass:"loading",dataAttr:null,optgroupField:"optgroup",valueField:"value",labelField:"text",disabledField:"disabled",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"ts-wrapper",controlClass:"ts-control",dropdownClass:"ts-dropdown",dropdownContentClass:"ts-dropdown-content",itemClass:"item",optionClass:"option",dropdownParent:null,controlInput:'',copyClassesToDropdown:!1,placeholder:null,hidePlaceholder:null,shouldLoad:function(e){return e.length>0},render:{}};const fe=e=>null==e?null:ve(e),ve=e=>"boolean"==typeof e?e?"1":"0":e+"",be=e=>(e+"").replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),ye=(e,t)=>t>0?setTimeout(e,t):(e.call(null),null),Oe=(e,t)=>{var n;return function(s,i){var r=this;n&&(r.loading=Math.max(r.loading-1,0),clearTimeout(n)),n=setTimeout((function(){n=null,r.loadedSearches[s]=!0,e.call(r,s,i)}),t)}},Ee=(e,t,n)=>{var s,i=e.trigger,r={};for(s of(e.trigger=function(){var n=arguments[0];if(-1===t.indexOf(n))return i.apply(e,arguments);r[n]=arguments},n.apply(e,[]),e.trigger=i,t))s in r&&i.apply(e,r[s])},we=e=>({start:e.selectionStart||0,length:(e.selectionEnd||0)-(e.selectionStart||0)}),Ae=(e,t=!1)=>{e&&(e.preventDefault(),t&&e.stopPropagation())},ke=(e,t,n,s)=>{e.addEventListener(t,n,s)},Se=(e,t)=>!!t&&!!t[e]&&1==(t.altKey?1:0)+(t.ctrlKey?1:0)+(t.shiftKey?1:0)+(t.metaKey?1:0),Ce=(e,t)=>{const n=e.getAttribute("id");return n||(e.setAttribute("id",t),t)},Me=e=>e.replace(/[\\"']/g,"\\$&"),Te=(e,t)=>{t&&e.append(t)};function Fe(e,t){var n=Object.assign({},ge,t),s=n.dataAttr,i=n.labelField,r=n.valueField,o=n.disabledField,a=n.optgroupField,l=n.optgroupLabelField,c=n.optgroupValueField,d=e.tagName.toLowerCase(),u=e.getAttribute("placeholder")||e.getAttribute("data-placeholder");if(!u&&!n.allowEmptyOption){let t=e.querySelector('option[value=""]');t&&(u=t.textContent)}var h={placeholder:u,options:[],optgroups:[],items:[],maxItems:null},p=()=>{const t=e.getAttribute(s);if(t)h.options=JSON.parse(t),B(h.options,(e=>{h.items.push(e[r])}));else{var o=e.value.trim()||"";if(!n.allowEmptyOption&&!o.length)return;const t=o.split(n.delimiter);B(t,(e=>{const t={};t[i]=e,t[r]=e,h.options.push(t)})),h.items=t}};return"select"===d?(()=>{var t,d=h.options,u={},p=1;let m=0;var g=e=>{var t=Object.assign({},e.dataset),n=s&&t[s];return"string"==typeof n&&n.length&&(t=Object.assign(t,JSON.parse(n))),t},f=(e,t)=>{var s=fe(e.value);if(null!=s&&(s||n.allowEmptyOption)){if(u.hasOwnProperty(s)){if(t){var l=u[s][a];l?Array.isArray(l)?l.push(t):u[s][a]=[l,t]:u[s][a]=t}}else{var c=g(e);c[i]=c[i]||e.textContent,c[r]=c[r]||s,c[o]=c[o]||e.disabled,c[a]=c[a]||t,c.$option=e,c.$order=c.$order||++m,u[s]=c,d.push(c)}e.selected&&h.items.push(s)}},v=e=>{var t,n;(n=g(e))[l]=n[l]||e.getAttribute("label")||"",n[c]=n[c]||p++,n[o]=n[o]||e.disabled,n.$order=n.$order||++m,h.optgroups.push(n),t=n[c],B(e.children,(e=>{f(e,t)}))};h.maxItems=e.hasAttribute("multiple")?null:1,B(e.children,(e=>{"optgroup"===(t=e.tagName.toLowerCase())?v(e):"option"===t&&f(e)}))})():p(),Object.assign({},ge,h,t)}var xe=0;class Ie extends(n(t)){constructor(e,t){var n;super(),this.control_input=void 0,this.wrapper=void 0,this.dropdown=void 0,this.control=void 0,this.dropdown_content=void 0,this.focus_node=void 0,this.order=0,this.settings=void 0,this.input=void 0,this.tabIndex=void 0,this.is_select_tag=void 0,this.rtl=void 0,this.inputId=void 0,this._destroy=void 0,this.sifter=void 0,this.isOpen=!1,this.isDisabled=!1,this.isReadOnly=!1,this.isRequired=void 0,this.isInvalid=!1,this.isValid=!0,this.isLocked=!1,this.isFocused=!1,this.isInputHidden=!1,this.isSetup=!1,this.ignoreFocus=!1,this.ignoreHover=!1,this.hasOptions=!1,this.currentResults=void 0,this.lastValue="",this.caretPos=0,this.loading=0,this.loadedSearches={},this.activeOption=null,this.activeItems=[],this.optgroups={},this.options={},this.userOptions={},this.items=[],this.refreshTimeout=null,xe++;var s=R(e);if(s.tomselect)throw new Error("Tom Select already initialized on this element");s.tomselect=this,n=(window.getComputedStyle&&window.getComputedStyle(s,null)).getPropertyValue("direction");const i=Fe(s,t);this.settings=i,this.input=s,this.tabIndex=s.tabIndex||0,this.is_select_tag="select"===s.tagName.toLowerCase(),this.rtl=/rtl/i.test(n),this.inputId=Ce(s,"tomselect-"+xe),this.isRequired=s.required,this.sifter=new j(this.options,{diacritics:i.diacritics}),i.mode=i.mode||(1===i.maxItems?"single":"multi"),"boolean"!=typeof i.hideSelected&&(i.hideSelected="multi"===i.mode),"boolean"!=typeof i.hidePlaceholder&&(i.hidePlaceholder="multi"!==i.mode);var r=i.createFilter;"function"!=typeof r&&("string"==typeof r&&(r=new RegExp(r)),r instanceof RegExp?i.createFilter=e=>r.test(e):i.createFilter=e=>this.settings.duplicates||!this.options[e]),this.initializePlugins(i.plugins),this.setupCallbacks(),this.setupTemplates();const o=R("
"),a=R("
"),l=this._render("dropdown"),c=R('
'),d=this.input.getAttribute("class")||"",u=i.mode;var h;z(o,i.wrapperClass,d,u),z(a,i.controlClass),Te(o,a),z(l,i.dropdownClass,u),i.copyClassesToDropdown&&z(l,d),z(c,i.dropdownContentClass),Te(l,c),R(i.dropdownParent||o).appendChild(l),q(i.controlInput)?(h=R(i.controlInput),D(["autocorrect","autocapitalize","autocomplete","spellcheck"],(e=>{s.getAttribute(e)&&ee(h,{[e]:s.getAttribute(e)})})),h.tabIndex=-1,a.appendChild(h),this.focus_node=h):i.controlInput?(h=R(i.controlInput),this.focus_node=h):(h=R(""),this.focus_node=a),this.wrapper=o,this.dropdown=l,this.dropdown_content=c,this.control=a,this.control_input=h,this.setup()}setup(){const e=this,t=e.settings,n=e.control_input,s=e.dropdown,i=e.dropdown_content,r=e.wrapper,o=e.control,l=e.input,c=e.focus_node,d={passive:!0},u=e.inputId+"-ts-dropdown";ee(i,{id:u}),ee(c,{role:"combobox","aria-haspopup":"listbox","aria-expanded":"false","aria-controls":u});const h=Ce(c,e.inputId+"-ts-control"),p="label[for='"+H(e.inputId)+"']",m=document.querySelector(p),g=e.focus.bind(e);if(m){ke(m,"click",g),ee(m,{for:h});const t=Ce(m,e.inputId+"-ts-label");ee(c,{"aria-labelledby":t}),ee(i,{"aria-labelledby":t})}if(r.style.width=l.style.width,e.plugins.names.length){const t="plugin-"+e.plugins.names.join(" plugin-");z([r,s],t)}(null===t.maxItems||t.maxItems>1)&&e.is_select_tag&&ee(l,{multiple:"multiple"}),t.placeholder&&ee(n,{placeholder:t.placeholder}),!t.splitOn&&t.delimiter&&(t.splitOn=new RegExp("\\s*"+a(t.delimiter)+"+\\s*")),t.load&&t.loadThrottle&&(t.load=Oe(t.load,t.loadThrottle)),ke(s,"mousemove",(()=>{e.ignoreHover=!1})),ke(s,"mouseenter",(t=>{var n=Q(t.target,"[data-selectable]",s);n&&e.onOptionHover(t,n)}),{capture:!0}),ke(s,"click",(t=>{const n=Q(t.target,"[data-selectable]");n&&(e.onOptionSelect(t,n),Ae(t,!0))})),ke(o,"click",(t=>{var s=Q(t.target,"[data-ts-item]",o);s&&e.onItemSelect(t,s)?Ae(t,!0):""==n.value&&(e.onClick(),Ae(t,!0))})),ke(c,"keydown",(t=>e.onKeyDown(t))),ke(n,"keypress",(t=>e.onKeyPress(t))),ke(n,"input",(t=>e.onInput(t))),ke(c,"blur",(t=>e.onBlur(t))),ke(c,"focus",(t=>e.onFocus(t))),ke(n,"paste",(t=>e.onPaste(t)));const f=t=>{const i=t.composedPath()[0];if(!r.contains(i)&&!s.contains(i))return e.isFocused&&e.blur(),void e.inputState();i==n&&e.isOpen?t.stopPropagation():Ae(t,!0)},v=()=>{e.isOpen&&e.positionDropdown()};ke(document,"mousedown",f),ke(window,"scroll",v,d),ke(window,"resize",v,d),this._destroy=()=>{document.removeEventListener("mousedown",f),window.removeEventListener("scroll",v),window.removeEventListener("resize",v),m&&m.removeEventListener("click",g)},this.revertSettings={innerHTML:l.innerHTML,tabIndex:l.tabIndex},l.tabIndex=-1,l.insertAdjacentElement("afterend",e.wrapper),e.sync(!1),t.items=[],delete t.optgroups,delete t.options,ke(l,"invalid",(()=>{e.isValid&&(e.isValid=!1,e.isInvalid=!0,e.refreshState())})),e.updateOriginalInput(),e.refreshItems(),e.close(!1),e.inputState(),e.isSetup=!0,l.disabled?e.disable():l.readOnly?e.setReadOnly(!0):e.enable(),e.on("change",this.onChange),z(l,"tomselected","ts-hidden-accessible"),e.trigger("initialize"),!0===t.preload&&e.preload()}setupOptions(e=[],t=[]){this.addOptions(e),D(t,(e=>{this.registerOptionGroup(e)}))}setupTemplates(){var e=this,t=e.settings.labelField,n=e.settings.optgroupLabelField,s={optgroup:e=>{let t=document.createElement("div");return t.className="optgroup",t.appendChild(e.options),t},optgroup_header:(e,t)=>'
'+t(e[n])+"
",option:(e,n)=>"
"+n(e[t])+"
",item:(e,n)=>"
"+n(e[t])+"
",option_create:(e,t)=>'
Add '+t(e.input)+"…
",no_results:()=>'
No results found
',loading:()=>'
',not_loading:()=>{},dropdown:()=>"
"};e.settings.render=Object.assign({},s,e.settings.render)}setupCallbacks(){var e,t,n={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",item_select:"onItemSelect",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(e in n)(t=this.settings[n[e]])&&this.on(e,t)}sync(e=!0){const t=this,n=e?Fe(t.input,{delimiter:t.settings.delimiter}):t.settings;t.setupOptions(n.options,n.optgroups),t.setValue(n.items||[],!0),t.lastQuery=null}onClick(){var e=this;if(e.activeItems.length>0)return e.clearActiveItems(),void e.focus();e.isFocused&&e.isOpen?e.blur():e.focus()}onMouseDown(){}onChange(){U(this.input,"input"),U(this.input,"change")}onPaste(e){var t=this;t.isInputHidden||t.isLocked?Ae(e):t.settings.splitOn&&setTimeout((()=>{var e=t.inputValue();if(e.match(t.settings.splitOn)){var n=e.trim().split(t.settings.splitOn);D(n,(e=>{fe(e)&&(this.options[e]?t.addItem(e):t.createItem(e))}))}}),0)}onKeyPress(e){var t=this;if(!t.isLocked){var n=String.fromCharCode(e.keyCode||e.which);return t.settings.create&&"multi"===t.settings.mode&&n===t.settings.delimiter?(t.createItem(),void Ae(e)):void 0}Ae(e)}onKeyDown(e){var t=this;if(t.ignoreHover=!0,t.isLocked)e.keyCode!==pe&&Ae(e);else{switch(e.keyCode){case ie:if(Se(me,e)&&""==t.control_input.value)return Ae(e),void t.selectAll();break;case oe:return t.isOpen&&(Ae(e,!0),t.close()),void t.clearActiveItems();case de:if(!t.isOpen&&t.hasOptions)t.open();else if(t.activeOption){let e=t.getAdjacent(t.activeOption,1);e&&t.setActiveOption(e)}return void Ae(e);case le:if(t.activeOption){let e=t.getAdjacent(t.activeOption,-1);e&&t.setActiveOption(e)}return void Ae(e);case re:return void(t.canSelect(t.activeOption)?(t.onOptionSelect(e,t.activeOption),Ae(e)):(t.settings.create&&t.createItem()||document.activeElement==t.control_input&&t.isOpen)&&Ae(e));case ae:return void t.advanceSelection(-1,e);case ce:return void t.advanceSelection(1,e);case pe:return void(t.settings.selectOnTab&&(t.canSelect(t.activeOption)&&(t.onOptionSelect(e,t.activeOption),Ae(e)),t.settings.create&&t.createItem()&&Ae(e)));case ue:case he:return void t.deleteSelection(e)}t.isInputHidden&&!Se(me,e)&&Ae(e)}}onInput(e){if(this.isLocked)return;const t=this.inputValue();this.lastValue!==t&&(this.lastValue=t,""!=t?(this.refreshTimeout&&clearTimeout(this.refreshTimeout),this.refreshTimeout=ye((()=>{this.refreshTimeout=null,this._onInput()}),this.settings.refreshThrottle)):this._onInput())}_onInput(){const e=this.lastValue;this.settings.shouldLoad.call(this,e)&&this.load(e),this.refreshOptions(),this.trigger("type",e)}onOptionHover(e,t){this.ignoreHover||this.setActiveOption(t,!1)}onFocus(e){var t=this,n=t.isFocused;if(t.isDisabled||t.isReadOnly)return t.blur(),void Ae(e);t.ignoreFocus||(t.isFocused=!0,"focus"===t.settings.preload&&t.preload(),n||t.trigger("focus"),t.activeItems.length||(t.inputState(),t.refreshOptions(!!t.settings.openOnFocus)),t.refreshState())}onBlur(e){if(!1!==document.hasFocus()){var t=this;if(t.isFocused){t.isFocused=!1,t.ignoreFocus=!1;var n=()=>{t.close(),t.setActiveItem(),t.setCaret(t.items.length),t.trigger("blur")};t.settings.create&&t.settings.createOnBlur?t.createItem(null,n):n()}}}onOptionSelect(e,t){var n,s=this;t.parentElement&&t.parentElement.matches("[data-disabled]")||(t.classList.contains("create")?s.createItem(null,(()=>{s.settings.closeAfterSelect&&s.close()})):void 0!==(n=t.dataset.value)&&(s.lastQuery=null,s.addItem(n),s.settings.closeAfterSelect&&s.close(),!s.settings.hideSelected&&e.type&&/click/.test(e.type)&&s.setActiveOption(t)))}canSelect(e){return!!(this.isOpen&&e&&this.dropdown_content.contains(e))}onItemSelect(e,t){var n=this;return!n.isLocked&&"multi"===n.settings.mode&&(Ae(e),n.setActiveItem(t,e),!0)}canLoad(e){return!!this.settings.load&&!this.loadedSearches.hasOwnProperty(e)}load(e){const t=this;if(!t.canLoad(e))return;z(t.wrapper,t.settings.loadingClass),t.loading++;const n=t.loadCallback.bind(t);t.settings.load.call(t,e,n)}loadCallback(e,t){const n=this;n.loading=Math.max(n.loading-1,0),n.lastQuery=null,n.clearActiveOption(),n.setupOptions(e,t),n.refreshOptions(n.isFocused&&!n.isInputHidden),n.loading||W(n.wrapper,n.settings.loadingClass),n.trigger("load",e,t)}preload(){var e=this.wrapper.classList;e.contains("preloaded")||(e.add("preloaded"),this.load(""))}setTextboxValue(e=""){var t=this.control_input;t.value!==e&&(t.value=e,U(t,"update"),this.lastValue=e)}getValue(){return this.is_select_tag&&this.input.hasAttribute("multiple")?this.items:this.items.join(this.settings.delimiter)}setValue(e,t){Ee(this,t?[]:["change"],(()=>{this.clear(t),this.addItems(e,t)}))}setMaxItems(e){0===e&&(e=null),this.settings.maxItems=e,this.refreshState()}setActiveItem(e,t){var n,s,i,r,o,a,l=this;if("single"!==l.settings.mode){if(!e)return l.clearActiveItems(),void(l.isFocused&&l.inputState());if("click"===(n=t&&t.type.toLowerCase())&&Se("shiftKey",t)&&l.activeItems.length){for(a=l.getLastActive(),(i=Array.prototype.indexOf.call(l.control.children,a))>(r=Array.prototype.indexOf.call(l.control.children,e))&&(o=i,i=r,r=o),s=i;s<=r;s++)e=l.control.children[s],-1===l.activeItems.indexOf(e)&&l.setActiveItemClass(e);Ae(t)}else"click"===n&&Se(me,t)||"keydown"===n&&Se("shiftKey",t)?e.classList.contains("active")?l.removeActiveItem(e):l.setActiveItemClass(e):(l.clearActiveItems(),l.setActiveItemClass(e));l.inputState(),l.isFocused||l.focus()}}setActiveItemClass(e){const t=this,n=t.control.querySelector(".last-active");n&&W(n,"last-active"),z(e,"active last-active"),t.trigger("item_select",e),-1==t.activeItems.indexOf(e)&&t.activeItems.push(e)}removeActiveItem(e){var t=this.activeItems.indexOf(e);this.activeItems.splice(t,1),W(e,"active")}clearActiveItems(){W(this.activeItems,"active"),this.activeItems=[]}setActiveOption(e,t=!0){e!==this.activeOption&&(this.clearActiveOption(),e&&(this.activeOption=e,ee(this.focus_node,{"aria-activedescendant":e.getAttribute("id")}),ee(e,{"aria-selected":"true"}),z(e,"active"),t&&this.scrollToOption(e)))}scrollToOption(e,t){if(!e)return;const n=this.dropdown_content,s=n.clientHeight,i=n.scrollTop||0,r=e.offsetHeight,o=e.getBoundingClientRect().top-n.getBoundingClientRect().top+i;o+r>s+i?this.scroll(o-s+r,t):o{e.setActiveItemClass(t)})))}inputState(){var e=this;e.control.contains(e.control_input)&&(ee(e.control_input,{placeholder:e.settings.placeholder}),e.activeItems.length>0||!e.isFocused&&e.settings.hidePlaceholder&&e.items.length>0?(e.setTextboxValue(),e.isInputHidden=!0):(e.settings.hidePlaceholder&&e.items.length>0&&ee(e.control_input,{placeholder:""}),e.isInputHidden=!1),e.wrapper.classList.toggle("input-hidden",e.isInputHidden))}inputValue(){return this.control_input.value.trim()}focus(){var e=this;e.isDisabled||e.isReadOnly||(e.ignoreFocus=!0,e.control_input.offsetWidth?e.control_input.focus():e.focus_node.focus(),setTimeout((()=>{e.ignoreFocus=!1,e.onFocus()}),0))}blur(){this.focus_node.blur(),this.onBlur()}getScoreFunction(e){return this.sifter.getScoreFunction(e,this.getSearchOptions())}getSearchOptions(){var e=this.settings,t=e.sortField;return"string"==typeof e.sortField&&(t=[{field:e.sortField}]),{fields:e.searchField,conjunction:e.searchConjunction,sort:t,nesting:e.nesting}}search(e){var t,n,s=this,i=this.getSearchOptions();if(s.settings.score&&"function"!=typeof(n=s.settings.score.call(s,e)))throw new Error('Tom Select "score" setting must be a function that returns a function');return e!==s.lastQuery?(s.lastQuery=e,t=s.sifter.search(e,Object.assign(i,{score:n})),s.currentResults=t):t=Object.assign({},s.currentResults),s.settings.hideSelected&&(t.items=t.items.filter((e=>{let t=fe(e.id);return!(t&&-1!==s.items.indexOf(t))}))),t}refreshOptions(e=!0){var t,n,s,i,r,o,a,l,c,d;const u={},h=[];var p=this,m=p.inputValue();const g=m===p.lastQuery||""==m&&null==p.lastQuery;var f=p.search(m),v=null,b=p.settings.shouldOpen||!1,y=p.dropdown_content;g&&(v=p.activeOption)&&(c=v.closest("[data-group]")),i=f.items.length,"number"==typeof p.settings.maxOptions&&(i=Math.min(i,p.settings.maxOptions)),i>0&&(b=!0);const O=(e,t)=>{let n=u[e];if(void 0!==n){let e=h[n];if(void 0!==e)return[n,e.fragment]}let s=document.createDocumentFragment();return n=h.length,h.push({fragment:s,order:t,optgroup:e}),[n,s]};for(t=0;t0&&(d=d.cloneNode(!0),ee(d,{id:a.$id+"-clone-"+n,"aria-selected":null}),d.classList.add("ts-cloned"),W(d,"active"),p.activeOption&&p.activeOption.dataset.value==i&&c&&c.dataset.group===r.toString()&&(v=d)),l.appendChild(d),""!=r&&(u[r]=s)}}p.settings.lockOptgroupOrder&&h.sort(((e,t)=>e.order-t.order)),a=document.createDocumentFragment(),D(h,(e=>{let t=e.fragment,n=e.optgroup;if(!t||!t.children.length)return;let s=p.optgroups[n];if(void 0!==s){let e=document.createDocumentFragment(),n=p.render("optgroup_header",s);Te(e,n),Te(e,t);let i=p.render("optgroup",{group:s,options:e});Te(a,i)}else Te(a,t)})),y.innerHTML="",Te(y,a),p.settings.highlight&&(se(y),f.query.length&&f.tokens.length&&D(f.tokens,(e=>{ne(y,e.regex)})));var E=e=>{let t=p.render(e,{input:m});return t&&(b=!0,y.insertBefore(t,y.firstChild)),t};if(p.loading?E("loading"):p.settings.shouldLoad.call(p,m)?0===f.items.length&&E("no_results"):E("not_loading"),(l=p.canCreate(m))&&(d=E("option_create")),p.hasOptions=f.items.length>0||l,b){if(f.items.length>0){if(v||"single"!==p.settings.mode||null==p.items[0]||(v=p.getOption(p.items[0])),!y.contains(v)){let e=0;d&&!p.settings.addPrecedence&&(e=1),v=p.selectable()[e]}}else d&&(v=d);e&&!p.isOpen&&(p.open(),p.scrollToOption(v,"auto")),p.setActiveOption(v)}else p.clearActiveOption(),e&&p.isOpen&&p.close(!1)}selectable(){return this.dropdown_content.querySelectorAll("[data-selectable]")}addOption(e,t=!1){const n=this;if(Array.isArray(e))return n.addOptions(e,t),!1;const s=fe(e[n.settings.valueField]);return null!==s&&!n.options.hasOwnProperty(s)&&(e.$order=e.$order||++n.order,e.$id=n.inputId+"-opt-"+e.$order,n.options[s]=e,n.lastQuery=null,t&&(n.userOptions[s]=t,n.trigger("option_add",s,e)),s)}addOptions(e,t=!1){D(e,(e=>{this.addOption(e,t)}))}registerOption(e){return this.addOption(e)}registerOptionGroup(e){var t=fe(e[this.settings.optgroupValueField]);return null!==t&&(e.$order=e.$order||++this.order,this.optgroups[t]=e,t)}addOptionGroup(e,t){var n;t[this.settings.optgroupValueField]=e,(n=this.registerOptionGroup(t))&&this.trigger("optgroup_add",n,t)}removeOptionGroup(e){this.optgroups.hasOwnProperty(e)&&(delete this.optgroups[e],this.clearCache(),this.trigger("optgroup_remove",e))}clearOptionGroups(){this.optgroups={},this.clearCache(),this.trigger("optgroup_clear")}updateOption(e,t){const n=this;var s,i;const r=fe(e),o=fe(t[n.settings.valueField]);if(null===r)return;const a=n.options[r];if(null==a)return;if("string"!=typeof o)throw new Error("Value must be set in option data");const l=n.getOption(r),c=n.getItem(r);if(t.$order=t.$order||a.$order,delete n.options[r],n.uncacheValue(o),n.options[o]=t,l){if(n.dropdown_content.contains(l)){const e=n._render("option",t);te(l,e),n.activeOption===l&&n.setActiveOption(e)}l.remove()}c&&(-1!==(i=n.items.indexOf(r))&&n.items.splice(i,1,o),s=n._render("item",t),c.classList.contains("active")&&z(s,"active"),te(c,s)),n.lastQuery=null}removeOption(e,t){const n=this;e=ve(e),n.uncacheValue(e),delete n.userOptions[e],delete n.options[e],n.lastQuery=null,n.trigger("option_remove",e),n.removeItem(e,t)}clearOptions(e){const t=(e||this.clearFilter).bind(this);this.loadedSearches={},this.userOptions={},this.clearCache();const n={};D(this.options,((e,s)=>{t(e,s)&&(n[s]=e)})),this.options=this.sifter.items=n,this.lastQuery=null,this.trigger("option_clear")}clearFilter(e,t){return this.items.indexOf(t)>=0}getOption(e,t=!1){const n=fe(e);if(null===n)return null;const s=this.options[n];if(null!=s){if(s.$div)return s.$div;if(t)return this._render("option",s)}return null}getAdjacent(e,t,n="option"){var s,i=this;if(!e)return null;s="item"==n?i.controlChildren():i.dropdown_content.querySelectorAll("[data-selectable]");for(let n=0;n0?s[n+1]:s[n-1];return null}getItem(e){if("object"==typeof e)return e;var t=fe(e);return null!==t?this.control.querySelector(`[data-value="${Me(t)}"]`):null}addItems(e,t){var n=this,s=Array.isArray(e)?e:[e];const i=(s=s.filter((e=>-1===n.items.indexOf(e))))[s.length-1];s.forEach((e=>{n.isPending=e!==i,n.addItem(e,t)}))}addItem(e,t){Ee(this,t?[]:["change","dropdown_close"],(()=>{var n,s;const i=this,r=i.settings.mode,o=fe(e);if((!o||-1===i.items.indexOf(o)||("single"===r&&i.close(),"single"!==r&&i.settings.duplicates))&&null!==o&&i.options.hasOwnProperty(o)&&("single"===r&&i.clear(t),"multi"!==r||!i.isFull())){if(n=i._render("item",i.options[o]),i.control.contains(n)&&(n=n.cloneNode(!0)),s=i.isFull(),i.items.splice(i.caretPos,0,o),i.insertAtCaret(n),i.isSetup){if(!i.isPending&&i.settings.hideSelected){let e=i.getOption(o),t=i.getAdjacent(e,1);t&&i.setActiveOption(t)}i.isPending||i.settings.closeAfterSelect||i.refreshOptions(i.isFocused&&"single"!==r),0!=i.settings.closeAfterSelect&&i.isFull()?i.close():i.isPending||i.positionDropdown(),i.trigger("item_add",o,n),i.isPending||i.updateOriginalInput({silent:t})}(!i.isPending||!s&&i.isFull())&&(i.inputState(),i.refreshState())}}))}removeItem(e=null,t){const n=this;if(!(e=n.getItem(e)))return;var s,i;const r=e.dataset.value;s=Z(e),e.remove(),e.classList.contains("active")&&(i=n.activeItems.indexOf(e),n.activeItems.splice(i,1),W(e,"active")),n.items.splice(s,1),n.lastQuery=null,!n.settings.persist&&n.userOptions.hasOwnProperty(r)&&n.removeOption(r,t),s{}){3===arguments.length&&(t=arguments[2]),"function"!=typeof t&&(t=()=>{});var n,s=this,i=s.caretPos;if(e=e||s.inputValue(),!s.canCreate(e))return t(),!1;s.lock();var r=!1,o=e=>{if(s.unlock(),!e||"object"!=typeof e)return t();var n=fe(e[s.settings.valueField]);if("string"!=typeof n)return t();s.setTextboxValue(),s.addOption(e,!0),s.setCaret(i),s.addItem(n),t(e),r=!0};return n="function"==typeof s.settings.create?s.settings.create.call(this,e,o):{[s.settings.labelField]:e,[s.settings.valueField]:e},r||o(n),!0}refreshItems(){var e=this;e.lastQuery=null,e.isSetup&&e.addItems(e.items),e.updateOriginalInput(),e.refreshState()}refreshState(){const e=this;e.refreshValidityState();const t=e.isFull(),n=e.isLocked;e.wrapper.classList.toggle("rtl",e.rtl);const s=e.wrapper.classList;s.toggle("focus",e.isFocused),s.toggle("disabled",e.isDisabled),s.toggle("readonly",e.isReadOnly),s.toggle("required",e.isRequired),s.toggle("invalid",!e.isValid),s.toggle("locked",n),s.toggle("full",t),s.toggle("input-active",e.isFocused&&!e.isInputHidden),s.toggle("dropdown-active",e.isOpen),s.toggle("has-options",Y(e.options)),s.toggle("has-items",e.items.length>0)}refreshValidityState(){var e=this;e.input.validity&&(e.isValid=e.input.validity.valid,e.isInvalid=!e.isValid)}isFull(){return null!==this.settings.maxItems&&this.items.length>=this.settings.maxItems}updateOriginalInput(e={}){const t=this;var n,s;const i=t.input.querySelector('option[value=""]');if(t.is_select_tag){const r=[],o=t.input.querySelectorAll("option:checked").length;function a(e,n,s){return e||(e=R('")),e!=i&&t.input.append(e),r.push(e),(e!=i||o>0)&&(e.selected=!0),e}t.input.querySelectorAll("option:checked").forEach((e=>{e.selected=!1})),0==t.items.length&&"single"==t.settings.mode?a(i,"",""):t.items.forEach((e=>{n=t.options[e],s=n[t.settings.labelField]||"",r.includes(n.$option)?a(t.input.querySelector(`option[value="${Me(e)}"]:not(:checked)`),e,s):n.$option=a(n.$option,e,s)}))}else t.input.value=t.getValue();t.isSetup&&(e.silent||t.trigger("change",t.getValue()))}open(){var e=this;e.isLocked||e.isOpen||"multi"===e.settings.mode&&e.isFull()||(e.isOpen=!0,ee(e.focus_node,{"aria-expanded":"true"}),e.refreshState(),K(e.dropdown,{visibility:"hidden",display:"block"}),e.positionDropdown(),K(e.dropdown,{visibility:"visible",display:"block"}),e.focus(),e.trigger("dropdown_open",e.dropdown))}close(e=!0){var t=this,n=t.isOpen;e&&(t.setTextboxValue(),"single"===t.settings.mode&&t.items.length&&t.inputState()),t.isOpen=!1,ee(t.focus_node,{"aria-expanded":"false"}),K(t.dropdown,{display:"none"}),t.settings.hideSelected&&t.clearActiveOption(),t.refreshState(),n&&t.trigger("dropdown_close",t.dropdown)}positionDropdown(){if("body"===this.settings.dropdownParent){var e=this.control,t=e.getBoundingClientRect(),n=e.offsetHeight+t.top+window.scrollY,s=t.left+window.scrollX;K(this.dropdown,{width:t.width+"px",top:n+"px",left:s+"px"})}}clear(e){var t=this;if(t.items.length){var n=t.controlChildren();D(n,(e=>{t.removeItem(e,!0)})),t.inputState(),e||t.updateOriginalInput(),t.trigger("clear")}}insertAtCaret(e){const t=this,n=t.caretPos,s=t.control;s.insertBefore(e,s.children[n]||null),t.setCaret(n+1)}deleteSelection(e){var t,n,s,i,r=this;t=e&&e.keyCode===ue?-1:1,n=we(r.control_input);const o=[];if(r.activeItems.length)i=X(r.activeItems,t),s=Z(i),t>0&&s++,D(r.activeItems,(e=>o.push(e)));else if((r.isFocused||"single"===r.settings.mode)&&r.items.length){const e=r.controlChildren();let s;t<0&&0===n.start&&0===n.length?s=e[r.caretPos-1]:t>0&&n.start===r.inputValue().length&&(s=e[r.caretPos]),void 0!==s&&o.push(s)}if(!r.shouldDelete(o,e))return!1;for(Ae(e,!0),void 0!==s&&r.setCaret(s);o.length;)r.removeItem(o.pop());return r.inputState(),r.positionDropdown(),r.refreshOptions(!1),!0}shouldDelete(e,t){const n=e.map((e=>e.dataset.value));return!(!n.length||"function"==typeof this.settings.onDelete&&!1===this.settings.onDelete(n,t))}advanceSelection(e,t){var n,s,i=this;i.rtl&&(e*=-1),i.inputValue().length||(Se(me,t)||Se("shiftKey",t)?(s=(n=i.getLastActive(e))?n.classList.contains("active")?i.getAdjacent(n,e,"item"):n:e>0?i.control_input.nextElementSibling:i.control_input.previousElementSibling)&&(s.classList.contains("active")&&i.removeActiveItem(n),i.setActiveItemClass(s)):i.moveCaret(e))}moveCaret(e){}getLastActive(e){let t=this.control.querySelector(".last-active");if(t)return t;var n=this.control.querySelectorAll(".active");return n?X(n,e):void 0}setCaret(e){this.caretPos=this.items.length}controlChildren(){return Array.from(this.control.querySelectorAll("[data-ts-item]"))}lock(){this.setLocked(!0)}unlock(){this.setLocked(!1)}setLocked(e=this.isReadOnly||this.isDisabled){this.isLocked=e,this.refreshState()}disable(){this.setDisabled(!0),this.close()}enable(){this.setDisabled(!1)}setDisabled(e){this.focus_node.tabIndex=e?-1:this.tabIndex,this.isDisabled=e,this.input.disabled=e,this.control_input.disabled=e,this.setLocked()}setReadOnly(e){this.isReadOnly=e,this.input.readOnly=e,this.control_input.readOnly=e,this.setLocked()}destroy(){var e=this,t=e.revertSettings;e.trigger("destroy"),e.off(),e.wrapper.remove(),e.dropdown.remove(),e.input.innerHTML=t.innerHTML,e.input.tabIndex=t.tabIndex,W(e.input,"tomselected","ts-hidden-accessible"),e._destroy(),delete e.input.tomselect}render(e,t){var n,s;const i=this;if("function"!=typeof this.settings.render[e])return null;if(!(s=i.settings.render[e].call(this,t,be)))return null;if(s=R(s),"option"===e||"option_create"===e?t[i.settings.disabledField]?ee(s,{"aria-disabled":"true"}):ee(s,{"data-selectable":""}):"optgroup"===e&&(n=t.group[i.settings.optgroupValueField],ee(s,{"data-group":n}),t.group[i.settings.disabledField]&&ee(s,{"data-disabled":""})),"option"===e||"item"===e){const n=ve(t[i.settings.valueField]);ee(s,{"data-value":n}),"item"===e?(z(s,i.settings.itemClass),ee(s,{"data-ts-item":""})):(z(s,i.settings.optionClass),ee(s,{role:"option",id:t.$id}),t.$div=s,i.options[n]=t)}return s}_render(e,t){const n=this.render(e,t);if(null==n)throw"HTMLElement expected";return n}clearCache(){D(this.options,(e=>{e.$div&&(e.$div.remove(),delete e.$div)}))}uncacheValue(e){const t=this.getOption(e);t&&t.remove()}canCreate(e){return this.settings.create&&e.length>0&&this.settings.createFilter.call(this,e)}hook(e,t,n){var s=this,i=s[t];s[t]=function(){var t,r;return"after"===e&&(t=i.apply(s,arguments)),r=n.apply(s,arguments),"instead"===e?r:("before"===e&&(t=i.apply(s,arguments)),t)}}}function Le(){ke(this.input,"change",(()=>{this.sync()}))}function Ne(e){var t=this,n=t.onOptionSelect;t.settings.hideSelected=!1;const s=Object.assign({className:"tomselect-checkbox",checkedClassNames:void 0,uncheckedClassNames:void 0},e);var i=function(e,t){t?(e.checked=!0,s.uncheckedClassNames&&e.classList.remove(...s.uncheckedClassNames),s.checkedClassNames&&e.classList.add(...s.checkedClassNames)):(e.checked=!1,s.checkedClassNames&&e.classList.remove(...s.checkedClassNames),s.uncheckedClassNames&&e.classList.add(...s.uncheckedClassNames))},r=function(e){setTimeout((()=>{var t=e.querySelector("input."+s.className);t instanceof HTMLInputElement&&i(t,e.classList.contains("selected"))}),1)};t.hook("after","setupTemplates",(()=>{var e=t.settings.render.option;t.settings.render.option=(n,r)=>{var o=R(e.call(t,n,r)),a=document.createElement("input");s.className&&a.classList.add(s.className),a.addEventListener("click",(function(e){Ae(e)})),a.type="checkbox";const l=fe(n[t.settings.valueField]);return i(a,!!(l&&t.items.indexOf(l)>-1)),o.prepend(a),o}})),t.on("item_remove",(e=>{var n=t.getOption(e);n&&(n.classList.remove("selected"),r(n))})),t.on("item_add",(e=>{var n=t.getOption(e);n&&r(n)})),t.hook("instead","onOptionSelect",((e,s)=>{if(s.classList.contains("selected"))return s.classList.remove("selected"),t.removeItem(s.dataset.value),t.refreshOptions(),void Ae(e,!0);n.call(t,e,s),r(s)}))}function Pe(e){const t=this,n=Object.assign({className:"clear-button",title:"Clear All",html:e=>`
⨯
`},e);t.on("initialize",(()=>{var e=R(n.html(n));e.addEventListener("click",(e=>{t.isLocked||(t.clear(),"single"===t.settings.mode&&t.settings.allowEmptyOption&&t.addItem(""),e.preventDefault(),e.stopPropagation())})),t.control.appendChild(e)}))}const _e=(e,t)=>{var n;null==(n=e.parentNode)||n.insertBefore(t,e.nextSibling)},$e=(e,t)=>{var n;null==(n=e.parentNode)||n.insertBefore(t,e)},De=(e,t)=>{do{var n;if(e==(t=null==(n=t)?void 0:n.previousElementSibling))return!0}while(t&&t.previousElementSibling);return!1};function Ve(){var e=this;if("multi"!==e.settings.mode)return;var t=e.lock,n=e.unlock;let s,i=!0;e.hook("after","setupTemplates",(()=>{var t=e.settings.render.item;e.settings.render.item=(n,r)=>{const o=R(t.call(e,n,r));ee(o,{draggable:"true"});const a=e=>{s=o,setTimeout((()=>{o.classList.add("ts-dragging")}),0)},l=e=>{e.preventDefault(),o.classList.add("ts-drag-over"),d(o,s)},c=()=>{o.classList.remove("ts-drag-over")},d=(e,t)=>{void 0!==t&&(De(t,o)?_e(e,t):$e(e,t))},u=()=>{var t;document.querySelectorAll(".ts-drag-over").forEach((e=>e.classList.remove("ts-drag-over"))),null==(t=s)||t.classList.remove("ts-dragging"),s=void 0;var n=[];e.control.querySelectorAll("[data-value]").forEach((e=>{if(e.dataset.value){let t=e.dataset.value;t&&n.push(t)}})),e.setValue(n)};return ke(o,"mousedown",(e=>{i||Ae(e),e.stopPropagation()})),ke(o,"dragstart",a),ke(o,"dragenter",l),ke(o,"dragover",l),ke(o,"dragleave",c),ke(o,"dragend",u),o}})),e.hook("instead","lock",(()=>(i=!1,t.call(e)))),e.hook("instead","unlock",(()=>(i=!0,n.call(e))))}function je(e){const t=this,n=Object.assign({title:"Untitled",headerClass:"dropdown-header",titleRowClass:"dropdown-header-title",labelClass:"dropdown-header-label",closeClass:"dropdown-header-close",html:e=>'
'+e.title+'×
'},e);t.on("initialize",(()=>{var e=R(n.html(n)),s=e.querySelector("."+n.closeClass);s&&s.addEventListener("click",(e=>{Ae(e,!0),t.close()})),t.dropdown.insertBefore(e,t.dropdown.firstChild)}))}function Be(){var e=this;e.hook("instead","setCaret",(t=>{"single"!==e.settings.mode&&e.control.contains(e.control_input)?(t=Math.max(0,Math.min(e.items.length,t)))==e.caretPos||e.isPending||e.controlChildren().forEach(((n,s)=>{s{if(!e.isFocused)return;const n=e.getLastActive(t);if(n){const s=Z(n);e.setCaret(t>0?s+1:s),e.setActiveItem(),W(n,"last-active")}else e.setCaret(e.caretPos+t)}))}function Re(){const e=this;e.settings.shouldOpen=!0,e.hook("before","setup",(()=>{e.focus_node=e.control,z(e.control_input,"dropdown-input");const t=R('