alchemy_cms 8.1.9 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -1
- data/app/assets/builds/alchemy/admin.css +1 -1
- data/app/assets/builds/alchemy/alchemy_admin.min.js +2 -0
- data/app/assets/builds/alchemy/alchemy_admin.min.js.map +1 -0
- data/app/assets/builds/alchemy/dark-theme.css +1 -1
- data/app/assets/builds/alchemy/light-theme.css +1 -1
- data/app/assets/builds/alchemy/theme.css +1 -1
- data/app/assets/builds/alchemy/welcome.css +1 -1
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
- data/app/assets/builds/tinymce/skins/content/alchemy-dark/content.min.css +1 -1
- data/app/assets/images/alchemy/icons-sprite.svg +1 -1
- data/app/components/alchemy/admin/current_user_name.rb +34 -0
- data/app/components/alchemy/admin/locale_select.rb +12 -8
- data/app/components/alchemy/admin/page_node.html.erb +3 -2
- data/app/components/alchemy/admin/picture_thumbnail.rb +1 -1
- data/app/components/alchemy/admin/preview_time_select.rb +55 -0
- data/app/components/alchemy/admin/publish_element_button.html.erb +41 -0
- data/app/components/alchemy/admin/publish_element_button.rb +13 -0
- data/app/components/alchemy/admin/timezone_select.rb +47 -0
- data/app/components/alchemy/ingredients/select_editor.rb +6 -1
- data/app/controllers/alchemy/admin/base_controller.rb +1 -0
- data/app/controllers/alchemy/admin/elements_controller.rb +54 -34
- data/app/controllers/alchemy/admin/pages_controller.rb +1 -0
- data/app/controllers/alchemy/admin/resources_controller.rb +11 -6
- data/app/controllers/alchemy/pages_controller.rb +1 -2
- data/app/helpers/alchemy/admin/base_helper.rb +4 -7
- data/app/helpers/alchemy/url_helper.rb +2 -10
- data/app/javascript/alchemy_admin/components/element_editor/publish_element_button.js +28 -27
- data/app/javascript/alchemy_admin/components/element_editor.js +11 -2
- data/app/javascript/alchemy_admin/components/message.js +5 -1
- data/app/javascript/alchemy_admin/components/uploader/file_upload.js +5 -5
- data/app/javascript/alchemy_admin/image_cropper.js +10 -6
- data/app/javascript/alchemy_admin/initializer.js +6 -33
- data/app/javascript/alchemy_admin/shoelace_theme.js +6 -2
- data/app/javascript/alchemy_admin/templates/compiled.js +1 -1
- data/app/javascript/alchemy_admin.js +12 -2
- data/app/models/alchemy/attachment.rb +1 -1
- data/app/models/alchemy/current.rb +5 -1
- data/app/models/alchemy/element/element_ingredients.rb +11 -3
- data/app/models/alchemy/element.rb +10 -0
- data/app/models/alchemy/ingredient.rb +2 -0
- data/app/models/alchemy/ingredients/select.rb +1 -2
- data/app/models/alchemy/page/etag_generator.rb +21 -0
- data/app/models/alchemy/page/url_path.rb +11 -2
- data/app/models/alchemy/page.rb +12 -2
- data/app/models/alchemy/page_version.rb +5 -5
- data/app/models/alchemy/picture.rb +19 -2
- data/app/models/alchemy/storage_adapter/active_storage.rb +9 -0
- data/app/models/alchemy/storage_adapter/dragonfly.rb +9 -0
- data/app/models/alchemy/storage_adapter.rb +1 -0
- data/app/models/concerns/alchemy/publishable.rb +20 -12
- data/app/models/concerns/alchemy/relatable_resource.rb +16 -2
- data/app/models/concerns/alchemy/touch_elements.rb +3 -3
- data/app/services/alchemy/element_preloader.rb +107 -0
- data/app/stylesheets/alchemy/_custom-properties.scss +1 -0
- data/app/stylesheets/alchemy/_mixins.scss +1 -1
- data/app/stylesheets/alchemy/_themes.scss +2 -0
- data/app/stylesheets/alchemy/admin/base.scss +2 -1
- data/app/stylesheets/alchemy/admin/elements.scss +22 -19
- data/app/stylesheets/alchemy/admin/form_fields.scss +3 -0
- data/app/stylesheets/alchemy/admin/forms.scss +14 -1
- data/app/stylesheets/alchemy/admin/frame.scss +9 -8
- data/app/stylesheets/alchemy/admin/notices.scss +1 -1
- data/app/stylesheets/alchemy/admin/popover.scss +37 -0
- data/app/stylesheets/alchemy/admin/selects.scss +4 -0
- data/app/stylesheets/alchemy/admin/shoelace.scss +16 -4
- data/app/stylesheets/alchemy/admin/toolbar.scss +8 -0
- data/app/stylesheets/alchemy/admin.scss +1 -0
- data/app/views/alchemy/admin/_header.html.erb +4 -0
- data/app/views/alchemy/admin/_left_menu.html.erb +24 -0
- data/app/views/alchemy/admin/_main_navi.html.erb +6 -0
- data/app/views/alchemy/admin/_top_menu.html.erb +6 -0
- data/app/views/alchemy/admin/_user_info.html.erb +5 -0
- data/app/views/alchemy/admin/crop.html.erb +6 -11
- data/app/views/alchemy/admin/elements/_header.html.erb +16 -6
- data/app/views/alchemy/admin/elements/_schedule.html.erb +62 -0
- data/app/views/alchemy/admin/elements/_toolbar.html.erb +1 -15
- data/app/views/alchemy/admin/elements/publish.turbo_stream.erb +28 -0
- data/app/views/alchemy/admin/nodes/index.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_locked_pages.html.erb +5 -0
- data/app/views/alchemy/admin/pages/_publication_fields.html.erb +4 -4
- data/app/views/alchemy/admin/pages/_table.html.erb +1 -1
- data/app/views/alchemy/admin/pages/edit.html.erb +6 -2
- data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +10 -10
- data/app/views/alchemy/admin/partials/_site_select.html.erb +6 -3
- data/app/views/alchemy/admin/pictures/index.html.erb +2 -2
- data/app/views/alchemy/admin/tinymce/_setup.html.erb +9 -16
- data/app/views/alchemy/admin/uploader/_setup.html.erb +1 -6
- data/app/views/alchemy/language_links/_language.html.erb +1 -2
- data/app/views/layouts/alchemy/admin.html.erb +2 -45
- data/config/importmap.rb +7 -2
- data/config/locales/alchemy.en.yml +35 -5
- data/lib/alchemy/admin/preview_time.rb +23 -0
- data/lib/alchemy/admin/preview_url.rb +13 -2
- data/lib/alchemy/admin/timezone.rb +56 -0
- data/lib/alchemy/configuration.rb +2 -0
- data/lib/alchemy/configurations/main.rb +13 -1
- data/lib/alchemy/tasks/tidy.rb +6 -7
- data/lib/alchemy/test_support/factories/element_factory.rb +2 -2
- data/lib/alchemy/test_support/relatable_resource_examples.rb +2 -2
- data/lib/alchemy/test_support/shared_publishable_examples.rb +44 -2
- data/lib/alchemy/upgrader.rb +3 -1
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +2 -0
- data/lib/generators/alchemy/install/install_generator.rb +2 -1
- data/vendor/javascript/handlebars.min.js +4 -4
- data/vendor/javascript/shoelace.min.js +1419 -1323
- data/vendor/javascript/sortable.min.js +2 -2
- data/vendor/javascript/tinymce.min.js +1 -1
- metadata +35 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alchemy_admin.min.js","sources":["../../../javascript/alchemy_admin/i18n.js","../../../javascript/alchemy_admin/hotkeys.js","../../../javascript/alchemy_admin/utils/dom_helpers.js","../../../javascript/alchemy_admin/spinner.js","../../../javascript/alchemy_admin/dialog.js","../../../javascript/alchemy_admin/confirm_dialog.js","../../../javascript/alchemy_admin/please_wait_overlay.js","../../../javascript/alchemy_admin/dirty.js","../../../javascript/alchemy_admin/fixed_elements.js","../../../javascript/alchemy_admin/growler.js","../../../javascript/alchemy_admin/link_dialog.js","../../../javascript/alchemy_admin/components/preview_window.js","../../../javascript/alchemy_admin/ingredient_anchor_link.js","../../../javascript/alchemy_admin/components/action.js","../../../javascript/alchemy_admin/components/alchemy_html_element.js","../../../javascript/alchemy_admin/utils/string_conversions.js","../../../javascript/alchemy_admin/components/remote_select.js","../../../javascript/alchemy_admin/components/attachment_select.js","../../../javascript/alchemy_admin/components/auto_submit.js","../../../javascript/alchemy_admin/components/button.js","../../../javascript/alchemy_admin/components/char_counter.js","../../../javascript/alchemy_admin/components/clipboard_button.js","../../../javascript/alchemy_admin/components/color_select.js","../../../javascript/alchemy_admin/components/datepicker.js","../../../javascript/alchemy_admin/components/dialog_link.js","../../../javascript/alchemy_admin/utils/ajax.js","../../../javascript/alchemy_admin/components/dom_id_select.js","../../../javascript/alchemy_admin/components/element_editor/publish_element_button.js","../../../javascript/alchemy_admin/components/element_editor/delete_element_button.js","../../../javascript/alchemy_admin/components/element_editor.js","../../../javascript/alchemy_admin/components/element_select.js","../../../javascript/alchemy_admin/components/elements_window.js","../../../javascript/alchemy_admin/components/elements_window_handle.js","../../../javascript/alchemy_admin/components/file_editor.js","../../../javascript/alchemy_admin/components/list_filter.js","../../../javascript/alchemy_admin/components/message.js","../../../javascript/alchemy_admin/components/growl.js","../../../javascript/alchemy_admin/components/icon.js","../../../javascript/alchemy_admin/components/ingredient_group.js","../../../javascript/alchemy_admin/components/link_buttons/link_button.js","../../../javascript/alchemy_admin/components/link_buttons/unlink_button.js","../../../javascript/alchemy_admin/components/link_buttons.js","../../../javascript/alchemy_admin/utils/format.js","../../../javascript/alchemy_admin/components/node_select.js","../../../javascript/alchemy_admin/components/uploader/file_upload.js","../../../javascript/alchemy_admin/components/uploader/progress.js","../../../javascript/alchemy_admin/components/uploader.js","../../../javascript/alchemy_admin/components/overlay.js","../../../javascript/alchemy_admin/components/page_node.js","../../../javascript/alchemy_admin/components/page_publication_fields.js","../../../javascript/alchemy_admin/components/page_select.js","../../../javascript/alchemy_admin/components/picture_description_select.js","../../../javascript/alchemy_admin/components/picture_editor.js","../../../javascript/alchemy_admin/utils/debounce.js","../../../javascript/alchemy_admin/utils/max.js","../../../javascript/alchemy_admin/components/picture_thumbnail.js","../../../javascript/alchemy_admin/components/publish_page_button.js","../../../javascript/alchemy_admin/components/select.js","../../../javascript/alchemy_admin/components/sitemap.js","../../../javascript/alchemy_admin/components/sortable_elements.js","../../../javascript/alchemy_admin/components/spinner.js","../../../javascript/alchemy_admin/components/tags_autocomplete.js","../../../javascript/alchemy_admin/components/tinymce.js","../../../javascript/alchemy_admin/components/update_check.js","../../../javascript/alchemy_admin/templates/compiled.js","../../../javascript/alchemy_admin/shoelace_theme.js","../../../javascript/alchemy_admin/utils/events.js","../../../javascript/alchemy_admin/image_cropper.js","../../../javascript/alchemy_admin/image_overlay.js","../../../javascript/alchemy_admin/picture_selector.js","../../../javascript/alchemy_admin/node_tree.js","../../../javascript/alchemy_admin.js","../../../javascript/alchemy_admin/initializer.js"],"sourcesContent":["const KEY_SEPARATOR = /\\./\n\nfunction nestedTranslation(translations, key) {\n const keys = key.split(KEY_SEPARATOR)\n const group = translations[keys[0]]\n if (group) {\n return group[keys[1]] || key\n }\n return key\n}\n\nfunction getTranslation(key) {\n const locale = currentLocale()\n const translations = Alchemy.translations\n\n if (!translations) {\n console.warn(`Translations for locale ${locale} not found!`)\n return key\n }\n\n if (KEY_SEPARATOR.test(key)) {\n return nestedTranslation(translations, key)\n }\n return translations[key] || key\n}\n\nexport function currentLocale() {\n if (document.documentElement.lang) {\n return document.documentElement.lang\n }\n return \"en\"\n}\n\nexport function translate(key, replacement = undefined) {\n let translation = getTranslation(key)\n\n if (replacement) {\n return translation.replace(/%\\{.+\\}/, replacement)\n }\n return translation\n}\n\nexport async function setupSelectLocale() {\n const locale = currentLocale()\n if (locale === \"en\") return\n\n await import(`select2/${locale}.js`)\n $.extend($.fn.select2.defaults, $.fn.select2.locales[locale])\n}\n","import \"keymaster\"\nimport { openDialog } from \"alchemy_admin/dialog\"\n\nconst bindedHotkeys = []\n\nfunction showHelp(evt) {\n if (\n !$(evt.target).is(\"input, textarea\") &&\n String.fromCharCode(evt.which) === \"?\"\n ) {\n openDialog(\"/admin/help\", {\n title: Alchemy.t(\"help\"),\n size: \"400x492\"\n })\n return false\n } else {\n return true\n }\n}\n\nexport default function (scope = document) {\n // The scope can be a jQuery object because we still use jQuery in alchemy_admin/dialog.js.\n if (scope instanceof jQuery) {\n scope = scope[0]\n }\n\n // Unbind all previously registered hotkeys if we are not inside a dialog.\n if (scope === document) {\n document.removeEventListener(\"keypress\", showHelp)\n document.addEventListener(\"keypress\", showHelp)\n bindedHotkeys.forEach((hotkey) => key.unbind(hotkey))\n }\n\n // Binds keyboard shortcuts to search fields.\n const search_fields = scope.querySelectorAll(\".search_input_field\")\n const search_fields_clear = scope.querySelectorAll(\n \".search_field_clear, .js_filter_field_clear\"\n )\n key(\"alt+f\", function () {\n key.setScope(\"search\")\n search_fields.forEach((el) => el.focus({ focusVisible: true }))\n return false\n })\n bindedHotkeys.push(\"alt+f\")\n key(\"esc\", \"search\", function () {\n search_fields_clear.forEach((el) => el.click())\n search_fields.forEach((el) => el.blur())\n })\n bindedHotkeys.push(\"esc\")\n\n // Binds click events to buttons with hotkeys.\n //\n // Simply add a data-alchemy-hotkey attribute to your link.\n // If a hotkey is triggered by user, the click event of the element gets triggerd.\n //\n scope.querySelectorAll(\"[data-alchemy-hotkey]\").forEach(function (el) {\n const hotkey = el.dataset.alchemyHotkey\n key(hotkey, () => el.click())\n bindedHotkeys.push(hotkey)\n })\n}\n","/**\n * create a HTML element\n * @param {string} text\n * @returns {HTMLElement}\n */\nexport function createHtmlElement(text) {\n const element = document.createElement(\"template\")\n element.innerHTML = text\n return element.content.children[0]\n}\n\n/**\n * wrap element with wrappingElement\n * @param {HTMLElement} element\n * @param {HTMLElement} wrappingElement\n */\nexport function wrap(element, wrappingElement) {\n element.replaceWith(wrappingElement)\n wrappingElement.appendChild(element)\n}\n","import { createHtmlElement } from \"alchemy_admin/utils/dom_helpers\"\n\nexport default class Spinner {\n constructor(size, color = \"currentColor\") {\n this.size = size\n this.color = color\n this.spinner = undefined\n }\n\n /**\n * @returns {HTMLElement|undefined}\n */\n get el() {\n return this.spinner\n }\n /**\n * @param {HTMLElement|undefined} parent\n */\n spin(parent) {\n if (typeof parent === \"undefined\") {\n parent = document.body\n }\n this.spinner = createHtmlElement(\n `<alchemy-spinner size=\"${this.size}\" color=\"${this.color}\"></alchemy-spinner>`\n )\n parent.append(this.spinner)\n return this\n }\n\n stop() {\n if (this.spinner) {\n this.spinner.remove()\n this.spinner = undefined\n }\n }\n}\n","import Hotkeys from \"alchemy_admin/hotkeys\"\nimport Spinner from \"alchemy_admin/spinner\"\n\n// Collection of all current dialog instances\nconst currentDialogs = []\n\nconst DEFAULTS = {\n header_height: 36,\n size: \"400x300\",\n padding: true,\n title: \"\",\n modal: true,\n overflow: \"visible\",\n ready: () => {},\n closed: () => {}\n}\n\nexport class Dialog {\n // Arguments:\n // - url: The url to load the content from via ajax\n // - options: A object holding options\n // - size: The maximum size of the Dialog\n // - title: The title of the Dialog\n constructor(url, options = {}) {\n this.url = url\n this.options = { ...DEFAULTS, ...options }\n this.$document = $(document)\n this.$window = $(window)\n this.$body = $(\"body\")\n const size = this.options.size.split(\"x\")\n this.width = parseInt(size[0], 10)\n this.height = parseInt(size[1], 10)\n this.build()\n this.resize()\n }\n\n // Opens the Dialog and loads the content via ajax.\n open() {\n this.dialog.trigger(\"Alchemy.DialogOpen\")\n this.bind_close_events()\n window.requestAnimationFrame(() => {\n this.dialog_container.addClass(\"open\")\n if (this.overlay != null) {\n return this.overlay.addClass(\"open\")\n }\n })\n this.$body.addClass(\"prevent-scrolling\")\n currentDialogs.push(this)\n this.load()\n }\n\n // Closes the Dialog and removes it from the DOM\n close() {\n this.dialog.trigger(\"DialogClose.Alchemy\")\n this.$document.off(\"keydown\")\n this.dialog_container.removeClass(\"open\")\n if (this.overlay != null) {\n this.overlay.removeClass(\"open\")\n }\n this.$document.on(\n \"webkitTransitionEnd transitionend oTransitionEnd\",\n () => {\n this.$document.off(\"webkitTransitionEnd transitionend oTransitionEnd\")\n this.dialog_container.remove()\n if (this.overlay != null) {\n this.overlay.remove()\n }\n this.$body.removeClass(\"prevent-scrolling\")\n currentDialogs.pop(this)\n if (this.options.closed != null) {\n return this.options.closed()\n }\n }\n )\n return true\n }\n\n // Loads the content via ajax and replaces the Dialog body with server response.\n load() {\n this.show_spinner()\n $.get(this.url, (data) => {\n this.replace(data)\n }).fail((xhr) => {\n this.show_error(xhr)\n })\n }\n\n // Reloads the Dialog content\n reload() {\n this.dialog_body.empty()\n this.load()\n }\n\n // Replaces the dialog body with given content and initializes it.\n replace(data) {\n this.remove_spinner()\n this.dialog_body.hide()\n this.dialog_body.html(data)\n this.init()\n this.dialog[0].dispatchEvent(\n new CustomEvent(\"DialogReady.Alchemy\", {\n bubbles: true,\n detail: {\n body: this.dialog_body[0]\n }\n })\n )\n if (this.options.ready != null) {\n this.options.ready(this.dialog_body)\n }\n this.dialog_body.show()\n }\n\n // Adds a spinner into Dialog body\n show_spinner() {\n this.spinner = new Spinner(\"medium\")\n this.spinner.spin(this.dialog_body[0])\n }\n\n // Removes the spinner from Dialog body\n remove_spinner() {\n this.spinner.stop()\n }\n\n // Initializes the Dialog body\n init() {\n Hotkeys(this.dialog_body)\n this.watch_remote_forms()\n }\n\n // Watches ajax requests inside of dialog body and replaces the content accordingly\n watch_remote_forms() {\n const $form = $('[data-remote=\"true\"]', this.dialog_body)\n\n $form.on(\"ajax:success\", (event) => {\n const xhr = event.detail[2]\n const content_type = xhr.getResponseHeader(\"Content-Type\")\n if (content_type.match(/javascript/)) {\n return\n } else {\n this.dialog_body.html(xhr.responseText)\n this.init()\n }\n })\n\n $form.on(\"ajax:error\", (event) => {\n const statusText = event.detail[1]\n const xhr = event.detail[2]\n this.show_error(xhr, statusText)\n })\n }\n\n // Displays an error message\n show_error(xhr, statusText) {\n if (xhr.status === 422) {\n this.dialog_body.html(xhr.responseText)\n this.init()\n return\n }\n\n const { error_body, error_header, error_type } = this.error_messages(\n xhr,\n statusText\n )\n\n const $errorDiv = $(`<alchemy-message type=\"${error_type}\">\n <h1>${error_header}</h1>\n <p>${error_body}</p>\n </alchemy-message>`)\n\n this.dialog_body.html($errorDiv)\n }\n\n // Returns error message based on xhr status\n error_messages(xhr, statusText) {\n let error_body,\n error_header,\n error_type = \"warning\"\n\n switch (xhr.status) {\n case 0:\n error_header = \"The server does not respond.\"\n error_body = \"Please check server and try again.\"\n break\n case 403:\n error_header = \"You are not authorized!\"\n error_body = \"Please close this window.\"\n break\n default:\n error_type = \"error\"\n if (statusText) {\n error_header = statusText\n console.error(xhr.responseText)\n } else {\n error_header = `${xhr.statusText} (${xhr.status})`\n }\n error_body = \"Please check log and try again.\"\n }\n\n return { error_header, error_body, error_type }\n }\n\n // Binds close events on:\n // - Close button\n // - Overlay (if the Dialog is a modal)\n // - ESC Key\n bind_close_events() {\n this.close_button.on(\"click\", () => {\n this.close()\n })\n this.dialog_container.addClass(\"closable\").on(\"click\", (e) => {\n if (e.target !== this.dialog_container.get(0)) {\n return true\n }\n this.close()\n return false\n })\n this.$document.keydown((e) => {\n if (e.which === 27) {\n this.close()\n return false\n } else {\n return true\n }\n })\n }\n\n // Builds the html structure of the Dialog\n build() {\n this.dialog_container = $('<div class=\"alchemy-dialog-container\" />')\n this.dialog = $('<div class=\"alchemy-dialog\" />')\n this.dialog_body = $('<div class=\"alchemy-dialog-body\" />')\n this.dialog_header = $('<div class=\"alchemy-dialog-header\" />')\n this.dialog_title = $('<div class=\"alchemy-dialog-title\" />')\n this.close_button = $(\n '<button class=\"alchemy-dialog-close\"><alchemy-icon name=\"close\"></alchemy-icon></button>'\n )\n this.dialog_title.text(this.options.title)\n this.dialog_header.append(this.dialog_title)\n this.dialog_header.append(this.close_button)\n this.dialog.append(this.dialog_header)\n this.dialog.append(this.dialog_body)\n this.dialog_container.append(this.dialog)\n if (this.options.modal) {\n this.dialog.addClass(\"modal\")\n }\n if (this.options.padding) {\n this.dialog_body.addClass(\"padded\")\n }\n if (this.options.modal) {\n this.overlay = $('<div class=\"alchemy-dialog-overlay\" />')\n this.$body.append(this.overlay)\n }\n this.$body.append(this.dialog_container)\n }\n\n // Sets the correct size of the dialog\n // It normalizes the given size, so that it never acceeds the window size.\n resize() {\n const { width, height } = this.getSize()\n\n this.dialog.css({\n width: width,\n \"min-height\": height,\n overflow: this.options.overflow\n })\n\n if (this.options.overflow === \"hidden\") {\n this.dialog_body.css({\n height: height,\n overflow: \"auto\"\n })\n } else {\n this.dialog_body.css({\n \"min-height\": height,\n overflow: \"visible\"\n })\n }\n }\n\n getSize() {\n const padding = this.options.padding ? 16 : 0\n const doc_width = this.$window.width()\n const doc_height = this.$window.height()\n\n let width = this.width\n let height = this.height\n\n if (width >= doc_width) {\n width = doc_width - padding\n }\n\n if (height >= doc_height) {\n height = doc_height - padding - DEFAULTS.header_height\n }\n\n return { width, height }\n }\n}\n\n// Gets the last dialog instantiated, which is the current one.\nexport function currentDialog() {\n const { length } = currentDialogs\n if (length === 0) {\n return\n }\n return currentDialogs[length - 1]\n}\n\n// Utility function to close the current Dialog\n//\n// You can pass a callback function, that gets triggered after the Dialog gets closed.\n//\nexport function closeCurrentDialog(callback) {\n const dialog = currentDialog()\n if (dialog != null) {\n dialog.options.closed = callback\n return dialog.close()\n }\n}\n\n// Utility function to open a new Dialog\nexport function openDialog(url, options) {\n if (!url) {\n throw \"No url given! Please provide an url.\"\n }\n const dialog = new Dialog(url, options)\n dialog.open()\n}\n","import { createHtmlElement } from \"alchemy_admin/utils/dom_helpers\"\nimport { translate } from \"alchemy_admin/i18n\"\n\nconst getDefaults = () => ({\n // The default size of the dialog\n size: \"300x100\",\n title: translate(\"Please confirm\"),\n ok_label: translate(\"Yes\"),\n cancel_label: translate(\"No\"),\n on_ok() {}\n})\n\nclass ConfirmDialog {\n constructor(message, options = {}) {\n this.message = message\n this.options = { ...getDefaults(), ...options }\n this.#build()\n this.#bindEvents()\n }\n\n open() {\n requestAnimationFrame(() => {\n this.dialog.show()\n })\n }\n\n #build() {\n const width = this.options.size.split(\"x\")[0]\n this.dialog = createHtmlElement(`\n <sl-dialog label=\"${this.options.title}\" style=\"--width: ${width}px\">\n ${this.message}\n <button slot=\"footer\" type=\"reset\" class=\"secondary mx-1 my-0\" autofocus>\n ${this.options.cancel_label}\n </button>\n <button slot=\"footer\" type=\"submit\" class=\"mx-1 my-0\">\n ${this.options.ok_label}\n </button>\n </sl-dialog>\n `)\n document.body.append(this.dialog)\n }\n\n #bindEvents() {\n this.cancelButton.addEventListener(\"click\", (evt) => {\n evt.preventDefault()\n this.options.on_cancel()\n this.dialog.hide()\n })\n this.okButton.addEventListener(\"click\", (evt) => {\n evt.preventDefault()\n this.options.on_ok()\n this.dialog.hide()\n })\n // Prevent the dialog from closing when the user clicks on the overlay\n this.dialog.addEventListener(\"sl-request-close\", (event) => {\n if (event.detail.source === \"overlay\") {\n this.options.on_cancel()\n event.preventDefault()\n }\n })\n // Remove the dialog from the DOM after it has been hidden\n this.dialog.addEventListener(\"sl-after-hide\", () => {\n this.dialog.remove()\n })\n }\n\n get cancelButton() {\n return this.dialog.querySelector(\"button[type=reset]\")\n }\n\n get okButton() {\n return this.dialog.querySelector(\"button[type=submit]\")\n }\n}\n\n/* Opens a confirm dialog\n *\n * @param {string} message - The message that will be displayed to the user\n * @param {Object} [options={}] - Configuration options for the dialog\n * @param {string} [options.title=\"Please confirm\"] - The title of the overlay window\n * @param {string} [options.cancel_label=\"No\"] - The label of the cancel button\n * @param {string} [options.ok_label=\"Yes\"] - The label of the ok button\n *\n * @returns {Promise<void>} A promise that resolves to true when the OK button is clicked and\n * resolves to false when the cancel button is clicked. Works as confirm dialog replacement\n * for Turbo.confirm.\n */\nexport function openConfirmDialog(message, options = {}) {\n return new Promise((resolve) => {\n const dialog = new ConfirmDialog(message, {\n ...options,\n on_ok() {\n resolve(true)\n },\n on_cancel() {\n resolve(false)\n }\n })\n dialog.open()\n })\n}\n","/**\n * To show the \"Please wait\" overlay.\n * Pass false to hide it.\n * @param {boolean} show\n */\nexport default function pleaseWaitOverlay(show = true) {\n document.querySelector(\"alchemy-overlay\").show = !!show\n}\n","import { openConfirmDialog } from \"alchemy_admin/confirm_dialog\"\nimport { translate } from \"alchemy_admin/i18n\"\nimport pleaseWaitOverlay from \"alchemy_admin/please_wait_overlay\"\n\nfunction checkPageDirtyness(element) {\n let callback = () => {}\n\n if ($(element).is(\"form\")) {\n callback = function () {\n const $form = $(\n `<form action=\"${element.action}\" method=\"POST\" style=\"display: none\" />`\n )\n $form.append($(element).find(\"input\"))\n $form.appendTo(\"body\")\n\n pleaseWaitOverlay()\n $form.trigger(\"submit\")\n }\n } else if ($(element).is(\"a\")) {\n callback = () => Turbo.visit(element.pathname)\n }\n\n const isPageDirty =\n document.querySelectorAll(\"alchemy-element-editor.dirty\").length > 0\n\n if (isPageDirty) {\n openConfirmDialog(translate(\"page_dirty_notice\"), {\n title: translate(\"warning\"),\n ok_label: translate(\"ok\"),\n cancel_label: translate(\"cancel\")\n }).then((proceed) => {\n if (proceed) {\n window.onbeforeunload = void 0\n callback()\n }\n })\n return false\n }\n return true\n}\n\nfunction PageLeaveObserver() {\n document.querySelectorAll(\"#main_navi a\").forEach((element) => {\n element.addEventListener(\"click\", (event) => {\n if (!checkPageDirtyness(event.currentTarget)) {\n event.preventDefault()\n }\n })\n })\n}\n\nexport default {\n checkPageDirtyness,\n PageLeaveObserver\n}\n","// Creates a fixed element tab.\nexport function createTab(element_id, label) {\n const fixed_elements = document.getElementById(\"fixed-elements\")\n const panel_name = `fixed-element-${element_id}`\n\n const tab = `<sl-tab slot=\"nav\" panel=\"${panel_name}\">${label}</sl-tab>`\n const panel = `<sl-tab-panel name=\"${panel_name}\" style=\"--padding: 0\" />`\n\n fixed_elements.innerHTML += tab + panel\n\n window.requestAnimationFrame(function () {\n fixed_elements.show(panel_name)\n })\n}\n\nexport function removeTab(element_id) {\n const fixed_elements = document.getElementById(\"fixed-elements\")\n const panel_name = `fixed-element-${element_id}`\n\n fixed_elements.querySelector(`sl-tab[panel=\"${panel_name}\"]`).remove()\n fixed_elements.querySelector(`sl-tab-panel[name=\"${panel_name}\"]`).remove()\n\n fixed_elements.show(\"main-content-elements\")\n}\n","import { createHtmlElement } from \"alchemy_admin/utils/dom_helpers\"\n\nfunction build(message, flashType) {\n const flashNotices = document.getElementById(\"flash_notices\")\n const flashMessage = createHtmlElement(`\n <alchemy-message type=\"${flashType}\" dismissable>\n ${message}\n </alchemy-message>\n `)\n flashNotices.append(flashMessage)\n}\n\nexport function growl(message, style = \"notice\") {\n build(message, style)\n}\n","import { translate } from \"alchemy_admin/i18n\"\nimport { Dialog } from \"alchemy_admin/dialog\"\n\n// Matches a URL fragment (#anchor) at the end of a string.\n// Covers RFC 3986 unreserved characters (ALPHA, DIGIT, \"-\", \".\", \"_\", \"~\")\n// which are the characters valid in URL fragments and common in DOM element IDs.\nconst ANCHOR_REGEX = /#[\\w.~-]+$/\n\n// Represents the link Dialog that appears, if a user clicks the link buttons\n// in TinyMCE or on an Ingredient that has links enabled (e.g. Picture)\n//\nexport class LinkDialog extends Dialog {\n #onCreateLink\n\n constructor(link) {\n const url = new URL(Alchemy.routes.link_admin_pages_path, window.location)\n const parameterMapping = {\n url: link.url,\n selected_tab: link.type,\n link_title: link.title,\n link_target: link.target\n }\n\n // searchParams.set would also add undefined values\n Object.keys(parameterMapping).forEach((key) => {\n if (parameterMapping[key]) {\n url.searchParams.set(key, parameterMapping[key])\n }\n })\n\n super(url.href, {\n size: \"600x320\",\n title: translate(\"Link\")\n })\n }\n\n /**\n * Called from Dialog class after the url was loaded\n */\n replace(data) {\n // let Dialog class handle the content replacement\n super.replace(data)\n this.#attachEvents()\n }\n\n /**\n * make the open method a promise\n * maybe in a future version the whole Dialog will respond with a promise result if the dialog is closing\n * @returns {Promise<unknown>}\n */\n open() {\n super.open()\n return new Promise((resolve) => (this.#onCreateLink = resolve))\n }\n\n /**\n * Attaches click events to forms in the link dialog.\n */\n #attachEvents() {\n // enable the dom selection in internal link tab\n const internalForm = document.querySelector(\n '[data-link-form-type=\"internal\"]'\n )\n const attachmentSelect = document.querySelector(\n '[data-link-form-type=\"file\"] alchemy-attachment-select'\n )\n\n internalForm.addEventListener(\"Alchemy.RemoteSelect.Change\", (e) => {\n this.#updatePage(e.detail.added)\n })\n\n attachmentSelect.addEventListener(\"Alchemy.RemoteSelect.Change\", (e) => {\n const attachment = e.detail.added\n document.getElementById(\"file_link\").value = attachment\n ? attachment.url\n : \"\"\n })\n\n document.querySelectorAll(\"[data-link-form-type]\").forEach((form) => {\n form.addEventListener(\"submit\", (e) => {\n e.preventDefault()\n this.#submitForm(e.target.dataset.linkFormType)\n })\n })\n }\n\n /**\n * update page select and set anchor select\n * @param page\n */\n #updatePage(page = null) {\n const internalLink = document.getElementById(\"internal_link\")\n const domIdSelect = document.querySelector(\n '[data-link-form-type=\"internal\"] alchemy-dom-id-api-select'\n )\n\n internalLink.value = page ? page.url_path : \"\"\n domIdSelect.page = page ? page.id : undefined\n }\n\n /**\n * submit the form itself\n * @param linkType\n */\n #submitForm(linkType) {\n const elementAnchor = document.getElementById(\"element_anchor\")\n let url = document.getElementById(`${linkType}_link`).value\n\n if (linkType === \"internal\" && elementAnchor.value !== \"\") {\n // remove possible fragments on the url and attach the fragment (which contains the #)\n url = url.replace(ANCHOR_REGEX, \"\") + elementAnchor.value\n } else if (linkType === \"external\" && !url.match(Alchemy.link_url_regexp)) {\n // show validation error and prevent link creation\n this.#showValidationError()\n return\n }\n\n // Create the link\n this.#onCreateLink({\n url: url.trim(),\n title: document.getElementById(`${linkType}_link_title`).value,\n target: document.getElementById(`${linkType}_link_target`)?.value,\n type: linkType\n })\n this.close()\n }\n\n /**\n * Shows validation errors\n */\n #showValidationError() {\n const errors = document.getElementById(\"errors\")\n errors.querySelector(\"ul\").innerHTML =\n `<li>${Alchemy.t(\"url_validation_failed\")}</li>`\n errors.style.display = \"block\"\n }\n}\n","import { growl } from \"alchemy_admin/growler\"\nimport { translate } from \"alchemy_admin/i18n\"\n\nclass PreviewWindow extends HTMLIFrameElement {\n #afterLoad\n #reloadIcon\n #loadTimeout\n #previewReadyHandler\n\n constructor() {\n super()\n this.addEventListener(\"load\", this)\n this.#previewReadyHandler = this.#handlePreviewReadyMessage.bind(this)\n }\n\n handleEvent(evt) {\n if (evt.type === \"load\") {\n this.#clearLoadTimeout()\n this.#stopSpinner()\n this.#afterLoad?.call(this, evt)\n }\n }\n\n #handlePreviewReadyMessage(event) {\n if (event.data.message === \"Alchemy.previewReady\") {\n this.#clearLoadTimeout()\n this.#stopSpinner()\n this.#afterLoad?.call(this, event)\n }\n }\n\n connectedCallback() {\n let url = this.url\n\n this.#attachEvents()\n window.addEventListener(\"message\", this.#previewReadyHandler)\n\n if (window.localStorage.getItem(\"alchemy-preview-url\")) {\n url = window.localStorage.getItem(\"alchemy-preview-url\")\n this.previewUrlSelect.value = url\n }\n\n this.refresh(url)\n }\n\n disconnectedCallback() {\n key.unbind(\"alt+r\")\n window.removeEventListener(\"message\", this.#previewReadyHandler)\n }\n\n postMessage(data) {\n this.contentWindow.postMessage(data, \"*\")\n }\n\n resize(width) {\n this.style.width = `${width}px`\n }\n\n refresh(url) {\n this.#startSpinner()\n\n if (url) {\n this.src = url\n } else {\n this.src = this.url\n }\n\n // Set 5s timeout as fallback - if iframe doesn't load, stop spinner anyway\n this.#clearLoadTimeout()\n this.#loadTimeout = setTimeout(() => {\n this.#stopSpinner()\n growl(translate(\"Preview failed to load\"), \"warning\")\n }, 5000)\n\n return new Promise((resolve) => {\n this.#afterLoad = resolve\n })\n }\n\n set isDragged(dragged) {\n this.style.transitionProperty = dragged ? \"none\" : null\n this.style.pointerEvents = dragged ? \"none\" : null\n }\n\n #attachEvents() {\n this.reloadButton?.addEventListener(\"click\", (evt) => {\n evt.preventDefault()\n this.refresh()\n })\n\n key(\"alt+r\", () => this.refresh())\n\n this.sizeSelect.addEventListener(\"change\", (evt) => {\n const select = evt.target\n const width = select.value\n\n if (width === \"\") {\n this.style.width = null\n } else {\n this.resize(width)\n }\n })\n\n this.previewUrlSelect?.addEventListener(\"change\", (evt) => {\n const url = evt.target.value\n window.localStorage.setItem(\"alchemy-preview-url\", url)\n this.refresh(url)\n })\n }\n\n #startSpinner() {\n // Only save the reload icon if we're not already showing a spinner\n if (!this.reloadButton.innerHTML.includes(\"alchemy-spinner\")) {\n this.#reloadIcon = this.reloadButton.innerHTML\n }\n this.reloadButton.innerHTML = `<alchemy-spinner size=\"small\"></alchemy-spinner>`\n }\n\n #stopSpinner() {\n this.reloadButton.innerHTML = this.#reloadIcon\n }\n\n #clearLoadTimeout() {\n if (this.#loadTimeout) {\n clearTimeout(this.#loadTimeout)\n this.#loadTimeout = null\n }\n }\n\n get url() {\n return this.getAttribute(\"url\")\n }\n\n get sizeSelect() {\n return document.querySelector(\"select#preview_size\")\n }\n\n get previewUrlSelect() {\n return document.querySelector(\"select#preview_url\")\n }\n\n get reloadButton() {\n return document.querySelector(\"#reload_preview_button\")\n }\n}\n\ncustomElements.define(\"alchemy-preview-window\", PreviewWindow, {\n extends: \"iframe\"\n})\n\nexport function reloadPreview() {\n const previewWindow = document.getElementById(\"alchemy_preview_window\")\n previewWindow.refresh()\n}\n","export default class IngredientAnchorLink {\n static updateIcon(ingredientId, active = false) {\n const ingredientEditor = document.querySelector(\n `[data-ingredient-id=\"${ingredientId}\"]`\n )\n if (ingredientEditor) {\n const icon = ingredientEditor.querySelector(\n \".edit-ingredient-anchor-link alchemy-icon\"\n )\n icon.setAttribute(\"icon-style\", active ? \"fill\" : \"line\")\n }\n }\n}\n","import { reloadPreview } from \"alchemy_admin/components/preview_window\"\nimport { removeTab } from \"alchemy_admin/fixed_elements\"\nimport { closeCurrentDialog } from \"alchemy_admin/dialog\"\nimport IngredientAnchorLink from \"alchemy_admin/ingredient_anchor_link\"\nimport pleaseWaitOverlay from \"alchemy_admin/please_wait_overlay\"\n\nclass Action extends HTMLElement {\n constructor() {\n super()\n\n // map action names with Javascript functions\n this.actions = {\n // add a intermediate closeCurrentDialog - action\n // this will be gone, if all dialogs are working with a promise and\n // we don't have to implicitly close the dialog\n closeCurrentDialog,\n reloadPreview,\n removeFixedElement: removeTab,\n updateAnchorIcon: IngredientAnchorLink.updateIcon,\n hidePleaseWaitOverlay() {\n pleaseWaitOverlay(false)\n }\n }\n }\n\n connectedCallback() {\n const func = this.actions[this.name]\n\n if (func) {\n func(...this.params)\n } else {\n console.error(`Unknown Alchemy action: ${this.name}`)\n }\n\n this.remove()\n }\n\n get name() {\n return this.getAttribute(\"name\")\n }\n\n get params() {\n if (this.hasAttribute(\"params\")) {\n return JSON.parse(this.getAttribute(\"params\"))\n }\n return []\n }\n}\n\ncustomElements.define(\"alchemy-action\", Action)\n","import { toCamelCase } from \"alchemy_admin/utils/string_conversions\"\n\nexport class AlchemyHTMLElement extends HTMLElement {\n static properties = {}\n\n /**\n * create the list of observed attributes\n * this function is a requirement for the `attributeChangedCallback` - method\n * @returns {string[]}\n * @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Components#reference\n */\n static get observedAttributes() {\n return Object.keys(this.properties)\n }\n\n constructor(options = {}) {\n super()\n\n this.options = options\n this.changeComponent = true\n this.initialContent = this.innerHTML // store the inner content of the component\n }\n\n /**\n * run when the component will be initialized by the Browser\n * this is a default function\n * @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Components#reference\n */\n async connectedCallback() {\n // parse the properties object and register property with the default values\n Object.keys(this.constructor.properties).forEach((name) => {\n // if the options was given via the constructor, they should be prefer (e.g. new <WebComponentName>({title: \"Foo\"}))\n this[name] =\n this.options[name] ?? this.constructor.properties[name].default\n })\n\n // then process the attributes\n this.getAttributeNames().forEach((name) => this._updateFromAttribute(name))\n\n // render the component\n this._updateComponent()\n await this.connected()\n }\n\n /**\n * disconnected callback if the component is removed from the DOM\n * this is currently only a Proxy to the disconnected - callback to use the same callback structure\n * as for the connected - callback\n * @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Components#reference\n */\n disconnectedCallback() {\n this.disconnected()\n }\n\n /**\n * triggered by the browser, if one of the observed attributes is changing\n * @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Components#reference\n */\n attributeChangedCallback(name) {\n this._updateFromAttribute(name)\n this._updateComponent()\n }\n\n /**\n * a connected method to make it easier to overwrite the connection callback\n */\n async connected() {}\n\n /**\n * a disconnected method to make it easier to overwrite the disconnection callback\n */\n disconnected() {}\n\n /**\n * empty method container to allow the child component to put the rendered string into this method\n * @returns {String}\n */\n render() {\n return this.initialContent\n }\n\n /**\n * after render callback\n * the function will be triggered after the DOM was updated\n */\n afterRender() {}\n\n /**\n * Dispatches a custom event with given name\n * @param {string} name The name of the custom event\n * @param {object} detail Optional event details\n */\n dispatchCustomEvent(name, detail = {}) {\n const event = new CustomEvent(`Alchemy.${name}`, { bubbles: true, detail })\n this.dispatchEvent(event)\n }\n\n /**\n * (re)render the component content inside the component container\n * @private\n */\n _updateComponent() {\n if (this.changeComponent) {\n this.innerHTML = this.render()\n this.changeComponent = false\n this.afterRender()\n }\n }\n\n /**\n * update the value from the given attribute\n *\n * @param {string} name\n * @private\n */\n _updateFromAttribute(name) {\n const attributeValue = this.getAttribute(name)\n const propertyName = toCamelCase(name)\n const isBooleanValue =\n attributeValue.length === 0 || attributeValue === \"true\"\n\n const value = isBooleanValue ? true : attributeValue\n\n if (this[propertyName] !== value) {\n this[propertyName] = value\n this.changeComponent = true\n }\n }\n}\n","/**\n * convert dashes and underscore strings into camelCase strings\n * @param {string} str\n * @returns {string}\n */\nexport function toCamelCase(str) {\n return str\n .split(/-|_/)\n .reduce((a, b) => a + b.charAt(0).toUpperCase() + b.slice(1))\n}\n","import { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\nimport { setupSelectLocale } from \"alchemy_admin/i18n\"\n\nexport function hightlightTerm(name, term) {\n return name.replace(new RegExp(term, \"gi\"), (match) => `<em>${match}</em>`)\n}\n\nexport class RemoteSelect extends AlchemyHTMLElement {\n static properties = {\n allowClear: { default: false },\n selection: { default: undefined },\n placeholder: { default: \"\" },\n queryParams: { default: \"{}\" },\n url: { default: \"\" }\n }\n\n async connected() {\n await setupSelectLocale()\n\n this.input.classList.add(\"alchemy_selectbox\")\n\n $(this.input)\n .select2(this.select2Config)\n .on(\"select2-open\", (evt) => {\n this.onOpen(evt)\n })\n .on(\"change\", (evt) => {\n this.onChange(evt)\n })\n }\n\n /**\n * Optional on change handler called by Select2.\n * @param {Event} event\n */\n onChange(event) {\n this.dispatchCustomEvent(\"RemoteSelect.Change\", {\n removed: event.removed,\n added: event.added\n })\n }\n\n /**\n * Optional on open handler called by Select2.\n * @param {Event} event\n */\n onOpen(event) {\n // add focus to the search input. Select2 is handling the focus on the first opening,\n // but it does not work the second time. One process in select2 is \"stealing\" the focus\n // if the command is not delayed. It is an intermediate solution until we are going to\n // move away from Select2\n setTimeout(() => {\n document.querySelector(\"#select2-drop .select2-input\").focus()\n }, 100)\n }\n\n get input() {\n return this.getElementsByTagName(\"input\")[0]\n }\n\n get select2Config() {\n return {\n placeholder: this.placeholder,\n allowClear: this.allowClear,\n initSelection: (_$el, callback) => {\n if (this.selection) {\n callback(JSON.parse(this.selection))\n }\n },\n ajax: this.ajaxConfig,\n formatSelection: (item) => this._renderResult(item),\n formatResult: (item, _el, query) =>\n this._renderListEntry(item, query.term)\n }\n }\n\n /**\n * Ajax configuration for Select2\n * @returns {object}\n */\n get ajaxConfig() {\n return {\n url: this.url,\n datatype: \"json\",\n quietMillis: 300,\n data: (term, page) => this._searchQuery(term, page),\n results: (response) => this._parseResponse(response)\n }\n }\n\n /**\n * Search query send to server from select2\n * @param {string} term\n * @param {number} page\n * @returns {object}\n * @private\n */\n _searchQuery(term, page) {\n return {\n q: {\n name_cont: term,\n ...JSON.parse(this.queryParams)\n },\n page: page\n }\n }\n\n /**\n * Parses server response into select2 results object\n * @param {object} response\n * @returns {object}\n * @private\n */\n _parseResponse(response) {\n const meta = response.meta\n return {\n results: response.data,\n more: meta.page * meta.per_page < meta.total_count\n }\n }\n\n /**\n * result which is visible if a page was selected\n * @param {object} item\n * @returns {string}\n * @private\n */\n _renderResult() {\n throw new Error(\n \"You need to define a _renderResult function on your sub class!\"\n )\n }\n\n /**\n * html template for each list entry\n * @param {object} item\n * @param {string} term\n * @returns {string}\n * @private\n */\n _renderListEntry() {\n throw new Error(\n \"You need to define a _renderListEntry function on your sub class!\"\n )\n }\n\n /**\n * hightlighted search term\n * @param {string} name\n * @param {string} term\n * @returns {string}\n * @private\n */\n _hightlightTerm(name, term) {\n return hightlightTerm(name, term)\n }\n}\n","import { RemoteSelect } from \"alchemy_admin/components/remote_select\"\n\nclass AttachmentSelect extends RemoteSelect {\n _renderResult(item) {\n return this._renderListEntry(item)\n }\n\n /**\n * html template for each list entry\n * @param {object} page\n * @param {string} term\n * @returns {string}\n * @private\n */\n _renderListEntry(attachment, term) {\n return `\n <div class=\"attachment-select--attachment\">\n <alchemy-icon name=\"${attachment.icon_css_class}\"></alchemy-icon>\n <span class=\"attachment-select--attachment-name\">${this._hightlightTerm(attachment.name, term)}</span>\n </div>\n `\n }\n}\n\ncustomElements.define(\"alchemy-attachment-select\", AttachmentSelect)\n","// Dispatch a submit event on change of input or select elements\n// contained in a form, so that Turbo can submit the form.\nclass AutoSubmit extends HTMLElement {\n connectedCallback() {\n // Still using jQuery here, because select2 does not emit\n // the event from the original select element.\n $(this).on(\"change\", function (event) {\n // We need to dispatch a submit event, so that Turbo that listens\n // to it submits the search form us.\n const submitEvent = new Event(\"submit\", {\n bubbles: true,\n cancelable: true\n })\n event.target.form.dispatchEvent(submitEvent)\n return false\n })\n }\n}\n\ncustomElements.define(\"alchemy-auto-submit\", AutoSubmit)\n","import Spinner from \"alchemy_admin/spinner\"\n\nclass Button extends HTMLButtonElement {\n connectedCallback() {\n if (this.form) {\n this.form.addEventListener(\"submit\", this)\n\n if (this.form.dataset.remote == \"true\") {\n this.form.addEventListener(\"ajax:complete\", this)\n }\n\n this.form.addEventListener(\"turbo:submit-end\", this)\n } else {\n console.warn(\"No form for button found!\", this)\n }\n }\n\n handleEvent(event) {\n switch (event.type) {\n case \"submit\":\n const isDisabled = this.getAttribute(\"disabled\") === \"disabled\"\n\n if (isDisabled) {\n event.preventDefault()\n event.stopPropagation()\n } else {\n this.disable()\n }\n break\n case \"ajax:complete\":\n case \"turbo:submit-end\":\n this.enable()\n break\n }\n }\n\n disable() {\n const spinner = new Spinner(\"small\")\n const rect = this.getBoundingClientRect()\n\n this.dataset.initialButtonText = this.innerHTML\n this.setAttribute(\"disabled\", \"disabled\")\n this.setAttribute(\"tabindex\", \"-1\")\n this.classList.add(\"disabled\")\n this.style.width = `${rect.width}px`\n this.style.height = `${rect.height}px`\n this.innerHTML = \" \"\n\n spinner.spin(this)\n }\n\n enable() {\n this.classList.remove(\"disabled\")\n this.removeAttribute(\"disabled\")\n this.removeAttribute(\"tabindex\")\n this.style.width = null\n this.style.height = null\n this.innerHTML = this.dataset.initialButtonText\n }\n}\n\ncustomElements.define(\"alchemy-button\", Button, { extends: \"button\" })\n","/**\n * Show the character counter below input fields and textareas\n */\nimport { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\nimport { translate } from \"alchemy_admin/i18n\"\n\nclass CharCounter extends AlchemyHTMLElement {\n static properties = {\n maxChars: { default: 60 }\n }\n connected() {\n this.translation = translate(\"allowed_chars\", this.maxChars)\n this.formField = this.getFormField()\n\n if (this.formField) {\n this.createDisplayElement()\n this.countCharacters()\n this.formField.addEventListener(\"keyup\", () => this.countCharacters()) // add arrow function to get a implicit this - binding\n }\n }\n\n getFormField() {\n const formFields = this.querySelectorAll(\"input, textarea\")\n return formFields.length > 0 ? formFields[0] : undefined\n }\n\n createDisplayElement() {\n this.display = document.createElement(\"small\")\n this.display.className = \"alchemy-char-counter\"\n this.formField.after(this.display)\n }\n\n countCharacters() {\n const charLength = this.formField.value.length\n this.display.textContent = `${charLength} ${this.translation}`\n this.display.classList.toggle(\"too-long\", charLength > this.maxChars)\n }\n}\n\ncustomElements.define(\"alchemy-char-counter\", CharCounter)\n","import \"clipboard\"\nimport { growl } from \"alchemy_admin/growler\"\n\nclass ClipboardButton extends HTMLElement {\n constructor() {\n super()\n\n this.innerHTML = `\n <alchemy-icon name=\"clipboard\"></alchemy-icon>\n `\n\n this.clipboard = new ClipboardJS(this, {\n text: () => {\n return this.getAttribute(\"content\")\n }\n })\n\n this.clipboard.on(\"success\", () => {\n growl(this.getAttribute(\"success-text\"))\n })\n }\n\n disconnectedCallback() {\n this.clipboard.destroy()\n }\n}\n\ncustomElements.define(\"alchemy-clipboard-button\", ClipboardButton)\n","const formatItem = (object) => {\n const optionEl = object.element[0]\n const swatch = optionEl.dataset.swatch || optionEl.value\n const customColor = optionEl.value === \"custom_color\"\n const colorIndicator = customColor\n ? `<alchemy-icon name=\"palette\"></alchemy-icon>`\n : `<span class=\"color-indicator\" style=\"--color: ${swatch}\"></span>`\n\n return `\n <div class=\"select-color-option\">\n ${colorIndicator}\n <span>${object.text}</span>\n </div>`\n}\n\nclass ColorSelect extends HTMLElement {\n connectedCallback() {\n if (this.select) {\n this.#initializeSelect2()\n $(this.select).on(\"change\", (event) =>\n this.#toggleColorPicker(event.val === \"custom_color\")\n )\n } else {\n this.colorInput?.addEventListener(\"input\", this)\n this.textInput?.addEventListener(\"input\", this)\n this.#toggleColorPicker(true)\n }\n }\n\n handleEvent(event) {\n switch (event.target) {\n case this.colorInput:\n this.textInput.value = this.colorInput.value\n break\n case this.textInput:\n this.colorInput.value = this.textInput.value\n break\n }\n }\n\n disconnectedCallback() {\n this.colorInput?.removeEventListener(\"input\", this)\n this.textInput?.removeEventListener(\"input\", this)\n }\n\n #initializeSelect2() {\n this.select.classList.add(\"alchemy_selectbox\")\n const options = {\n minimumResultsForSearch: 10,\n formatResult: formatItem,\n formatSelection: formatItem\n }\n $(this.select).select2(options)\n }\n\n #toggleColorPicker(enabled = true) {\n this.colorInput.disabled = !enabled\n }\n\n get colorInput() {\n return this.querySelector(\"input[type='color']\")\n }\n\n get textInput() {\n return this.querySelector(\"input[type='text']\")\n }\n\n get select() {\n return this.querySelector(\"select\")\n }\n}\n\ncustomElements.define(\"alchemy-color-select\", ColorSelect)\n","import { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\nimport { translate, currentLocale } from \"alchemy_admin/i18n\"\nimport flatpickr from \"flatpickr\"\n\nconst locale = currentLocale()\n\nclass Datepicker extends AlchemyHTMLElement {\n static properties = {\n inputType: { default: \"date\" }\n }\n\n constructor() {\n super()\n this.flatpickr = undefined\n }\n\n // Load the locales for flatpickr before setting it up.\n async connected() {\n // English is the default locale for flatpickr, so we don't need to load it\n if (locale !== \"en\") {\n await import(`flatpickr/${locale}.js`)\n }\n\n this.flatpickr = flatpickr(this.inputField, this.flatpickrOptions)\n }\n\n disconnected() {\n this.flatpickr.destroy()\n }\n\n get flatpickrOptions() {\n const enableTime = /time/.test(this.inputType)\n const options = {\n // alchemy_i18n supports `zh_CN` etc., but flatpickr only has two-letter codes (`zh`)\n locale: locale.slice(0, 2),\n altInput: true,\n altFormat: translate(`formats.${this.inputType}`),\n altInputClass: \"flatpickr-input\",\n enableTime,\n noCalendar: this.inputType === \"time\",\n time_24hr: translate(\"formats.time_24hr\"),\n onValueUpdate(_selectedDates, _dateStr, instance) {\n instance.element\n .closest(\"alchemy-element-editor\")\n ?.setDirty(this.inputField)\n }\n }\n\n if (enableTime) {\n options.dateFormat = \"Z\"\n }\n\n return options\n }\n\n get inputField() {\n return this.querySelector(\"input\")\n }\n}\n\ncustomElements.define(\"alchemy-datepicker\", Datepicker)\n","import { Dialog } from \"alchemy_admin/dialog\"\n\nexport class DialogLink extends HTMLAnchorElement {\n constructor() {\n super()\n this.addEventListener(\"click\", this)\n }\n\n handleEvent(evt) {\n if (!this.disabled) {\n this.openDialog()\n }\n evt.preventDefault()\n }\n\n openDialog() {\n this.dialog = new Dialog(this.getAttribute(\"href\"), this.dialogOptions)\n this.dialog.open()\n }\n\n get dialogOptions() {\n const options = this.dataset.dialogOptions\n ? JSON.parse(this.dataset.dialogOptions)\n : {}\n return options\n }\n\n get disabled() {\n return this.classList.contains(\"disabled\")\n }\n}\n\ncustomElements.define(\"alchemy-dialog-link\", DialogLink, { extends: \"a\" })\n","const JSON_CONTENT_TYPE = \"application/json\"\nconst TURBO_STREAM_CONTENT_TYPE = \"text/vnd.turbo-stream.html\"\n\nfunction isGetRequest(method) {\n return method.toLowerCase() === \"get\"\n}\n\nfunction prepareURL(path, data, method) {\n const url = new URL(window.location.origin + path)\n\n if (data && isGetRequest(method)) {\n url.search = new URLSearchParams(data).toString()\n }\n\n return url.toString()\n}\n\nfunction prepareHeaders(accept) {\n return {\n \"Content-Type\": \"application/json; charset=utf-8\",\n Accept: accept,\n \"X-Requested-With\": \"XMLHttpRequest\",\n \"X-CSRF-Token\": getToken()\n }\n}\n\nfunction prepareOptions(method, data, accept) {\n const headers = prepareHeaders(accept)\n const options = { method, headers }\n\n if (data && !isGetRequest(method)) {\n options.body = JSON.stringify(data)\n }\n\n return options\n}\n\nexport function getToken() {\n const metaTag = document.querySelector('meta[name=\"csrf-token\"]')\n return metaTag.attributes.content.textContent\n}\n\nexport function get(url, params) {\n return ajax(\"GET\", url, params)\n}\n\nexport function patch(url, data, accept) {\n return ajax(\"PATCH\", url, data, accept)\n}\n\nexport function post(url, data, accept = JSON_CONTENT_TYPE) {\n return ajax(\"POST\", url, data, accept)\n}\n\nexport default async function ajax(\n method,\n path,\n data,\n accept = JSON_CONTENT_TYPE\n) {\n const response = await fetch(\n prepareURL(path, data, method),\n prepareOptions(method, data, accept)\n )\n const contentType = response.headers.get(\"content-type\")\n const isJson = contentType?.includes(JSON_CONTENT_TYPE)\n const isTurboStream = contentType?.includes(TURBO_STREAM_CONTENT_TYPE)\n\n let responseData = null\n if (isJson) {\n responseData = await response.json()\n } else if (isTurboStream) {\n responseData = await response.text()\n // Automatically render Turbo Stream if Turbo is available\n if (typeof Turbo !== \"undefined\") {\n Turbo.renderStreamMessage(responseData)\n }\n }\n\n if (response.ok) {\n return { data: responseData, status: response.status }\n } else {\n throw responseData || new Error(\"An error occurred during the transaction\")\n }\n}\n","import { get } from \"alchemy_admin/utils/ajax\"\nimport { translate } from \"alchemy_admin/i18n\"\n\nclass DomIdSelect extends HTMLElement {\n dataItem(hash) {\n return {\n id: `#${hash}`,\n text: `#${hash}`\n }\n }\n\n get selectElement() {\n return this.querySelector('select[is=\"alchemy-select\"]')\n }\n}\n\nclass DomIdApiSelect extends DomIdSelect {\n #pageId = undefined\n\n connectedCallback() {\n this.page = this.getAttribute(\"page\")\n }\n\n async #fetchDomIds() {\n const result = await get(Alchemy.routes.api_ingredients_path, {\n page_id: this.#pageId\n })\n const options = result.data.ingredients\n .filter((ingredient) => ingredient.data?.dom_id)\n .map((ingredient) => this.dataItem(ingredient.data.dom_id))\n const prompt =\n options.length > 0 ? translate(\"None\") : translate(\"No anchors found\")\n\n this.selectElement.setOptions(options, prompt)\n this.selectElement.enable()\n }\n\n #reset() {\n // wait a tick to initialize the alchemy-select\n requestAnimationFrame(() => {\n this.selectElement.disable()\n this.selectElement.setOptions([], translate(\"Select a page first\"))\n })\n }\n\n set page(pageId) {\n this.#pageId = pageId\n pageId ? this.#fetchDomIds() : this.#reset()\n }\n}\n\nclass DomIdPreviewSelect extends DomIdSelect {\n connectedCallback() {\n // wait a tick to let the browser initialize the inner select component\n requestAnimationFrame(() => {\n const frame = document.getElementById(\"alchemy_preview_window\")\n const elements = frame.contentDocument?.querySelectorAll(\"[id]\") || []\n if (elements.length > 0) {\n const options = Array.from(elements).map((element) => {\n return this.dataItem(element.id)\n })\n this.selectElement.setOptions(options, translate(\"None\"))\n }\n })\n }\n}\n\ncustomElements.define(\"alchemy-dom-id-api-select\", DomIdApiSelect)\ncustomElements.define(\"alchemy-dom-id-preview-select\", DomIdPreviewSelect)\n","export class PublishElementButton extends HTMLElement {\n #scheduleButtonVariant\n\n connectedCallback() {\n this.#scheduleButtonVariant = this.scheduleButton.getAttribute(\"variant\")\n this.publishButton.addEventListener(\"click\", this)\n this.dropdown.addEventListener(\"sl-show\", this)\n this.dropdown.addEventListener(\"sl-hide\", this)\n }\n\n disconnectedCallback() {\n this.publishButton.removeEventListener(\"click\", this)\n this.dropdown.removeEventListener(\"sl-show\", this)\n this.dropdown.removeEventListener(\"sl-hide\", this)\n }\n\n handleEvent(event) {\n switch (event.type) {\n case \"click\":\n this.publishButton.loading = true\n break\n case \"sl-show\":\n this.scheduleButton.setAttribute(\"variant\", \"primary\")\n break\n case \"sl-hide\":\n this.scheduleButton.setAttribute(\"variant\", this.#scheduleButtonVariant)\n break\n }\n }\n\n get publishButton() {\n return this.querySelector(\"sl-button[type='submit']\")\n }\n\n get dropdown() {\n return this.querySelector(\"sl-dropdown\")\n }\n\n get scheduleButton() {\n return this.querySelector(\"sl-button[slot='trigger']\")\n }\n}\n\ncustomElements.define(\"alchemy-publish-element-button\", PublishElementButton)\n","import ajax from \"alchemy_admin/utils/ajax\"\nimport { removeTab } from \"alchemy_admin/fixed_elements\"\nimport { growl } from \"alchemy_admin/growler\"\nimport { reloadPreview } from \"alchemy_admin/components/preview_window\"\nimport { openConfirmDialog } from \"alchemy_admin/confirm_dialog\"\nimport { dispatchPageDirtyEvent } from \"alchemy_admin/components/element_editor\"\n\nexport class DeleteElementButton extends HTMLElement {\n constructor() {\n super()\n this.button?.addEventListener(\"click\", this)\n }\n\n async handleEvent() {\n const confirmed = await openConfirmDialog(this.message)\n if (confirmed) {\n const response = await ajax(\"DELETE\", this.url)\n this.#removeElement(response.data)\n }\n }\n\n #removeElement(data) {\n const elementEditor = this.closest(\"alchemy-element-editor\")\n elementEditor.addEventListener(\"transitionend\", () => {\n if (elementEditor.fixed) {\n removeTab(elementEditor.elementId)\n }\n elementEditor.remove()\n })\n elementEditor.classList.add(\"dismiss\")\n growl(data.message)\n if (data.pageHasUnpublishedChanges) {\n dispatchPageDirtyEvent(data)\n }\n reloadPreview()\n }\n\n get url() {\n return this.getAttribute(\"href\")\n }\n\n get message() {\n return this.getAttribute(\"message\")\n }\n\n get button() {\n return this.querySelector(\"button\")\n }\n}\n\ncustomElements.define(\"alchemy-delete-element-button\", DeleteElementButton)\n","import IngredientAnchorLink from \"alchemy_admin/ingredient_anchor_link\"\nimport { post } from \"alchemy_admin/utils/ajax\"\nimport { createHtmlElement } from \"alchemy_admin/utils/dom_helpers\"\nimport { growl } from \"alchemy_admin/growler\"\n\nimport \"alchemy_admin/components/element_editor/publish_element_button\"\nimport \"alchemy_admin/components/element_editor/delete_element_button\"\n\nexport function dispatchPageDirtyEvent(data) {\n document.dispatchEvent(\n new CustomEvent(\"alchemy:page-dirty\", {\n detail: { tooltip: data.publishButtonTooltip }\n })\n )\n}\n\nexport class ElementEditor extends HTMLElement {\n constructor() {\n super()\n\n // Add event listeners\n this.addEventListener(\"click\", this)\n // Triggered by child elements\n this.addEventListener(\"alchemy:element-update-title\", this)\n // We use of @rails/ujs for Rails remote forms\n this.addEventListener(\"ajax:complete\", this)\n\n // Dirty observer still needs to be jQuery\n // in order to support select2.\n $(this.form).on(\"change\", this.onChange)\n\n this.header?.addEventListener(\"dblclick\", () => {\n this.toggle()\n })\n this.toggleButton?.addEventListener(\"click\", (evt) => {\n const elementEditor = evt.target.closest(\"alchemy-element-editor\")\n if (elementEditor === this) {\n this.toggle()\n }\n })\n }\n\n connectedCallback() {\n // The placeholder while be being dragged is empty.\n if (this.classList.contains(\"ui-sortable-placeholder\")) {\n return\n }\n\n // When newly created, focus the element and refresh the preview\n if (this.hasAttribute(\"created\")) {\n this.focusElement()\n this.previewWindow?.refresh().then(() => {\n this.focusElementPreview()\n })\n this.removeAttribute(\"created\")\n }\n }\n\n handleEvent(event) {\n switch (event.type) {\n case \"click\":\n const elementEditor = event.target.closest(\"alchemy-element-editor\")\n if (elementEditor === this) {\n this.onClickElement()\n }\n break\n case \"ajax:complete\":\n if (event.target === this.body) {\n const xhr = event.detail[0]\n event.stopPropagation()\n this.onSaveElement(xhr)\n }\n break\n case \"alchemy:element-update-title\":\n if (!this.hasEditors && event.target == this.firstChild) {\n this.setTitle(event.detail.title)\n }\n break\n }\n }\n\n onChange(event) {\n const target = event.target\n // SortableJS fires a native change event :/\n // and we do not want to set the element editor dirty\n // when this happens\n if (target.classList.contains(\"nested-elements\")) {\n return\n }\n this.closest(\"alchemy-element-editor\").setDirty(target)\n event.stopPropagation()\n return false\n }\n\n /**\n * Scrolls to and highlights element\n * Expands if collapsed\n * Also chooses the right fixed elements tab, if necessary.\n * Can be triggered through custom event 'FocusElementEditor.Alchemy'\n * Used by the elements on click events in the preview frame.\n */\n async focusElement() {\n // Select tab if necessary\n if (document.querySelector(\"#fixed-elements\")) {\n await this.selectTabForElement()\n }\n // Expand if necessary\n await this.expand()\n this.selectElement(true)\n }\n\n focusElementPreview() {\n this.previewWindow?.postMessage({\n message: \"Alchemy.focusElement\",\n element_id: this.elementId\n })\n }\n\n onClickElement() {\n this.selectElement()\n this.focusElementPreview()\n }\n\n /**\n * Sets the element to saved state\n * Updates title\n * JS event bubbling will also update the parents element quote.\n * Shows error messages if ingredient validations fail\n * @argument {XMLHttpRequest} xhr\n */\n onSaveElement(xhr) {\n const data = JSON.parse(xhr.responseText)\n // Reset errors that might be visible from last save attempt\n this.setClean()\n // If validation failed\n if (xhr.status === 422) {\n const warning = data.warning\n // Create error messages\n // Mark ingredients as failed\n data.ingredientsWithErrors.forEach((ingredient) => {\n const ingredientEditor = this.querySelector(\n `[data-ingredient-id=\"${ingredient.id}\"]`\n )\n const errorDisplay = createHtmlElement(\n `<small class=\"error\">${ingredient.errorMessage}</small>`\n )\n ingredientEditor?.appendChild(errorDisplay)\n ingredientEditor?.classList.add(\"validation_failed\")\n })\n // Show message\n growl(warning, \"warn\")\n this.elementErrors.classList.remove(\"hidden\")\n } else {\n growl(data.notice)\n this.previewWindow?.refresh().then(() => {\n this.focusElementPreview()\n })\n this.updateTitle(data.previewText)\n data.ingredientAnchors.forEach((anchor) => {\n IngredientAnchorLink.updateIcon(anchor.ingredientId, anchor.active)\n })\n if (data.pageHasUnpublishedChanges) {\n dispatchPageDirtyEvent(data)\n }\n }\n }\n\n /**\n * Smoothly scrolls to element\n */\n scrollToElement() {\n // The timeout gives the browser some time to calculate the position\n // of nested elements correctly\n setTimeout(() => {\n this.scrollIntoView({\n behavior: \"smooth\"\n })\n }, 50)\n }\n\n /**\n * Highlight element and optionally scroll into view\n * @param {boolean} scroll smoothly scroll element into view. Default (false)\n */\n selectElement(scroll = false) {\n document\n .querySelectorAll(\"alchemy-element-editor.selected\")\n .forEach((el) => {\n el.classList.remove(\"selected\")\n })\n window.requestAnimationFrame(() => {\n this.classList.add(\"selected\")\n })\n if (scroll) this.scrollToElement()\n }\n\n /**\n * Selects tab for given element\n * Resolves the promise if this is done.\n * @returns {Promise}\n */\n selectTabForElement() {\n return new Promise((resolve, reject) => {\n const tabs = document.querySelector(\"#fixed-elements\")\n const panel = this.closest(\"sl-tab-panel\")\n if (tabs && panel) {\n tabs.show(panel.getAttribute(\"name\"))\n resolve()\n } else {\n reject(new Error(\"No tabs present\"))\n }\n })\n }\n\n /**\n * Sets the element into clean (safed) state\n */\n setClean() {\n this.dirty = false\n window.onbeforeunload = null\n this.elementErrors.classList.add(\"hidden\")\n\n if (this.hasEditors) {\n this.body.querySelectorAll(\".ingredient-editor\").forEach((el) => {\n el.classList.remove(\"dirty\", \"validation_failed\")\n el.querySelectorAll(\"small.error\").forEach((e) => e.remove())\n })\n }\n }\n\n /**\n * Sets the element into dirty (unsafed) state\n * @param {HTMLElement} editor\n */\n setDirty(editor) {\n if (this.hasEditors) {\n this.dirty = true\n\n if (!window.onbeforeunload) {\n window.onbeforeunload = (event) => event.preventDefault()\n }\n\n editor?.closest(\".ingredient-editor\")?.classList.add(\"dirty\")\n }\n }\n\n /**\n * Sets the title quote\n * @param {string} title\n */\n setTitle(title) {\n const quote = this.querySelector(\".element-header .preview_text_quote\")\n quote.textContent = title\n }\n\n /**\n * Expands or collapses element editor\n * If the element is dirty (has unsaved changes) it displays a confirm first.\n */\n async toggle() {\n if (this.collapsed) {\n await this.expand()\n } else {\n await this.collapse()\n }\n }\n\n /**\n * Collapses the element editor and persists the state on the server\n * @returns {Promise}\n */\n collapse() {\n if (this.collapsed || this.compact || this.fixed) {\n return Promise.resolve(\"Element is already collapsed.\")\n }\n\n const spinner = new Alchemy.Spinner(\"small\")\n spinner.spin(this.toggleButton)\n this.toggleIcon?.classList?.add(\"hidden\")\n return post(Alchemy.routes.collapse_admin_element_path(this.elementId))\n .then((response) => {\n const data = response.data\n\n this.collapsed = true\n this.toggleButton?.setAttribute(\"title\", data.title)\n\n // Collapse all nested elements if necessarry\n if (data.nestedElementIds.length) {\n const selector = data.nestedElementIds\n .map((id) => `#element_${id}`)\n .join(\", \")\n this.querySelectorAll(selector).forEach((nestedElement) => {\n nestedElement.collapsed = true\n nestedElement.toggleButton?.setAttribute(\"title\", data.title)\n })\n }\n })\n .catch((error) => {\n growl(error.message, \"error\")\n console.error(error)\n })\n .finally(() => {\n this.toggleIcon?.classList?.remove(\"hidden\")\n spinner.stop()\n })\n }\n\n /**\n * Collapses the element editor and persists the state on the server\n * @* @returns {Promise}\n */\n expand() {\n if (this.expanded && !this.compact) {\n return Promise.resolve(\"Element is already expanded.\")\n }\n\n if (this.compact && this.parentElementEditor) {\n return this.parentElementEditor.expand()\n } else {\n const spinner = new Alchemy.Spinner(\"small\")\n spinner.spin(this.toggleButton)\n this.toggleIcon?.classList.add(\"hidden\")\n\n return new Promise((resolve, reject) => {\n post(Alchemy.routes.expand_admin_element_path(this.elementId))\n .then((response) => {\n const data = response.data\n\n // First expand all parent elements if necessary\n if (data.parentElementIds.length) {\n const selector = data.parentElementIds\n .map((id) => `#element_${id}`)\n .join(\", \")\n document.querySelectorAll(selector).forEach((parentElement) => {\n parentElement.collapsed = false\n parentElement.toggleButton?.setAttribute(\"title\", data.title)\n })\n }\n // Finally expand ourselve\n this.collapsed = false\n this.toggleButton?.setAttribute(\"title\", data.title)\n // Resolve the promise that scrolls to the element very last\n resolve()\n })\n .catch((error) => {\n growl(error.message, \"error\")\n console.error(error)\n reject(error)\n })\n .finally(() => {\n this.toggleIcon?.classList?.remove(\"hidden\")\n spinner.stop()\n })\n })\n }\n }\n\n /**\n * Updates the quote in the element header and dispatches event\n * to parent elements\n * @param {string} title\n */\n updateTitle(title) {\n this.setTitle(title)\n this.dispatchEvent(\n new CustomEvent(\"alchemy:element-update-title\", {\n bubbles: true,\n detail: { title }\n })\n )\n }\n\n /**\n * Sets element published or hidden\n * @param {boolean}\n */\n set published(isPublished) {\n if (isPublished) {\n this.classList.remove(\"element-hidden\")\n } else {\n this.classList.add(\"element-hidden\")\n }\n }\n\n /**\n * Is element published or hidden\n * @returns {boolean}\n */\n get published() {\n return !this.classList.contains(\"hidden\")\n }\n\n /**\n * @returns {boolean}\n */\n get compact() {\n return this.getAttribute(\"compact\") !== null\n }\n\n /**\n * @returns {boolean}\n */\n get fixed() {\n return this.getAttribute(\"fixed\") !== null\n }\n\n /**\n * @param {boolean} value\n */\n set collapsed(value) {\n this.classList.toggle(\"folded\", value)\n this.classList.toggle(\"expanded\", !value)\n this.toggleIcon &&\n (this.toggleIcon.name = value ? \"arrow-left-s\" : \"arrow-down-s\")\n }\n\n /**\n * @returns {boolean}\n */\n get collapsed() {\n return this.classList.contains(\"folded\")\n }\n\n /**\n * @returns {boolean}\n */\n get expanded() {\n return !this.collapsed\n }\n\n /**\n * Toggles the dirty class\n *\n * @param {boolean} value\n */\n set dirty(value) {\n this.classList.toggle(\"dirty\", value)\n }\n\n /**\n * Returns the dirty state of this element\n *\n * @returns {boolean}\n */\n get dirty() {\n return this.classList.contains(\"dirty\")\n }\n\n /**\n * Returns the element header\n *\n * @returns {HTMLElement|undefined}\n */\n get header() {\n return this.querySelector(`.element-header`)\n }\n\n /**\n * Returns the immediate body container of this element if present\n *\n * Makes sure it does not return a nested elements body\n * by scoping the selector to this elements id.\n *\n * @returns {HTMLElement|undefined}\n */\n get body() {\n return this.querySelector(this.bodySelector)\n }\n\n get bodySelector() {\n return `#${this.id} > .element-body`\n }\n\n /**\n * Returns the immediate footer container of this element if present\n *\n * Makes sure it does not return a nested elements footer\n * by scoping the selector to this elements id.\n *\n * @returns {HTMLElement|undefined}\n */\n get footer() {\n return this.querySelector(`#${this.id} > .element-footer`)\n }\n\n /**\n * The collapse/expand toggle button\n *\n * @returns {HTMLButtonElement|undefined}\n */\n get toggleButton() {\n return this.querySelector(\".element-toggle\")\n }\n\n /**\n * The collapse/expand toggle buttons icon\n *\n * @returns {HTMLElement|undefined}\n */\n get toggleIcon() {\n return this.toggleButton?.querySelector(\"alchemy-icon\")\n }\n\n /**\n * The validation messages list container\n *\n * @returns {HTMLElement}\n */\n get elementErrors() {\n return this.body.querySelector(\".element_errors\")\n }\n\n /**\n * The element database id\n *\n * @returns {string}\n */\n get elementId() {\n return this.dataset.elementId\n }\n\n /**\n * The element defintion name\n *\n * @returns {string}\n */\n get elementName() {\n return this.dataset.elementName\n }\n\n /**\n * Does this element have ingredient editor fields?\n *\n * @returns {boolean}\n */\n get hasEditors() {\n return !!this.body?.querySelector(\".element-ingredient-editors\")\n }\n\n /**\n * Does this element have nested elements?\n *\n * @returns {boolean}\n */\n get hasChildren() {\n return !!this.querySelector(\".nested-elements\")\n }\n\n /**\n * The first child element editor if present\n *\n * @returns {HTMLButtonElement|undefined}\n */\n get firstChild() {\n return this.querySelector(\"alchemy-element-editor\")\n }\n\n /**\n * The form element if present\n *\n * @returns {HTMLFormElement|undefined}\n */\n get form() {\n return this.querySelector(\"form.element-body\")\n }\n\n /**\n * The parent element editor if present\n *\n * @returns {ElementEditor|undefined}\n */\n get parentElementEditor() {\n return this.parentElement?.closest(\"alchemy-element-editor\")\n }\n\n get previewWindow() {\n return document.getElementById(\"alchemy_preview_window\")\n }\n}\n\ncustomElements.define(\"alchemy-element-editor\", ElementEditor)\n","import { hightlightTerm } from \"alchemy_admin/components/remote_select\"\n\nconst formatSelection = (option) => {\n return `\n <div class=\"element-select-name\">\n ${option.icon}<span>${option.text}</span>\n </div>\n `\n}\n\nconst formatItem = (icon, text, hint) => {\n const description = hint\n ? `<div class=\"element-select-description\">${hint}</div>`\n : \"\"\n return `\n <div class=\"element-select-item\">\n ${formatSelection({ icon, text })}\n ${description}\n </div>\n `\n}\n\nclass ElementSelect extends HTMLElement {\n constructor() {\n super()\n }\n\n connectedCallback() {\n const results = this.options\n const options = {\n minimumResultsForSearch: 3,\n dropdownAutoWidth: true,\n data() {\n return { results }\n },\n formatResult: (option, _el, search) => {\n let text\n\n if (option.id === \"\") return option.text\n if (search.term !== \"\") {\n text = hightlightTerm(option.text, search.term)\n } else {\n text = option.text\n }\n\n return formatItem(option.icon, text, option.hint)\n },\n formatSelection,\n placeholder: this.placeholder\n }\n $(this.inputField).select2(options)\n }\n\n get options() {\n return JSON.parse(this.getAttribute(\"options\"))\n }\n\n get placeholder() {\n return this.getAttribute(\"placeholder\")\n }\n\n get inputField() {\n return this.querySelector(\"input\")\n }\n}\n\ncustomElements.define(\"alchemy-element-select\", ElementSelect)\n","import { ElementEditor } from \"alchemy_admin/components/element_editor\"\n\nclass ElementsWindow extends HTMLElement {\n #visible = true\n #turboFrame = null\n\n constructor() {\n super()\n this.#attachEvents()\n }\n\n connectedCallback() {\n this.toggleButton?.addEventListener(\"click\", (evt) => {\n evt.preventDefault()\n this.toggle()\n })\n if (window.location.hash) {\n this.focusElementEditor(window.location.hash)\n }\n this.resize()\n }\n\n collapseAllElements() {\n this.querySelectorAll(\n \"alchemy-element-editor:not([compact]):not([fixed])\"\n ).forEach((editor) => editor.collapse())\n }\n\n toggle() {\n this.#visible ? this.hide() : this.show()\n }\n\n show() {\n document.body.classList.add(\"elements-window-visible\")\n this.#visible = true\n this.toggleButton.closest(\"sl-tooltip\").content = Alchemy.t(\"Hide elements\")\n this.toggleButton\n .querySelector(\"alchemy-icon\")\n .setAttribute(\"name\", \"menu-unfold\")\n this.resize()\n }\n\n hide() {\n document.body.classList.remove(\"elements-window-visible\")\n document.body.style.removeProperty(\"--elements-window-width\")\n this.#visible = false\n this.toggleButton.closest(\"sl-tooltip\").content = Alchemy.t(\"Show elements\")\n this.toggleButton\n .querySelector(\"alchemy-icon\")\n .setAttribute(\"name\", \"menu-fold\")\n }\n\n resize(width) {\n if (width === undefined) {\n width = this.widthFromCookie\n }\n\n if (width) {\n document.body.style.setProperty(\"--elements-window-width\", `${width}px`)\n document.cookie = `alchemy-elements-window-width=${width}; SameSite=Lax; Path=/;`\n }\n }\n\n // Focus element editor and element in preview if URL contains hash to element editor.\n // The link is coming from the assignment view when showing assigned files or pictures.\n focusElementEditor(dom_id) {\n const el = document.querySelector(dom_id)\n\n if (el instanceof ElementEditor) {\n el.focusElement() && el.focusElementPreview()\n }\n }\n\n get collapseButton() {\n return this.querySelector(\"#collapse-all-elements-button\")\n }\n\n get toggleButton() {\n return document.querySelector(\"#element_window_button\")\n }\n\n get previewWindow() {\n return document.getElementById(\"alchemy_preview_window\")\n }\n\n get turboFrame() {\n if (!this.#turboFrame) {\n this.#turboFrame = this.closest(\"turbo-frame\")\n }\n return this.#turboFrame\n }\n\n get widthFromCookie() {\n return document.cookie\n .split(\"; \")\n .find((row) => row.startsWith(\"alchemy-elements-window-width=\"))\n ?.split(\"=\")[1]\n }\n\n set isDragged(dragged) {\n this.turboFrame.style.transitionProperty = dragged ? \"none\" : null\n this.turboFrame.style.pointerEvents = dragged ? \"none\" : null\n }\n\n #attachEvents() {\n this.collapseButton?.addEventListener(\"click\", () => {\n this.collapseAllElements()\n })\n window.addEventListener(\"message\", (event) => {\n const data = event.data\n if (data?.message == \"Alchemy.focusElementEditor\") {\n const element = document.getElementById(`element_${data.element_id}`)\n this.show()\n element?.focusElement()\n }\n })\n document.body.addEventListener(\"click\", (evt) => {\n if (!evt.target.closest(\"alchemy-element-editor\")) {\n this.querySelectorAll(\"alchemy-element-editor\").forEach((editor) => {\n editor.classList.remove(\"selected\")\n })\n this.previewWindow?.postMessage({ message: \"Alchemy.blurElements\" })\n }\n })\n }\n}\n\ncustomElements.define(\"alchemy-elements-window\", ElementsWindow)\n","class ElementsWindowHandle extends HTMLElement {\n #dragging = false\n #elementsWindow = null\n #previewWindow = null\n\n constructor() {\n super()\n\n this.addEventListener(\"mousedown\", this)\n window.addEventListener(\"mousemove\", this)\n window.addEventListener(\"mouseup\", this)\n }\n\n handleEvent(event) {\n switch (event.type) {\n case \"mousedown\":\n event.stopPropagation()\n this.onMouseDown()\n break\n case \"mouseup\":\n this.onMouseUp()\n break\n case \"mousemove\":\n if (this.#dragging) {\n this.onDrag(event.pageX)\n }\n break\n }\n }\n\n onMouseDown() {\n this.#dragging = true\n this.elementsWindow.isDragged = true\n this.previewWindow.isDragged = true\n this.classList.add(\"is-dragged\")\n }\n\n onMouseUp() {\n this.#dragging = false\n this.elementsWindow.isDragged = false\n this.previewWindow.isDragged = false\n this.classList.remove(\"is-dragged\")\n }\n\n onDrag(pageX) {\n const elementWindowWidth = window.innerWidth - pageX\n this.elementsWindow.resize(elementWindowWidth)\n }\n\n get elementsWindow() {\n if (!this.#elementsWindow) {\n this.#elementsWindow = document.querySelector(\"alchemy-elements-window\")\n }\n return this.#elementsWindow\n }\n\n get previewWindow() {\n if (!this.#previewWindow) {\n this.#previewWindow = document.getElementById(\"alchemy_preview_window\")\n }\n return this.#previewWindow\n }\n}\n\ncustomElements.define(\"alchemy-elements-window-handle\", ElementsWindowHandle)\n","class FileEditor extends HTMLElement {\n constructor() {\n super()\n this.deleteLink = this.querySelector(\".remove_file_link\")\n this.fileIcon = this.querySelector(\".file_icon\")\n this.fileName = this.querySelector(\".file_name\")\n this.formFieldId = this.deleteLink?.dataset.formFieldId\n this.formField = this.querySelector(`#${this.formFieldId}`)\n this.deleteLink?.addEventListener(\"click\", this)\n }\n\n handleEvent(event) {\n if (event.type === \"click\") this.removeFile()\n event.stopPropagation()\n }\n\n removeFile() {\n this.formField.value = \"\"\n this.fileIcon.innerHTML = \"\"\n this.fileName.innerHTML = \"\"\n this.deleteLink?.classList.add(\"hidden\")\n this.closest(\"alchemy-element-editor\").setDirty(this.formField)\n }\n}\n\ncustomElements.define(\"alchemy-file-editor\", FileEditor)\n","const DEFAULT_DEBOUNCE_TIME = 150\n\nclass ListFilter extends HTMLElement {\n #debounceTimer\n\n constructor() {\n super()\n this.#attachEvents()\n }\n\n #attachEvents() {\n if (this.hotkey) {\n key(this.hotkey, () => {\n this.filterField.focus()\n return false\n })\n }\n this.filterField.addEventListener(\"keyup\", () => {\n clearTimeout(this.#debounceTimer)\n this.#debounceTimer = setTimeout(() => {\n const term = this.filterField.value\n this.clearButton.style.visibility = term ? \"visible\" : \"hidden\"\n this.filter(term)\n }, this.debounceTime)\n })\n this.clearButton.addEventListener(\"click\", (e) => {\n e.preventDefault()\n this.clear()\n })\n this.filterField.addEventListener(\"focus\", () =>\n key.setScope(\"list_filter\")\n )\n key(\"esc\", \"list_filter\", () => {\n this.clear()\n this.filterField.blur()\n })\n }\n\n disconnectedCallback() {\n if (this.hotkey) {\n key.unbind(this.hotkey)\n }\n key.unbind(\"esc\", \"list_filter\")\n }\n\n filter(term) {\n if (term === \"\") {\n this.clearButton.style.visibility = \"hidden\"\n }\n\n const matchedItems = []\n const itemsToShow = new Set()\n const lowerTerm = term.toLowerCase()\n\n // First pass: find matching items and mark their ancestors as visible too\n this.items.forEach((item) => {\n const name = item.getAttribute(this.nameAttribute)?.toLowerCase()\n // indexOf is much faster then match()\n if (name.indexOf(lowerTerm) !== -1) {\n matchedItems.push(item)\n itemsToShow.add(item)\n // Mark ancestor items as visible so nested matches stay visible\n let ancestor = item.parentElement?.closest(this.itemsSelector)\n while (ancestor) {\n itemsToShow.add(ancestor)\n ancestor = ancestor.parentElement?.closest(this.itemsSelector)\n }\n }\n })\n\n // Second pass: apply visibility\n this.items.forEach((item) => {\n item.classList.toggle(\"hidden\", !itemsToShow.has(item))\n })\n\n // Scroll into view if only one match\n if (matchedItems.length === 1) {\n matchedItems[0].scrollIntoView({ behavior: \"smooth\", block: \"nearest\" })\n }\n }\n\n clear() {\n this.filterField.value = \"\"\n this.clearButton.style.visibility = \"hidden\"\n this.items.forEach((item) => item.classList.remove(\"hidden\"))\n }\n\n get nameAttribute() {\n return this.getAttribute(\"name-attribute\") || \"name\"\n }\n\n get clearButton() {\n return this.querySelector('button[type=\"button\"]')\n }\n\n get filterField() {\n return this.querySelector('input[type=\"text\"]')\n }\n\n get items() {\n return document.querySelectorAll(this.itemsSelector)\n }\n\n get itemsSelector() {\n return this.getAttribute(\"items-selector\")\n }\n\n get debounceTime() {\n return parseInt(this.getAttribute(\"debounce-time\")) || DEFAULT_DEBOUNCE_TIME\n }\n\n get hotkey() {\n return this.getAttribute(\"hotkey\")\n }\n}\n\ncustomElements.define(\"alchemy-list-filter\", ListFilter)\n","const DISMISS_DELAY = 5000\n\nclass Message extends HTMLElement {\n #message\n\n constructor() {\n super()\n this.#message = this.innerHTML\n if (this.dismissable || this.type === \"error\") {\n this.addEventListener(\"click\", this)\n }\n }\n\n handleEvent(event) {\n if (event.type === \"click\") {\n this.dismiss()\n }\n }\n\n connectedCallback() {\n this.innerHTML = `\n <alchemy-icon name=\"${this.iconName}\"></alchemy-icon>\n ${this.dismissable && this.type === \"error\" ? '<alchemy-icon name=\"close\"></alchemy-icon>' : \"\"}\n ${this.#message}\n `\n if (this.dismissable && this.type !== \"error\") {\n setTimeout(() => {\n this.dismiss()\n }, this.dismissDelay)\n }\n }\n\n dismiss() {\n this.addEventListener(\"transitionend\", () => this.remove())\n this.classList.add(\"dismissed\")\n }\n\n get dismissable() {\n return this.hasAttribute(\"dismissable\")\n }\n\n get icon() {\n return this.getAttribute(\"icon\")\n }\n\n get type() {\n return this.getAttribute(\"type\") || \"notice\"\n }\n\n get dismissDelay() {\n return parseInt(\n this.noticesWrapper?.dataset.autoDismissDelay || DISMISS_DELAY\n )\n }\n\n get iconName() {\n switch (this.icon || this.type) {\n case \"warning\":\n case \"warn\":\n case \"alert\":\n return \"alert\"\n case \"notice\":\n return \"check\"\n case \"info\":\n case \"hint\":\n return \"information\"\n case \"error\":\n return \"bug\"\n default:\n return this.type\n }\n }\n\n get noticesWrapper() {\n return this.closest(\"#flash_notices\")\n }\n}\n\ncustomElements.define(\"alchemy-message\", Message)\n","import { growl } from \"alchemy_admin/growler\"\n\nclass Growl extends HTMLElement {\n connectedCallback() {\n growl(this.message, this.getAttribute(\"type\") || \"notice\")\n this.remove()\n }\n\n get message() {\n return this.getAttribute(\"message\") || this.innerHTML\n }\n}\n\ncustomElements.define(\"alchemy-growl\", Growl)\n","class Icon extends HTMLElement {\n static get observedAttributes() {\n return [\"name\", \"size\", \"icon-style\"]\n }\n\n constructor() {\n super()\n this.spriteUrl = document\n .querySelector('link[rel=\"preload\"][as=\"image\"]')\n .getAttribute(\"href\")\n }\n\n connectedCallback() {\n this.render()\n }\n\n attributeChangedCallback() {\n this.render()\n }\n\n render() {\n const sizeClass = this.size ? ` icon--${this.size}` : \"\"\n this.innerHTML = `<svg class=\"icon${sizeClass}\"><use href=\"${this.spriteUrl}#ri-${this.iconName}${this.style}\" /></svg>`\n }\n\n set name(value) {\n this.setAttribute(\"name\", value)\n }\n\n get iconName() {\n return this.getAttribute(\"name\")\n }\n\n get size() {\n return this.getAttribute(\"size\")\n }\n\n get style() {\n const value = this.getAttribute(\"icon-style\")\n switch (value) {\n case \"none\":\n return \"\"\n case null:\n return \"-line\"\n default:\n return `-${value}`\n }\n }\n}\n\ncustomElements.define(\"alchemy-icon\", Icon)\n","export class IngredientGroup extends HTMLDetailsElement {\n #localStorageKey = \"Alchemy.expanded_ingredient_groups\"\n\n constructor() {\n super()\n\n this.addEventListener(\"toggle\", this)\n\n if (this.isInLocalStorage) {\n this.open = true\n }\n }\n\n /**\n * Toggle visibility of the ingredient fields in this group\n */\n handleEvent() {\n let expanded_ingredient_groups = this.localStorageItem\n\n if (this.open) {\n this.toggleIcon.name = \"arrow-down-s\"\n if (!this.isInLocalStorage) expanded_ingredient_groups.push(this.id)\n } else {\n this.toggleIcon.name = \"arrow-left-s\"\n expanded_ingredient_groups = expanded_ingredient_groups.filter(\n (value) => value !== this.id\n )\n }\n\n localStorage.setItem(\n this.#localStorageKey,\n JSON.stringify(expanded_ingredient_groups)\n )\n }\n\n get isInLocalStorage() {\n return this.localStorageItem.includes(this.id)\n }\n\n get localStorageItem() {\n const item = localStorage.getItem(this.#localStorageKey)\n\n if (!item) return []\n\n try {\n return JSON.parse(item)\n } catch (error) {\n console.error(error)\n return []\n }\n }\n\n get toggleIcon() {\n return this.querySelector(\"alchemy-icon\")\n }\n}\n\ncustomElements.define(\"alchemy-ingredient-group\", IngredientGroup, {\n extends: \"details\"\n})\n","class LinkButton extends HTMLButtonElement {\n constructor() {\n super()\n this.addEventListener(\"click\", this)\n this.classList.add(\"icon_button\")\n // Prevent accidental form submits if this component is wrapped inside a form\n this.setAttribute(\"type\", \"button\")\n this.innerHTML = '<alchemy-icon name=\"link\" icon-style=\"m\"></alchemy-icon>'\n }\n\n handleEvent(event) {\n const dialog = new Alchemy.LinkDialog({\n url: this.linkUrl,\n title: this.linkTitle,\n target: this.linkTarget,\n type: this.linkClass\n })\n dialog.open().then((link) => this.setLink(link))\n event.preventDefault()\n }\n\n setLink(link) {\n if (link.url === \"\") {\n this.classList.remove(\"linked\")\n this.dispatchEvent(new CustomEvent(\"alchemy:unlink\", { bubbles: true }))\n } else {\n this.classList.add(\"linked\")\n this.dispatchEvent(\n new CustomEvent(\"alchemy:link\", {\n bubbles: true,\n detail: link\n })\n )\n }\n }\n\n get linkUrl() {\n return this.linkButtons.linkUrlField.value\n }\n\n get linkTitle() {\n return this.linkButtons.linkTitleField.value\n }\n\n get linkTarget() {\n return this.linkButtons.linkTargetField.value\n }\n\n get linkClass() {\n return this.linkButtons.linkClassField.value\n }\n\n get linkButtons() {\n return this.closest(\"alchemy-link-buttons\")\n }\n}\n\ncustomElements.define(\"alchemy-link-button\", LinkButton, { extends: \"button\" })\n","class UnlinkButton extends HTMLButtonElement {\n constructor() {\n super()\n this.addEventListener(\"click\", this)\n this.classList.add(\"icon_button\")\n // Prevent accidental form submits if this component is wrapped inside a form\n this.setAttribute(\"type\", \"button\")\n this.linked = this.linked\n this.innerHTML =\n '<alchemy-icon name=\"link-unlink\" icon-style=\"m\"></alchemy-icon>'\n }\n\n handleEvent(event) {\n if (this.linked) {\n this.linked = false\n this.blur()\n this.dispatchEvent(new CustomEvent(\"alchemy:unlink\", { bubbles: true }))\n }\n event.preventDefault()\n }\n\n set linked(isLinked) {\n if (isLinked) {\n this.classList.replace(\"disabled\", \"linked\")\n this.removeAttribute(\"tabindex\")\n } else {\n this.classList.replace(\"linked\", \"disabled\")\n this.setAttribute(\"tabindex\", \"-1\")\n }\n }\n\n get linked() {\n return this.classList.contains(\"linked\")\n }\n}\n\ncustomElements.define(\"alchemy-unlink-button\", UnlinkButton, {\n extends: \"button\"\n})\n","import \"alchemy_admin/components/link_buttons/link_button\"\nimport \"alchemy_admin/components/link_buttons/unlink_button\"\n\nclass LinkButtons extends HTMLElement {\n constructor() {\n super()\n this.addEventListener(\"alchemy:link\", this)\n this.addEventListener(\"alchemy:unlink\", this)\n }\n\n handleEvent(event) {\n switch (event.type) {\n case \"alchemy:link\":\n this.setLink(event.detail)\n break\n case \"alchemy:unlink\":\n this.removeLink()\n }\n event.stopPropagation()\n }\n\n setLink(data) {\n this.linkUrlField.value = data.url\n this.linkUrlField.dispatchEvent(new Event(\"change\"))\n this.linkTitleField.value = data.title\n this.linkClassField.value = data.type\n this.linkTargetField.value = data.target\n\n this.unlinkButton.linked = true\n this.setElementDirty()\n }\n\n removeLink() {\n this.linkUrlField.value = \"\"\n this.linkUrlField.dispatchEvent(new Event(\"change\"))\n this.linkTitleField.value = \"\"\n this.linkClassField.value = \"\"\n this.linkTargetField.value = \"\"\n\n this.linkButton.classList.remove(\"linked\")\n this.unlinkButton.linked = false\n\n this.setElementDirty()\n }\n\n setElementDirty() {\n this.elementEditor.setDirty(this)\n }\n\n get linkButton() {\n return this.querySelector('[is=\"alchemy-link-button\"]')\n }\n\n get unlinkButton() {\n return this.querySelector('[is=\"alchemy-unlink-button\"]')\n }\n\n get ingredientEditor() {\n const ingredientId = this.dataset.ingredientId\n return this.parentElement.closest(`[data-ingredient-id='${ingredientId}']`)\n }\n\n get elementEditor() {\n return this.closest(\"alchemy-element-editor\")\n }\n\n get linkUrlField() {\n return this.ingredientEditor.querySelector(\"[data-link-value]\")\n }\n\n get linkTitleField() {\n return this.ingredientEditor.querySelector(\"[data-link-title]\")\n }\n\n get linkTargetField() {\n return this.ingredientEditor.querySelector(\"[data-link-target]\")\n }\n\n get linkClassField() {\n return this.ingredientEditor.querySelector(\"[data-link-class]\")\n }\n}\n\ncustomElements.define(\"alchemy-link-buttons\", LinkButtons)\n","export function formatFileSize(bytes) {\n let exponent = bytes === 0 ? 0 : Math.floor(Math.log(bytes) / Math.log(1024))\n\n // prevent format higher than GB\n if (exponent > 3) {\n exponent = 3\n }\n\n let value = (bytes / Math.pow(1024, exponent)).toFixed(2)\n return value + \" \" + [\"B\", \"kB\", \"MB\", \"GB\"][exponent]\n}\n","import { RemoteSelect } from \"alchemy_admin/components/remote_select\"\n\nclass NodeSelect extends RemoteSelect {\n _searchQuery(term, page) {\n return {\n filter: {\n name_or_page_name_cont: term,\n ...JSON.parse(this.queryParams)\n },\n page: page\n }\n }\n\n _renderResult(item) {\n return this._renderListEntry(item)\n }\n\n /**\n * html template for each list entry\n * @param {object} node\n * @param {string} term\n * @returns {string}\n * @private\n */\n _renderListEntry(node, term) {\n const ancestors = node.ancestors.map((a) => a.name)\n const seperator = `<alchemy-icon name=\"arrow-right-s\"></alchemy-icon>`\n\n return `\n <div class=\"node-select--node\">\n <alchemy-icon name=\"menu-2\"></alchemy-icon>\n <div class=\"node-select--node-display_name\">\n <span class=\"node-select--node-ancestors\">\n ${ancestors.length > 0 ? ancestors.join(seperator) + seperator : \"\"}\n </span>\n <span class=\"node-select--node-name\">\n ${this._hightlightTerm(node.name, term)}\n </span>\n </div>\n <div class=\"node-select--node-url\">\n ${node.url || \"\"}\n </div>\n </div>\n `\n }\n}\n\ncustomElements.define(\"alchemy-node-select\", NodeSelect)\n","import { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\nimport { formatFileSize } from \"alchemy_admin/utils/format\"\nimport { translate } from \"alchemy_admin/i18n\"\nimport { growl } from \"alchemy_admin/growler\"\n\nexport class FileUpload extends AlchemyHTMLElement {\n constructor() {\n super()\n\n this.file = null\n this.request = null\n\n this.progressEventLoaded = 0\n this.progressEventTotal = 0\n this.className = \"in-progress\"\n this.valid = true\n this.value = 0\n }\n\n /**\n * Initialize the component with file and request\n * @param {File} file\n * @param {XMLHttpRequest} request\n */\n initialize(file, request) {\n this.file = file\n this.request = request\n this.progressEventTotal = file ? file.size : 0\n\n this._validateFile()\n this._addRequestEventListener()\n }\n\n render() {\n return `\n <sl-progress-bar value=\"${this.value}\"></sl-progress-bar>\n <div class=\"description\">\n <span class=\"file-name\">${this.file?.name}</span>\n <span class=\"loaded-size\">${this.loadedSize}</span>\n <span class=\"error-message\">${this.errorMessage}</span>\n </div>\n <sl-tooltip content=\"${translate(\"Abort upload\")}\">\n <button class=\"icon_button\" aria-label=\"${translate(\"Abort upload\")}\">\n <alchemy-icon name=\"close\"></alchemy-icon>\n </button>\n </sl-tooltip>\n `\n }\n\n afterRender() {\n this.querySelector(\"button\").addEventListener(\"click\", () => this.cancel())\n\n if (this.file?.type.includes(\"image\")) {\n const reader = new FileReader()\n reader.readAsDataURL(this.file)\n reader.addEventListener(\"load\", () => {\n const image = new Image()\n image.src = reader.result\n this.prepend(image)\n })\n }\n }\n\n /**\n * cancel the upload\n */\n cancel() {\n if (!this.finished) {\n this.status = \"canceled\"\n this.request?.abort()\n this.dispatchCustomEvent(\"FileUpload.Change\")\n }\n }\n\n /**\n * validate given file with the `Alchemy.uploader_defaults` - configuration\n * @private\n */\n _validateFile() {\n const config = Alchemy.uploader_defaults\n const maxFileSize = config.file_size_limit * Math.pow(1024, 2) // in Byte\n let errorMessage = undefined\n\n if (this.file?.size > maxFileSize) {\n errorMessage = translate(\"Uploaded bytes exceed file size\")\n }\n\n const allowedFiletypes = this.file?.type.includes(\"image\")\n ? config.allowed_filetypes.alchemy_pictures\n : config.allowed_filetypes.alchemy_attachments\n\n const isFileFormatSupported =\n allowedFiletypes.includes(\"*\") ||\n allowedFiletypes.includes(\n this.file?.type.replace(/^\\w+\\/(\\w+)(\\+\\w+)?/i, \"$1\")\n )\n\n if (!isFileFormatSupported) {\n errorMessage = translate(\"File type not allowed\")\n }\n\n if (errorMessage) {\n this.valid = false\n this.errorMessage = errorMessage\n }\n }\n\n /**\n * register event listeners to react on request changes\n * @private\n */\n _addRequestEventListener() {\n // prevent errors if the component will be called without a request - object\n if (!this.request) {\n return\n }\n\n // update the progress bar and currently loaded size information\n this.request.upload.onprogress = (progressEvent) => {\n this.progressEvent = progressEvent\n }\n\n // triggers, when the upload is done\n this.request.onload = () => {\n if (this.request.status < 400) {\n this.status = \"successful\"\n growl(this.responseMessage)\n } else {\n this.status = \"failed\"\n this.errorMessage = this.responseMessage\n }\n this.dispatchCustomEvent(\"FileUpload.Change\")\n }\n\n // catch request errors\n this.request.onerror = () => {\n this.errorMessage = translate(\"An error occurred during the transaction\")\n }\n }\n\n /**\n * @returns {boolean}\n */\n get active() {\n return this.valid && this.status !== \"canceled\"\n }\n\n /**\n * @returns {string}\n */\n get errorMessage() {\n return this._errorMessage || \"\"\n }\n\n /**\n * @param {string} message\n */\n set errorMessage(message) {\n this._errorMessage = message\n const errorMessageContainer = this.querySelector(\".error-message\")\n if (errorMessageContainer) {\n errorMessageContainer.textContent = message\n }\n growl(message, \"error\")\n }\n\n /**\n * @returns {boolean}\n */\n get finished() {\n return [\"canceled\", \"successful\", \"failed\"].includes(this.status)\n }\n\n /**\n * format the loaded and total size and present that as a string\n * @returns {string}\n */\n get loadedSize() {\n return `${formatFileSize(this.progressEventLoaded)} / ${formatFileSize(\n this.progressEventTotal\n )}`\n }\n\n /**\n * @returns {HTMLProgressElement|undefined}\n */\n get progressElement() {\n return this.querySelector(\"sl-progress-bar\")\n }\n\n /**\n * @param {ProgressEvent} progressEvent\n */\n set progressEvent(progressEvent) {\n this.progressEventLoaded = progressEvent.loaded\n this.progressEventTotal = progressEvent.total\n\n this.value = Math.round((progressEvent.loaded / progressEvent.total) * 100)\n this.querySelector(\".loaded-size\").textContent = this.loadedSize\n }\n\n /**\n * @returns {string}\n */\n get responseMessage() {\n try {\n const response = JSON.parse(this.request.responseText)\n return response[\"message\"]\n } catch (error) {\n return `${this.request.status}: ${this.request.statusText}`\n }\n }\n\n /**\n * @returns {string}\n */\n get status() {\n return this._status\n }\n\n /**\n * @param {string} status\n */\n set status(status) {\n this._status = status\n this.className = status\n\n this.progressElement?.toggleAttribute(\n \"indeterminate\",\n status === \"upload-finished\"\n )\n }\n\n /**\n * @returns {boolean}\n */\n get valid() {\n return this._valid\n }\n\n /**\n * @param {boolean} isValid\n */\n set valid(isValid) {\n this._valid = isValid\n this.classList.toggle(\"invalid\", !isValid)\n }\n\n /**\n * get the progress value of the current file\n * @returns {number}\n */\n get value() {\n return this._value\n }\n\n /**\n * @param {number} value\n */\n set value(value) {\n this._value = value\n if (this.progressElement) {\n this.progressElement.value = value\n }\n\n if (value === 100) {\n this.status = \"upload-finished\"\n }\n\n this.dispatchCustomEvent(\"FileUpload.Change\")\n }\n}\n\ncustomElements.define(\"alchemy-file-upload\", FileUpload)\n","import { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\nimport { FileUpload } from \"alchemy_admin/components/uploader/file_upload\"\nimport { formatFileSize } from \"alchemy_admin/utils/format\"\nimport { translate } from \"alchemy_admin/i18n\"\n\nexport class Progress extends AlchemyHTMLElement {\n #visible = false\n\n constructor() {\n super()\n this.buttonLabel = translate(\"Cancel all uploads\")\n this.fileUploads = []\n this.fileCount = 0\n this.className = \"in-progress\"\n this.visible = true\n this.handleFileChange = () => this._updateView()\n }\n\n /**\n * Initialize the component with file uploads\n * @param {FileUpload[]} fileUploads\n */\n initialize(fileUploads = []) {\n this.fileUploads = fileUploads\n this.fileCount = fileUploads.length\n }\n\n /**\n * append file progress - components for each file\n */\n afterRender() {\n this.actionButton = this.querySelector(\"button\")\n this.actionButton.addEventListener(\"click\", () => {\n if (this.finished) {\n this.onComplete(this.status)\n } else {\n this.cancel()\n }\n })\n\n this.fileUploads.forEach((fileUpload) => {\n this.querySelector(\".single-uploads\").append(fileUpload)\n })\n }\n\n /**\n * cancel requests in all remaining uploads\n */\n cancel() {\n this._activeUploads().forEach((upload) => {\n upload.cancel()\n })\n this._setupCloseButton()\n }\n\n /**\n * update view and register change event\n */\n connected() {\n this._updateView()\n this.addEventListener(\"Alchemy.FileUpload.Change\", this.handleFileChange)\n }\n\n /**\n * deregister file upload change - event\n */\n disconnected() {\n this.removeEventListener(\"Alchemy.FileUpload.Change\", this.handleFileChange)\n }\n\n /**\n * a complete hook to allow the uploader to react and trigger an event\n * it would be possible to trigger the event here, but the dispatching would happen\n * in the scope of that component and can't be cached o uploader - component level\n */\n onComplete(_status) {}\n\n render() {\n return `\n <sl-progress-bar value=\"0\"></sl-progress-bar>\n <div class=\"overall-progress-value\">\n <span class=\"value-text\"></span>\n\n <sl-tooltip content=\"${this.buttonLabel}\">\n <button class=\"icon_button\" aria-label=\"${this.buttonLabel}\">\n <alchemy-icon name=\"close\"></alchemy-icon>\n </button>\n </sl-tooltip>\n </div>\n <div class=\"single-uploads\" style=\"--progress-columns: ${\n this.fileCount > 3 ? 3 : this.fileCount\n }\"></div>\n <div class=\"overall-upload-value value-text\"></div>\n `\n }\n\n /**\n * get all active upload components\n * @returns {FileUpload[]}\n * @private\n */\n _activeUploads() {\n return this.fileUploads.filter((upload) => upload.active)\n }\n\n /**\n * replace cancel button to be the close button\n * @private\n */\n _setupCloseButton() {\n this.buttonLabel = translate(\"Close\")\n this.actionButton.ariaLabel = this.buttonLabel\n this.actionButton.parentElement.content = this.buttonLabel // update tooltip content\n }\n\n /**\n * @param {string} field\n * @returns {number}\n * @private\n */\n _sumFileProgresses(field) {\n return this._activeUploads().reduce(\n (accumulator, upload) => upload[field] + accumulator,\n 0\n )\n }\n\n /**\n * don't render the whole element new, because it would prevent selecting buttons\n * @private\n */\n _updateView() {\n const status = this.status\n this.className = status\n\n // update progress bar\n this.progressElement.value = this.totalProgress\n this.progressElement.toggleAttribute(\n \"indeterminate\",\n status === \"upload-finished\"\n )\n\n // show progress in file size and percentage\n this.querySelector(`.overall-progress-value > span`).textContent =\n this.overallProgressValue\n this.querySelector(`.overall-upload-value`).textContent =\n this.overallUploadSize\n\n if (this.finished) {\n this._setupCloseButton()\n this.onComplete(status)\n } else {\n this.visible = true\n }\n }\n\n /**\n * @returns {boolean}\n */\n get finished() {\n return this._activeUploads().every((entry) => entry.finished)\n }\n\n /**\n * @returns {string}\n */\n get overallUploadSize() {\n const uploadedFileCount = this._activeUploads().filter(\n (fileProgress) => fileProgress.value >= 100\n ).length\n const overallProgressValue = `${\n this.totalProgress\n }% (${uploadedFileCount} / ${this._activeUploads().length})`\n\n return `${formatFileSize(\n this._sumFileProgresses(\"progressEventLoaded\")\n )} / ${formatFileSize(this._sumFileProgresses(\"progressEventTotal\"))}`\n }\n\n /**\n * @returns {string}\n */\n get overallProgressValue() {\n const uploadedFileCount = this._activeUploads().filter(\n (fileProgress) => fileProgress.value >= 100\n ).length\n return `${this.totalProgress}% (${uploadedFileCount} / ${\n this._activeUploads().length\n })`\n }\n\n /**\n * @returns {HTMLProgressElement|undefined}\n */\n get progressElement() {\n return this.querySelector(\"sl-progress-bar\")\n }\n\n /**\n * get status of file progresses and accumulate the overall status\n * @returns {string}\n */\n get status() {\n const uploadsStatuses = this._activeUploads().map(\n (upload) => upload.className\n )\n\n // mark as failed, if any upload failed\n if (uploadsStatuses.includes(\"failed\")) {\n return \"failed\"\n }\n\n // no active upload means that every upload was canceled\n if (uploadsStatuses.length === 0) {\n return \"canceled\"\n }\n\n // all uploads are successful or upload-finished or in-progress\n if (uploadsStatuses.every((entry) => entry === uploadsStatuses[0])) {\n return uploadsStatuses[0]\n }\n\n return \"in-progress\"\n }\n\n /**\n * @returns {number}\n */\n get totalProgress() {\n const totalSize = this._activeUploads().reduce(\n (accumulator, upload) => accumulator + upload.file.size,\n 0\n )\n let totalProgress = Math.ceil(\n this._activeUploads().reduce((accumulator, upload) => {\n const weight = upload.file.size / totalSize\n return upload.value * weight + accumulator\n }, 0)\n )\n // prevent rounding errors\n if (totalProgress > 100) {\n totalProgress = 100\n }\n return totalProgress\n }\n\n /**\n * @returns {boolean}\n */\n get visible() {\n return this.#visible\n }\n\n /**\n * @param {boolean} visible\n */\n set visible(visible) {\n this.classList.toggle(\"visible\", visible)\n this.#visible = visible\n }\n}\n\ncustomElements.define(\"alchemy-upload-progress\", Progress)\n","/**\n * @typedef {object} PersistedFile\n * @property {string} name\n * @property {number} size\n */\nimport { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\nimport { Progress } from \"alchemy_admin/components/uploader/progress\"\nimport { FileUpload } from \"alchemy_admin/components/uploader/file_upload\"\nimport { translate } from \"alchemy_admin/i18n\"\nimport { getToken } from \"alchemy_admin/utils/ajax\"\n\nexport class Uploader extends AlchemyHTMLElement {\n static properties = {\n dropzone: { default: false }\n }\n\n connected() {\n this.fileInput.addEventListener(\"change\", (event) => {\n this._uploadFiles(Array.from(event.target.files))\n })\n if (this.dropzone) {\n this._dragAndDropBehavior()\n }\n this.addEventListener(\"Alchemy.upload.successful\", this)\n }\n\n handleEvent(evt) {\n switch (evt.type) {\n case \"Alchemy.upload.successful\":\n this._handleUploadComplete()\n break\n }\n }\n\n _handleUploadComplete() {\n setTimeout(() => {\n const url = this.redirectUrl\n const turboFrame = this.closest(\"turbo-frame\")\n this.uploadProgress.visible = false\n\n if (!url) return\n\n if (turboFrame) {\n turboFrame.setAttribute(\"src\", url)\n turboFrame.reload()\n } else {\n Turbo.visit(url)\n }\n }, 750)\n }\n\n /**\n * add dragover class to indicate, if the file is draggable\n * @private\n */\n _dragAndDropBehavior() {\n const dropzoneElement = document.querySelector(this.dropzone)\n let isDraggedOver = false\n\n const toggleDropzoneClass = (enabled) => {\n if (isDraggedOver !== enabled) {\n isDraggedOver = enabled\n dropzoneElement.classList.toggle(\"dragover\")\n }\n }\n\n dropzoneElement.addEventListener(\"dragleave\", () =>\n toggleDropzoneClass(false)\n )\n dropzoneElement.addEventListener(\"drop\", async (event) => {\n event.preventDefault()\n toggleDropzoneClass(false)\n\n const files = [...event.dataTransfer.items].map((item) =>\n item.getAsFile()\n )\n\n this._uploadFiles(files)\n })\n\n dropzoneElement.addEventListener(\"dragover\", (event) => {\n event.preventDefault() // dragover has to be disabled to use the custom drop event\n toggleDropzoneClass(true)\n })\n }\n\n /**\n * @param {File[]} files\n * @private\n */\n _uploadFiles(files) {\n // prepare file progress bars and server request\n let fileUploadCount = 0\n\n const fileUploads = files.map((file) => {\n const request = new XMLHttpRequest()\n const fileUpload = new FileUpload()\n fileUpload.initialize(file, request)\n\n if (Alchemy.uploader_defaults.upload_limit - 1 < fileUploadCount) {\n fileUpload.valid = false\n fileUpload.errorMessage = translate(\"Maximum number of files exceeded\")\n } else if (fileUpload.valid) {\n fileUploadCount++\n this._submitFile(request, file)\n }\n\n return fileUpload\n })\n\n this._createProgress(fileUploads)\n }\n\n /**\n * @param {XMLHttpRequest} request\n * @param {File} file\n * @private\n */\n _submitFile(request, file) {\n const form = this.querySelector(\"form\")\n const formData = new FormData(form)\n formData.set(this.fileInput.name, file)\n request.open(\"POST\", form.action)\n request.setRequestHeader(\"X-CSRF-Token\", getToken())\n request.setRequestHeader(\"X-Requested-With\", \"XMLHttpRequest\")\n request.setRequestHeader(\"Accept\", \"application/json\")\n request.send(formData)\n }\n\n /**\n * create (and maybe remove the old) progress bar - component\n * @param {FileUpload[]} fileUploads\n * @private\n */\n _createProgress(fileUploads) {\n if (this.uploadProgress) {\n this.uploadProgress.cancel()\n document.body.removeChild(this.uploadProgress)\n }\n this.uploadProgress = new Progress()\n this.uploadProgress.initialize(fileUploads)\n this.uploadProgress.onComplete = (status) => {\n this.dispatchCustomEvent(`upload.${status}`)\n }\n\n document.body.append(this.uploadProgress)\n }\n\n /**\n * @returns {HTMLInputElement}\n */\n get fileInput() {\n return this.querySelector(\"input[type='file']\")\n }\n\n get redirectUrl() {\n return this.getAttribute(\"redirect-url\")\n }\n}\n\ncustomElements.define(\"alchemy-uploader\", Uploader)\n","import { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\n\nclass Overlay extends AlchemyHTMLElement {\n render() {\n return `\n <alchemy-spinner></alchemy-spinner>\n <div id=\"overlay_text_box\">\n <span id=\"overlay_text\">${this.getAttribute(\"text\")}</span>\n </div>\n `\n }\n\n set show(value) {\n this.classList.toggle(\"visible\", value)\n }\n}\n\ncustomElements.define(\"alchemy-overlay\", Overlay)\n","import { patch } from \"alchemy_admin/utils/ajax\"\nimport { growl } from \"alchemy_admin/growler\"\nimport Spinner from \"alchemy_admin/spinner\"\n\nconst BUTTON = \"BUTTON\"\nconst SPAN = \"SPAN\"\n\n/**\n * Custom element for page nodes in the sitemap tree\n * Handles folding/unfolding of page children\n */\nexport class AlchemyPageNode extends HTMLElement {\n connectedCallback() {\n this.pageId = this.getAttribute(\"page-id\")\n this.folded = this.hasAttribute(\"folded\")\n\n this.folderButton?.addEventListener(\"click\", this)\n }\n\n disconnectedCallback() {\n this.folderButton?.removeEventListener(\"click\", this)\n }\n\n async handleEvent(event) {\n if (event.type === \"click\") {\n await this.handleFolderClick(event)\n }\n }\n\n async handleFolderClick(event) {\n event.preventDefault()\n event.stopPropagation()\n\n const folderButton = event.currentTarget\n folderButton.innerHTML = \"\"\n const spinner = new Spinner(\"small\")\n spinner.spin(folderButton)\n\n try {\n await patch(\n Alchemy.routes.fold_admin_page_path(this.pageId),\n null,\n \"text/vnd.turbo-stream.html\"\n )\n\n this.folded = !this.folded\n this.toggleAttribute(\"folded\", this.folded)\n this.toggleChildren()\n this.updateFolderIcon()\n } catch (error) {\n growl(error.message || error, \"error\")\n this.updateFolderIcon()\n } finally {\n spinner.stop()\n }\n }\n\n toggleChildren() {\n const childrenContainer = this.querySelector(\n `#page_${this.pageId}_children`\n )\n if (childrenContainer) {\n childrenContainer.classList.toggle(\"hidden\", this.folded)\n }\n }\n\n updateFolderIcon() {\n if (this.folderButton) {\n const iconName = this.folded ? \"arrow-right-s\" : \"arrow-down-s\"\n this.folderButton.innerHTML = `<alchemy-icon name=\"${iconName}\"></alchemy-icon>`\n }\n }\n\n /**\n * Updates the folder button state based on whether the node has children\n * Converts between button and span as needed\n */\n updateFolderButton() {\n const folderElement = this.querySelector(\".page_folder\")\n if (!folderElement) return\n\n const shouldShowButton = this.hasChildren || this.folded\n\n if (shouldShowButton && folderElement.tagName === SPAN) {\n // Convert span to button with icon\n const iconName = this.folded ? \"arrow-right-s\" : \"arrow-down-s\"\n folderElement.outerHTML = `<button class=\"page_folder icon_button\">\n <alchemy-icon name=\"${iconName}\"></alchemy-icon>\n </button>`\n\n // Re-attach event listener to the new button element\n this.folderButton?.addEventListener(\"click\", this)\n } else if (!shouldShowButton && folderElement.tagName === BUTTON) {\n // Convert button to empty span (no children and not folded)\n folderElement.outerHTML = '<span class=\"page_folder\"></span>'\n } else if (shouldShowButton && folderElement.tagName === BUTTON) {\n // Button exists, just update the icon direction\n this.updateFolderIcon()\n }\n }\n\n get hasChildren() {\n const childrenContainer = this.querySelector(\n `#page_${this.pageId}_children`\n )\n if (!childrenContainer) return false\n\n return (\n childrenContainer.querySelectorAll(\":scope > alchemy-page-node\").length >\n 0\n )\n }\n\n get folderButton() {\n return this.querySelector(\"button.page_folder\")\n }\n}\n\ncustomElements.define(\"alchemy-page-node\", AlchemyPageNode)\n","// Handles the page publication date fields\nexport class PagePublicationFields extends HTMLElement {\n connectedCallback() {\n const public_on_picker = this.querySelector(\n \"alchemy-datepicker:has(#page_public_on)\"\n )\n const public_until_picker = this.querySelector(\n \"alchemy-datepicker:has(#page_public_until)\"\n )\n const publication_date_fields = this.querySelector(\n \".page-publication-date-fields\"\n )\n const public_field = this.querySelector(\"#page_public\")\n\n if (!public_field) return\n\n public_field.addEventListener(\"click\", function (evt) {\n const checkbox = evt.target\n const now = new Date()\n\n if (checkbox.checked) {\n publication_date_fields.classList.remove(\"hidden\")\n public_on_picker.flatpickr.setDate(now)\n } else {\n publication_date_fields.classList.add(\"hidden\")\n public_on_picker.flatpickr.clear()\n }\n public_until_picker.flatpickr?.clear()\n })\n }\n}\n\ncustomElements.define(\"alchemy-page-publication-fields\", PagePublicationFields)\n","import { RemoteSelect } from \"alchemy_admin/components/remote_select\"\n\nclass PageSelect extends RemoteSelect {\n get pageId() {\n return this.selection ? JSON.parse(this.selection)[\"id\"] : undefined\n }\n\n _searchQuery(term, page) {\n return {\n q: {\n name_cont: term,\n ...JSON.parse(this.queryParams)\n },\n page: page\n }\n }\n\n _parseResponse(response) {\n const meta = response.meta\n return {\n results: response.pages,\n more: meta.page * meta.per_page < meta.total_count\n }\n }\n\n /**\n * result which is visible if a page was selected\n * @param {object} page\n * @returns {string}\n * @private\n */\n _renderResult(page) {\n return page.text || page.name\n }\n\n /**\n * html template for each list entry\n * @param {object} page\n * @param {string} term\n * @returns {string}\n * @private\n */\n _renderListEntry(page, term) {\n return `\n <div class=\"page-select--page\">\n <div class=\"page-select--top\">\n <alchemy-icon name=\"file-3\"></alchemy-icon>\n <span class=\"page-select--page-name\">${this._hightlightTerm(page.name, term)}</span>\n <span class=\"page-select--site-name\">${page.site.name}</span>\n </div>\n <div class=\"page-select--bottom\">\n <span class=\"page-select--page-urlname\">${page.url_path}</span>\n <span class=\"page-select--language-code\">${page.language_code}</span>\n </div>\n </div>\n `\n }\n}\n\ncustomElements.define(\"alchemy-page-select\", PageSelect)\n","class PictureDescriptionSelect extends HTMLElement {\n constructor() {\n super()\n this.addEventListener(\"change\", this)\n }\n\n handleEvent(event) {\n switch (event.type) {\n case \"change\":\n this.onChange()\n break\n }\n }\n\n onChange() {\n const url = new URL(this.getAttribute(\"url\"))\n const select = this.querySelector(\"select\")\n url.searchParams.set(\"language_id\", select.value)\n Turbo.visit(url, { frame: \"picture_descriptions\" })\n }\n}\n\ncustomElements.define(\n \"alchemy-picture-description-select\",\n PictureDescriptionSelect\n)\n","import debounce from \"alchemy_admin/utils/debounce\"\nimport max from \"alchemy_admin/utils/max\"\nimport { get } from \"alchemy_admin/utils/ajax\"\nimport { growl } from \"alchemy_admin/growler\"\n\nconst UPDATE_DELAY = 125\nconst IMAGE_PLACEHOLDER = '<alchemy-icon name=\"image\" size=\"xl\"></alchemy-icon>'\nconst THUMBNAIL_SIZE = \"160x120\"\n\nexport class PictureEditor extends HTMLElement {\n constructor() {\n super()\n\n this.cropFromField = this.querySelector(\"[data-crop-from]\")\n this.cropSizeField = this.querySelector(\"[data-crop-size]\")\n this.pictureIdField = this.querySelector(\"[data-picture-id]\")\n this.targetSizeField = this.querySelector(\"[data-target-size]\")\n this.imageCropperField = this.querySelector(\"[data-image-cropper]\")\n this.image = this.querySelector(\"img\")\n this.pictureThumbnail = this.querySelector(\"alchemy-picture-thumbnail\")\n this.deleteButton = this.querySelector(\".picture_tool.delete\")\n this.cropLink = this.querySelector(\".crop_link\")\n\n this.targetSize = this.targetSizeField.dataset.targetSize\n this.pictureId = this.pictureIdField.value\n\n // The mutation observer is observing multiple fields that all get updated\n // simultaneously. We only want to update the image once, so we debounce.\n this.update = debounce(() => {\n this.updateImage()\n this.updateCropLink()\n }, UPDATE_DELAY)\n\n this.deleteButton?.addEventListener(\"click\", this.removeImage.bind(this))\n }\n\n connectedCallback() {\n this.observer = new MutationObserver(this.mutationCallback.bind(this))\n\n this.observer.observe(this.cropFromField, { attributes: true })\n this.observer.observe(this.cropSizeField, { attributes: true })\n this.observer.observe(this.pictureIdField, { attributes: true })\n }\n\n disconnectedCallback() {\n this.observer.disconnect()\n }\n\n mutationCallback(mutationsList) {\n for (const mutation of mutationsList) {\n if (\"pictureId\" in mutation.target.dataset) {\n this.cropFromField.value = \"\"\n this.cropSizeField.value = \"\"\n this.pictureId = mutation.target.value\n }\n this.update()\n }\n }\n\n updateImage() {\n if (!this.pictureId) return\n\n this.pictureThumbnail.loading = true\n get(Alchemy.routes.url_admin_picture_path(this.pictureId), {\n crop: this.imageCropperEnabled,\n crop_from: this.cropFrom,\n crop_size: this.cropSize,\n flatten: true,\n size: THUMBNAIL_SIZE\n })\n .then(({ data }) => {\n this.pictureThumbnail.src = data.url\n this.pictureThumbnail.image.alt = data.alt\n this.pictureThumbnail.image.title = data.title\n this.setElementDirty()\n })\n .catch((error) => {\n console.error(error.message || error)\n growl(error.message || error, \"error\")\n })\n }\n\n removeImage() {\n this.pictureThumbnail.innerHTML = IMAGE_PLACEHOLDER\n this.pictureIdField.value = \"\"\n this.image = null\n this.cropLink.classList.add(\"disabled\")\n this.setElementDirty()\n }\n\n setElementDirty() {\n this.closest(\".element-editor\").setDirty(this)\n }\n\n updateCropLink() {\n if (!this.pictureId || !this.imageCropperEnabled) return\n\n this.cropLink.classList.remove(\"disabled\")\n\n if (this.cropLink.href.match(/(picture_id=)\\d+/)) {\n this.cropLink.href = this.cropLink.href.replace(\n /(picture_id=)\\d+/,\n \"$1\" + this.pictureId\n )\n } else {\n this.cropLink.href = this.cropLink.href + `&picture_id=${this.pictureId}`\n }\n }\n\n get cropFrom() {\n if (this.cropFromField.value === \"\") {\n return this.defaultCropFrom.join(\"x\")\n }\n return this.cropFromField.value\n }\n\n get cropSize() {\n if (this.cropSizeField.value === \"\") {\n return this.defaultCropSize.join(\"x\")\n }\n return this.cropSizeField.value\n }\n\n get defaultCropSize() {\n if (!this.imageCropperEnabled) return []\n\n const mask = this.targetSize.split(\"x\").map((n) => parseInt(n))\n const zoom = max(\n mask[0] / this.imageFileWidth,\n mask[1] / this.imageFileHeight\n )\n\n return [Math.round(mask[0] / zoom), Math.round(mask[1] / zoom)]\n }\n\n get defaultCropFrom() {\n if (!this.imageCropperEnabled) return []\n\n const dimensions = this.defaultCropSize\n\n return [\n Math.round((this.imageFileWidth - dimensions[0]) / 2),\n Math.round((this.imageFileHeight - dimensions[1]) / 2)\n ]\n }\n\n get imageFileWidth() {\n return parseInt(this.pictureIdField.dataset.imageFileWidth)\n }\n\n get imageFileHeight() {\n return parseInt(this.pictureIdField.dataset.imageFileHeight)\n }\n\n get imageCropperEnabled() {\n return this.targetSizeField.dataset.imageCropper === \"true\"\n }\n}\n\ncustomElements.define(\"alchemy-picture-editor\", PictureEditor)\n","export default function (func, delay) {\n let timeout\n\n return function (...args) {\n const that = this\n\n clearTimeout(timeout)\n timeout = setTimeout(() => func.apply(that, args), delay)\n }\n}\n","export default function (a, b) {\n return a >= b ? a : b\n}\n","// Shows spinner while loading images and\n// fades the image after its been loaded\n\nimport Spinner from \"alchemy_admin/spinner\"\n\nexport default class PictureThumbnail extends HTMLElement {\n constructor() {\n super()\n\n this.classList.add(\"thumbnail_background\")\n this.spinner = new Spinner(\"small\")\n\n if (this.src) {\n this.start()\n }\n }\n\n handleEvent(evt) {\n switch (evt.type) {\n case \"load\":\n this.#onLoaded()\n break\n case \"error\":\n this.#onError(evt)\n break\n default:\n break\n }\n }\n\n connectedCallback() {\n this.#setImage()\n }\n\n disconnectedCallback() {\n this.image?.removeEventListener(\"load\", this)\n this.image?.removeEventListener(\"error\", this)\n this.stop()\n }\n\n createImage(src = this.src, alt = this.name) {\n this.image = new Image()\n this.image.src = src\n if (alt) {\n this.image.alt = alt\n }\n this.image.loading = \"lazy\"\n }\n\n start(src) {\n this.createImage(src)\n this.image.addEventListener(\"load\", this)\n this.image.addEventListener(\"error\", this)\n this.load()\n }\n\n load() {\n if (this.image?.complete) {\n return\n }\n this.setAttribute(\"loading\", \"loading\")\n this.innerHTML = \"\"\n this.spinner.spin(this)\n }\n\n stop() {\n this.classList.remove(\"loading\")\n this.spinner.stop()\n }\n\n #onLoaded() {\n this.spinner.stop()\n this.removeAttribute(\"loading\")\n }\n\n #onError(evt) {\n const message = `Could not load ${this.image.src}`\n const hoist = this.closest(\".ingredient-editor\")\n this.spinner.stop()\n this.innerHTML = `\n <sl-tooltip content=\"${message}\" ${hoist ? \"hoist\" : \"\"}>\n <alchemy-icon name=\"alert\" class=\"error\"></alchemy-icon>\n </sl-tooltip>\n `\n console.error(message, evt)\n }\n\n #setImage() {\n if (this.image?.complete) {\n this.replaceChildren(this.image)\n } else if (this.image) {\n this.append(this.image)\n }\n }\n\n set loading(value) {\n value ? this.load() : this.stop()\n }\n\n set src(src) {\n this.start(src)\n this.#setImage()\n }\n\n get name() {\n return this.getAttribute(\"name\")\n }\n\n get src() {\n return this.getAttribute(\"src\")\n }\n}\n\ncustomElements.define(\"alchemy-picture-thumbnail\", PictureThumbnail)\n","class PublishPageButton extends HTMLElement {\n constructor() {\n super()\n this.addEventListener(\"submit\", this)\n }\n\n connectedCallback() {\n document.addEventListener(\"alchemy:page-dirty\", this)\n }\n\n disconnectedCallback() {\n document.removeEventListener(\"alchemy:page-dirty\", this)\n }\n\n handleEvent(event) {\n switch (event.type) {\n case \"alchemy:page-dirty\":\n this.markDirty(event.detail)\n break\n case \"submit\":\n this.button.loading = true\n break\n }\n }\n\n markDirty(detail) {\n this.button.variant = \"primary\"\n this.button.disabled = false\n this.tooltip.content = detail.tooltip\n }\n\n get button() {\n return this.querySelector(\"sl-button\")\n }\n\n get tooltip() {\n return this.querySelector(\"sl-tooltip\")\n }\n}\n\ncustomElements.define(\"alchemy-publish-page-button\", PublishPageButton)\n","class Select extends HTMLSelectElement {\n #select2Element\n\n connectedCallback() {\n this.classList.add(\"alchemy_selectbox\")\n\n this.#select2Element = $(this).select2({\n minimumResultsForSearch: 5,\n dropdownAutoWidth: true,\n allowClear: !!this.allowClear\n })\n\n // For single selects, remove the close button if allowClear is not set\n // For multiple selects, always keep the close buttons\n if (!this.allowClear && !this.multiple) {\n this.#select2Element\n .prev(\".select2-container\")\n .find(\".select2-search-choice-close\")\n .remove()\n }\n }\n\n enable() {\n this.removeAttribute(\"disabled\")\n this.#updateSelect2()\n }\n\n disable() {\n this.setAttribute(\"disabled\", \"disabled\")\n this.#updateSelect2()\n }\n\n setOptions(data, prompt = undefined) {\n let selectedValue = this.value\n\n // reset the old options and insert the placeholder(s) first\n this.innerHTML = \"\"\n if (prompt) {\n this.add(new Option(prompt, \"\"))\n }\n\n // add the new options to the select\n data.forEach((item) => {\n this.add(new Option(item.text, item.id, false, item.id === selectedValue))\n })\n\n this.#updateSelect2()\n }\n\n /**\n * inform Select2 to update\n */\n #updateSelect2() {\n this.#select2Element.trigger(\"change\")\n }\n\n get allowClear() {\n return this.dataset.hasOwnProperty(\"allowClear\") || this.multiple\n }\n}\n\ncustomElements.define(\"alchemy-select\", Select, { extends: \"select\" })\n","import Sortable from \"sortablejs\"\nimport { growl } from \"alchemy_admin/growler\"\nimport { patch } from \"alchemy_admin/utils/ajax\"\nimport { translate } from \"alchemy_admin/i18n\"\nimport pleaseWaitOverlay from \"alchemy_admin/please_wait_overlay\"\n\n/**\n * Custom element for the sitemap container\n * Handles search/filter functionality and drag-and-drop sorting\n */\nexport class AlchemySitemap extends HTMLElement {\n connectedCallback() {\n this.searchInput = document.querySelector(\".search_input_field\")\n this.clearButton = document.querySelector(\"#search_field_clear\")\n this.resultCounter = document.querySelector(\"#page_filter_result\")\n\n this.setupSearch()\n\n // Wait for child custom elements to be defined before setting up sortables\n requestAnimationFrame(() => {\n this.setupSortables()\n })\n\n // Set up MutationObserver to re-initialize sortables when children containers are added\n this.observer = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.addedNodes.forEach((node) => {\n if (node.nodeType !== Node.ELEMENT_NODE) return\n\n // If the added node itself is a children container, initialize it\n if (node.classList?.contains(\"children\")) {\n this.setupSortable(node)\n }\n\n // Also check for children containers nested within the added node\n // This handles cases where a parent element with nested children is added at once\n node\n .querySelectorAll(\".children\")\n .forEach((el) => this.setupSortable(el))\n })\n })\n })\n\n // Observe the sitemap for added nodes\n this.observer.observe(this, {\n childList: true,\n subtree: true\n })\n }\n\n disconnectedCallback() {\n this.teardownSearch()\n this.observer?.disconnect()\n }\n\n setupSearch() {\n this.searchInput?.addEventListener(\"input\", this)\n this.clearButton?.addEventListener(\"click\", this)\n }\n\n teardownSearch() {\n this.searchInput?.removeEventListener(\"input\", this)\n this.clearButton?.removeEventListener(\"click\", this)\n }\n\n handleEvent(event) {\n if (event.type === \"input\" && event.target === this.searchInput) {\n this.handleSearch(event)\n } else if (event.type === \"click\" && event.target === this.clearButton) {\n this.handleClearSearch(event)\n }\n }\n\n handleSearch(event) {\n const term = event.target.value.toLowerCase().trim()\n\n if (term === \"\") {\n this.clearFilter()\n return\n }\n\n this.filterPages(term)\n }\n\n filterPages(term) {\n const allPages = this.querySelectorAll(\".sitemap_page\")\n let matchCount = 0\n let firstMatch = null\n\n allPages.forEach((pageElement) => {\n const pageName = pageElement.getAttribute(\"name\") || \"\"\n\n if (pageName.toLowerCase().includes(term)) {\n pageElement.classList.add(\"highlight\")\n pageElement.classList.remove(\"no-match\")\n matchCount++\n if (!firstMatch) firstMatch = pageElement\n } else {\n pageElement.classList.remove(\"highlight\")\n pageElement.classList.add(\"no-match\")\n }\n })\n\n // Update result counter\n\n if (matchCount === 1) {\n this.resultCounter.textContent = `1 ${translate(\"page_found\")}`\n this.resultCounter.style.display = \"block\"\n } else if (matchCount > 1) {\n this.resultCounter.textContent = `${matchCount} ${translate(\"pages_found\")}`\n this.resultCounter.style.display = \"block\"\n } else {\n this.resultCounter.style.display = \"none\"\n }\n\n // Scroll first match into view\n if (firstMatch) {\n firstMatch.scrollIntoView({ behavior: \"smooth\", block: \"center\" })\n }\n }\n\n clearFilter() {\n const allPages = this.querySelectorAll(\".sitemap_page\")\n allPages.forEach((pageElement) => {\n pageElement.classList.remove(\"highlight\", \"no-match\")\n })\n\n this.resultCounter.style.display = \"none\"\n }\n\n handleClearSearch(event) {\n event.preventDefault()\n this.searchInput.value = \"\"\n this.clearFilter()\n }\n\n setupSortable(container) {\n new Sortable(container, {\n group: \"pages\",\n animation: 150,\n fallbackOnBody: true,\n swapThreshold: 0.65,\n handle: \".page-icon.handle\",\n draggable: \"alchemy-page-node\",\n onEnd: (evt) => this.handleSort(evt)\n })\n }\n\n setupSortables() {\n const sortables = this.querySelectorAll(\".children\")\n sortables.forEach((el) => this.setupSortable(el))\n }\n\n async handleSort(evt) {\n // Only proceed if actually moved to different position/container\n if (evt.from === evt.to && evt.oldIndex === evt.newIndex) {\n return\n }\n\n // evt.item is the <alchemy-page-node> element being dragged\n const pageNode = evt.item\n const pageId = pageNode.pageId\n const url = Alchemy.routes.move_admin_page_path(pageId)\n const data = {\n target_parent_id: evt.to.dataset.parentId,\n new_position: evt.newIndex\n }\n\n pleaseWaitOverlay(true)\n\n try {\n const response = await patch(url, data)\n const pageData = await response.data\n\n // Update the URL path of the moved page\n const pageEl = pageNode.querySelector(`#page_${pageId}`)\n if (pageEl) {\n const urlPathEl = pageEl.querySelector(\".sitemap_url\")\n if (urlPathEl && pageData.url_path) {\n urlPathEl.textContent = pageData.url_path\n }\n }\n\n // Update folder icons for affected parent pages\n this.updateFolderIcons(evt.from, evt.to)\n\n growl(translate(\"Successfully moved page\"))\n } catch (error) {\n growl(error.message || error, \"error\")\n // Revert the DOM change by reloading on error\n window.location.reload()\n } finally {\n pleaseWaitOverlay(false)\n }\n }\n\n updateFolderIcons(fromContainer, toContainer) {\n // Update folder icon for source parent (might now have no children)\n const fromParent = fromContainer.closest(\"alchemy-page-node\")\n fromParent?.updateFolderButton()\n\n // Update folder icon for destination parent (now definitely has children)\n if (fromContainer !== toContainer) {\n const toParent = toContainer.closest(\"alchemy-page-node\")\n toParent?.updateFolderButton()\n }\n }\n}\n\ncustomElements.define(\"alchemy-sitemap\", AlchemySitemap)\n","import Sortable from \"sortablejs\"\nimport { growl } from \"alchemy_admin/growler\"\nimport { post } from \"alchemy_admin/utils/ajax\"\nimport { reloadPreview } from \"alchemy_admin/components/preview_window\"\nimport { dispatchPageDirtyEvent } from \"alchemy_admin/components/element_editor\"\n\nconst SORTABLE_OPTIONS = {\n draggable: \".element-editor\",\n handle: \".element-handle.draggable\",\n ghostClass: \"dragged\",\n animation: 150,\n swapThreshold: 0.65,\n easing: \"cubic-bezier(1, 0, 0, 1)\"\n}\n\nfunction onStart(event) {\n const name = event.item.dataset.elementName\n document\n .querySelectorAll(`[data-droppable-elements~=\"${name}\"]`)\n .forEach((dropzone) => dropzone.classList.add(\"droppable-elements\"))\n}\n\nfunction onSort(event) {\n const item = event.item\n const parentElement = event.to.parentElement.closest(\".element-editor\")\n const params = {\n element_id: item.dataset.elementId,\n position: event.newIndex + 1\n }\n\n if (parentElement) {\n params.parent_element_id = parentElement.dataset.elementId\n }\n\n // Only send the request if the item was moved to a different container\n // or sorted in the same list. Not on the old list in order to avoid incrementing\n // the position of the other elements.\n if (event.target === event.to) {\n post(Alchemy.routes.order_admin_elements_path, params).then((response) => {\n const data = response.data\n growl(data.message)\n if (data.pageHasUnpublishedChanges) {\n dispatchPageDirtyEvent(data)\n }\n reloadPreview()\n item.updateTitle(data.preview_text)\n })\n }\n}\n\nfunction onEnd() {\n const dropzones = document.querySelectorAll(\"[data-droppable-elements]\")\n dropzones.forEach((dropzone) =>\n dropzone.classList.remove(\"droppable-elements\")\n )\n}\n\nclass SortableElements extends HTMLElement {\n connectedCallback() {\n const group = {\n name: this.dataset.elementName,\n put(to, _from, item) {\n return to.el.dataset.droppableElements\n .split(\" \")\n .includes(item.dataset.elementName)\n }\n }\n new Sortable(this, {\n ...SORTABLE_OPTIONS,\n onStart,\n onSort,\n onEnd,\n group\n })\n }\n}\n\ncustomElements.define(\"alchemy-sortable-elements\", SortableElements)\n","import { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\n\nclass Spinner extends AlchemyHTMLElement {\n static properties = {\n size: { default: \"medium\" },\n color: { default: \"currentColor\" }\n }\n\n render() {\n this.className = `spinner spinner--${this.size}`\n\n return `\n <svg width=\"100%\" viewBox=\"0 0 28 28\" style=\"--spinner-color: ${this.color}\">\n <path\n class=\"hex1\"\n d=\"M5.938,18.07l-5.878-5.9l2.154-8.058l8.024-2.161l5.87,5.9l-2.144,8.058L5.938,18.07z\"\n />\n <path\n class=\"hex2\"\n d=\"M19.686,20.785l-4.731-4.754l1.725-6.487l6.468-1.742l4.733,4.754l-1.734,6.487L19.686,20.785z\"\n />\n <path\n class=\"hex3\"\n d=\"M11.708,26.294l-3.47-3.485l1.276-4.758l4.74-1.276l3.468,3.485l-1.265,4.758L11.708,26.294z\"\n />\n </svg>\n `\n }\n}\n\ncustomElements.define(\"alchemy-spinner\", Spinner)\n","import { setupSelectLocale } from \"alchemy_admin/i18n\"\n\nclass TagsAutocomplete extends HTMLElement {\n async connectedCallback() {\n await setupSelectLocale()\n\n this.classList.add(\"autocomplete_tag_list\")\n $(this.input).select2(this.select2Config)\n }\n\n get input() {\n return this.getElementsByTagName(\"input\")[0]\n }\n\n get select2Config() {\n return {\n tags: true,\n tokenSeparators: [\",\"],\n openOnEnter: false,\n minimumInputLength: 1,\n createSearchChoice: this.#createSearchChoice,\n ajax: {\n url: this.getAttribute(\"url\"),\n dataType: \"json\",\n data: (term) => {\n return { term }\n },\n results: (data) => {\n return { results: data }\n }\n },\n initSelection: this.#initSelection\n }\n }\n\n #createSearchChoice(term, data) {\n if (\n $(data).filter(function () {\n return this.text.localeCompare(term) === 0\n }).length === 0\n ) {\n return {\n id: term,\n text: term\n }\n }\n }\n\n #initSelection(element, callback) {\n const data = []\n $(element.val().split(\",\")).each(function () {\n data.push({\n id: this.trim(),\n text: this\n })\n })\n callback(data)\n }\n}\n\ncustomElements.define(\"alchemy-tags-autocomplete\", TagsAutocomplete)\n","import \"tinymce\"\nimport { AlchemyHTMLElement } from \"alchemy_admin/components/alchemy_html_element\"\nimport { currentLocale } from \"alchemy_admin/i18n\"\n\nconst DARK_THEME = \"alchemy-dark\"\nconst LIGHT_THEME = \"alchemy\"\n\nclass Tinymce extends AlchemyHTMLElement {\n #min_height = null\n\n /**\n * the observer will initialize Tinymce if the textarea becomes visible\n */\n connected() {\n this.className = \"tinymce_container\"\n\n const observerCallback = (entries, observer) => {\n entries.forEach((entry) => {\n if (entry.intersectionRatio > 0) {\n this._initTinymceEditor()\n // disable observer after the Tinymce was initialized\n observer.unobserve(entry.target)\n }\n })\n }\n\n const options = {\n root: document.getElementById(\"element_area\"),\n rootMargin: \"0px\",\n threshold: [0.05]\n }\n\n this.tinymceIntersectionObserver = new IntersectionObserver(\n observerCallback,\n options\n )\n this.tinymceIntersectionObserver.observe(this)\n\n // Set up theme change listener\n this._setupThemeChangeListener()\n }\n\n /**\n * disconnect intersection observer and remove Tinymce editor if the web components get destroyed\n */\n disconnected() {\n if (this.tinymceIntersectionObserver !== null) {\n this.tinymceIntersectionObserver.disconnect()\n }\n\n // Remove theme change listener\n this._removeThemeChangeListener()\n\n tinymce.get(this.editorId)?.remove(this.editorId)\n }\n\n render() {\n return `\n ${this.initialContent}\n <alchemy-spinner size=\"small\"></alchemy-spinner>\n `\n }\n\n /**\n * hide the textarea until TinyMCE is ready to show the editor\n */\n afterRender() {\n this.style.minHeight = `${this.minHeight}px`\n this.editor.style.display = \"none\"\n }\n\n /**\n * initialize Richtext area after the Intersection observer triggered\n * @private\n */\n _initTinymceEditor() {\n tinymce.init(this.configuration).then((editors) => {\n editors.forEach((editor) => this._setupEditor(editor))\n })\n }\n\n /**\n * Setup editor after initialization\n * @param {Object} editor - The TinyMCE editor instance\n * @private\n */\n _setupEditor(editor) {\n // mark the editor container as visible\n // without these correction the editor remains hidden\n // after a drag and drop action\n editor.show()\n\n // remove the spinner after the Tinymce initialized (only on first init)\n const spinner = this.getElementsByTagName(\"alchemy-spinner\")[0]\n if (spinner) {\n spinner.remove()\n }\n\n // event listener to mark the editor as dirty\n if (this.elementEditor) {\n editor.on(\"dirty\", (evt) => {\n this.elementEditor.setDirty(evt.target.editorContainer)\n })\n editor.on(\"click\", () => this.elementEditor.onClickElement(false))\n }\n }\n\n /**\n * Set up listener for OS theme changes\n * @private\n */\n _setupThemeChangeListener() {\n this.darkModeMediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\")\n this.themeChangeHandler = (event) => this._handleThemeChange(event)\n this.darkModeMediaQuery.addEventListener(\"change\", this.themeChangeHandler)\n }\n\n /**\n * Remove theme change listener\n * @private\n */\n _removeThemeChangeListener() {\n if (this.darkModeMediaQuery && this.themeChangeHandler) {\n this.darkModeMediaQuery.removeEventListener(\n \"change\",\n this.themeChangeHandler\n )\n }\n }\n\n /**\n * Handle OS theme change and update TinyMCE skin\n * @param {MediaQueryListEvent} event - The media query change event\n * @private\n */\n _handleThemeChange(event) {\n const editor = tinymce.get(this.editorId)\n if (editor) {\n const skin = event.matches ? DARK_THEME : LIGHT_THEME\n const content_css = event.matches ? DARK_THEME : LIGHT_THEME\n\n // Update the skin by reinitializing the editor with new configuration\n editor.remove()\n tinymce\n .init({\n content_css,\n ...this.configuration,\n skin\n })\n .then((editors) => {\n editors.forEach((editor) => this._setupEditor(editor))\n })\n }\n }\n\n get configuration() {\n const customConfig = {}\n\n // read the attributes on the component and add them as custom configuration\n this.getAttributeNames().forEach((attributeName) => {\n if (![\"class\", \"id\", \"is\", \"name\", \"style\"].includes(attributeName)) {\n const config = this.getAttribute(attributeName)\n const key = attributeName.replaceAll(\"-\", \"_\")\n\n // Handle boolean HTML attributes (e.g., readonly=\"readonly\" or readonly=\"\")\n if (config === attributeName || config === \"\") {\n customConfig[key] = true\n } else {\n try {\n customConfig[key] = JSON.parse(config)\n } catch (e) {\n // also string values as parameter\n customConfig[key] = config\n }\n }\n }\n })\n\n const config = {\n content_css: this.preferredTheme,\n ...Alchemy.TinymceDefaults,\n ...customConfig,\n language: currentLocale(),\n selector: `#${this.editorId}`,\n skin: this.preferredTheme\n }\n\n // Tinymce has a height of 400px by default\n // if the element has a min_height set, we use this value for the height as well\n // so we do not need to set both values in the element configuration\n config.height = config.min_height\n\n return config\n }\n\n get preferredTheme() {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n ? DARK_THEME\n : LIGHT_THEME\n }\n\n get editorId() {\n return this.editor.id\n }\n\n get editor() {\n return this.getElementsByTagName(\"textarea\")[0]\n }\n\n get elementEditor() {\n return document\n .getElementById(this.editorId)\n .closest(\"alchemy-element-editor\")\n }\n\n get minHeight() {\n return this.#min_height || this.configuration.min_height\n }\n\n set minHeight(value) {\n this.#min_height = value\n }\n}\n\ncustomElements.define(\"alchemy-tinymce\", Tinymce)\n","import Spinner from \"alchemy_admin/spinner\"\n\nclass UpdateCheck extends HTMLElement {\n async connectedCallback() {\n const spinner = new Spinner(\"small\")\n spinner.spin(this)\n\n try {\n const response = await fetch(this.url, { credentials: \"include\" })\n const responseJSON = await response.json()\n\n if (response.ok) {\n this.showStatus(responseJSON)\n } else {\n this.showError(response)\n }\n } catch (error) {\n this.showError(error)\n } finally {\n spinner.stop()\n }\n }\n\n get url() {\n return this.getAttribute(\"url\")\n }\n\n showStatus(responseJSON) {\n if (responseJSON[\"status\"] == \"true\") {\n this.querySelector(\".update_available\").classList.remove(\"hidden\")\n } else {\n this.querySelector(\".up_to_date\").classList.remove(\"hidden\")\n }\n }\n\n showError(error) {\n this.querySelector(\".error\").classList.remove(\"hidden\")\n console.error(\"[alchemy] Error fetching update status\", error)\n }\n}\n\ncustomElements.define(\"alchemy-update-check\", UpdateCheck)\n","(()=>{var n=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})[\"node_folder.hbs\"]=n({0:function(n,e,l,a,r){return\"right\"},1:function(n,e,l,a,r){return\"down\"},compiler:[8,\">= 4.3.0\"],main:function(n,e,l,a,r){var o,t=n.lambda,d=n.escapeExpression,u=n.lookupProperty||function(n,e){if(Object.prototype.hasOwnProperty.call(n,e))return n[e]};return'<a class=\"node_folder\" data-record-id=\"'+d(t(null!=(o=null!=e?u(e,\"node\"):e)?u(o,\"id\"):o,e))+'\" data-record-type=\"'+d(t(null!=(o=null!=e?u(e,\"node\"):e)?u(o,\"type\"):o,e))+'\">\\n <alchemy-icon name=\"arrow-'+(null!=(o=u(l,\"if\").call(null!=e?e:n.nullContext||{},null!=(o=null!=e?u(e,\"node\"):e)?u(o,\"folded\"):o,{name:\"if\",hash:{},fn:n.program(0,r,0),inverse:n.program(1,r,0),data:r,loc:{start:{line:2,column:28},end:{line:2,column:72}}}))?o:\"\")+'-s\"></alchemy-icon>\\n</a>\\n'},useData:!0})})();","import { registerIconLibrary, setDefaultAnimation } from \"shoelace\"\n\n// Change the default animation for all tooltips\nsetDefaultAnimation(\"tooltip.show\", {\n keyframes: [\n { transform: \"translateY(10px)\", opacity: \"0\" },\n { transform: \"translateY(0)\", opacity: \"1\" }\n ],\n options: {\n duration: 100\n }\n})\n\nsetDefaultAnimation(\"tooltip.hide\", {\n keyframes: [\n { transform: \"translateY(0)\", opacity: \"1\" },\n { transform: \"translateY(10px)\", opacity: \"0\" }\n ],\n options: {\n duration: 100\n }\n})\n\n// Change the default animation for all dialogs\nsetDefaultAnimation(\"dialog.show\", {\n keyframes: [\n { transform: \"scale(0.98)\", opacity: \"0\" },\n { transform: \"scale(1)\", opacity: \"1\" }\n ],\n options: {\n duration: 150\n }\n})\n\nsetDefaultAnimation(\"dialog.hide\", {\n keyframes: [\n { transform: \"scale(1)\", opacity: \"1\" },\n { transform: \"scale(0.98)\", opacity: \"0\" }\n ],\n options: {\n duration: 150\n }\n})\n\nconst spriteUrl = document\n .querySelector('link[rel=\"preload\"][as=\"image\"]')\n .getAttribute(\"href\")\n\nconst iconMap = {\n \"x-lg\": \"close\",\n caret: \"arrow-down-s\"\n}\n\nconst options = {\n resolver: (name) => `${spriteUrl}#ri-${iconMap[name] || name}-line`,\n mutator: (svg) => {\n svg.setAttribute(\"fill\", \"currentColor\")\n svg.setAttribute(\"viewBox\", \"0 0 24 24\")\n },\n spriteSheet: true\n}\n\nregisterIconLibrary(\"default\", options)\nregisterIconLibrary(\"system\", options)\n","export function on(eventName, baseSelector, targetSelector, callback) {\n document.querySelectorAll(baseSelector).forEach((baseNode) => {\n baseNode.addEventListener(eventName, (evt) => {\n const targets = Array.from(baseNode.querySelectorAll(targetSelector))\n let currentNode = evt.target\n\n while (currentNode !== baseNode) {\n if (targets.includes(currentNode)) {\n callback.call(currentNode, evt)\n return\n }\n currentNode = currentNode.parentElement\n }\n })\n })\n}\n","import Cropper from \"cropperjs\"\n\nexport default class ImageCropper {\n #initialized = false\n #cropper = null\n #cropFromField = null\n #cropSizeField = null\n\n constructor(image, settings) {\n this.image = image\n this.defaultBox = settings.default_box\n this.aspectRatio = settings.ratio\n this.#cropFromField = document.getElementById(\n settings.crop_from_form_field_id\n )\n this.#cropSizeField = document.getElementById(\n settings.crop_size_form_field_id\n )\n this.elementId = settings.element_id\n this.dialog = Alchemy.currentDialog()\n if (this.dialog) {\n this.dialog.options.closed = () => this.destroy()\n this.bind()\n }\n this.init()\n }\n\n get cropperOptions() {\n return {\n aspectRatio: this.aspectRatio,\n viewMode: 1,\n zoomable: false,\n checkCrossOrigin: false, // Prevent CORS issues\n checkOrientation: false, // Prevent loading the image via AJAX which can cause CORS issues\n data: this.box,\n cropend: () => {\n const data = this.#cropper.getData(true)\n this.update(data)\n }\n }\n }\n\n get cropFrom() {\n if (this.#cropFromField?.value) {\n return this.#cropFromField.value.split(\"x\").map((v) => parseInt(v))\n }\n }\n\n get cropSize() {\n if (this.#cropSizeField?.value) {\n return this.#cropSizeField.value.split(\"x\").map((v) => parseInt(v))\n }\n }\n\n get box() {\n if (this.cropFrom && this.cropSize) {\n return {\n x: this.cropFrom[0],\n y: this.cropFrom[1],\n width: this.cropSize[0],\n height: this.cropSize[1]\n }\n } else {\n return this.defaultBoxSize\n }\n }\n\n get defaultBoxSize() {\n return {\n x: this.defaultBox[0],\n y: this.defaultBox[1],\n width: this.defaultBox[2],\n height: this.defaultBox[3]\n }\n }\n\n init() {\n if (!this.#initialized) {\n this.#cropper = new Cropper(this.image, this.cropperOptions)\n this.#initialized = true\n }\n }\n\n update(coords) {\n this.#cropFromField.value = `${coords.x}x${coords.y}`\n this.#cropFromField.dispatchEvent(new Event(\"change\"))\n this.#cropSizeField.value = `${coords.width}x${coords.height}`\n this.#cropSizeField.dispatchEvent(new Event(\"change\"))\n }\n\n reset() {\n this.#cropper.setData(this.defaultBoxSize)\n this.update(this.defaultBoxSize)\n }\n\n destroy() {\n if (this.#cropper) {\n this.#cropper.destroy()\n }\n this.#initialized = false\n return true\n }\n\n bind() {\n this.dialog.dialog_body.find('button[type=\"submit\"]').on(\"click\", () => {\n const elementEditor = document.querySelector(\n `[data-element-id='${this.elementId}']`\n )\n elementEditor.setDirty()\n this.dialog.close()\n return false\n })\n this.dialog.dialog_body.find('button[type=\"reset\"]').on(\"click\", () => {\n this.reset()\n return false\n })\n }\n}\n","import { Dialog } from \"alchemy_admin/dialog\"\n\nexport default class ImageOverlay extends Dialog {\n constructor(url, options = {}) {\n super(url, options)\n }\n\n init() {\n $(\".zoomed-picture-background\").on(\"click\", (e) => {\n e.stopPropagation()\n if (e.target.nodeName === \"IMG\") {\n return\n }\n this.close()\n return false\n })\n $(\".picture-overlay-handle\").on(\"click\", (e) => {\n this.dialog.toggleClass(\"hide-form\")\n return false\n })\n this.$previous = $(\".previous-picture\")\n this.$next = $(\".next-picture\")\n this.#initKeyboardNavigation()\n super.init()\n }\n\n previous() {\n if (this.$previous[0] != null) {\n this.$previous[0].click()\n }\n }\n\n next() {\n if (this.$next[0] != null) {\n this.$next[0].click()\n }\n }\n\n build() {\n this.dialog_container = $('<div class=\"alchemy-image-overlay-container\" />')\n this.dialog = $('<div class=\"alchemy-image-overlay-dialog\" />')\n this.dialog_body = $('<div class=\"alchemy-image-overlay-body\" />')\n this.close_button = $(`<a class=\"alchemy-image-overlay-close\">\n <alchemy-icon name=\"close\" size=\"xl\"></alchemy-icon>\n </a>`)\n this.dialog.append(this.close_button)\n this.dialog.append(this.dialog_body)\n this.dialog_container.append(this.dialog)\n this.overlay = $('<div class=\"alchemy-image-overlay\" />')\n this.$body.append(this.overlay)\n this.$body.append(this.dialog_container)\n }\n\n #initKeyboardNavigation() {\n this.$document.keydown((e) => {\n if (e.target.nodeName === \"INPUT\" || e.target.nodeName === \"TEXTAREA\") {\n return true\n }\n switch (e.which) {\n case 37:\n this.previous()\n return false\n case 39:\n this.next()\n return false\n default:\n return true\n }\n })\n }\n}\n","import { on } from \"alchemy_admin/utils/events\"\nimport { openDialog } from \"alchemy_admin/dialog\"\n\nfunction toggleCheckboxes(state) {\n document\n .querySelectorAll(\".picture_tool.select input[type='checkbox']\")\n .forEach((checkbox) => {\n checkbox.checked = state\n checkbox.closest(\".picture_thumbnail\").classList.toggle(\"active\", state)\n })\n}\n\nfunction checkedInputs() {\n return document.querySelectorAll(\"#picture_archive input:checked\")\n}\n\nfunction editMultiplePicturesUrl(href) {\n const url = new URL(href)\n\n checkedInputs().forEach((entry) =>\n url.searchParams.append(entry.name, entry.value)\n )\n\n return url.toString()\n}\n\n/**\n * Multiple picture select handler for the picture archive.\n */\nexport default function PictureSelector() {\n const selectAllButton = document.querySelector(\"#select_all_pictures\")\n const selectedItemTools = document.querySelector(\".selected_item_tools\")\n\n on(\"click\", \".toolbar_buttons\", \"a#select_all_pictures\", (event) => {\n event.preventDefault()\n\n selectAllButton.classList.toggle(\"active\")\n\n const state = selectAllButton.classList.contains(\"active\")\n\n toggleCheckboxes(state)\n\n selectedItemTools.classList.toggle(\"hidden\", !state)\n })\n\n // make the item toolbar visible and show the checkbox also if it is not hovered anymore\n on(\"change\", \".picture_tool.select\", \"input\", (event) => {\n selectedItemTools.classList.toggle(\"hidden\", checkedInputs().length === 0)\n\n const parentElementClassList = event.target.parentElement.classList\n const checked = event.target.checked\n\n parentElementClassList.toggle(\"visible\", checked)\n })\n\n // open the edit view in a dialog modal\n on(\"click\", \".selected_item_tools\", \"a#edit_multiple_pictures\", (event) => {\n event.preventDefault()\n\n const url = editMultiplePicturesUrl(event.target.href)\n\n openDialog(url, {\n title: event.target.title,\n size: \"400x295\"\n })\n })\n}\n","import Sortable from \"sortablejs\"\nimport { patch } from \"alchemy_admin/utils/ajax\"\nimport { on } from \"alchemy_admin/utils/events\"\nimport { growl } from \"alchemy_admin/growler\"\n\nfunction displayNodeFolders() {\n document.querySelectorAll(\"li.menu-item\").forEach((el) => {\n const leftIconArea = el.querySelector(\".nodes_tree-left_images\")\n const list = el.querySelector(\".children\")\n const node = {\n folded: el.dataset.folded === \"true\",\n id: el.dataset.id,\n type: el.dataset.type\n }\n\n if (list.children.length > 0 || node.folded) {\n leftIconArea.innerHTML = Handlebars.templates[\"node_folder.hbs\"]({\n node: node\n })\n } else {\n leftIconArea.innerHTML = \" \"\n }\n })\n}\n\nfunction onFinishDragging(evt) {\n const url = Alchemy.routes[evt.item.dataset.type].move_api_path(\n evt.item.dataset.id\n )\n const data = {\n target_parent_id: evt.to.dataset.recordId,\n new_position: evt.newIndex\n }\n\n patch(url, data)\n .then(() => {\n const message = Alchemy.t(\"Successfully moved menu item\")\n growl(message)\n displayNodeFolders()\n })\n .catch((error) => {\n growl(error.message || error, \"error\")\n })\n}\n\nfunction handleNodeFolders() {\n on(\"click\", \".nodes_tree\", \".node_folder\", function () {\n const nodeId = this.dataset.recordId\n const menu_item = this.closest(\"li.menu-item\")\n const url =\n Alchemy.routes[this.dataset.recordType].toggle_folded_api_path(nodeId)\n const list = menu_item.querySelector(\".children\")\n\n patch(url)\n .then(() => {\n list.classList.toggle(\"folded\")\n menu_item.dataset.folded =\n menu_item.dataset.folded == \"true\" ? \"false\" : \"true\"\n displayNodeFolders()\n })\n .catch((error) => {\n growl(error.message || error)\n })\n })\n}\n\nexport default function NodeTree() {\n handleNodeFolders()\n displayNodeFolders()\n\n document.querySelectorAll(\".nodes_tree ul.children\").forEach((el) => {\n new Sortable(el, {\n group: \"nodes\",\n animation: 150,\n fallbackOnBody: true,\n swapThreshold: 0.65,\n handle: \".node_name\",\n invertSwap: true,\n onEnd: onFinishDragging\n })\n })\n}\n","// We still use jQuery in some places (ie. select2)\nimport \"handlebars\"\nimport \"jquery\"\nimport \"@ungap/custom-elements\"\nimport { Turbo } from \"@hotwired/turbo-rails\"\nimport \"select2\"\n\nimport Rails from \"@rails/ujs\"\n\nimport { translate } from \"alchemy_admin/i18n\"\nimport { currentDialog, closeCurrentDialog } from \"alchemy_admin/dialog\"\nimport Dirty from \"alchemy_admin/dirty\"\nimport * as FixedElements from \"alchemy_admin/fixed_elements\"\nimport { growl } from \"alchemy_admin/growler\"\nimport Initializer from \"alchemy_admin/initializer\"\nimport { LinkDialog } from \"alchemy_admin/link_dialog\"\nimport pleaseWaitOverlay from \"alchemy_admin/please_wait_overlay\"\nimport Spinner from \"alchemy_admin/spinner\"\nimport { reloadPreview } from \"alchemy_admin/components/preview_window\"\nimport { openConfirmDialog } from \"alchemy_admin/confirm_dialog\"\n\n// Web Components\nimport \"alchemy_admin/components\"\n\n// Handlebars Templates\nimport \"alchemy_admin/templates/compiled\"\n\n// Shoelace Setup\nimport \"alchemy_admin/shoelace_theme\"\n\n// Global Alchemy object\nif (typeof window.Alchemy === \"undefined\") {\n window.Alchemy = {}\n}\n\n// Enhance the global Alchemy object with imported features\nObject.assign(Alchemy, {\n closeCurrentDialog,\n currentDialog,\n ...Dirty,\n t: translate, // Global utility method for translating a given string\n FixedElements,\n growl,\n LinkDialog,\n pleaseWaitOverlay,\n Spinner,\n reloadPreview\n})\n\nRails.start()\nTurbo.config.forms.confirm = openConfirmDialog\ndocument.addEventListener(\"turbo:load\", Initializer)\n\n// Public API for extensions\nexport { RemoteSelect } from \"alchemy_admin/components/remote_select\"\nexport { on } from \"alchemy_admin/utils/events\"\n\n// Page-specific modules - bundled to avoid dual-loading\nexport { default as ImageCropper } from \"alchemy_admin/image_cropper\"\nexport { default as ImageOverlay } from \"alchemy_admin/image_overlay\"\nexport { default as pictureSelector } from \"alchemy_admin/picture_selector\"\nexport { default as NodeTree } from \"alchemy_admin/node_tree\"\n","import Hotkeys from \"alchemy_admin/hotkeys\"\nimport pleaseWaitOverlay from \"alchemy_admin/please_wait_overlay\"\n\nexport default function Initializer() {\n // We obviously have javascript enabled.\n document.documentElement.classList.remove(\"no-js\")\n\n // Initialize hotkeys.\n Hotkeys()\n\n // Add observer for please wait overlay.\n document.querySelectorAll(\".please_wait\").forEach((element) => {\n element.addEventListener(\"click\", pleaseWaitOverlay)\n })\n\n // Hack for enabling tab focus for <a>'s styled as button.\n document.querySelectorAll(\"a.button\").forEach((button) => {\n button.setAttribute(\"tabindex\", 0)\n })\n\n // Override the filter of keymaster.js so we can blur the fields on esc key.\n key.filter = function (event) {\n let tagName = (event.target || event.srcElement).tagName\n return (\n key.isPressed(\"esc\") ||\n !(tagName === \"INPUT\" || tagName === \"SELECT\" || tagName === \"TEXTAREA\")\n )\n }\n}\n"],"names":["KEY_SEPARATOR","getTranslation","key","locale","currentLocale","translations","Alchemy","test","keys","split","group","nestedTranslation","console","warn","document","documentElement","lang","translate","replacement","undefined","translation","replace","async","setupSelectLocale","import","$","extend","fn","select2","defaults","locales","bindedHotkeys","showHelp","evt","target","is","String","fromCharCode","which","openDialog","title","t","size","Hotkeys","scope","jQuery","removeEventListener","addEventListener","forEach","hotkey","unbind","search_fields","querySelectorAll","search_fields_clear","setScope","el","focus","focusVisible","push","click","blur","dataset","alchemyHotkey","createHtmlElement","text","element","createElement","innerHTML","content","children","Spinner$1","constructor","color","this","spinner","spin","parent","body","append","stop","remove","currentDialogs","DEFAULTS","header_height","padding","modal","overflow","ready","closed","Dialog","url","options","$document","$window","window","$body","width","parseInt","height","build","resize","open","dialog","trigger","bind_close_events","requestAnimationFrame","dialog_container","addClass","overlay","load","close","off","removeClass","on","pop","show_spinner","get","data","fail","xhr","show_error","reload","dialog_body","empty","remove_spinner","hide","html","init","dispatchEvent","CustomEvent","bubbles","detail","show","Spinner","watch_remote_forms","$form","event","getResponseHeader","match","responseText","statusText","status","error_body","error_header","error_type","error_messages","$errorDiv","error","close_button","e","keydown","dialog_header","dialog_title","getSize","css","doc_width","doc_height","currentDialog","length","closeCurrentDialog","callback","ConfirmDialog","message","ok_label","cancel_label","on_ok","bindEvents","cancelButton","preventDefault","on_cancel","okButton","source","querySelector","openConfirmDialog","Promise","resolve","pleaseWaitOverlay","checkPageDirtyness","action","find","appendTo","Turbo","visit","pathname","then","proceed","onbeforeunload","Dirty","PageLeaveObserver","currentTarget","removeTab","element_id","fixed_elements","getElementById","panel_name","label","tab","panel","growl","style","flashType","flashNotices","flashMessage","ANCHOR_REGEX","PreviewWindow","HTMLIFrameElement","afterLoad","reloadIcon","loadTimeout","previewReadyHandler","super","handlePreviewReadyMessage","bind","handleEvent","type","clearLoadTimeout","stopSpinner","call","connectedCallback","attachEvents","localStorage","getItem","previewUrlSelect","value","refresh","disconnectedCallback","postMessage","contentWindow","startSpinner","src","setTimeout","isDragged","dragged","transitionProperty","pointerEvents","reloadButton","sizeSelect","setItem","includes","clearTimeout","getAttribute","reloadPreview","customElements","define","extends","IngredientAnchorLink","updateIcon","ingredientId","active","ingredientEditor","setAttribute","Action","HTMLElement","actions","removeFixedElement","updateAnchorIcon","hidePleaseWaitOverlay","func","name","params","hasAttribute","JSON","parse","AlchemyHTMLElement","static","observedAttributes","Object","properties","changeComponent","initialContent","default","getAttributeNames","_updateFromAttribute","_updateComponent","connected","disconnected","attributeChangedCallback","render","afterRender","dispatchCustomEvent","attributeValue","propertyName","reduce","a","b","charAt","toUpperCase","slice","hightlightTerm","term","RegExp","RemoteSelect","allowClear","selection","placeholder","queryParams","input","classList","add","select2Config","onOpen","onChange","removed","added","getElementsByTagName","initSelection","_$el","ajax","ajaxConfig","formatSelection","item","_renderResult","formatResult","_el","query","_renderListEntry","datatype","quietMillis","page","_searchQuery","results","response","_parseResponse","q","name_cont","meta","more","per_page","total_count","Error","_hightlightTerm","attachment","icon_css_class","AutoSubmit","submitEvent","Event","cancelable","form","Button","HTMLButtonElement","remote","stopPropagation","disable","enable","rect","getBoundingClientRect","initialButtonText","removeAttribute","maxChars","formField","getFormField","createDisplayElement","countCharacters","formFields","display","className","after","charLength","textContent","toggle","ClipboardButton","clipboard","ClipboardJS","destroy","formatItem","object","optionEl","swatch","ColorSelect","select","initializeSelect2","toggleColorPicker","val","colorInput","textInput","minimumResultsForSearch","enabled","disabled","inputType","flatpickr","inputField","flatpickrOptions","enableTime","altInput","altFormat","altInputClass","noCalendar","time_24hr","onValueUpdate","_selectedDates","_dateStr","instance","closest","setDirty","dateFormat","DialogLink","HTMLAnchorElement","dialogOptions","contains","JSON_CONTENT_TYPE","TURBO_STREAM_CONTENT_TYPE","isGetRequest","method","toLowerCase","prepareOptions","accept","headers","Accept","getToken","prepareHeaders","stringify","attributes","patch","post","path","fetch","URL","location","origin","search","URLSearchParams","toString","prepareURL","contentType","isJson","isTurboStream","responseData","json","renderStreamMessage","ok","DomIdSelect","dataItem","hash","id","selectElement","pageId","fetchDomIds","routes","api_ingredients_path","page_id","ingredients","filter","ingredient","dom_id","map","prompt","setOptions","reset","frame","elements","contentDocument","Array","from","PublishElementButton","scheduleButtonVariant","scheduleButton","publishButton","dropdown","loading","DeleteElementButton","button","removeElement","elementEditor","fixed","elementId","pageHasUnpublishedChanges","dispatchPageDirtyEvent","tooltip","publishButtonTooltip","ElementEditor","header","toggleButton","focusElement","previewWindow","focusElementPreview","onClickElement","onSaveElement","hasEditors","firstChild","setTitle","selectTabForElement","expand","setClean","warning","ingredientsWithErrors","errorDisplay","errorMessage","appendChild","elementErrors","notice","updateTitle","previewText","ingredientAnchors","anchor","scrollToElement","scrollIntoView","behavior","scroll","reject","tabs","dirty","editor","collapsed","collapse","compact","toggleIcon","collapse_admin_element_path","nestedElementIds","selector","join","nestedElement","catch","finally","expanded","parentElementEditor","expand_admin_element_path","parentElementIds","parentElement","published","isPublished","bodySelector","footer","elementName","hasChildren","option","icon","ElementSelect","dropdownAutoWidth","hint","description","ElementsWindow","visible","turboFrame","focusElementEditor","collapseAllElements","removeProperty","widthFromCookie","setProperty","cookie","collapseButton","row","startsWith","ElementsWindowHandle","dragging","elementsWindow","onMouseDown","onMouseUp","onDrag","pageX","elementWindowWidth","innerWidth","FileEditor","deleteLink","fileIcon","fileName","formFieldId","removeFile","ListFilter","debounceTimer","filterField","clearButton","visibility","debounceTime","clear","matchedItems","itemsToShow","Set","lowerTerm","items","nameAttribute","indexOf","ancestor","itemsSelector","has","block","Message","dismissable","dismiss","iconName","dismissDelay","noticesWrapper","autoDismissDelay","Growl","Icon","spriteUrl","sizeClass","IngredientGroup","HTMLDetailsElement","localStorageKey","isInLocalStorage","expanded_ingredient_groups","localStorageItem","LinkButton","LinkDialog","linkUrl","linkTitle","linkTarget","linkClass","link","setLink","linkButtons","linkUrlField","linkTitleField","linkTargetField","linkClassField","UnlinkButton","linked","isLinked","LinkButtons","removeLink","unlinkButton","setElementDirty","linkButton","formatFileSize","bytes","exponent","Math","floor","log","pow","toFixed","name_or_page_name_cont","node","ancestors","seperator","FileUpload","file","request","progressEventLoaded","progressEventTotal","valid","initialize","_validateFile","_addRequestEventListener","loadedSize","cancel","reader","FileReader","readAsDataURL","image","Image","result","prepend","finished","abort","config","uploader_defaults","maxFileSize","file_size_limit","allowedFiletypes","allowed_filetypes","alchemy_pictures","alchemy_attachments","upload","onprogress","progressEvent","onload","responseMessage","onerror","_errorMessage","errorMessageContainer","progressElement","loaded","total","round","_status","toggleAttribute","_valid","isValid","_value","Progress","buttonLabel","fileUploads","fileCount","handleFileChange","_updateView","actionButton","onComplete","fileUpload","_activeUploads","_setupCloseButton","ariaLabel","_sumFileProgresses","field","accumulator","totalProgress","overallProgressValue","overallUploadSize","every","entry","fileProgress","uploadedFileCount","uploadsStatuses","totalSize","ceil","weight","dropzone","fileInput","_uploadFiles","files","_dragAndDropBehavior","_handleUploadComplete","redirectUrl","uploadProgress","dropzoneElement","isDraggedOver","toggleDropzoneClass","dataTransfer","getAsFile","fileUploadCount","XMLHttpRequest","upload_limit","_submitFile","_createProgress","formData","FormData","set","setRequestHeader","send","removeChild","BUTTON","AlchemyPageNode","folded","folderButton","handleFolderClick","fold_admin_page_path","toggleChildren","updateFolderIcon","childrenContainer","updateFolderButton","folderElement","shouldShowButton","tagName","outerHTML","PagePublicationFields","public_on_picker","public_until_picker","publication_date_fields","public_field","checkbox","now","Date","checked","setDate","pages","site","url_path","language_code","PictureDescriptionSelect","searchParams","PictureEditor","cropFromField","cropSizeField","pictureIdField","targetSizeField","imageCropperField","pictureThumbnail","deleteButton","cropLink","targetSize","pictureId","update","delay","timeout","args","that","apply","debounce","updateImage","updateCropLink","removeImage","observer","MutationObserver","mutationCallback","observe","disconnect","mutationsList","mutation","url_admin_picture_path","crop","imageCropperEnabled","crop_from","cropFrom","crop_size","cropSize","flatten","alt","href","defaultCropFrom","defaultCropSize","mask","n","zoom","imageFileWidth","imageFileHeight","dimensions","imageCropper","PictureThumbnail","start","onLoaded","onError","setImage","createImage","complete","hoist","replaceChildren","PublishPageButton","markDirty","variant","Select","HTMLSelectElement","select2Element","multiple","prev","updateSelect2","selectedValue","Option","hasOwnProperty","AlchemySitemap","searchInput","resultCounter","setupSearch","setupSortables","mutations","addedNodes","nodeType","Node","ELEMENT_NODE","setupSortable","childList","subtree","teardownSearch","handleSearch","handleClearSearch","trim","filterPages","clearFilter","allPages","matchCount","firstMatch","pageElement","container","Sortable","animation","fallbackOnBody","swapThreshold","handle","draggable","onEnd","handleSort","to","oldIndex","newIndex","pageNode","move_admin_page_path","target_parent_id","parentId","new_position","pageData","pageEl","urlPathEl","updateFolderIcons","fromContainer","toContainer","fromParent","toParent","SORTABLE_OPTIONS","ghostClass","easing","onStart","onSort","position","parent_element_id","order_admin_elements_path","preview_text","SortableElements","put","_from","droppableElements","TagsAutocomplete","tags","tokenSeparators","openOnEnter","minimumInputLength","createSearchChoice","dataType","localeCompare","each","DARK_THEME","LIGHT_THEME","min_height","root","rootMargin","threshold","tinymceIntersectionObserver","IntersectionObserver","entries","intersectionRatio","_initTinymceEditor","unobserve","_setupThemeChangeListener","_removeThemeChangeListener","tinymce","editorId","minHeight","configuration","editors","_setupEditor","editorContainer","darkModeMediaQuery","matchMedia","themeChangeHandler","_handleThemeChange","skin","matches","content_css","customConfig","attributeName","replaceAll","preferredTheme","TinymceDefaults","language","UpdateCheck","credentials","responseJSON","showStatus","showError","Handlebars","template","templates","l","r","compiler","main","o","lambda","d","escapeExpression","u","lookupProperty","prototype","nullContext","program","inverse","loc","line","column","end","useData","setDefaultAnimation","keyframes","transform","opacity","duration","iconMap","caret","resolver","mutator","svg","spriteSheet","eventName","baseSelector","targetSelector","baseNode","targets","currentNode","registerIconLibrary","ImageCropper","initialized","cropper","settings","defaultBox","default_box","aspectRatio","ratio","crop_from_form_field_id","crop_size_form_field_id","cropperOptions","viewMode","zoomable","checkCrossOrigin","checkOrientation","box","cropend","getData","v","x","y","defaultBoxSize","Cropper","coords","setData","ImageOverlay","nodeName","toggleClass","$previous","$next","initKeyboardNavigation","previous","next","checkedInputs","PictureSelector","selectAllButton","selectedItemTools","state","toggleCheckboxes","parentElementClassList","editMultiplePicturesUrl","displayNodeFolders","leftIconArea","list","onFinishDragging","move_api_path","recordId","NodeTree","nodeId","menu_item","recordType","toggle_folded_api_path","invertSwap","assign","FixedElements","onCreateLink","link_admin_pages_path","parameterMapping","selected_tab","link_title","link_target","internalForm","attachmentSelect","updatePage","submitForm","linkFormType","internalLink","domIdSelect","linkType","elementAnchor","link_url_regexp","showValidationError","errors","Rails","forms","confirm","srcElement","isPressed"],"mappings":"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"}
|