diff --git a/AGENTS.md b/AGENTS.md index f3a24817d0..bb469e0661 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,7 +42,8 @@ bun run test bun run build # Build individual components -bun run build:js # Rollup JavaScript bundling +bun run build:admin # Bundle admin JavaScript (app/javascript/alchemy_admin/**) +bun run build:js # Bundle vendored dependencies (sortablejs, shoelace, tinymce, etc.) bun run build:css # Sass compilation bun run handlebars:compile # Compile Handlebars templates bun run build:icons # Generate icon sprite diff --git a/app/assets/builds/alchemy/alchemy_admin.min.js b/app/assets/builds/alchemy/alchemy_admin.min.js index f215ff3cd6..e73396f26c 100644 --- a/app/assets/builds/alchemy/alchemy_admin.min.js +++ b/app/assets/builds/alchemy/alchemy_admin.min.js @@ -1,2 +1,2 @@ -import"handlebars";import"jquery";import"@ungap/custom-elements";import{Turbo as e}from"@hotwired/turbo-rails";import"select2";import t from"@rails/ujs";import"keymaster";import"clipboard";import s from"flatpickr";import i from"sortablejs";import"tinymce";import{setDefaultAnimation as n,registerIconLibrary as r}from"shoelace";import l from"cropperjs";const o=/\./;function a(e){const t=c(),s=Alchemy.translations;return s?o.test(e)?function(e,t){const s=t.split(o),i=e[s[0]];return i&&i[s[1]]||t}(s,e):s[e]||e:(console.warn(`Translations for locale ${t} not found!`),e)}function c(){return document.documentElement.lang?document.documentElement.lang:"en"}function h(e,t=void 0){let s=a(e);return t?s.replace(/%\{.+\}/,t):s}async function d(){const e=c();"en"!==e&&(await import(`select2/${e}.js`),$.extend($.fn.select2.defaults,$.fn.select2.locales[e]))}const u=[];function m(e){return!(!$(e.target).is("input, textarea")&&"?"===String.fromCharCode(e.which))||(_("/admin/help",{title:Alchemy.t("help"),size:"400x492"}),!1)}function p(e=document){e instanceof jQuery&&(e=e[0]),e===document&&(document.removeEventListener("keypress",m),document.addEventListener("keypress",m),u.forEach((e=>key.unbind(e))));const t=e.querySelectorAll(".search_input_field"),s=e.querySelectorAll(".search_field_clear, .js_filter_field_clear");key("alt+f",(function(){return key.setScope("search"),t.forEach((e=>e.focus({focusVisible:!0}))),!1})),u.push("alt+f"),key("esc","search",(function(){s.forEach((e=>e.click())),t.forEach((e=>e.blur()))})),u.push("esc"),e.querySelectorAll("[data-alchemy-hotkey]").forEach((function(e){const t=e.dataset.alchemyHotkey;key(t,(()=>e.click())),u.push(t)}))}function g(e){const t=document.createElement("template");return t.innerHTML=e,t.content.children[0]}let y=class{constructor(e,t="currentColor"){this.size=e,this.color=t,this.spinner=void 0}get el(){return this.spinner}spin(e){return void 0===e&&(e=document.body),this.spinner=g(``),e.append(this.spinner),this}stop(){this.spinner&&(this.spinner.remove(),this.spinner=void 0)}};const v=[],f={header_height:36,size:"400x300",padding:!0,title:"",modal:!0,overflow:"visible",ready:()=>{},closed:()=>{}};class b{constructor(e,t={}){this.url=e,this.options={...f,...t},this.$document=$(document),this.$window=$(window),this.$body=$("body");const s=this.options.size.split("x");this.width=parseInt(s[0],10),this.height=parseInt(s[1],10),this.build(),this.resize()}open(){this.dialog.trigger("Alchemy.DialogOpen"),this.bind_close_events(),window.requestAnimationFrame((()=>{if(this.dialog_container.addClass("open"),null!=this.overlay)return this.overlay.addClass("open")})),this.$body.addClass("prevent-scrolling"),v.push(this),this.load()}close(){return this.dialog.trigger("DialogClose.Alchemy"),this.$document.off("keydown"),this.dialog_container.removeClass("open"),null!=this.overlay&&this.overlay.removeClass("open"),this.$document.on("webkitTransitionEnd transitionend oTransitionEnd",(()=>{if(this.$document.off("webkitTransitionEnd transitionend oTransitionEnd"),this.dialog_container.remove(),null!=this.overlay&&this.overlay.remove(),this.$body.removeClass("prevent-scrolling"),v.pop(this),null!=this.options.closed)return this.options.closed()})),!0}load(){this.show_spinner(),$.get(this.url,(e=>{this.replace(e)})).fail((e=>{this.show_error(e)}))}reload(){this.dialog_body.empty(),this.load()}replace(e){this.remove_spinner(),this.dialog_body.hide(),this.dialog_body.html(e),this.init(),this.dialog[0].dispatchEvent(new CustomEvent("DialogReady.Alchemy",{bubbles:!0,detail:{body:this.dialog_body[0]}})),null!=this.options.ready&&this.options.ready(this.dialog_body),this.dialog_body.show()}show_spinner(){this.spinner=new y("medium"),this.spinner.spin(this.dialog_body[0])}remove_spinner(){this.spinner.stop()}init(){p(this.dialog_body),this.watch_remote_forms()}watch_remote_forms(){const e=$('[data-remote="true"]',this.dialog_body);e.on("ajax:success",(e=>{const t=e.detail[2];t.getResponseHeader("Content-Type").match(/javascript/)||(this.dialog_body.html(t.responseText),this.init())})),e.on("ajax:error",(e=>{const t=e.detail[1],s=e.detail[2];this.show_error(s,t)}))}show_error(e,t){if(422===e.status)return this.dialog_body.html(e.responseText),void this.init();const{error_body:s,error_header:i,error_type:n}=this.error_messages(e,t),r=$(`\n

${i}

\n

${s}

\n
`);this.dialog_body.html(r)}error_messages(e,t){let s,i,n="warning";switch(e.status){case 0:i="The server does not respond.",s="Please check server and try again.";break;case 403:i="You are not authorized!",s="Please close this window.";break;default:n="error",t?(i=t,console.error(e.responseText)):i=`${e.statusText} (${e.status})`,s="Please check log and try again."}return{error_header:i,error_body:s,error_type:n}}bind_close_events(){this.close_button.on("click",(()=>{this.close()})),this.dialog_container.addClass("closable").on("click",(e=>e.target!==this.dialog_container.get(0)||(this.close(),!1))),this.$document.keydown((e=>27!==e.which||(this.close(),!1)))}build(){this.dialog_container=$('
'),this.dialog=$('
'),this.dialog_body=$('
'),this.dialog_header=$('
'),this.dialog_title=$('
'),this.close_button=$(''),this.dialog_title.text(this.options.title),this.dialog_header.append(this.dialog_title),this.dialog_header.append(this.close_button),this.dialog.append(this.dialog_header),this.dialog.append(this.dialog_body),this.dialog_container.append(this.dialog),this.options.modal&&this.dialog.addClass("modal"),this.options.padding&&this.dialog_body.addClass("padded"),this.options.modal&&(this.overlay=$('
'),this.$body.append(this.overlay)),this.$body.append(this.dialog_container)}resize(){const{width:e,height:t}=this.getSize();this.dialog.css({width:e,"min-height":t,overflow:this.options.overflow}),"hidden"===this.options.overflow?this.dialog_body.css({height:t,overflow:"auto"}):this.dialog_body.css({"min-height":t,overflow:"visible"})}getSize(){const e=this.options.padding?16:0,t=this.$window.width(),s=this.$window.height();let i=this.width,n=this.height;return i>=t&&(i=t-e),n>=s&&(n=s-e-f.header_height),{width:i,height:n}}}function E(){const{length:e}=v;if(0!==e)return v[e-1]}function w(e){const t=E();if(null!=t)return t.options.closed=e,t.close()}function _(e,t){if(!e)throw"No url given! Please provide an url.";new b(e,t).open()}class k{constructor(e,t={}){this.message=e,this.options={...{size:"300x100",title:h("Please confirm"),ok_label:h("Yes"),cancel_label:h("No"),on_ok(){}},...t},this.#e(),this.#t()}open(){requestAnimationFrame((()=>{this.dialog.show()}))}#e(){const e=this.options.size.split("x")[0];this.dialog=g(`\n \n ${this.message}\n \n \n \n `),document.body.append(this.dialog)}#t(){this.cancelButton.addEventListener("click",(e=>{e.preventDefault(),this.options.on_cancel(),this.dialog.hide()})),this.okButton.addEventListener("click",(e=>{e.preventDefault(),this.options.on_ok(),this.dialog.hide()})),this.dialog.addEventListener("sl-request-close",(e=>{"overlay"===e.detail.source&&(this.options.on_cancel(),e.preventDefault())})),this.dialog.addEventListener("sl-after-hide",(()=>{this.dialog.remove()}))}get cancelButton(){return this.dialog.querySelector("button[type=reset]")}get okButton(){return this.dialog.querySelector("button[type=submit]")}}function L(e,t={}){return new Promise((s=>{new k(e,{...t,on_ok(){s(!0)},on_cancel(){s(!1)}}).open()}))}function S(e=!0){document.querySelector("alchemy-overlay").show=!!e}function C(e){let t=()=>{};$(e).is("form")?t=function(){const t=$(`
`);t.append($(e).find("input")),t.appendTo("body"),S(),t.trigger("submit")}:$(e).is("a")&&(t=()=>Turbo.visit(e.pathname));return!(document.querySelectorAll("alchemy-element-editor.dirty").length>0)||(L(h("page_dirty_notice"),{title:h("warning"),ok_label:h("ok"),cancel_label:h("cancel")}).then((e=>{e&&(window.onbeforeunload=void 0,t())})),!1)}var x={checkPageDirtyness:C,PageLeaveObserver:function(){document.querySelectorAll("#main_navi a").forEach((e=>{e.addEventListener("click",(e=>{C(e.currentTarget)||e.preventDefault()}))}))}};function T(e){const t=document.getElementById("fixed-elements"),s=`fixed-element-${e}`;t.querySelector(`sl-tab[panel="${s}"]`).remove(),t.querySelector(`sl-tab-panel[name="${s}"]`).remove(),t.show("main-content-elements")}var A=Object.freeze({__proto__:null,createTab:function(e,t){const s=document.getElementById("fixed-elements"),i=`fixed-element-${e}`,n=`${t}`,r=``;s.innerHTML+=n+r,window.requestAnimationFrame((function(){s.show(i)}))},removeTab:T});function q(e,t="notice"){!function(e,t){const s=document.getElementById("flash_notices"),i=g(`\n \n ${e}\n \n `);s.append(i)}(e,t)}const F=/#[\w.~-]+$/;class I extends HTMLIFrameElement{#s;#i;#n;#r;constructor(){super(),this.addEventListener("load",this),this.#r=this.#l.bind(this)}handleEvent(e){"load"===e.type&&(this.#o(),this.#a(),this.#s?.call(this,e))}#l(e){"Alchemy.previewReady"===e.data.message&&(this.#o(),this.#a(),this.#s?.call(this,e))}connectedCallback(){let e=this.url;this.#c(),window.addEventListener("message",this.#r),window.localStorage.getItem("alchemy-preview-url")&&(e=window.localStorage.getItem("alchemy-preview-url"),this.previewUrlSelect.value=e),this.refresh(e)}disconnectedCallback(){key.unbind("alt+r"),window.removeEventListener("message",this.#r)}postMessage(e){this.contentWindow.postMessage(e,"*")}resize(e){this.style.width=`${e}px`}refresh(e){return this.#h(),this.src=e||this.url,this.#o(),this.#n=setTimeout((()=>{this.#a(),q(h("Preview failed to load"),"warning")}),5e3),new Promise((e=>{this.#s=e}))}set isDragged(e){this.style.transitionProperty=e?"none":null,this.style.pointerEvents=e?"none":null}#c(){this.reloadButton?.addEventListener("click",(e=>{e.preventDefault(),this.refresh()})),key("alt+r",(()=>this.refresh())),this.sizeSelect.addEventListener("change",(e=>{const t=e.target.value;""===t?this.style.width=null:this.resize(t)})),this.previewUrlSelect?.addEventListener("change",(e=>{const t=e.target.value;window.localStorage.setItem("alchemy-preview-url",t),this.refresh(t)}))}#h(){this.reloadButton.innerHTML.includes("alchemy-spinner")||(this.#i=this.reloadButton.innerHTML),this.reloadButton.innerHTML=''}#a(){this.reloadButton.innerHTML=this.#i}#o(){this.#n&&(clearTimeout(this.#n),this.#n=null)}get url(){return this.getAttribute("url")}get sizeSelect(){return document.querySelector("select#preview_size")}get previewUrlSelect(){return document.querySelector("select#preview_url")}get reloadButton(){return document.querySelector("#reload_preview_button")}}function M(){document.getElementById("alchemy_preview_window").refresh()}customElements.define("alchemy-preview-window",I,{extends:"iframe"});class z{static updateIcon(e,t=!1){const s=document.querySelector(`[data-ingredient-id="${e}"]`);if(s){s.querySelector(".edit-ingredient-anchor-link alchemy-icon").setAttribute("icon-style",t?"fill":"line")}}}class B extends HTMLElement{constructor(){super(),this.actions={closeCurrentDialog:w,reloadPreview:M,removeFixedElement:T,updateAnchorIcon:z.updateIcon,hidePleaseWaitOverlay(){S(!1)}}}connectedCallback(){const e=this.actions[this.name];e?e(...this.params):console.error(`Unknown Alchemy action: ${this.name}`),this.remove()}get name(){return this.getAttribute("name")}get params(){return this.hasAttribute("params")?JSON.parse(this.getAttribute("params")):[]}}function H(e,t){return e.replace(new RegExp(t,"gi"),(e=>`${e}`))}customElements.define("alchemy-action",B);class D extends HTMLElement{#d=null;async connectedCallback(){await d(),this.isConnected&&(this.input.classList.add("alchemy_selectbox"),this.#d=$(this.input).select2(this.select2Config).on("select2-open",this.#u).on("change",this.#m))}disconnectedCallback(){this.#d&&(this.#d.off("select2-open",this.#u),this.#d.off("change",this.#m),this.#d.select2("destroy"),this.#d=null)}#u=e=>this.onOpen(e);#m=e=>this.onChange(e);onChange(e){this.dispatchCustomEvent("RemoteSelect.Change",{removed:e.removed,added:e.added})}onOpen(e){setTimeout((()=>{document.querySelector("#select2-drop .select2-input").focus()}),100)}dispatchCustomEvent(e,t={}){this.dispatchEvent(new CustomEvent(`Alchemy.${e}`,{bubbles:!0,detail:t}))}get allowClear(){return this.hasAttribute("allow-clear")}get selection(){return this.getAttribute("selection")}get placeholder(){return this.getAttribute("placeholder")??""}get queryParams(){return this.getAttribute("query-params")??"{}"}get url(){return this.getAttribute("url")??""}get input(){return this.getElementsByTagName("input")[0]}get select2Config(){return{placeholder:this.placeholder,allowClear:this.allowClear,initSelection:(e,t)=>{this.selection&&t(JSON.parse(this.selection))},ajax:this.ajaxConfig,formatSelection:e=>this._renderResult(e),formatResult:(e,t,s)=>this._renderListEntry(e,s.term)}}get ajaxConfig(){return{url:this.url,datatype:"json",quietMillis:300,data:(e,t)=>this._searchQuery(e,t),results:e=>this._parseResponse(e)}}_searchQuery(e,t){return{q:{name_cont:e,...JSON.parse(this.queryParams)},page:t}}_parseResponse(e){const t=e.meta;return{results:e.data,more:t.page*t.per_page\n \n ${this._hightlightTerm(e.name,t)}\n
\n `}});class P extends HTMLElement{connectedCallback(){$(this).on("change",(function(e){const t=new Event("submit",{bubbles:!0,cancelable:!0});return e.target.form.dispatchEvent(t),!1}))}}customElements.define("alchemy-auto-submit",P);class O extends HTMLButtonElement{connectedCallback(){this.form?(this.form.addEventListener("submit",this),"true"==this.form.dataset.remote&&this.form.addEventListener("ajax:complete",this),this.form.addEventListener("turbo:submit-end",this)):console.warn("No form for button found!",this)}handleEvent(e){switch(e.type){case"submit":"disabled"===this.getAttribute("disabled")?(e.preventDefault(),e.stopPropagation()):this.disable();break;case"ajax:complete":case"turbo:submit-end":this.enable()}}disable(){const e=new y("small"),t=this.getBoundingClientRect();this.dataset.initialButtonText=this.innerHTML,this.setAttribute("disabled","disabled"),this.setAttribute("tabindex","-1"),this.classList.add("disabled"),this.style.width=`${t.width}px`,this.style.height=`${t.height}px`,this.innerHTML=" ",e.spin(this)}enable(){this.classList.remove("disabled"),this.removeAttribute("disabled"),this.removeAttribute("tabindex"),this.style.width=null,this.style.height=null,this.innerHTML=this.dataset.initialButtonText}}customElements.define("alchemy-button",O,{extends:"button"});class N extends HTMLElement{connectedCallback(){this.translation=h("allowed_chars",this.maxChars),this.formField=this.getFormField(),this.formField&&(this.createDisplayElement(),this.countCharacters(),this.formField.addEventListener("keyup",this))}disconnectedCallback(){this.formField?.removeEventListener("keyup",this)}handleEvent(e){"keyup"===e.type&&this.countCharacters()}getFormField(){const e=this.querySelectorAll("input, textarea");return e.length>0?e[0]:void 0}createDisplayElement(){this.display=this.querySelector(":scope > .alchemy-char-counter"),this.display||(this.display=document.createElement("small"),this.display.className="alchemy-char-counter",this.formField.after(this.display))}countCharacters(){const e=this.formField.value.length;this.display.textContent=`${e} ${this.translation}`,this.display.classList.toggle("too-long",e>this.maxChars)}get maxChars(){return this.getAttribute("max-chars")??60}}customElements.define("alchemy-char-counter",N);class U extends HTMLElement{constructor(){super(),this.innerHTML='\n \n ',this.clipboard=new ClipboardJS(this,{text:()=>this.getAttribute("content")}),this.clipboard.on("success",(()=>{q(this.getAttribute("success-text"))}))}disconnectedCallback(){this.clipboard.destroy()}}customElements.define("alchemy-clipboard-button",U);const R=e=>{const t=e.element[0],s=t.dataset.swatch||t.value;return`\n
\n ${"custom_color"===t.value?'':``}\n ${e.text}\n
`};class j extends HTMLElement{connectedCallback(){this.select?(this.#p(),$(this.select).on("change",(e=>this.#g("custom_color"===e.val)))):(this.colorInput?.addEventListener("input",this),this.textInput?.addEventListener("input",this),this.#g(!0))}handleEvent(e){switch(e.target){case this.colorInput:this.textInput.value=this.colorInput.value;break;case this.textInput:this.colorInput.value=this.textInput.value}}disconnectedCallback(){this.colorInput?.removeEventListener("input",this),this.textInput?.removeEventListener("input",this)}#p(){this.select.classList.add("alchemy_selectbox");const e={minimumResultsForSearch:10,formatResult:R,formatSelection:R};$(this.select).select2(e)}#g(e=!0){this.colorInput.disabled=!e}get colorInput(){return this.querySelector("input[type='color']")}get textInput(){return this.querySelector("input[type='text']")}get select(){return this.querySelector("select")}}customElements.define("alchemy-color-select",j);const W=c();class J extends HTMLElement{async connectedCallback(){"en"!==W&&await import(`flatpickr/${W}.js`),this.isConnected&&(this.flatpickr=s(this.inputField,this.flatpickrOptions))}disconnectedCallback(){this.flatpickr?.destroy()}get flatpickrOptions(){const e=/time/.test(this.inputType),t={locale:W.slice(0,2),altInput:!0,altFormat:h(`formats.${this.inputType}`),altInputClass:"flatpickr-input",enableTime:e,noCalendar:"time"===this.inputType,time_24hr:h("formats.time_24hr"),onValueUpdate(e,t,s){s.element.closest("alchemy-element-editor")?.setDirty(this.inputField)}};return e&&(t.dateFormat="Z"),t}get inputField(){return this.querySelector("input")}get inputType(){return this.getAttribute("input-type")||"date"}}customElements.define("alchemy-datepicker",J);class V extends HTMLAnchorElement{constructor(){super(),this.addEventListener("click",this)}handleEvent(e){this.disabled||this.openDialog(),e.preventDefault()}openDialog(){this.dialog=new b(this.getAttribute("href"),this.dialogOptions),this.dialog.open()}get dialogOptions(){return this.dataset.dialogOptions?JSON.parse(this.dataset.dialogOptions):{}}get disabled(){return this.classList.contains("disabled")}}customElements.define("alchemy-dialog-link",V,{extends:"a"});const X="application/json",Q="text/vnd.turbo-stream.html";function Y(e){return"get"===e.toLowerCase()}function K(e,t,s){const i=function(e){return{"Content-Type":"application/json; charset=utf-8",Accept:e,"X-Requested-With":"XMLHttpRequest","X-CSRF-Token":G()}}(s),n={method:e,headers:i};return t&&!Y(e)&&(n.body=JSON.stringify(t)),n}function G(){return document.querySelector('meta[name="csrf-token"]').attributes.content.textContent}function Z(e,t){return se("GET",e,t)}function ee(e,t,s){return se("PATCH",e,t,s)}function te(e,t,s=X){return se("POST",e,t,s)}async function se(e,t,s,i=X){const n=await fetch(function(e,t,s){const i=new URL(window.location.origin+e);return t&&Y(s)&&(i.search=new URLSearchParams(t).toString()),i.toString()}(t,s,e),K(e,s,i)),r=n.headers.get("content-type"),l=r?.includes(X),o=r?.includes(Q);let a=null;if(l?a=await n.json():o&&(a=await n.text(),"undefined"!=typeof Turbo&&Turbo.renderStreamMessage(a)),n.ok)return{data:a,status:n.status};throw a||new Error("An error occurred during the transaction")}class ie extends HTMLElement{dataItem(e){return{id:`#${e}`,text:`#${e}`}}get selectElement(){return this.querySelector('select[is="alchemy-select"]')}}customElements.define("alchemy-dom-id-api-select",class extends ie{#y=void 0;connectedCallback(){this.page=this.getAttribute("page")}async#v(){const e=(await Z(Alchemy.routes.api_ingredients_path,{page_id:this.#y})).data.ingredients.filter((e=>e.data?.dom_id)).map((e=>this.dataItem(e.data.dom_id))),t=e.length>0?h("None"):h("No anchors found");this.selectElement.setOptions(e,t),this.selectElement.enable()}#f(){requestAnimationFrame((()=>{this.selectElement.disable(),this.selectElement.setOptions([],h("Select a page first"))}))}set page(e){this.#y=e,e?this.#v():this.#f()}}),customElements.define("alchemy-dom-id-preview-select",class extends ie{connectedCallback(){requestAnimationFrame((()=>{const e=document.getElementById("alchemy_preview_window"),t=e.contentDocument?.querySelectorAll("[id]")||[];if(t.length>0){const e=Array.from(t).map((e=>this.dataItem(e.id)));this.selectElement.setOptions(e,h("None"))}}))}});class ne extends HTMLElement{#b;connectedCallback(){this.#b=this.scheduleButton.getAttribute("variant"),this.publishButton.addEventListener("click",this),this.dropdown.addEventListener("sl-show",this),this.dropdown.addEventListener("sl-hide",this)}disconnectedCallback(){this.publishButton.removeEventListener("click",this),this.dropdown.removeEventListener("sl-show",this),this.dropdown.removeEventListener("sl-hide",this)}handleEvent(e){switch(e.type){case"click":this.publishButton.loading=!0;break;case"sl-show":this.scheduleButton.setAttribute("variant","primary");break;case"sl-hide":this.scheduleButton.setAttribute("variant",this.#b)}}get publishButton(){return this.querySelector("sl-button[type='submit']")}get dropdown(){return this.querySelector("sl-dropdown")}get scheduleButton(){return this.querySelector("sl-button[slot='trigger']")}}customElements.define("alchemy-publish-element-button",ne);class re extends HTMLElement{constructor(){super(),this.button?.addEventListener("click",this)}async handleEvent(){if(await L(this.message)){const e=await se("DELETE",this.url);this.#E(e.data)}}#E(e){const t=this.closest("alchemy-element-editor");t.addEventListener("transitionend",(()=>{t.fixed&&T(t.elementId),t.remove()})),t.classList.add("dismiss"),q(e.message),e.pageHasUnpublishedChanges&&le(e),M()}get url(){return this.getAttribute("href")}get message(){return this.getAttribute("message")}get button(){return this.querySelector("button")}}function le(e){document.dispatchEvent(new CustomEvent("alchemy:page-dirty",{detail:{tooltip:e.publishButtonTooltip}}))}customElements.define("alchemy-delete-element-button",re);class oe extends HTMLElement{constructor(){super(),this.addEventListener("click",this),this.addEventListener("alchemy:element-update-title",this),this.addEventListener("ajax:complete",this),$(this.form).on("change",this.onChange),this.header?.addEventListener("dblclick",(()=>{this.toggle()})),this.toggleButton?.addEventListener("click",(e=>{e.target.closest("alchemy-element-editor")===this&&this.toggle()}))}connectedCallback(){this.classList.contains("ui-sortable-placeholder")||this.hasAttribute("created")&&(this.focusElement(),this.previewWindow?.refresh().then((()=>{this.focusElementPreview()})),this.removeAttribute("created"))}handleEvent(e){switch(e.type){case"click":e.target.closest("alchemy-element-editor")===this&&this.onClickElement();break;case"ajax:complete":if(e.target===this.body){const t=e.detail[0];e.stopPropagation(),this.onSaveElement(t)}break;case"alchemy:element-update-title":this.hasEditors||e.target!=this.firstChild||this.setTitle(e.detail.title)}}onChange(e){const t=e.target;if(!t.classList.contains("nested-elements"))return this.closest("alchemy-element-editor").setDirty(t),e.stopPropagation(),!1}async focusElement(){document.querySelector("#fixed-elements")&&await this.selectTabForElement(),await this.expand(),this.selectElement(!0)}focusElementPreview(){this.previewWindow?.postMessage({message:"Alchemy.focusElement",element_id:this.elementId})}onClickElement(){this.selectElement(),this.focusElementPreview()}onSaveElement(e){const t=JSON.parse(e.responseText);if(this.setClean(),422===e.status){const e=t.warning;t.ingredientsWithErrors.forEach((e=>{const t=this.querySelector(`[data-ingredient-id="${e.id}"]`),s=g(`${e.errorMessage}`);t?.appendChild(s),t?.classList.add("validation_failed")})),q(e,"warn"),this.elementErrors.classList.remove("hidden")}else q(t.notice),this.previewWindow?.refresh().then((()=>{this.focusElementPreview()})),this.updateTitle(t.previewText),t.ingredientAnchors.forEach((e=>{z.updateIcon(e.ingredientId,e.active)})),t.pageHasUnpublishedChanges&&le(t)}scrollToElement(){setTimeout((()=>{this.scrollIntoView({behavior:"smooth"})}),50)}selectElement(e=!1){document.querySelectorAll("alchemy-element-editor.selected").forEach((e=>{e.classList.remove("selected")})),window.requestAnimationFrame((()=>{this.classList.add("selected")})),e&&this.scrollToElement()}selectTabForElement(){return new Promise(((e,t)=>{const s=document.querySelector("#fixed-elements"),i=this.closest("sl-tab-panel");s&&i?(s.show(i.getAttribute("name")),e()):t(new Error("No tabs present"))}))}setClean(){this.dirty=!1,window.onbeforeunload=null,this.elementErrors.classList.add("hidden"),this.hasEditors&&this.body.querySelectorAll(".ingredient-editor").forEach((e=>{e.classList.remove("dirty","validation_failed"),e.querySelectorAll("small.error").forEach((e=>e.remove()))}))}setDirty(e){this.hasEditors&&(this.dirty=!0,window.onbeforeunload||(window.onbeforeunload=e=>e.preventDefault()),e?.closest(".ingredient-editor")?.classList.add("dirty"))}setTitle(e){this.querySelector(".element-header .preview_text_quote").textContent=e}async toggle(){this.collapsed?await this.expand():await this.collapse()}collapse(){if(this.collapsed||this.compact||this.fixed)return Promise.resolve("Element is already collapsed.");const e=new Alchemy.Spinner("small");return e.spin(this.toggleButton),this.toggleIcon?.classList?.add("hidden"),te(Alchemy.routes.collapse_admin_element_path(this.elementId)).then((e=>{const t=e.data;if(this.collapsed=!0,this.toggleButton?.setAttribute("title",t.title),t.nestedElementIds.length){const e=t.nestedElementIds.map((e=>`#element_${e}`)).join(", ");this.querySelectorAll(e).forEach((e=>{e.collapsed=!0,e.toggleButton?.setAttribute("title",t.title)}))}})).catch((e=>{q(e.message,"error"),console.error(e)})).finally((()=>{this.toggleIcon?.classList?.remove("hidden"),e.stop()}))}expand(){if(this.expanded&&!this.compact)return Promise.resolve("Element is already expanded.");if(this.compact&&this.parentElementEditor)return this.parentElementEditor.expand();{const e=new Alchemy.Spinner("small");return e.spin(this.toggleButton),this.toggleIcon?.classList.add("hidden"),new Promise(((t,s)=>{te(Alchemy.routes.expand_admin_element_path(this.elementId)).then((e=>{const s=e.data;if(s.parentElementIds.length){const e=s.parentElementIds.map((e=>`#element_${e}`)).join(", ");document.querySelectorAll(e).forEach((e=>{e.collapsed=!1,e.toggleButton?.setAttribute("title",s.title)}))}this.collapsed=!1,this.toggleButton?.setAttribute("title",s.title),t()})).catch((e=>{q(e.message,"error"),console.error(e),s(e)})).finally((()=>{this.toggleIcon?.classList?.remove("hidden"),e.stop()}))}))}}updateTitle(e){this.setTitle(e),this.dispatchEvent(new CustomEvent("alchemy:element-update-title",{bubbles:!0,detail:{title:e}}))}set published(e){e?this.classList.remove("element-hidden"):this.classList.add("element-hidden")}get published(){return!this.classList.contains("hidden")}get compact(){return null!==this.getAttribute("compact")}get fixed(){return null!==this.getAttribute("fixed")}set collapsed(e){this.classList.toggle("folded",e),this.classList.toggle("expanded",!e),this.toggleIcon&&(this.toggleIcon.name=e?"arrow-left-s":"arrow-down-s")}get collapsed(){return this.classList.contains("folded")}get expanded(){return!this.collapsed}set dirty(e){this.classList.toggle("dirty",e)}get dirty(){return this.classList.contains("dirty")}get header(){return this.querySelector(".element-header")}get body(){return this.querySelector(this.bodySelector)}get bodySelector(){return`#${this.id} > .element-body`}get footer(){return this.querySelector(`#${this.id} > .element-footer`)}get toggleButton(){return this.querySelector(".element-toggle")}get toggleIcon(){return this.toggleButton?.querySelector("alchemy-icon")}get elementErrors(){return this.body.querySelector(".element_errors")}get elementId(){return this.dataset.elementId}get elementName(){return this.dataset.elementName}get hasEditors(){return!!this.body?.querySelector(".element-ingredient-editors")}get hasChildren(){return!!this.querySelector(".nested-elements")}get firstChild(){return this.querySelector("alchemy-element-editor")}get form(){return this.querySelector("form.element-body")}get parentElementEditor(){return this.parentElement?.closest("alchemy-element-editor")}get previewWindow(){return document.getElementById("alchemy_preview_window")}}customElements.define("alchemy-element-editor",oe);const ae=e=>`\n
\n ${e.icon}${e.text}\n
\n `;class ce extends HTMLElement{constructor(){super()}connectedCallback(){const e=this.options,t={minimumResultsForSearch:3,dropdownAutoWidth:!0,data:()=>({results:e}),formatResult:(e,t,s)=>{let i;return""===e.id?e.text:(i=""!==s.term?H(e.text,s.term):e.text,((e,t,s)=>{const i=s?`
${s}
`:"";return`\n
\n ${ae({icon:e,text:t})}\n ${i}\n
\n `})(e.icon,i,e.hint))},formatSelection:ae,placeholder:this.placeholder};$(this.inputField).select2(t)}get options(){return JSON.parse(this.getAttribute("options"))}get placeholder(){return this.getAttribute("placeholder")}get inputField(){return this.querySelector("input")}}customElements.define("alchemy-element-select",ce);class he extends HTMLElement{#w=!0;#_=null;constructor(){super(),this.#c()}connectedCallback(){this.toggleButton?.addEventListener("click",(e=>{e.preventDefault(),this.toggle()})),window.location.hash&&this.focusElementEditor(window.location.hash),this.resize()}collapseAllElements(){this.querySelectorAll("alchemy-element-editor:not([compact]):not([fixed])").forEach((e=>e.collapse()))}toggle(){this.#w?this.hide():this.show()}show(){document.body.classList.add("elements-window-visible"),this.#w=!0,this.toggleButton.closest("sl-tooltip").content=Alchemy.t("Hide elements"),this.toggleButton.querySelector("alchemy-icon").setAttribute("name","menu-unfold"),this.resize()}hide(){document.body.classList.remove("elements-window-visible"),document.body.style.removeProperty("--elements-window-width"),this.#w=!1,this.toggleButton.closest("sl-tooltip").content=Alchemy.t("Show elements"),this.toggleButton.querySelector("alchemy-icon").setAttribute("name","menu-fold")}resize(e){void 0===e&&(e=this.widthFromCookie),e&&(document.body.style.setProperty("--elements-window-width",`${e}px`),document.cookie=`alchemy-elements-window-width=${e}; SameSite=Lax; Path=/;`)}focusElementEditor(e){const t=document.querySelector(e);t instanceof oe&&t.focusElement()&&t.focusElementPreview()}get collapseButton(){return this.querySelector("#collapse-all-elements-button")}get toggleButton(){return document.querySelector("#element_window_button")}get previewWindow(){return document.getElementById("alchemy_preview_window")}get turboFrame(){return this.#_||(this.#_=this.closest("turbo-frame")),this.#_}get widthFromCookie(){return document.cookie.split("; ").find((e=>e.startsWith("alchemy-elements-window-width=")))?.split("=")[1]}set isDragged(e){this.turboFrame.style.transitionProperty=e?"none":null,this.turboFrame.style.pointerEvents=e?"none":null}#c(){this.collapseButton?.addEventListener("click",(()=>{this.collapseAllElements()})),window.addEventListener("message",(e=>{const t=e.data;if("Alchemy.focusElementEditor"==t?.message){const e=document.getElementById(`element_${t.element_id}`);this.show(),e?.focusElement()}})),document.body.addEventListener("click",(e=>{e.target.closest("alchemy-element-editor")||(this.querySelectorAll("alchemy-element-editor").forEach((e=>{e.classList.remove("selected")})),this.previewWindow?.postMessage({message:"Alchemy.blurElements"}))}))}}customElements.define("alchemy-elements-window",he);class de extends HTMLElement{#k=!1;#L=null;#S=null;constructor(){super(),this.addEventListener("mousedown",this),window.addEventListener("mousemove",this),window.addEventListener("mouseup",this)}handleEvent(e){switch(e.type){case"mousedown":e.stopPropagation(),this.onMouseDown();break;case"mouseup":this.onMouseUp();break;case"mousemove":this.#k&&this.onDrag(e.pageX)}}onMouseDown(){this.#k=!0,this.elementsWindow.isDragged=!0,this.previewWindow.isDragged=!0,this.classList.add("is-dragged")}onMouseUp(){this.#k=!1,this.elementsWindow.isDragged=!1,this.previewWindow.isDragged=!1,this.classList.remove("is-dragged")}onDrag(e){const t=window.innerWidth-e;this.elementsWindow.resize(t)}get elementsWindow(){return this.#L||(this.#L=document.querySelector("alchemy-elements-window")),this.#L}get previewWindow(){return this.#S||(this.#S=document.getElementById("alchemy_preview_window")),this.#S}}customElements.define("alchemy-elements-window-handle",de);class ue extends HTMLElement{constructor(){super(),this.deleteLink=this.querySelector(".remove_file_link"),this.fileIcon=this.querySelector(".file_icon"),this.fileName=this.querySelector(".file_name"),this.formFieldId=this.deleteLink?.dataset.formFieldId,this.formField=this.querySelector(`#${this.formFieldId}`),this.deleteLink?.addEventListener("click",this)}handleEvent(e){"click"===e.type&&this.removeFile(),e.stopPropagation()}removeFile(){this.formField.value="",this.fileIcon.innerHTML="",this.fileName.innerHTML="",this.deleteLink?.classList.add("hidden"),this.closest("alchemy-element-editor").setDirty(this.formField)}}customElements.define("alchemy-file-editor",ue);class me extends HTMLElement{#C;constructor(){super(),this.#c()}#c(){this.hotkey&&key(this.hotkey,(()=>(this.filterField.focus(),!1))),this.filterField.addEventListener("keyup",(()=>{clearTimeout(this.#C),this.#C=setTimeout((()=>{const e=this.filterField.value;this.clearButton.style.visibility=e?"visible":"hidden",this.filter(e)}),this.debounceTime)})),this.clearButton.addEventListener("click",(e=>{e.preventDefault(),this.clear()})),this.filterField.addEventListener("focus",(()=>key.setScope("list_filter"))),key("esc","list_filter",(()=>{this.clear(),this.filterField.blur()}))}disconnectedCallback(){this.hotkey&&key.unbind(this.hotkey),key.unbind("esc","list_filter")}filter(e){""===e&&(this.clearButton.style.visibility="hidden");const t=[],s=new Set,i=e.toLowerCase();this.items.forEach((e=>{const n=e.getAttribute(this.nameAttribute)?.toLowerCase();if(-1!==n.indexOf(i)){t.push(e),s.add(e);let i=e.parentElement?.closest(this.itemsSelector);for(;i;)s.add(i),i=i.parentElement?.closest(this.itemsSelector)}})),this.items.forEach((e=>{e.classList.toggle("hidden",!s.has(e))})),1===t.length&&t[0].scrollIntoView({behavior:"smooth",block:"nearest"})}clear(){this.filterField.value="",this.clearButton.style.visibility="hidden",this.items.forEach((e=>e.classList.remove("hidden")))}get nameAttribute(){return this.getAttribute("name-attribute")||"name"}get clearButton(){return this.querySelector('button[type="button"]')}get filterField(){return this.querySelector('input[type="text"]')}get items(){return document.querySelectorAll(this.itemsSelector)}get itemsSelector(){return this.getAttribute("items-selector")}get debounceTime(){return parseInt(this.getAttribute("debounce-time"))||150}get hotkey(){return this.getAttribute("hotkey")}}customElements.define("alchemy-list-filter",me);class pe extends HTMLElement{#x;constructor(){super(),this.#x=this.innerHTML,(this.dismissable||"error"===this.type)&&this.addEventListener("click",this)}handleEvent(e){"click"===e.type&&this.dismiss()}connectedCallback(){this.innerHTML=`\n \n ${this.dismissable&&"error"===this.type?'':""}\n ${this.#x}\n `,this.dismissable&&"error"!==this.type&&setTimeout((()=>{this.dismiss()}),this.dismissDelay)}dismiss(){this.addEventListener("transitionend",(()=>this.remove())),this.classList.add("dismissed")}get dismissable(){return this.hasAttribute("dismissable")}get icon(){return this.getAttribute("icon")}get type(){return this.getAttribute("type")||"notice"}get dismissDelay(){return parseInt(this.noticesWrapper?.dataset.autoDismissDelay||5e3)}get iconName(){switch(this.icon||this.type){case"warning":case"warn":case"alert":return"alert";case"notice":return"check";case"info":case"hint":return"information";case"error":return"bug";default:return this.type}}get noticesWrapper(){return this.closest("#flash_notices")}}customElements.define("alchemy-message",pe);class ge extends HTMLElement{connectedCallback(){q(this.message,this.getAttribute("type")||"notice"),this.remove()}get message(){return this.getAttribute("message")||this.innerHTML}}customElements.define("alchemy-growl",ge);class ye extends HTMLElement{static get observedAttributes(){return["name","size","icon-style"]}constructor(){super(),this.spriteUrl=document.querySelector('link[rel="preload"][as="image"]').getAttribute("href")}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}render(){const e=this.size?` icon--${this.size}`:"";this.innerHTML=``}set name(e){this.setAttribute("name",e)}get iconName(){return this.getAttribute("name")}get size(){return this.getAttribute("size")}get style(){const e=this.getAttribute("icon-style");switch(e){case"none":return"";case null:return"-line";default:return`-${e}`}}}customElements.define("alchemy-icon",ye);class ve extends HTMLDetailsElement{#T="Alchemy.expanded_ingredient_groups";constructor(){super(),this.addEventListener("toggle",this),this.isInLocalStorage&&(this.open=!0)}handleEvent(){let e=this.localStorageItem;this.open?(this.toggleIcon.name="arrow-down-s",this.isInLocalStorage||e.push(this.id)):(this.toggleIcon.name="arrow-left-s",e=e.filter((e=>e!==this.id))),localStorage.setItem(this.#T,JSON.stringify(e))}get isInLocalStorage(){return this.localStorageItem.includes(this.id)}get localStorageItem(){const e=localStorage.getItem(this.#T);if(!e)return[];try{return JSON.parse(e)}catch(e){return console.error(e),[]}}get toggleIcon(){return this.querySelector("alchemy-icon")}}customElements.define("alchemy-ingredient-group",ve,{extends:"details"});class fe extends HTMLButtonElement{constructor(){super(),this.addEventListener("click",this),this.classList.add("icon_button"),this.setAttribute("type","button"),this.innerHTML=''}handleEvent(e){new Alchemy.LinkDialog({url:this.linkUrl,title:this.linkTitle,target:this.linkTarget,type:this.linkClass}).open().then((e=>this.setLink(e))),e.preventDefault()}setLink(e){""===e.url?(this.classList.remove("linked"),this.dispatchEvent(new CustomEvent("alchemy:unlink",{bubbles:!0}))):(this.classList.add("linked"),this.dispatchEvent(new CustomEvent("alchemy:link",{bubbles:!0,detail:e})))}get linkUrl(){return this.linkButtons.linkUrlField.value}get linkTitle(){return this.linkButtons.linkTitleField.value}get linkTarget(){return this.linkButtons.linkTargetField.value}get linkClass(){return this.linkButtons.linkClassField.value}get linkButtons(){return this.closest("alchemy-link-buttons")}}customElements.define("alchemy-link-button",fe,{extends:"button"});class be extends HTMLButtonElement{constructor(){super(),this.addEventListener("click",this),this.classList.add("icon_button"),this.setAttribute("type","button"),this.linked=this.linked,this.innerHTML=''}handleEvent(e){this.linked&&(this.linked=!1,this.blur(),this.dispatchEvent(new CustomEvent("alchemy:unlink",{bubbles:!0}))),e.preventDefault()}set linked(e){e?(this.classList.replace("disabled","linked"),this.removeAttribute("tabindex")):(this.classList.replace("linked","disabled"),this.setAttribute("tabindex","-1"))}get linked(){return this.classList.contains("linked")}}customElements.define("alchemy-unlink-button",be,{extends:"button"});class Ee extends HTMLElement{constructor(){super(),this.addEventListener("alchemy:link",this),this.addEventListener("alchemy:unlink",this)}handleEvent(e){switch(e.type){case"alchemy:link":this.setLink(e.detail);break;case"alchemy:unlink":this.removeLink()}e.stopPropagation()}setLink(e){this.linkUrlField.value=e.url,this.linkUrlField.dispatchEvent(new Event("change")),this.linkTitleField.value=e.title,this.linkClassField.value=e.type,this.linkTargetField.value=e.target,this.unlinkButton.linked=!0,this.setElementDirty()}removeLink(){this.linkUrlField.value="",this.linkUrlField.dispatchEvent(new Event("change")),this.linkTitleField.value="",this.linkClassField.value="",this.linkTargetField.value="",this.linkButton.classList.remove("linked"),this.unlinkButton.linked=!1,this.setElementDirty()}setElementDirty(){this.elementEditor.setDirty(this)}get linkButton(){return this.querySelector('[is="alchemy-link-button"]')}get unlinkButton(){return this.querySelector('[is="alchemy-unlink-button"]')}get ingredientEditor(){const e=this.dataset.ingredientId;return this.parentElement.closest(`[data-ingredient-id='${e}']`)}get elementEditor(){return this.closest("alchemy-element-editor")}get linkUrlField(){return this.ingredientEditor.querySelector("[data-link-value]")}get linkTitleField(){return this.ingredientEditor.querySelector("[data-link-title]")}get linkTargetField(){return this.ingredientEditor.querySelector("[data-link-target]")}get linkClassField(){return this.ingredientEditor.querySelector("[data-link-class]")}}customElements.define("alchemy-link-buttons",Ee);function we(e){let t=0===e?0:Math.floor(Math.log(e)/Math.log(1024));return t>3&&(t=3),(e/Math.pow(1024,t)).toFixed(2)+" "+["B","kB","MB","GB"][t]}customElements.define("alchemy-node-select",class extends D{_searchQuery(e,t){return{filter:{name_or_page_name_cont:e,...JSON.parse(this.queryParams)},page:t}}_renderResult(e){return this._renderListEntry(e)}_renderListEntry(e,t){const s=e.ancestors.map((e=>e.name)),i='';return`\n
\n \n
\n \n ${s.length>0?s.join(i)+i:""}\n \n \n ${this._hightlightTerm(e.name,t)}\n \n
\n
\n ${e.url||""}\n
\n
\n `}});class _e extends HTMLElement{file=null;request=null;progressEventLoaded=0;progressEventTotal=0;#A=!0;#$=0;#q=void 0;#F="";connectedCallback(){if(this.innerHTML=`\n \n
\n ${this.file?.name}\n ${this.loadedSize}\n ${this.errorMessage}\n
\n \n \n \n `,this.querySelector("button").addEventListener("click",(()=>this.cancel())),this.file?.type.includes("image")){const e=new FileReader;e.readAsDataURL(this.file),e.addEventListener("load",(()=>{const t=new Image;t.src=e.result,this.prepend(t)}))}}initialize(e,t){this.file=e,this.request=t,this.progressEventTotal=e?e.size:0,this.status="in-progress",this.#I(),this.#M()}cancel(){this.finished||(this.status="canceled",this.request?.abort(),this.dispatchCustomEvent("FileUpload.Change"))}dispatchCustomEvent(e){this.dispatchEvent(new CustomEvent(`Alchemy.${e}`,{bubbles:!0}))}#I(){const e=Alchemy.uploader_defaults,t=e.file_size_limit*Math.pow(1024,2);let s;this.file?.size>t&&(s=h("Uploaded bytes exceed file size"));const i=this.file?.type.includes("image")?e.allowed_filetypes.alchemy_pictures:e.allowed_filetypes.alchemy_attachments;i.includes("*")||i.includes(this.file?.type.replace(/^\w+\/(\w+)(\+\w+)?/i,"$1"))||(s=h("File type not allowed")),s&&(this.valid=!1,this.errorMessage=s)}#M(){this.request&&(this.request.upload.onprogress=e=>{this.progressEvent=e},this.request.onload=()=>{this.request.status<400?(this.status="successful",q(this.responseMessage)):(this.status="failed",this.errorMessage=this.responseMessage),this.dispatchCustomEvent("FileUpload.Change")},this.request.onerror=()=>{this.errorMessage=h("An error occurred during the transaction")})}get active(){return this.valid&&"canceled"!==this.status}get errorMessage(){return this.#F||""}set errorMessage(e){this.#F=e;const t=this.querySelector(".error-message");t&&(t.textContent=e),q(e,"error")}get finished(){return["canceled","successful","failed"].includes(this.status)}get loadedSize(){return`${we(this.progressEventLoaded)} / ${we(this.progressEventTotal)}`}get progressElement(){return this.querySelector("sl-progress-bar")}set progressEvent(e){this.progressEventLoaded=e.loaded,this.progressEventTotal=e.total,this.value=Math.round(e.loaded/e.total*100),this.querySelector(".loaded-size").textContent=this.loadedSize}get responseMessage(){try{return JSON.parse(this.request.responseText).message}catch(e){return`${this.request.status}: ${this.request.statusText}`}}get status(){return this.#q}set status(e){this.#q=e,this.className=e,this.progressElement?.toggleAttribute("indeterminate","upload-finished"===e)}get valid(){return this.#A}set valid(e){this.#A=e,this.classList.toggle("invalid",!e)}get value(){return this.#$}set value(e){this.#$=e,this.progressElement&&(this.progressElement.value=e),100===e&&(this.status="upload-finished"),this.dispatchCustomEvent("FileUpload.Change")}}customElements.define("alchemy-file-upload",_e);class ke extends HTMLElement{fileCount=0;#z=[];#B=h("Cancel all uploads");#H=null;#w=!1;#D=()=>this.#P();connectedCallback(){var e,t;this.innerHTML=(e=this.#B,t=this.fileCount,`\n \n
\n \n\n \n \n \n
\n
\n
\n`),this.visible=!0,this.#H=this.querySelector("button"),this.#H.addEventListener("click",(()=>{this.finished?this.onComplete(this.status):this.cancel()})),this.#z.forEach((e=>{this.querySelector(".single-uploads").append(e)})),this.#P(),this.addEventListener("Alchemy.FileUpload.Change",this.#D)}disconnectedCallback(){this.removeEventListener("Alchemy.FileUpload.Change",this.#D)}initialize(e=[]){this.#z=e,this.fileCount=e.length}cancel(){this.#O().forEach((e=>{e.cancel()})),this.#N()}onComplete(e){}#O(){return this.#z.filter((e=>e.active))}#N(){this.#B=h("Close"),this.#H.ariaLabel=this.#B,this.#H.parentElement.content=this.#B}#U(e){return this.#O().reduce(((t,s)=>s[e]+t),0)}#P(){const e=this.status;this.className=e,this.progressElement.value=this.totalProgress,this.progressElement.toggleAttribute("indeterminate","upload-finished"===e),this.querySelector(".overall-progress-value > span").textContent=this.overallProgressValue,this.querySelector(".overall-upload-value").textContent=this.overallUploadSize,this.finished?(this.#N(),this.onComplete(e)):this.visible=!0}get finished(){return this.#O().every((e=>e.finished))}get overallUploadSize(){this.#O().filter((e=>e.value>=100)).length;return this.totalProgress,this.#O().length,`${we(this.#U("progressEventLoaded"))} / ${we(this.#U("progressEventTotal"))}`}get overallProgressValue(){const e=this.#O().filter((e=>e.value>=100)).length;return`${this.totalProgress}% (${e} / ${this.#O().length})`}get progressElement(){return this.querySelector("sl-progress-bar")}get status(){const e=this.#O().map((e=>e.className));return e.includes("failed")?"failed":0===e.length?"canceled":e.every((t=>t===e[0]))?e[0]:"in-progress"}get totalProgress(){const e=this.#O().reduce(((e,t)=>e+t.file.size),0);let t=Math.ceil(this.#O().reduce(((t,s)=>{const i=s.file.size/e;return s.value*i+t}),0));return t>100&&(t=100),t}get visible(){return this.#w}set visible(e){this.classList.toggle("visible",e),this.#w=e}}customElements.define("alchemy-upload-progress",ke);class Le extends HTMLElement{#R=null;#j=!1;connectedCallback(){this.fileInput.addEventListener("change",this.#W),this.dropzone&&this.#J(),this.addEventListener("Alchemy.upload.successful",this)}disconnectedCallback(){this.fileInput?.removeEventListener("change",this.#W),this.#R&&(this.#R.removeEventListener("dragleave",this.#V),this.#R.removeEventListener("drop",this.#X),this.#R.removeEventListener("dragover",this.#Q),this.#R=null)}handleEvent(e){if("Alchemy.upload.successful"===e.type)this.#Y()}#W=e=>{this.uploadFiles(Array.from(e.target.files))};#K=e=>{this.#j!==e&&(this.#j=e,this.#R.classList.toggle("dragover"))};#V=()=>this.#K(!1);#X=async e=>{e.preventDefault(),this.#K(!1);const t=[...e.dataTransfer.items].map((e=>e.getAsFile()));this.uploadFiles(t)};#Q=e=>{e.preventDefault(),this.#K(!0)};#Y(){setTimeout((()=>{const e=this.redirectUrl,t=this.closest("turbo-frame");this.uploadProgress.visible=!1,e&&(t?(t.setAttribute("src",e),t.reload()):Turbo.visit(e))}),750)}#J(){this.#R=document.querySelector(this.dropzone),this.#R&&(this.#R.addEventListener("dragleave",this.#V),this.#R.addEventListener("drop",this.#X),this.#R.addEventListener("dragover",this.#Q))}uploadFiles(e){let t=0;const s=e.map((e=>{const s=new XMLHttpRequest,i=new _e;return i.initialize(e,s),Alchemy.uploader_defaults.upload_limit-1{this.dispatchEvent(new CustomEvent(`Alchemy.upload.${e}`,{bubbles:!0}))},document.body.append(this.uploadProgress)}get dropzone(){return this.getAttribute("dropzone")}get fileInput(){return this.querySelector("input[type='file']")}get redirectUrl(){return this.getAttribute("redirect-url")}}customElements.define("alchemy-uploader",Le);class Se extends HTMLElement{connectedCallback(){this.innerHTML=`\n \n
\n ${this.getAttribute("text")??""}\n
\n `}set show(e){this.classList.toggle("visible",e)}}customElements.define("alchemy-overlay",Se);const Ce="BUTTON";class xe extends HTMLElement{connectedCallback(){this.pageId=this.getAttribute("page-id"),this.folded=this.hasAttribute("folded"),this.folderButton?.addEventListener("click",this)}disconnectedCallback(){this.folderButton?.removeEventListener("click",this)}async handleEvent(e){"click"===e.type&&await this.handleFolderClick(e)}async handleFolderClick(e){e.preventDefault(),e.stopPropagation();const t=e.currentTarget;t.innerHTML="";const s=new y("small");s.spin(t);try{await ee(Alchemy.routes.fold_admin_page_path(this.pageId),null,"text/vnd.turbo-stream.html"),this.folded=!this.folded,this.toggleAttribute("folded",this.folded),this.toggleChildren(),this.updateFolderIcon()}catch(e){q(e.message||e,"error"),this.updateFolderIcon()}finally{s.stop()}}toggleChildren(){const e=this.querySelector(`#page_${this.pageId}_children`);e&&e.classList.toggle("hidden",this.folded)}updateFolderIcon(){if(this.folderButton){const e=this.folded?"arrow-right-s":"arrow-down-s";this.folderButton.innerHTML=``}}updateFolderButton(){const e=this.querySelector(".page_folder");if(!e)return;const t=this.hasChildren||this.folded;if(t&&"SPAN"===e.tagName){const t=this.folded?"arrow-right-s":"arrow-down-s";e.outerHTML=``,this.folderButton?.addEventListener("click",this)}else t||e.tagName!==Ce?t&&e.tagName===Ce&&this.updateFolderIcon():e.outerHTML=''}get hasChildren(){const e=this.querySelector(`#page_${this.pageId}_children`);return!!e&&e.querySelectorAll(":scope > alchemy-page-node").length>0}get folderButton(){return this.querySelector("button.page_folder")}}customElements.define("alchemy-page-node",xe);class Te extends HTMLElement{connectedCallback(){const e=this.querySelector("alchemy-datepicker:has(#page_public_on)"),t=this.querySelector("alchemy-datepicker:has(#page_public_until)"),s=this.querySelector(".page-publication-date-fields"),i=this.querySelector("#page_public");i&&i.addEventListener("click",(function(i){const n=i.target,r=new Date;n.checked?(s.classList.remove("hidden"),e.flatpickr.setDate(r)):(s.classList.add("hidden"),e.flatpickr.clear()),t.flatpickr?.clear()}))}}customElements.define("alchemy-page-publication-fields",Te);customElements.define("alchemy-page-select",class extends D{get pageId(){return this.selection?JSON.parse(this.selection).id:void 0}_searchQuery(e,t){return{q:{name_cont:e,...JSON.parse(this.queryParams)},page:t}}_parseResponse(e){const t=e.meta;return{results:e.pages,more:t.page*t.per_page\n
\n \n ${this._hightlightTerm(e.name,t)}\n ${e.site.name}\n
\n
\n ${e.url_path}\n ${e.language_code}\n
\n
\n `}});class Ae extends HTMLElement{constructor(){super(),this.addEventListener("change",this)}handleEvent(e){if("change"===e.type)this.onChange()}onChange(){const e=new URL(this.getAttribute("url")),t=this.querySelector("select");e.searchParams.set("language_id",t.value),Turbo.visit(e,{frame:"picture_descriptions"})}}customElements.define("alchemy-picture-description-select",Ae);class $e extends HTMLElement{constructor(){super(),this.cropFromField=this.querySelector("[data-crop-from]"),this.cropSizeField=this.querySelector("[data-crop-size]"),this.pictureIdField=this.querySelector("[data-picture-id]"),this.targetSizeField=this.querySelector("[data-target-size]"),this.imageCropperField=this.querySelector("[data-image-cropper]"),this.image=this.querySelector("img"),this.pictureThumbnail=this.querySelector("alchemy-picture-thumbnail"),this.deleteButton=this.querySelector(".picture_tool.delete"),this.cropLink=this.querySelector(".crop_link"),this.targetSize=this.targetSizeField.dataset.targetSize,this.pictureId=this.pictureIdField.value,this.update=function(e,t){let s;return function(...i){const n=this;clearTimeout(s),s=setTimeout((()=>e.apply(n,i)),t)}}((()=>{this.updateImage(),this.updateCropLink()}),125),this.deleteButton?.addEventListener("click",this.removeImage.bind(this))}connectedCallback(){this.observer=new MutationObserver(this.mutationCallback.bind(this)),this.observer.observe(this.cropFromField,{attributes:!0}),this.observer.observe(this.cropSizeField,{attributes:!0}),this.observer.observe(this.pictureIdField,{attributes:!0})}disconnectedCallback(){this.observer.disconnect()}mutationCallback(e){for(const t of e)"pictureId"in t.target.dataset&&(this.cropFromField.value="",this.cropSizeField.value="",this.pictureId=t.target.value),this.update()}updateImage(){this.pictureId&&(this.pictureThumbnail.loading=!0,Z(Alchemy.routes.url_admin_picture_path(this.pictureId),{crop:this.imageCropperEnabled,crop_from:this.cropFrom,crop_size:this.cropSize,flatten:!0,size:"160x120"}).then((({data:e})=>{this.pictureThumbnail.src=e.url,this.pictureThumbnail.image.alt=e.alt,this.pictureThumbnail.image.title=e.title,this.setElementDirty()})).catch((e=>{console.error(e.message||e),q(e.message||e,"error")})))}removeImage(){this.pictureThumbnail.innerHTML='',this.pictureIdField.value="",this.image=null,this.cropLink.classList.add("disabled"),this.setElementDirty()}setElementDirty(){this.closest(".element-editor").setDirty(this)}updateCropLink(){this.pictureId&&this.imageCropperEnabled&&(this.cropLink.classList.remove("disabled"),this.cropLink.href.match(/(picture_id=)\d+/)?this.cropLink.href=this.cropLink.href.replace(/(picture_id=)\d+/,"$1"+this.pictureId):this.cropLink.href=this.cropLink.href+`&picture_id=${this.pictureId}`)}get cropFrom(){return""===this.cropFromField.value?this.defaultCropFrom.join("x"):this.cropFromField.value}get cropSize(){return""===this.cropSizeField.value?this.defaultCropSize.join("x"):this.cropSizeField.value}get defaultCropSize(){if(!this.imageCropperEnabled)return[];const e=this.targetSize.split("x").map((e=>parseInt(e))),t=(s=e[0]/this.imageFileWidth,i=e[1]/this.imageFileHeight,s>=i?s:i);var s,i;return[Math.round(e[0]/t),Math.round(e[1]/t)]}get defaultCropFrom(){if(!this.imageCropperEnabled)return[];const e=this.defaultCropSize;return[Math.round((this.imageFileWidth-e[0])/2),Math.round((this.imageFileHeight-e[1])/2)]}get imageFileWidth(){return parseInt(this.pictureIdField.dataset.imageFileWidth)}get imageFileHeight(){return parseInt(this.pictureIdField.dataset.imageFileHeight)}get imageCropperEnabled(){return"true"===this.targetSizeField.dataset.imageCropper}}customElements.define("alchemy-picture-editor",$e);class qe extends HTMLElement{constructor(){super(),this.classList.add("thumbnail_background"),this.spinner=new y("small"),this.src&&this.start()}handleEvent(e){switch(e.type){case"load":this.#ee();break;case"error":this.#te(e)}}connectedCallback(){this.#se()}disconnectedCallback(){this.image?.removeEventListener("load",this),this.image?.removeEventListener("error",this),this.stop()}createImage(e=this.src,t=this.name){this.image=new Image,this.image.src=e,t&&(this.image.alt=t)}start(e){this.createImage(e),this.image.addEventListener("load",this),this.image.addEventListener("error",this),this.load()}load(){this.image?.complete||(this.setAttribute("loading","loading"),this.innerHTML="",this.spinner.spin(this))}stop(){this.classList.remove("loading"),this.spinner.stop()}#ee(){this.spinner.stop(),this.removeAttribute("loading")}#te(e){const t=`Could not load ${this.image.src}`,s=this.closest(".ingredient-editor");this.spinner.stop(),this.innerHTML=`\n \n \n \n `,console.error(t,e)}#se(){this.image?.complete?this.replaceChildren(this.image):this.image&&this.append(this.image)}set loading(e){e?this.load():this.stop()}set src(e){this.start(e),this.#se()}get name(){return this.getAttribute("name")}get src(){return this.getAttribute("src")}}customElements.define("alchemy-picture-thumbnail",qe);class Fe extends HTMLElement{constructor(){super(),this.addEventListener("submit",this)}connectedCallback(){document.addEventListener("alchemy:page-dirty",this)}disconnectedCallback(){document.removeEventListener("alchemy:page-dirty",this)}handleEvent(e){switch(e.type){case"alchemy:page-dirty":this.markDirty(e.detail);break;case"submit":this.button.loading=!0}}markDirty(e){this.button.variant="primary",this.button.disabled=!1,this.tooltip.content=e.tooltip}get button(){return this.querySelector("sl-button")}get tooltip(){return this.querySelector("sl-tooltip")}}customElements.define("alchemy-publish-page-button",Fe);class Ie extends HTMLSelectElement{#ie;connectedCallback(){this.classList.add("alchemy_selectbox"),this.#ie=$(this).select2({minimumResultsForSearch:5,dropdownAutoWidth:!0,allowClear:!!this.allowClear}),this.allowClear||this.multiple||this.#ie.prev(".select2-container").find(".select2-search-choice-close").remove()}enable(){this.removeAttribute("disabled"),this.#ne()}disable(){this.setAttribute("disabled","disabled"),this.#ne()}setOptions(e,t=void 0){let s=this.value;this.innerHTML="",t&&this.add(new Option(t,"")),e.forEach((e=>{this.add(new Option(e.text,e.id,!1,e.id===s))})),this.#ne()}#ne(){this.#ie.trigger("change")}get allowClear(){return this.dataset.hasOwnProperty("allowClear")||this.multiple}}customElements.define("alchemy-select",Ie,{extends:"select"});class Me extends HTMLElement{connectedCallback(){this.searchInput=document.querySelector(".search_input_field"),this.clearButton=document.querySelector("#search_field_clear"),this.resultCounter=document.querySelector("#page_filter_result"),this.setupSearch(),requestAnimationFrame((()=>{this.setupSortables()})),this.observer=new MutationObserver((e=>{e.forEach((e=>{e.addedNodes.forEach((e=>{e.nodeType===Node.ELEMENT_NODE&&(e.classList?.contains("children")&&this.setupSortable(e),e.querySelectorAll(".children").forEach((e=>this.setupSortable(e))))}))}))})),this.observer.observe(this,{childList:!0,subtree:!0})}disconnectedCallback(){this.teardownSearch(),this.observer?.disconnect()}setupSearch(){this.searchInput?.addEventListener("input",this),this.clearButton?.addEventListener("click",this)}teardownSearch(){this.searchInput?.removeEventListener("input",this),this.clearButton?.removeEventListener("click",this)}handleEvent(e){"input"===e.type&&e.target===this.searchInput?this.handleSearch(e):"click"===e.type&&e.target===this.clearButton&&this.handleClearSearch(e)}handleSearch(e){const t=e.target.value.toLowerCase().trim();""!==t?this.filterPages(t):this.clearFilter()}filterPages(e){const t=this.querySelectorAll(".sitemap_page");let s=0,i=null;t.forEach((t=>{(t.getAttribute("name")||"").toLowerCase().includes(e)?(t.classList.add("highlight"),t.classList.remove("no-match"),s++,i||(i=t)):(t.classList.remove("highlight"),t.classList.add("no-match"))})),1===s?(this.resultCounter.textContent=`1 ${h("page_found")}`,this.resultCounter.style.display="block"):s>1?(this.resultCounter.textContent=`${s} ${h("pages_found")}`,this.resultCounter.style.display="block"):this.resultCounter.style.display="none",i&&i.scrollIntoView({behavior:"smooth",block:"center"})}clearFilter(){this.querySelectorAll(".sitemap_page").forEach((e=>{e.classList.remove("highlight","no-match")})),this.resultCounter.style.display="none"}handleClearSearch(e){e.preventDefault(),this.searchInput.value="",this.clearFilter()}setupSortable(e){new i(e,{group:"pages",animation:150,fallbackOnBody:!0,swapThreshold:.65,handle:".page-icon.handle",draggable:"alchemy-page-node",onEnd:e=>this.handleSort(e)})}setupSortables(){this.querySelectorAll(".children").forEach((e=>this.setupSortable(e)))}async handleSort(e){if(e.from===e.to&&e.oldIndex===e.newIndex)return;const t=e.item,s=t.pageId,i=Alchemy.routes.move_admin_page_path(s),n={target_parent_id:e.to.dataset.parentId,new_position:e.newIndex};S(!0);try{const r=await ee(i,n),l=await r.data,o=t.querySelector(`#page_${s}`);if(o){const e=o.querySelector(".sitemap_url");e&&l.url_path&&(e.textContent=l.url_path)}this.updateFolderIcons(e.from,e.to),q(h("Successfully moved page"))}catch(e){q(e.message||e,"error"),window.location.reload()}finally{S(!1)}}updateFolderIcons(e,t){const s=e.closest("alchemy-page-node");if(s?.updateFolderButton(),e!==t){const e=t.closest("alchemy-page-node");e?.updateFolderButton()}}}customElements.define("alchemy-sitemap",Me);const ze={draggable:".element-editor",handle:".element-handle.draggable",ghostClass:"dragged",animation:150,swapThreshold:.65,easing:"cubic-bezier(1, 0, 0, 1)"};function Be(e){const t=e.item.dataset.elementName;document.querySelectorAll(`[data-droppable-elements~="${t}"]`).forEach((e=>e.classList.add("droppable-elements")))}function He(e){const t=e.item,s=e.to.parentElement.closest(".element-editor"),i={element_id:t.dataset.elementId,position:e.newIndex+1};s&&(i.parent_element_id=s.dataset.elementId),e.target===e.to&&te(Alchemy.routes.order_admin_elements_path,i).then((e=>{const s=e.data;q(s.message),s.pageHasUnpublishedChanges&&le(s),M(),t.updateTitle(s.preview_text)}))}function De(){document.querySelectorAll("[data-droppable-elements]").forEach((e=>e.classList.remove("droppable-elements")))}class Pe extends HTMLElement{connectedCallback(){const e={name:this.dataset.elementName,put:(e,t,s)=>e.el.dataset.droppableElements.split(" ").includes(s.dataset.elementName)};new i(this,{...ze,onStart:Be,onSort:He,onEnd:De,group:e})}}customElements.define("alchemy-sortable-elements",Pe);class Oe extends HTMLElement{connectedCallback(){this.className=`spinner spinner--${this.size}`,this.innerHTML=`\n \n \n \n \n \n `}get size(){return this.getAttribute("size")||"medium"}get color(){return this.getAttribute("color")||"currentColor"}}customElements.define("alchemy-spinner",Oe);class Ne extends HTMLElement{async connectedCallback(){await d(),this.classList.add("autocomplete_tag_list"),$(this.input).select2(this.select2Config)}get input(){return this.getElementsByTagName("input")[0]}get select2Config(){return{tags:!0,tokenSeparators:[","],openOnEnter:!1,minimumInputLength:1,createSearchChoice:this.#re,ajax:{url:this.getAttribute("url"),dataType:"json",data:e=>({term:e}),results:e=>({results:e})},initSelection:this.#le}}#re(e,t){if(0===$(t).filter((function(){return 0===this.text.localeCompare(e)})).length)return{id:e,text:e}}#le(e,t){const s=[];$(e.val().split(",")).each((function(){s.push({id:this.trim(),text:this})})),t(s)}}customElements.define("alchemy-tags-autocomplete",Ne);const Ue="alchemy-dark",Re="alchemy";class je extends HTMLElement{#oe=null;connectedCallback(){this.className="tinymce_container",this.querySelector(":scope > alchemy-spinner")||this.insertAdjacentHTML("beforeend",''),this.style.minHeight=`${this.minHeight}px`,this.editor.style.display="none";const e={root:document.getElementById("element_area"),rootMargin:"0px",threshold:[.05]};this.tinymceIntersectionObserver=new IntersectionObserver(((e,t)=>{e.forEach((e=>{e.intersectionRatio>0&&(this._initTinymceEditor(),t.unobserve(e.target))}))}),e),this.tinymceIntersectionObserver.observe(this),this._setupThemeChangeListener()}disconnectedCallback(){this.tinymceIntersectionObserver?.disconnect(),this._removeThemeChangeListener(),tinymce.get(this.editorId)?.remove(this.editorId)}_initTinymceEditor(){tinymce.init(this.configuration).then((e=>{e.forEach((e=>this._setupEditor(e)))}))}_setupEditor(e){e.show();const t=this.getElementsByTagName("alchemy-spinner")[0];t&&t.remove(),this.elementEditor&&(e.on("dirty",(e=>{this.elementEditor.setDirty(e.target.editorContainer)})),e.on("click",(()=>this.elementEditor.onClickElement(!1))))}_setupThemeChangeListener(){this.darkModeMediaQuery=window.matchMedia("(prefers-color-scheme: dark)"),this.themeChangeHandler=e=>this._handleThemeChange(e),this.darkModeMediaQuery.addEventListener("change",this.themeChangeHandler)}_removeThemeChangeListener(){this.darkModeMediaQuery&&this.themeChangeHandler&&this.darkModeMediaQuery.removeEventListener("change",this.themeChangeHandler)}_handleThemeChange(e){const t=tinymce.get(this.editorId);if(t){const s=e.matches?Ue:Re,i=e.matches?Ue:Re;t.remove(),tinymce.init({content_css:i,...this.configuration,skin:s}).then((e=>{e.forEach((e=>this._setupEditor(e)))}))}}get configuration(){const e={};this.getAttributeNames().forEach((t=>{if(!["class","id","is","name","style"].includes(t)){const s=this.getAttribute(t),i=t.replaceAll("-","_");if(s===t||""===s)e[i]=!0;else try{e[i]=JSON.parse(s)}catch(t){e[i]=s}}}));const t={content_css:this.preferredTheme,...Alchemy.TinymceDefaults,...e,language:c(),selector:`#${this.editorId}`,skin:this.preferredTheme};return t.height=t.min_height,t}get preferredTheme(){return window.matchMedia("(prefers-color-scheme: dark)").matches?Ue:Re}get editorId(){return this.editor.id}get editor(){return this.getElementsByTagName("textarea")[0]}get elementEditor(){return document.getElementById(this.editorId).closest("alchemy-element-editor")}get minHeight(){return this.#oe||this.configuration.min_height}set minHeight(e){this.#oe=e}}customElements.define("alchemy-tinymce",je);class We extends HTMLElement{async connectedCallback(){const e=new y("small");e.spin(this);try{const e=await fetch(this.url,{credentials:"include"}),t=await e.json();e.ok?this.showStatus(t):this.showError(e)}catch(e){this.showError(e)}finally{e.stop()}}get url(){return this.getAttribute("url")}showStatus(e){"true"==e.status?this.querySelector(".update_available").classList.remove("hidden"):this.querySelector(".up_to_date").classList.remove("hidden")}showError(e){this.querySelector(".error").classList.remove("hidden"),console.error("[alchemy] Error fetching update status",e)}}var Je;customElements.define("alchemy-update-check",We),Je=Handlebars.template,(Handlebars.templates=Handlebars.templates||{})["node_folder.hbs"]=Je({0:function(e,t,s,i,n){return"right"},1:function(e,t,s,i,n){return"down"},compiler:[8,">= 4.3.0"],main:function(e,t,s,i,n){var r,l=e.lambda,o=e.escapeExpression,a=e.lookupProperty||function(e,t){if(Object.prototype.hasOwnProperty.call(e,t))return e[t]};return'\n \n\n'},useData:!0}),n("tooltip.show",{keyframes:[{transform:"translateY(10px)",opacity:"0"},{transform:"translateY(0)",opacity:"1"}],options:{duration:100}}),n("tooltip.hide",{keyframes:[{transform:"translateY(0)",opacity:"1"},{transform:"translateY(10px)",opacity:"0"}],options:{duration:100}}),n("dialog.show",{keyframes:[{transform:"scale(0.98)",opacity:"0"},{transform:"scale(1)",opacity:"1"}],options:{duration:150}}),n("dialog.hide",{keyframes:[{transform:"scale(1)",opacity:"1"},{transform:"scale(0.98)",opacity:"0"}],options:{duration:150}});const Ve=document.querySelector('link[rel="preload"][as="image"]').getAttribute("href"),Xe={"x-lg":"close",caret:"arrow-down-s"},Qe={resolver:e=>`${Ve}#ri-${Xe[e]||e}-line`,mutator:e=>{e.setAttribute("fill","currentColor"),e.setAttribute("viewBox","0 0 24 24")},spriteSheet:!0};function Ye(e,t,s,i){document.querySelectorAll(t).forEach((t=>{t.addEventListener(e,(e=>{const n=Array.from(t.querySelectorAll(s));let r=e.target;for(;r!==t;){if(n.includes(r))return void i.call(r,e);r=r.parentElement}}))}))}r("default",Qe),r("system",Qe);class Ke{#ae=!1;#ce=null;#he=null;#de=null;constructor(e,t){this.image=e,this.defaultBox=t.default_box,this.aspectRatio=t.ratio,this.#he=document.getElementById(t.crop_from_form_field_id),this.#de=document.getElementById(t.crop_size_form_field_id),this.elementId=t.element_id,this.dialog=Alchemy.currentDialog(),this.dialog&&(this.dialog.options.closed=()=>this.destroy(),this.bind()),this.init()}get cropperOptions(){return{aspectRatio:this.aspectRatio,viewMode:1,zoomable:!1,checkCrossOrigin:!1,checkOrientation:!1,data:this.box,cropend:()=>{const e=this.#ce.getData(!0);this.update(e)}}}get cropFrom(){if(this.#he?.value)return this.#he.value.split("x").map((e=>parseInt(e)))}get cropSize(){if(this.#de?.value)return this.#de.value.split("x").map((e=>parseInt(e)))}get box(){return this.cropFrom&&this.cropSize?{x:this.cropFrom[0],y:this.cropFrom[1],width:this.cropSize[0],height:this.cropSize[1]}:this.defaultBoxSize}get defaultBoxSize(){return{x:this.defaultBox[0],y:this.defaultBox[1],width:this.defaultBox[2],height:this.defaultBox[3]}}init(){this.#ae||(this.#ce=new l(this.image,this.cropperOptions),this.#ae=!0)}update(e){this.#he.value=`${e.x}x${e.y}`,this.#he.dispatchEvent(new Event("change")),this.#de.value=`${e.width}x${e.height}`,this.#de.dispatchEvent(new Event("change"))}reset(){this.#ce.setData(this.defaultBoxSize),this.update(this.defaultBoxSize)}destroy(){return this.#ce&&this.#ce.destroy(),this.#ae=!1,!0}bind(){this.dialog.dialog_body.find('button[type="submit"]').on("click",(()=>(document.querySelector(`[data-element-id='${this.elementId}']`).setDirty(),this.dialog.close(),!1))),this.dialog.dialog_body.find('button[type="reset"]').on("click",(()=>(this.reset(),!1)))}}class Ge extends b{constructor(e,t={}){super(e,t)}init(){$(".zoomed-picture-background").on("click",(e=>{if(e.stopPropagation(),"IMG"!==e.target.nodeName)return this.close(),!1})),$(".picture-overlay-handle").on("click",(e=>(this.dialog.toggleClass("hide-form"),!1))),this.$previous=$(".previous-picture"),this.$next=$(".next-picture"),this.#ue(),super.init()}previous(){null!=this.$previous[0]&&this.$previous[0].click()}next(){null!=this.$next[0]&&this.$next[0].click()}build(){this.dialog_container=$('
'),this.dialog=$('
'),this.dialog_body=$('
'),this.close_button=$('\n \n '),this.dialog.append(this.close_button),this.dialog.append(this.dialog_body),this.dialog_container.append(this.dialog),this.overlay=$('
'),this.$body.append(this.overlay),this.$body.append(this.dialog_container)}#ue(){this.$document.keydown((e=>{if("INPUT"===e.target.nodeName||"TEXTAREA"===e.target.nodeName)return!0;switch(e.which){case 37:return this.previous(),!1;case 39:return this.next(),!1;default:return!0}}))}}function Ze(){return document.querySelectorAll("#picture_archive input:checked")}function et(){const e=document.querySelector("#select_all_pictures"),t=document.querySelector(".selected_item_tools");Ye("click",".toolbar_buttons","a#select_all_pictures",(s=>{s.preventDefault(),e.classList.toggle("active");const i=e.classList.contains("active");!function(e){document.querySelectorAll(".picture_tool.select input[type='checkbox']").forEach((t=>{t.checked=e,t.closest(".picture_thumbnail").classList.toggle("active",e)}))}(i),t.classList.toggle("hidden",!i)})),Ye("change",".picture_tool.select","input",(e=>{t.classList.toggle("hidden",0===Ze().length);const s=e.target.parentElement.classList,i=e.target.checked;s.toggle("visible",i)})),Ye("click",".selected_item_tools","a#edit_multiple_pictures",(e=>{e.preventDefault();_(function(e){const t=new URL(e);return Ze().forEach((e=>t.searchParams.append(e.name,e.value))),t.toString()}(e.target.href),{title:e.target.title,size:"400x295"})}))}function tt(){document.querySelectorAll("li.menu-item").forEach((e=>{const t=e.querySelector(".nodes_tree-left_images"),s=e.querySelector(".children"),i={folded:"true"===e.dataset.folded,id:e.dataset.id,type:e.dataset.type};s.children.length>0||i.folded?t.innerHTML=Handlebars.templates["node_folder.hbs"]({node:i}):t.innerHTML=" "}))}function st(e){ee(Alchemy.routes[e.item.dataset.type].move_api_path(e.item.dataset.id),{target_parent_id:e.to.dataset.recordId,new_position:e.newIndex}).then((()=>{q(Alchemy.t("Successfully moved menu item")),tt()})).catch((e=>{q(e.message||e,"error")}))}function it(){Ye("click",".nodes_tree",".node_folder",(function(){const e=this.dataset.recordId,t=this.closest("li.menu-item"),s=Alchemy.routes[this.dataset.recordType].toggle_folded_api_path(e),i=t.querySelector(".children");ee(s).then((()=>{i.classList.toggle("folded"),t.dataset.folded="true"==t.dataset.folded?"false":"true",tt()})).catch((e=>{q(e.message||e)}))})),tt(),document.querySelectorAll(".nodes_tree ul.children").forEach((e=>{new i(e,{group:"nodes",animation:150,fallbackOnBody:!0,swapThreshold:.65,handle:".node_name",invertSwap:!0,onEnd:st})}))}void 0===window.Alchemy&&(window.Alchemy={}),Object.assign(Alchemy,{closeCurrentDialog:w,currentDialog:E,...x,t:h,FixedElements:A,growl:q,LinkDialog:class extends b{#me;constructor(e){const t=new URL(Alchemy.routes.link_admin_pages_path,window.location),s={url:e.url,selected_tab:e.type,link_title:e.title,link_target:e.target};Object.keys(s).forEach((e=>{s[e]&&t.searchParams.set(e,s[e])})),super(t.href,{size:"600x320",title:h("Link")})}replace(e){super.replace(e),this.#c()}open(){return super.open(),new Promise((e=>this.#me=e))}#c(){const e=document.querySelector('[data-link-form-type="internal"]'),t=document.querySelector('[data-link-form-type="file"] alchemy-attachment-select');e.addEventListener("Alchemy.RemoteSelect.Change",(e=>{this.#pe(e.detail.added)})),t.addEventListener("Alchemy.RemoteSelect.Change",(e=>{const t=e.detail.added;document.getElementById("file_link").value=t?t.url:""})),document.querySelectorAll("[data-link-form-type]").forEach((e=>{e.addEventListener("submit",(e=>{e.preventDefault(),this.#ge(e.target.dataset.linkFormType)}))}))}#pe(e=null){const t=document.getElementById("internal_link"),s=document.querySelector('[data-link-form-type="internal"] alchemy-dom-id-api-select');t.value=e?e.url_path:"",s.page=e?e.id:void 0}#ge(e){const t=document.getElementById("element_anchor");let s=document.getElementById(`${e}_link`).value;if("internal"===e&&""!==t.value)s=s.replace(F,"")+t.value;else if("external"===e&&!s.match(Alchemy.link_url_regexp))return void this.#ye();this.#me({url:s.trim(),title:document.getElementById(`${e}_link_title`).value,target:document.getElementById(`${e}_link_target`)?.value,type:e}),this.close()}#ye(){const e=document.getElementById("errors");e.querySelector("ul").innerHTML=`
  • ${Alchemy.t("url_validation_failed")}
  • `,e.style.display="block"}},pleaseWaitOverlay:S,Spinner:y,reloadPreview:M}),t.start(),e.config.forms.confirm=L,document.addEventListener("turbo:load",(function(){document.documentElement.classList.remove("no-js"),p(),document.querySelectorAll(".please_wait").forEach((e=>{e.addEventListener("click",S)})),document.querySelectorAll("a.button").forEach((e=>{e.setAttribute("tabindex",0)})),key.filter=function(e){let t=(e.target||e.srcElement).tagName;return key.isPressed("esc")||!("INPUT"===t||"SELECT"===t||"TEXTAREA"===t)}}));export{Ke as ImageCropper,Ge as ImageOverlay,it as NodeTree,D as RemoteSelect,Ye as on,et as pictureSelector}; +import"handlebars";import"jquery";import"@ungap/custom-elements";import{Turbo as e}from"@hotwired/turbo-rails";import"select2";import t from"@rails/ujs";import"keymaster";import"clipboard";import s from"flatpickr";import i from"sortablejs";import"tinymce";import{setDefaultAnimation as n,registerIconLibrary as l}from"shoelace";import o from"cropperjs";const r=/\./;function a(e){const t=c(),s=Alchemy.translations;return s?r.test(e)?function(e,t){const s=t.split(r),i=e[s[0]];return i&&i[s[1]]||t}(s,e):s[e]||e:(console.warn(`Translations for locale ${t} not found!`),e)}function c(){return document.documentElement.lang?document.documentElement.lang:"en"}function h(e,t=void 0){let s=a(e);return t?s.replace(/%\{.+\}/,t):s}async function d(){const e=c();"en"!==e&&(await import(`select2/${e}.js`),$.extend($.fn.select2.defaults,$.fn.select2.locales[e]))}const u=[];function m(e){return!(!$(e.target).is("input, textarea")&&"?"===String.fromCharCode(e.which))||(k("/admin/help",{title:Alchemy.t("help"),size:"400x492"}),!1)}function p(e=document){e instanceof jQuery&&(e=e[0]),e===document&&(document.removeEventListener("keypress",m),document.addEventListener("keypress",m),u.forEach((e=>key.unbind(e))));const t=e.querySelectorAll(".search_input_field"),s=e.querySelectorAll(".search_field_clear, .js_filter_field_clear");key("alt+f",(function(){return key.setScope("search"),t.forEach((e=>e.focus({focusVisible:!0}))),!1})),u.push("alt+f"),key("esc","search",(function(){s.forEach((e=>e.click())),t.forEach((e=>e.blur()))})),u.push("esc"),e.querySelectorAll("[data-alchemy-hotkey]").forEach((function(e){const t=e.dataset.alchemyHotkey;key(t,(()=>e.click())),u.push(t)}))}function g(e){const t=document.createElement("template");return t.innerHTML=e,t.content.children[0]}let y=class{constructor(e,t="currentColor"){this.size=e,this.color=t,this.spinner=void 0}get el(){return this.spinner}spin(e){return void 0===e&&(e=document.body),this.spinner=g(``),e.append(this.spinner),this}stop(){this.spinner&&(this.spinner.remove(),this.spinner=void 0)}};const v=[],b={header_height:36,size:"400x300",padding:!0,title:"",modal:!0,overflow:"visible",ready:()=>{},closed:()=>{}};class f{constructor(e,t={}){this.url=e,this.options={...b,...t},this.$document=$(document),this.$window=$(window),this.$body=$("body");const s=this.options.size.split("x");this.width=parseInt(s[0],10),this.height=parseInt(s[1],10),this.build(),this.resize()}open(){this.dialog.trigger("Alchemy.DialogOpen"),this.bind_close_events(),window.requestAnimationFrame((()=>{if(this.dialog_container.addClass("open"),null!=this.overlay)return this.overlay.addClass("open")})),this.$body.addClass("prevent-scrolling"),v.push(this),this.load()}close(){return this.dialog.trigger("DialogClose.Alchemy"),this.$document.off("keydown"),this.dialog_container.removeClass("open"),null!=this.overlay&&this.overlay.removeClass("open"),this.$document.on("webkitTransitionEnd transitionend oTransitionEnd",(()=>{if(this.$document.off("webkitTransitionEnd transitionend oTransitionEnd"),this.dialog_container.remove(),null!=this.overlay&&this.overlay.remove(),this.$body.removeClass("prevent-scrolling"),v.pop(this),null!=this.options.closed)return this.options.closed()})),!0}load(){this.show_spinner(),$.get(this.url,(e=>{this.replace(e)})).fail((e=>{this.show_error(e)}))}reload(){this.dialog_body.empty(),this.load()}replace(e){this.remove_spinner(),this.dialog_body.hide(),this.dialog_body.html(e),this.init(),this.dialog[0].dispatchEvent(new CustomEvent("DialogReady.Alchemy",{bubbles:!0,detail:{body:this.dialog_body[0]}})),null!=this.options.ready&&this.options.ready(this.dialog_body),this.dialog_body.show()}show_spinner(){this.spinner=new y("medium"),this.spinner.spin(this.dialog_body[0])}remove_spinner(){this.spinner.stop()}init(){p(this.dialog_body),this.watch_remote_forms()}watch_remote_forms(){const e=$('[data-remote="true"]',this.dialog_body);e.on("ajax:success",(e=>{const t=e.detail[2];t.getResponseHeader("Content-Type").match(/javascript/)||(this.dialog_body.html(t.responseText),this.init())})),e.on("ajax:error",(e=>{const t=e.detail[1],s=e.detail[2];this.show_error(s,t)}))}show_error(e,t){if(422===e.status)return this.dialog_body.html(e.responseText),void this.init();const{error_body:s,error_header:i,error_type:n}=this.error_messages(e,t),l=$(`\n

    ${i}

    \n

    ${s}

    \n
    `);this.dialog_body.html(l)}error_messages(e,t){let s,i,n="warning";switch(e.status){case 0:i="The server does not respond.",s="Please check server and try again.";break;case 403:i="You are not authorized!",s="Please close this window.";break;default:n="error",t?(i=t,console.error(e.responseText)):i=`${e.statusText} (${e.status})`,s="Please check log and try again."}return{error_header:i,error_body:s,error_type:n}}bind_close_events(){this.close_button.on("click",(()=>{this.close()})),this.dialog_container.addClass("closable").on("click",(e=>e.target!==this.dialog_container.get(0)||(this.close(),!1))),this.$document.keydown((e=>27!==e.which||(this.close(),!1)))}build(){this.dialog_container=$('
    '),this.dialog=$('
    '),this.dialog_body=$('
    '),this.dialog_header=$('
    '),this.dialog_title=$('
    '),this.close_button=$(''),this.dialog_title.text(this.options.title),this.dialog_header.append(this.dialog_title),this.dialog_header.append(this.close_button),this.dialog.append(this.dialog_header),this.dialog.append(this.dialog_body),this.dialog_container.append(this.dialog),this.options.modal&&this.dialog.addClass("modal"),this.options.padding&&this.dialog_body.addClass("padded"),this.options.modal&&(this.overlay=$('
    '),this.$body.append(this.overlay)),this.$body.append(this.dialog_container)}resize(){const{width:e,height:t}=this.getSize();this.dialog.css({width:e,"min-height":t,overflow:this.options.overflow}),"hidden"===this.options.overflow?this.dialog_body.css({height:t,overflow:"auto"}):this.dialog_body.css({"min-height":t,overflow:"visible"})}getSize(){const e=this.options.padding?16:0,t=this.$window.width(),s=this.$window.height();let i=this.width,n=this.height;return i>=t&&(i=t-e),n>=s&&(n=s-e-b.header_height),{width:i,height:n}}}function E(){const{length:e}=v;if(0!==e)return v[e-1]}function w(e){const t=E();if(null!=t)return t.options.closed=e,t.close()}function k(e,t){if(!e)throw"No url given! Please provide an url.";new f(e,t).open()}class L{constructor(e,t={}){this.message=e,this.options={...{size:"300x100",title:h("Please confirm"),ok_label:h("Yes"),cancel_label:h("No"),on_ok(){}},...t},this.#e(),this.#t()}open(){requestAnimationFrame((()=>{this.dialog.show()}))}#e(){const e=this.options.size.split("x")[0];this.dialog=g(`\n \n ${this.message}\n \n \n \n `),document.body.append(this.dialog)}#t(){this.cancelButton.addEventListener("click",(e=>{e.preventDefault(),this.options.on_cancel(),this.dialog.hide()})),this.okButton.addEventListener("click",(e=>{e.preventDefault(),this.options.on_ok(),this.dialog.hide()})),this.dialog.addEventListener("sl-request-close",(e=>{"overlay"===e.detail.source&&(this.options.on_cancel(),e.preventDefault())})),this.dialog.addEventListener("sl-after-hide",(()=>{this.dialog.remove()}))}get cancelButton(){return this.dialog.querySelector("button[type=reset]")}get okButton(){return this.dialog.querySelector("button[type=submit]")}}function _(e,t={}){return new Promise((s=>{new L(e,{...t,on_ok(){s(!0)},on_cancel(){s(!1)}}).open()}))}function C(e=!0){document.querySelector("alchemy-overlay").show=!!e}function S(e){let t=()=>{};$(e).is("form")?t=function(){const t=$(``);t.append($(e).find("input")),t.appendTo("body"),C(),t.trigger("submit")}:$(e).is("a")&&(t=()=>Turbo.visit(e.pathname));return!(document.querySelectorAll("alchemy-element-editor.dirty").length>0)||(_(h("page_dirty_notice"),{title:h("warning"),ok_label:h("ok"),cancel_label:h("cancel")}).then((e=>{e&&(window.onbeforeunload=void 0,t())})),!1)}var x={checkPageDirtyness:S,PageLeaveObserver:function(){document.querySelectorAll("#main_navi a").forEach((e=>{e.addEventListener("click",(e=>{S(e.currentTarget)||e.preventDefault()}))}))}};function T(e){const t=document.getElementById("fixed-elements"),s=`fixed-element-${e}`;t.querySelector(`sl-tab[panel="${s}"]`).remove(),t.querySelector(`sl-tab-panel[name="${s}"]`).remove(),t.show("main-content-elements")}var A=Object.freeze({__proto__:null,createTab:function(e,t){const s=document.getElementById("fixed-elements"),i=`fixed-element-${e}`,n=`${t}`,l=``;s.innerHTML+=n+l,window.requestAnimationFrame((function(){s.show(i)}))},removeTab:T});function F(e,t="notice"){!function(e,t){const s=document.getElementById("flash_notices"),i=g(`\n \n ${e}\n \n `);s.append(i)}(e,t)}const I=/#[\w.~-]+$/;class q extends HTMLIFrameElement{#s;#i;#n;handleEvent(e){"load"===e.type&&(this.#l(),this.#o(),this.#s?.call(this,e))}#r=e=>{"Alchemy.previewReady"===e.data.message&&(this.#l(),this.#o(),this.#s?.call(this,e))};connectedCallback(){let e=this.url;this.addEventListener("load",this),this.#a(),window.addEventListener("message",this.#r),window.localStorage.getItem("alchemy-preview-url")&&(e=window.localStorage.getItem("alchemy-preview-url"),this.previewUrlSelect.value=e),this.refresh(e)}disconnectedCallback(){this.removeEventListener("load",this),key.unbind("alt+r"),window.removeEventListener("message",this.#r)}postMessage(e){this.contentWindow.postMessage(e,"*")}resize(e){this.style.width=`${e}px`}refresh(e){return this.#c(),this.src=e||this.url,this.#l(),this.#n=setTimeout((()=>{this.#o(),F(h("Preview failed to load"),"warning")}),5e3),new Promise((e=>{this.#s=e}))}set isDragged(e){this.style.transitionProperty=e?"none":null,this.style.pointerEvents=e?"none":null}#a(){this.reloadButton?.addEventListener("click",(e=>{e.preventDefault(),this.refresh()})),key("alt+r",(()=>this.refresh())),this.sizeSelect.addEventListener("change",(e=>{const t=e.target.value;""===t?this.style.width=null:this.resize(t)})),this.previewUrlSelect?.addEventListener("change",(e=>{const t=e.target.value;window.localStorage.setItem("alchemy-preview-url",t),this.refresh(t)}))}#c(){this.reloadButton.innerHTML.includes("alchemy-spinner")||(this.#i=this.reloadButton.innerHTML),this.reloadButton.innerHTML=''}#o(){this.reloadButton.innerHTML=this.#i}#l(){this.#n&&(clearTimeout(this.#n),this.#n=null)}get url(){return this.getAttribute("url")}get sizeSelect(){return document.querySelector("select#preview_size")}get previewUrlSelect(){return document.querySelector("select#preview_url")}get reloadButton(){return document.querySelector("#reload_preview_button")}}function B(){document.getElementById("alchemy_preview_window").refresh()}customElements.define("alchemy-preview-window",q,{extends:"iframe"});class M{static updateIcon(e,t=!1){const s=document.querySelector(`[data-ingredient-id="${e}"]`);if(s){s.querySelector(".edit-ingredient-anchor-link alchemy-icon").setAttribute("icon-style",t?"fill":"line")}}}class z extends HTMLElement{constructor(){super(),this.actions={closeCurrentDialog:w,reloadPreview:B,removeFixedElement:T,updateAnchorIcon:M.updateIcon,hidePleaseWaitOverlay(){C(!1)}}}connectedCallback(){const e=this.actions[this.name];e?e(...this.params):console.error(`Unknown Alchemy action: ${this.name}`),this.remove()}get name(){return this.getAttribute("name")}get params(){return this.hasAttribute("params")?JSON.parse(this.getAttribute("params")):[]}}function D(e,t){return e.replace(new RegExp(t,"gi"),(e=>`${e}`))}customElements.define("alchemy-action",z);class H extends HTMLElement{#h=null;async connectedCallback(){await d(),this.isConnected&&(this.input.classList.add("alchemy_selectbox"),this.#h=$(this.input).select2(this.select2Config).on("select2-open",this.#d).on("change",this.#u))}disconnectedCallback(){this.#h&&(this.#h.off("select2-open",this.#d),this.#h.off("change",this.#u),this.#h.select2("destroy"),this.#h=null)}#d=e=>this.onOpen(e);#u=e=>this.onChange(e);onChange(e){this.dispatchCustomEvent("RemoteSelect.Change",{removed:e.removed,added:e.added})}onOpen(e){setTimeout((()=>{document.querySelector("#select2-drop .select2-input").focus()}),100)}dispatchCustomEvent(e,t={}){this.dispatchEvent(new CustomEvent(`Alchemy.${e}`,{bubbles:!0,detail:t}))}get allowClear(){return this.hasAttribute("allow-clear")}get selection(){return this.getAttribute("selection")}get placeholder(){return this.getAttribute("placeholder")??""}get queryParams(){return this.getAttribute("query-params")??"{}"}get url(){return this.getAttribute("url")??""}get input(){return this.getElementsByTagName("input")[0]}get select2Config(){return{placeholder:this.placeholder,allowClear:this.allowClear,initSelection:(e,t)=>{this.selection&&t(JSON.parse(this.selection))},ajax:this.ajaxConfig,formatSelection:e=>this._renderResult(e),formatResult:(e,t,s)=>this._renderListEntry(e,s.term)}}get ajaxConfig(){return{url:this.url,datatype:"json",quietMillis:300,data:(e,t)=>this._searchQuery(e,t),results:e=>this._parseResponse(e)}}_searchQuery(e,t){return{q:{name_cont:e,...JSON.parse(this.queryParams)},page:t}}_parseResponse(e){const t=e.meta;return{results:e.data,more:t.page*t.per_page\n \n ${this._hightlightTerm(e.name,t)}\n
    \n `}});class P extends HTMLElement{connectedCallback(){$(this).on("change",this.#u)}disconnectedCallback(){$(this).off("change",this.#u)}#u=e=>{const t=new Event("submit",{bubbles:!0,cancelable:!0});return e.target.form.dispatchEvent(t),!1}}customElements.define("alchemy-auto-submit",P);class O extends HTMLButtonElement{connectedCallback(){this.form?(this.form.addEventListener("submit",this),"true"==this.form.dataset.remote&&this.form.addEventListener("ajax:complete",this),this.form.addEventListener("turbo:submit-end",this)):console.warn("No form for button found!",this)}handleEvent(e){switch(e.type){case"submit":"disabled"===this.getAttribute("disabled")?(e.preventDefault(),e.stopPropagation()):this.disable();break;case"ajax:complete":case"turbo:submit-end":this.enable()}}disable(){const e=new y("small"),t=this.getBoundingClientRect();this.dataset.initialButtonText=this.innerHTML,this.setAttribute("disabled","disabled"),this.setAttribute("tabindex","-1"),this.classList.add("disabled"),this.style.width=`${t.width}px`,this.style.height=`${t.height}px`,this.innerHTML=" ",e.spin(this)}enable(){this.classList.remove("disabled"),this.removeAttribute("disabled"),this.removeAttribute("tabindex"),this.style.width=null,this.style.height=null,this.innerHTML=this.dataset.initialButtonText}}customElements.define("alchemy-button",O,{extends:"button"});class N extends HTMLElement{connectedCallback(){this.translation=h("allowed_chars",this.maxChars),this.formField=this.getFormField(),this.formField&&(this.createDisplayElement(),this.countCharacters(),this.formField.addEventListener("keyup",this))}disconnectedCallback(){this.formField?.removeEventListener("keyup",this)}handleEvent(e){"keyup"===e.type&&this.countCharacters()}getFormField(){const e=this.querySelectorAll("input, textarea");return e.length>0?e[0]:void 0}createDisplayElement(){this.display=this.querySelector(":scope > .alchemy-char-counter"),this.display||(this.display=document.createElement("small"),this.display.className="alchemy-char-counter",this.formField.after(this.display))}countCharacters(){const e=this.formField.value.length;this.display.textContent=`${e} ${this.translation}`,this.display.classList.toggle("too-long",e>this.maxChars)}get maxChars(){return this.getAttribute("max-chars")??60}}customElements.define("alchemy-char-counter",N);class U extends HTMLElement{connectedCallback(){this.innerHTML='',this.clipboard=new ClipboardJS(this,{text:()=>this.getAttribute("content")}),this.clipboard.on("success",(()=>{F(this.getAttribute("success-text"))}))}disconnectedCallback(){this.clipboard.destroy()}}customElements.define("alchemy-clipboard-button",U);const R=e=>{const t=e.element[0],s=t.dataset.swatch||t.value;return`\n
    \n ${"custom_color"===t.value?'':``}\n ${e.text}\n
    `};class W extends HTMLElement{#h=null;connectedCallback(){this.select?(this.#m(),this.#h.on("change",this.#p)):(this.colorInput?.addEventListener("input",this),this.textInput?.addEventListener("input",this),this.#g(!0))}handleEvent(e){switch(e.target){case this.colorInput:this.textInput.value=this.colorInput.value;break;case this.textInput:this.colorInput.value=this.textInput.value}}disconnectedCallback(){this.colorInput?.removeEventListener("input",this),this.textInput?.removeEventListener("input",this),this.#h&&(this.#h.off("change",this.#p),this.#h.select2("destroy"),this.#h=null)}#p=e=>{this.#g("custom_color"===e.val)};#m(){this.select.classList.add("alchemy_selectbox");const e={minimumResultsForSearch:10,formatResult:R,formatSelection:R};this.#h=$(this.select).select2(e)}#g(e=!0){this.colorInput.disabled=!e}get colorInput(){return this.querySelector("input[type='color']")}get textInput(){return this.querySelector("input[type='text']")}get select(){return this.querySelector("select")}}customElements.define("alchemy-color-select",W);const j=c();class J extends HTMLElement{async connectedCallback(){"en"!==j&&await import(`flatpickr/${j}.js`),this.isConnected&&(this.flatpickr=s(this.inputField,this.flatpickrOptions))}disconnectedCallback(){this.flatpickr?.destroy()}get flatpickrOptions(){const e=/time/.test(this.inputType),t={locale:j.slice(0,2),altInput:!0,altFormat:h(`formats.${this.inputType}`),altInputClass:"flatpickr-input",enableTime:e,noCalendar:"time"===this.inputType,time_24hr:h("formats.time_24hr"),onValueUpdate(e,t,s){s.element.closest("alchemy-element-editor")?.setDirty(this.inputField)}};return e&&(t.dateFormat="Z"),t}get inputField(){return this.querySelector("input")}get inputType(){return this.getAttribute("input-type")||"date"}}customElements.define("alchemy-datepicker",J);class V extends HTMLAnchorElement{connectedCallback(){this.addEventListener("click",this)}disconnectedCallback(){this.removeEventListener("click",this)}handleEvent(e){this.disabled||this.openDialog(),e.preventDefault()}openDialog(){this.dialog=new f(this.getAttribute("href"),this.dialogOptions),this.dialog.open()}get dialogOptions(){return this.dataset.dialogOptions?JSON.parse(this.dataset.dialogOptions):{}}get disabled(){return this.classList.contains("disabled")}}customElements.define("alchemy-dialog-link",V,{extends:"a"});const X="application/json",Q="text/vnd.turbo-stream.html";function K(e){return"get"===e.toLowerCase()}function Y(e,t,s){const i=function(e){return{"Content-Type":"application/json; charset=utf-8",Accept:e,"X-Requested-With":"XMLHttpRequest","X-CSRF-Token":G()}}(s),n={method:e,headers:i};return t&&!K(e)&&(n.body=JSON.stringify(t)),n}function G(){return document.querySelector('meta[name="csrf-token"]').attributes.content.textContent}function Z(e,t){return se("GET",e,t)}function ee(e,t,s){return se("PATCH",e,t,s)}function te(e,t,s=X){return se("POST",e,t,s)}async function se(e,t,s,i=X){const n=await fetch(function(e,t,s){const i=new URL(window.location.origin+e);return t&&K(s)&&(i.search=new URLSearchParams(t).toString()),i.toString()}(t,s,e),Y(e,s,i)),l=n.headers.get("content-type"),o=l?.includes(X),r=l?.includes(Q);let a=null;if(o?a=await n.json():r&&(a=await n.text(),"undefined"!=typeof Turbo&&Turbo.renderStreamMessage(a)),n.ok)return{data:a,status:n.status};throw a||new Error("An error occurred during the transaction")}class ie extends HTMLElement{dataItem(e){return{id:`#${e}`,text:`#${e}`}}get selectElement(){return this.querySelector('select[is="alchemy-select"]')}}customElements.define("alchemy-dom-id-api-select",class extends ie{#y=void 0;connectedCallback(){this.page=this.getAttribute("page")}async#v(){const e=(await Z(Alchemy.routes.api_ingredients_path,{page_id:this.#y})).data.ingredients.filter((e=>e.data?.dom_id)).map((e=>this.dataItem(e.data.dom_id))),t=e.length>0?h("None"):h("No anchors found");this.selectElement.setOptions(e,t),this.selectElement.enable()}#b(){requestAnimationFrame((()=>{this.selectElement.disable(),this.selectElement.setOptions([],h("Select a page first"))}))}set page(e){this.#y=e,e?this.#v():this.#b()}}),customElements.define("alchemy-dom-id-preview-select",class extends ie{connectedCallback(){requestAnimationFrame((()=>{const e=document.getElementById("alchemy_preview_window"),t=e.contentDocument?.querySelectorAll("[id]")||[];if(t.length>0){const e=Array.from(t).map((e=>this.dataItem(e.id)));this.selectElement.setOptions(e,h("None"))}}))}});class ne extends HTMLElement{#f;connectedCallback(){this.#f=this.scheduleButton.getAttribute("variant"),this.publishButton.addEventListener("click",this),this.dropdown.addEventListener("sl-show",this),this.dropdown.addEventListener("sl-hide",this)}disconnectedCallback(){this.publishButton.removeEventListener("click",this),this.dropdown.removeEventListener("sl-show",this),this.dropdown.removeEventListener("sl-hide",this)}handleEvent(e){switch(e.type){case"click":this.publishButton.loading=!0;break;case"sl-show":this.scheduleButton.setAttribute("variant","primary");break;case"sl-hide":this.scheduleButton.setAttribute("variant",this.#f)}}get publishButton(){return this.querySelector("sl-button[type='submit']")}get dropdown(){return this.querySelector("sl-dropdown")}get scheduleButton(){return this.querySelector("sl-button[slot='trigger']")}}customElements.define("alchemy-publish-element-button",ne);class le extends HTMLElement{connectedCallback(){this.button?.addEventListener("click",this)}disconnectedCallback(){this.button?.removeEventListener("click",this)}async handleEvent(){if(await _(this.message)){const e=await se("DELETE",this.url);this.#E(e.data)}}#E(e){const t=this.closest("alchemy-element-editor");t.addEventListener("transitionend",(()=>{t.fixed&&T(t.elementId),t.remove()})),t.classList.add("dismiss"),F(e.message),e.pageHasUnpublishedChanges&&oe(e),B()}get url(){return this.getAttribute("href")}get message(){return this.getAttribute("message")}get button(){return this.querySelector("button")}}function oe(e){document.dispatchEvent(new CustomEvent("alchemy:page-dirty",{detail:{tooltip:e.publishButtonTooltip}}))}customElements.define("alchemy-delete-element-button",le);class re extends HTMLElement{#w=null;#k=null;#L=null;connectedCallback(){this.classList.contains("ui-sortable-placeholder")||(this.addEventListener("click",this),this.addEventListener("alchemy:element-update-title",this),this.addEventListener("ajax:complete",this),this.#w=this.form,this.#w&&$(this.#w).on("change",this.onChange),this.#k=this.header,this.#k?.addEventListener("dblclick",this.#_),this.#L=this.toggleButton,this.#L?.addEventListener("click",this.#C),this.hasAttribute("created")&&(this.focusElement(),this.previewWindow?.refresh().then((()=>{this.focusElementPreview()})),this.removeAttribute("created")))}disconnectedCallback(){this.removeEventListener("click",this),this.removeEventListener("alchemy:element-update-title",this),this.removeEventListener("ajax:complete",this),this.#w&&($(this.#w).off("change",this.onChange),this.#w=null),this.#k?.removeEventListener("dblclick",this.#_),this.#k=null,this.#L?.removeEventListener("click",this.#C),this.#L=null}handleEvent(e){switch(e.type){case"click":e.target.closest("alchemy-element-editor")===this&&this.onClickElement();break;case"ajax:complete":if(e.target===this.body){const t=e.detail[0];e.stopPropagation(),this.onSaveElement(t)}break;case"alchemy:element-update-title":this.hasEditors||e.target!=this.firstChild||this.setTitle(e.detail.title)}}onChange=e=>{const t=e.target;if(!t.classList.contains("nested-elements"))return this.setDirty(t),e.stopPropagation(),!1};async focusElement(){document.querySelector("#fixed-elements")&&await this.selectTabForElement(),await this.expand(),this.selectElement(!0)}focusElementPreview(){this.previewWindow?.postMessage({message:"Alchemy.focusElement",element_id:this.elementId})}onClickElement(){this.selectElement(),this.focusElementPreview()}onSaveElement(e){const t=JSON.parse(e.responseText);if(this.setClean(),422===e.status){const e=t.warning;t.ingredientsWithErrors.forEach((e=>{const t=this.querySelector(`[data-ingredient-id="${e.id}"]`),s=g(`${e.errorMessage}`);t?.appendChild(s),t?.classList.add("validation_failed")})),F(e,"warn"),this.elementErrors.classList.remove("hidden")}else F(t.notice),this.previewWindow?.refresh().then((()=>{this.focusElementPreview()})),this.updateTitle(t.previewText),t.ingredientAnchors.forEach((e=>{M.updateIcon(e.ingredientId,e.active)})),t.pageHasUnpublishedChanges&&oe(t)}scrollToElement(){setTimeout((()=>{this.scrollIntoView({behavior:"smooth"})}),50)}selectElement(e=!1){document.querySelectorAll("alchemy-element-editor.selected").forEach((e=>{e.classList.remove("selected")})),window.requestAnimationFrame((()=>{this.classList.add("selected")})),e&&this.scrollToElement()}selectTabForElement(){return new Promise(((e,t)=>{const s=document.querySelector("#fixed-elements"),i=this.closest("sl-tab-panel");s&&i?(s.show(i.getAttribute("name")),e()):t(new Error("No tabs present"))}))}setClean(){this.dirty=!1,window.onbeforeunload=null,this.elementErrors.classList.add("hidden"),this.hasEditors&&this.body.querySelectorAll(".ingredient-editor").forEach((e=>{e.classList.remove("dirty","validation_failed"),e.querySelectorAll("small.error").forEach((e=>e.remove()))}))}setDirty(e){this.hasEditors&&(this.dirty=!0,window.onbeforeunload||(window.onbeforeunload=e=>e.preventDefault()),e?.closest(".ingredient-editor")?.classList.add("dirty"))}setTitle(e){this.querySelector(".element-header .preview_text_quote").textContent=e}async toggle(){this.collapsed?await this.expand():await this.collapse()}collapse(){if(this.collapsed||this.compact||this.fixed)return Promise.resolve("Element is already collapsed.");const e=new Alchemy.Spinner("small");return e.spin(this.toggleButton),this.toggleIcon?.classList?.add("hidden"),te(Alchemy.routes.collapse_admin_element_path(this.elementId)).then((e=>{const t=e.data;if(this.collapsed=!0,this.toggleButton?.setAttribute("title",t.title),t.nestedElementIds.length){const e=t.nestedElementIds.map((e=>`#element_${e}`)).join(", ");this.querySelectorAll(e).forEach((e=>{e.collapsed=!0,e.toggleButton?.setAttribute("title",t.title)}))}})).catch((e=>{F(e.message,"error"),console.error(e)})).finally((()=>{this.toggleIcon?.classList?.remove("hidden"),e.stop()}))}expand(){if(this.expanded&&!this.compact)return Promise.resolve("Element is already expanded.");if(this.compact&&this.parentElementEditor)return this.parentElementEditor.expand();{const e=new Alchemy.Spinner("small");return e.spin(this.toggleButton),this.toggleIcon?.classList.add("hidden"),new Promise(((t,s)=>{te(Alchemy.routes.expand_admin_element_path(this.elementId)).then((e=>{const s=e.data;if(s.parentElementIds.length){const e=s.parentElementIds.map((e=>`#element_${e}`)).join(", ");document.querySelectorAll(e).forEach((e=>{e.collapsed=!1,e.toggleButton?.setAttribute("title",s.title)}))}this.collapsed=!1,this.toggleButton?.setAttribute("title",s.title),t()})).catch((e=>{F(e.message,"error"),console.error(e),s(e)})).finally((()=>{this.toggleIcon?.classList?.remove("hidden"),e.stop()}))}))}}updateTitle(e){this.setTitle(e),this.dispatchEvent(new CustomEvent("alchemy:element-update-title",{bubbles:!0,detail:{title:e}}))}set published(e){e?this.classList.remove("element-hidden"):this.classList.add("element-hidden")}get published(){return!this.classList.contains("hidden")}get compact(){return null!==this.getAttribute("compact")}get fixed(){return null!==this.getAttribute("fixed")}set collapsed(e){this.classList.toggle("folded",e),this.classList.toggle("expanded",!e),this.toggleIcon&&(this.toggleIcon.name=e?"arrow-left-s":"arrow-down-s")}get collapsed(){return this.classList.contains("folded")}get expanded(){return!this.collapsed}set dirty(e){this.classList.toggle("dirty",e)}get dirty(){return this.classList.contains("dirty")}get header(){return this.querySelector(".element-header")}get body(){return this.querySelector(this.bodySelector)}get bodySelector(){return`#${this.id} > .element-body`}get footer(){return this.querySelector(`#${this.id} > .element-footer`)}get toggleButton(){return this.querySelector(".element-toggle")}get toggleIcon(){return this.toggleButton?.querySelector("alchemy-icon")}get elementErrors(){return this.body.querySelector(".element_errors")}get elementId(){return this.dataset.elementId}get elementName(){return this.dataset.elementName}get hasEditors(){return!!this.body?.querySelector(".element-ingredient-editors")}get hasChildren(){return!!this.querySelector(".nested-elements")}get firstChild(){return this.querySelector("alchemy-element-editor")}get form(){return this.querySelector("form.element-body")}get parentElementEditor(){return this.parentElement?.closest("alchemy-element-editor")}get previewWindow(){return document.getElementById("alchemy_preview_window")}#_=()=>{this.toggle()};#C=e=>{e.target.closest("alchemy-element-editor")===this&&this.toggle()}}customElements.define("alchemy-element-editor",re);const ae=e=>`\n
    \n ${e.icon}${e.text}\n
    \n `;class ce extends HTMLElement{#h=null;connectedCallback(){const e=this.options,t={minimumResultsForSearch:3,dropdownAutoWidth:!0,data:()=>({results:e}),formatResult:(e,t,s)=>{let i;return""===e.id?e.text:(i=""!==s.term?D(e.text,s.term):e.text,((e,t,s)=>{const i=s?`
    ${s}
    `:"";return`\n
    \n ${ae({icon:e,text:t})}\n ${i}\n
    \n `})(e.icon,i,e.hint))},formatSelection:ae,placeholder:this.placeholder};this.#h=$(this.inputField).select2(t)}disconnectedCallback(){this.#h?.select2("destroy"),this.#h=null}get options(){return JSON.parse(this.getAttribute("options"))}get placeholder(){return this.getAttribute("placeholder")}get inputField(){return this.querySelector("input")}}customElements.define("alchemy-element-select",ce);class he extends HTMLElement{#S=!0;#x=null;connectedCallback(){this.toggleButton?.addEventListener("click",this.#C),this.collapseButton?.addEventListener("click",this.#T),window.addEventListener("message",this.#A),document.body.addEventListener("click",this.#F),window.location.hash&&this.focusElementEditor(window.location.hash),this.resize()}disconnectedCallback(){this.toggleButton?.removeEventListener("click",this.#C),this.collapseButton?.removeEventListener("click",this.#T),window.removeEventListener("message",this.#A),document.body.removeEventListener("click",this.#F)}#C=e=>{e.preventDefault(),this.toggle()};#T=()=>{this.collapseAllElements()};#A=e=>{const t=e.data;if("Alchemy.focusElementEditor"==t?.message){const e=document.getElementById(`element_${t.element_id}`);this.show(),e?.focusElement()}};#F=e=>{e.target.closest("alchemy-element-editor")||(this.querySelectorAll("alchemy-element-editor").forEach((e=>{e.classList.remove("selected")})),this.previewWindow?.postMessage({message:"Alchemy.blurElements"}))};collapseAllElements(){this.querySelectorAll("alchemy-element-editor:not([compact]):not([fixed])").forEach((e=>e.collapse()))}toggle(){this.#S?this.hide():this.show()}show(){document.body.classList.add("elements-window-visible"),this.#S=!0,this.toggleButton.closest("sl-tooltip").content=Alchemy.t("Hide elements"),this.toggleButton.querySelector("alchemy-icon").setAttribute("name","menu-unfold"),this.resize()}hide(){document.body.classList.remove("elements-window-visible"),document.body.style.removeProperty("--elements-window-width"),this.#S=!1,this.toggleButton.closest("sl-tooltip").content=Alchemy.t("Show elements"),this.toggleButton.querySelector("alchemy-icon").setAttribute("name","menu-fold")}resize(e){void 0===e&&(e=this.widthFromCookie),e&&(document.body.style.setProperty("--elements-window-width",`${e}px`),document.cookie=`alchemy-elements-window-width=${e}; SameSite=Lax; Path=/;`)}focusElementEditor(e){const t=document.querySelector(e);t instanceof re&&t.focusElement()&&t.focusElementPreview()}get collapseButton(){return this.querySelector("#collapse-all-elements-button")}get toggleButton(){return document.querySelector("#element_window_button")}get previewWindow(){return document.getElementById("alchemy_preview_window")}get turboFrame(){return this.#x||(this.#x=this.closest("turbo-frame")),this.#x}get widthFromCookie(){return document.cookie.split("; ").find((e=>e.startsWith("alchemy-elements-window-width=")))?.split("=")[1]}set isDragged(e){this.turboFrame.style.transitionProperty=e?"none":null,this.turboFrame.style.pointerEvents=e?"none":null}}customElements.define("alchemy-elements-window",he);class de extends HTMLElement{#$=!1;#I=null;#q=null;connectedCallback(){this.addEventListener("mousedown",this),window.addEventListener("mousemove",this),window.addEventListener("mouseup",this)}disconnectedCallback(){this.removeEventListener("mousedown",this),window.removeEventListener("mousemove",this),window.removeEventListener("mouseup",this)}handleEvent(e){switch(e.type){case"mousedown":e.stopPropagation(),this.onMouseDown();break;case"mouseup":this.onMouseUp();break;case"mousemove":this.#$&&this.onDrag(e.pageX)}}onMouseDown(){this.#$=!0,this.elementsWindow.isDragged=!0,this.previewWindow.isDragged=!0,this.classList.add("is-dragged")}onMouseUp(){this.#$=!1,this.elementsWindow.isDragged=!1,this.previewWindow.isDragged=!1,this.classList.remove("is-dragged")}onDrag(e){const t=window.innerWidth-e;this.elementsWindow.resize(t)}get elementsWindow(){return this.#I||(this.#I=document.querySelector("alchemy-elements-window")),this.#I}get previewWindow(){return this.#q||(this.#q=document.getElementById("alchemy_preview_window")),this.#q}}customElements.define("alchemy-elements-window-handle",de);class ue extends HTMLElement{connectedCallback(){this.deleteLink=this.querySelector(".remove_file_link"),this.fileIcon=this.querySelector(".file_icon"),this.fileName=this.querySelector(".file_name"),this.formFieldId=this.deleteLink?.dataset.formFieldId,this.formField=this.querySelector(`#${this.formFieldId}`),this.deleteLink?.addEventListener("click",this)}disconnectedCallback(){this.deleteLink?.removeEventListener("click",this)}handleEvent(e){"click"===e.type&&this.removeFile(),e.stopPropagation()}removeFile(){this.formField.value="",this.fileIcon.innerHTML="",this.fileName.innerHTML="",this.deleteLink?.classList.add("hidden"),this.closest("alchemy-element-editor").setDirty(this.formField)}}customElements.define("alchemy-file-editor",ue);class me extends HTMLElement{#B;#M=null;#z=null;connectedCallback(){this.hotkey&&key(this.hotkey,this.#D),this.#M=this.filterField,this.#M.addEventListener("keyup",this.#H),this.#M.addEventListener("focus",this.#P),this.#z=this.clearButton,this.#z.addEventListener("click",this.#O),key("esc","list_filter",this.#N)}disconnectedCallback(){clearTimeout(this.#B),this.#M?.removeEventListener("keyup",this.#H),this.#M?.removeEventListener("focus",this.#P),this.#M=null,this.#z?.removeEventListener("click",this.#O),this.#z=null,this.hotkey&&key.unbind(this.hotkey),key.unbind("esc","list_filter")}filter(e){""===e&&(this.clearButton.style.visibility="hidden");const t=[],s=new Set,i=e.toLowerCase();this.items.forEach((e=>{const n=e.getAttribute(this.nameAttribute)?.toLowerCase();if(-1!==n.indexOf(i)){t.push(e),s.add(e);let i=e.parentElement?.closest(this.itemsSelector);for(;i;)s.add(i),i=i.parentElement?.closest(this.itemsSelector)}})),this.items.forEach((e=>{e.classList.toggle("hidden",!s.has(e))})),1===t.length&&t[0].scrollIntoView({behavior:"smooth",block:"nearest"})}clear(){this.filterField.value="",this.clearButton.style.visibility="hidden",this.items.forEach((e=>e.classList.remove("hidden")))}get nameAttribute(){return this.getAttribute("name-attribute")||"name"}get clearButton(){return this.querySelector('button[type="button"]')}get filterField(){return this.querySelector('input[type="text"]')}get items(){return document.querySelectorAll(this.itemsSelector)}get itemsSelector(){return this.getAttribute("items-selector")}get debounceTime(){return parseInt(this.getAttribute("debounce-time"))||150}get hotkey(){return this.getAttribute("hotkey")}#D=()=>(this.filterField.focus(),!1);#H=()=>{clearTimeout(this.#B),this.#B=setTimeout((()=>{const e=this.filterField.value;this.clearButton.style.visibility=e?"visible":"hidden",this.filter(e)}),this.debounceTime)};#P=()=>key.setScope("list_filter");#O=e=>{e.preventDefault(),this.clear()};#N=()=>{this.clear(),this.filterField.blur()}}customElements.define("alchemy-list-filter",me);class pe extends HTMLElement{#U=null;handleEvent(e){"click"===e.type&&this.dismiss()}connectedCallback(){if(!this.querySelector(":scope > alchemy-icon")){const e=this.dismissable&&"error"===this.type?'':"";this.insertAdjacentHTML("afterbegin",`${e}`)}(this.dismissable||"error"===this.type)&&this.addEventListener("click",this),this.dismissable&&"error"!==this.type&&(this.#U=setTimeout((()=>{this.dismiss()}),this.dismissDelay))}disconnectedCallback(){null!==this.#U&&(clearTimeout(this.#U),this.#U=null)}dismiss(){this.addEventListener("transitionend",(()=>this.remove())),this.classList.add("dismissed")}get dismissable(){return this.hasAttribute("dismissable")}get icon(){return this.getAttribute("icon")}get type(){return this.getAttribute("type")||"notice"}get dismissDelay(){return parseInt(this.noticesWrapper?.dataset.autoDismissDelay||5e3)}get iconName(){switch(this.icon||this.type){case"warning":case"warn":case"alert":return"alert";case"notice":return"check";case"info":case"hint":return"information";case"error":return"bug";default:return this.type}}get noticesWrapper(){return this.closest("#flash_notices")}}customElements.define("alchemy-message",pe);class ge extends HTMLElement{connectedCallback(){F(this.message,this.getAttribute("type")||"notice"),this.remove()}get message(){return this.getAttribute("message")||this.innerHTML}}customElements.define("alchemy-growl",ge);class ye extends HTMLElement{static get observedAttributes(){return["name","size","icon-style"]}constructor(){super(),this.spriteUrl=document.querySelector('link[rel="preload"][as="image"]').getAttribute("href")}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}render(){const e=this.size?` icon--${this.size}`:"";this.innerHTML=``}set name(e){this.setAttribute("name",e)}get iconName(){return this.getAttribute("name")}get size(){return this.getAttribute("size")}get style(){const e=this.getAttribute("icon-style");switch(e){case"none":return"";case null:return"-line";default:return`-${e}`}}}customElements.define("alchemy-icon",ye);class ve extends HTMLDetailsElement{#R="Alchemy.expanded_ingredient_groups";connectedCallback(){this.addEventListener("toggle",this),this.isInLocalStorage&&(this.open=!0)}disconnectedCallback(){this.removeEventListener("toggle",this)}handleEvent(){let e=this.localStorageItem;this.open?(this.toggleIcon.name="arrow-down-s",this.isInLocalStorage||e.push(this.id)):(this.toggleIcon.name="arrow-left-s",e=e.filter((e=>e!==this.id))),localStorage.setItem(this.#R,JSON.stringify(e))}get isInLocalStorage(){return this.localStorageItem.includes(this.id)}get localStorageItem(){const e=localStorage.getItem(this.#R);if(!e)return[];try{return JSON.parse(e)}catch(e){return console.error(e),[]}}get toggleIcon(){return this.querySelector("alchemy-icon")}}customElements.define("alchemy-ingredient-group",ve,{extends:"details"});class be extends HTMLButtonElement{connectedCallback(){this.addEventListener("click",this),this.classList.add("icon_button"),this.setAttribute("type","button"),this.innerHTML=''}handleEvent(e){new Alchemy.LinkDialog({url:this.linkUrl,title:this.linkTitle,target:this.linkTarget,type:this.linkClass}).open().then((e=>this.setLink(e))),e.preventDefault()}setLink(e){""===e.url?(this.classList.remove("linked"),this.dispatchEvent(new CustomEvent("alchemy:unlink",{bubbles:!0}))):(this.classList.add("linked"),this.dispatchEvent(new CustomEvent("alchemy:link",{bubbles:!0,detail:e})))}get linkUrl(){return this.linkButtons.linkUrlField.value}get linkTitle(){return this.linkButtons.linkTitleField.value}get linkTarget(){return this.linkButtons.linkTargetField.value}get linkClass(){return this.linkButtons.linkClassField.value}get linkButtons(){return this.closest("alchemy-link-buttons")}}customElements.define("alchemy-link-button",be,{extends:"button"});class fe extends HTMLButtonElement{connectedCallback(){this.addEventListener("click",this),this.classList.add("icon_button"),this.setAttribute("type","button"),this.linked=this.linked,this.innerHTML=''}handleEvent(e){this.linked&&(this.linked=!1,this.blur(),this.dispatchEvent(new CustomEvent("alchemy:unlink",{bubbles:!0}))),e.preventDefault()}set linked(e){e?(this.classList.replace("disabled","linked"),this.removeAttribute("tabindex")):(this.classList.replace("linked","disabled"),this.setAttribute("tabindex","-1"))}get linked(){return this.classList.contains("linked")}}customElements.define("alchemy-unlink-button",fe,{extends:"button"});class Ee extends HTMLElement{connectedCallback(){this.addEventListener("alchemy:link",this),this.addEventListener("alchemy:unlink",this)}disconnectedCallback(){this.removeEventListener("alchemy:link",this),this.removeEventListener("alchemy:unlink",this)}handleEvent(e){switch(e.type){case"alchemy:link":this.setLink(e.detail);break;case"alchemy:unlink":this.removeLink()}e.stopPropagation()}setLink(e){this.linkUrlField.value=e.url,this.linkUrlField.dispatchEvent(new Event("change")),this.linkTitleField.value=e.title,this.linkClassField.value=e.type,this.linkTargetField.value=e.target,this.unlinkButton.linked=!0,this.setElementDirty()}removeLink(){this.linkUrlField.value="",this.linkUrlField.dispatchEvent(new Event("change")),this.linkTitleField.value="",this.linkClassField.value="",this.linkTargetField.value="",this.linkButton.classList.remove("linked"),this.unlinkButton.linked=!1,this.setElementDirty()}setElementDirty(){this.elementEditor.setDirty(this)}get linkButton(){return this.querySelector('[is="alchemy-link-button"]')}get unlinkButton(){return this.querySelector('[is="alchemy-unlink-button"]')}get ingredientEditor(){const e=this.dataset.ingredientId;return this.parentElement.closest(`[data-ingredient-id='${e}']`)}get elementEditor(){return this.closest("alchemy-element-editor")}get linkUrlField(){return this.ingredientEditor.querySelector("[data-link-value]")}get linkTitleField(){return this.ingredientEditor.querySelector("[data-link-title]")}get linkTargetField(){return this.ingredientEditor.querySelector("[data-link-target]")}get linkClassField(){return this.ingredientEditor.querySelector("[data-link-class]")}}customElements.define("alchemy-link-buttons",Ee);function we(e){let t=0===e?0:Math.floor(Math.log(e)/Math.log(1024));return t>3&&(t=3),(e/Math.pow(1024,t)).toFixed(2)+" "+["B","kB","MB","GB"][t]}customElements.define("alchemy-node-select",class extends H{_searchQuery(e,t){return{filter:{name_or_page_name_cont:e,...JSON.parse(this.queryParams)},page:t}}_renderResult(e){return this._renderListEntry(e)}_renderListEntry(e,t){const s=e.ancestors.map((e=>e.name)),i='';return`\n
    \n \n
    \n \n ${s.length>0?s.join(i)+i:""}\n \n \n ${this._hightlightTerm(e.name,t)}\n \n
    \n
    \n ${e.url||""}\n
    \n
    \n `}});class ke extends HTMLElement{file=null;request=null;progressEventLoaded=0;progressEventTotal=0;#W=!0;#j=0;#J=void 0;#V="";connectedCallback(){if(this.innerHTML=`\n \n
    \n ${this.file?.name}\n ${this.loadedSize}\n ${this.errorMessage}\n
    \n \n \n \n `,this.querySelector("button").addEventListener("click",(()=>this.cancel())),this.file?.type.includes("image")){const e=new FileReader;e.readAsDataURL(this.file),e.addEventListener("load",(()=>{const t=new Image;t.src=e.result,this.prepend(t)}))}}initialize(e,t){this.file=e,this.request=t,this.progressEventTotal=e?e.size:0,this.status="in-progress",this.#X(),this.#Q()}cancel(){this.finished||(this.status="canceled",this.request?.abort(),this.dispatchCustomEvent("FileUpload.Change"))}dispatchCustomEvent(e){this.dispatchEvent(new CustomEvent(`Alchemy.${e}`,{bubbles:!0}))}#X(){const e=Alchemy.uploader_defaults,t=e.file_size_limit*Math.pow(1024,2);let s;this.file?.size>t&&(s=h("Uploaded bytes exceed file size"));const i=this.file?.type.includes("image")?e.allowed_filetypes.alchemy_pictures:e.allowed_filetypes.alchemy_attachments;i.includes("*")||i.includes(this.file?.type.replace(/^\w+\/(\w+)(\+\w+)?/i,"$1"))||(s=h("File type not allowed")),s&&(this.valid=!1,this.errorMessage=s)}#Q(){this.request&&(this.request.upload.onprogress=e=>{this.progressEvent=e},this.request.onload=()=>{this.request.status<400?(this.status="successful",F(this.responseMessage)):(this.status="failed",this.errorMessage=this.responseMessage),this.dispatchCustomEvent("FileUpload.Change")},this.request.onerror=()=>{this.errorMessage=h("An error occurred during the transaction")})}get active(){return this.valid&&"canceled"!==this.status}get errorMessage(){return this.#V||""}set errorMessage(e){this.#V=e;const t=this.querySelector(".error-message");t&&(t.textContent=e),F(e,"error")}get finished(){return["canceled","successful","failed"].includes(this.status)}get loadedSize(){return`${we(this.progressEventLoaded)} / ${we(this.progressEventTotal)}`}get progressElement(){return this.querySelector("sl-progress-bar")}set progressEvent(e){this.progressEventLoaded=e.loaded,this.progressEventTotal=e.total,this.value=Math.round(e.loaded/e.total*100),this.querySelector(".loaded-size").textContent=this.loadedSize}get responseMessage(){try{return JSON.parse(this.request.responseText).message}catch(e){return`${this.request.status}: ${this.request.statusText}`}}get status(){return this.#J}set status(e){this.#J=e,this.className=e,this.progressElement?.toggleAttribute("indeterminate","upload-finished"===e)}get valid(){return this.#W}set valid(e){this.#W=e,this.classList.toggle("invalid",!e)}get value(){return this.#j}set value(e){this.#j=e,this.progressElement&&(this.progressElement.value=e),100===e&&(this.status="upload-finished"),this.dispatchCustomEvent("FileUpload.Change")}}customElements.define("alchemy-file-upload",ke);class Le extends HTMLElement{fileCount=0;#K=[];#Y=h("Cancel all uploads");#G=null;#S=!1;#Z=()=>this.#ee();connectedCallback(){var e,t;this.innerHTML=(e=this.#Y,t=this.fileCount,`\n \n
    \n \n\n \n \n \n
    \n
    \n
    \n`),this.visible=!0,this.#G=this.querySelector("button"),this.#G.addEventListener("click",(()=>{this.finished?this.onComplete(this.status):this.cancel()})),this.#K.forEach((e=>{this.querySelector(".single-uploads").append(e)})),this.#ee(),this.addEventListener("Alchemy.FileUpload.Change",this.#Z)}disconnectedCallback(){this.removeEventListener("Alchemy.FileUpload.Change",this.#Z)}initialize(e=[]){this.#K=e,this.fileCount=e.length}cancel(){this.#te().forEach((e=>{e.cancel()})),this.#se()}onComplete(e){}#te(){return this.#K.filter((e=>e.active))}#se(){this.#Y=h("Close"),this.#G.ariaLabel=this.#Y,this.#G.parentElement.content=this.#Y}#ie(e){return this.#te().reduce(((t,s)=>s[e]+t),0)}#ee(){const e=this.status;this.className=e,this.progressElement.value=this.totalProgress,this.progressElement.toggleAttribute("indeterminate","upload-finished"===e),this.querySelector(".overall-progress-value > span").textContent=this.overallProgressValue,this.querySelector(".overall-upload-value").textContent=this.overallUploadSize,this.finished?(this.#se(),this.onComplete(e)):this.visible=!0}get finished(){return this.#te().every((e=>e.finished))}get overallUploadSize(){this.#te().filter((e=>e.value>=100)).length;return this.totalProgress,this.#te().length,`${we(this.#ie("progressEventLoaded"))} / ${we(this.#ie("progressEventTotal"))}`}get overallProgressValue(){const e=this.#te().filter((e=>e.value>=100)).length;return`${this.totalProgress}% (${e} / ${this.#te().length})`}get progressElement(){return this.querySelector("sl-progress-bar")}get status(){const e=this.#te().map((e=>e.className));return e.includes("failed")?"failed":0===e.length?"canceled":e.every((t=>t===e[0]))?e[0]:"in-progress"}get totalProgress(){const e=this.#te().reduce(((e,t)=>e+t.file.size),0);let t=Math.ceil(this.#te().reduce(((t,s)=>{const i=s.file.size/e;return s.value*i+t}),0));return t>100&&(t=100),t}get visible(){return this.#S}set visible(e){this.classList.toggle("visible",e),this.#S=e}}customElements.define("alchemy-upload-progress",Le);class _e extends HTMLElement{#ne=null;#le=!1;connectedCallback(){this.fileInput.addEventListener("change",this.#oe),this.dropzone&&this.#re(),this.addEventListener("Alchemy.upload.successful",this)}disconnectedCallback(){this.fileInput?.removeEventListener("change",this.#oe),this.#ne&&(this.#ne.removeEventListener("dragleave",this.#ae),this.#ne.removeEventListener("drop",this.#ce),this.#ne.removeEventListener("dragover",this.#he),this.#ne=null)}handleEvent(e){if("Alchemy.upload.successful"===e.type)this.#de()}#oe=e=>{this.uploadFiles(Array.from(e.target.files))};#ue=e=>{this.#le!==e&&(this.#le=e,this.#ne.classList.toggle("dragover"))};#ae=()=>this.#ue(!1);#ce=async e=>{e.preventDefault(),this.#ue(!1);const t=[...e.dataTransfer.items].map((e=>e.getAsFile()));this.uploadFiles(t)};#he=e=>{e.preventDefault(),this.#ue(!0)};#de(){setTimeout((()=>{const e=this.redirectUrl,t=this.closest("turbo-frame");this.uploadProgress.visible=!1,e&&(t?(t.setAttribute("src",e),t.reload()):Turbo.visit(e))}),750)}#re(){this.#ne=document.querySelector(this.dropzone),this.#ne&&(this.#ne.addEventListener("dragleave",this.#ae),this.#ne.addEventListener("drop",this.#ce),this.#ne.addEventListener("dragover",this.#he))}uploadFiles(e){let t=0;const s=e.map((e=>{const s=new XMLHttpRequest,i=new ke;return i.initialize(e,s),Alchemy.uploader_defaults.upload_limit-1{this.dispatchEvent(new CustomEvent(`Alchemy.upload.${e}`,{bubbles:!0}))},document.body.append(this.uploadProgress)}get dropzone(){return this.getAttribute("dropzone")}get fileInput(){return this.querySelector("input[type='file']")}get redirectUrl(){return this.getAttribute("redirect-url")}}customElements.define("alchemy-uploader",_e);class Ce extends HTMLElement{connectedCallback(){this.innerHTML=`\n \n
    \n ${this.getAttribute("text")??""}\n
    \n `}set show(e){this.classList.toggle("visible",e)}}customElements.define("alchemy-overlay",Ce);const Se="BUTTON";class xe extends HTMLElement{connectedCallback(){this.pageId=this.getAttribute("page-id"),this.folded=this.hasAttribute("folded"),this.folderButton?.addEventListener("click",this)}disconnectedCallback(){this.folderButton?.removeEventListener("click",this)}async handleEvent(e){"click"===e.type&&await this.handleFolderClick(e)}async handleFolderClick(e){e.preventDefault(),e.stopPropagation();const t=e.currentTarget;t.innerHTML="";const s=new y("small");s.spin(t);try{await ee(Alchemy.routes.fold_admin_page_path(this.pageId),null,"text/vnd.turbo-stream.html"),this.folded=!this.folded,this.toggleAttribute("folded",this.folded),this.toggleChildren(),this.updateFolderIcon()}catch(e){F(e.message||e,"error"),this.updateFolderIcon()}finally{s.stop()}}toggleChildren(){const e=this.querySelector(`#page_${this.pageId}_children`);e&&e.classList.toggle("hidden",this.folded)}updateFolderIcon(){if(this.folderButton){const e=this.folded?"arrow-right-s":"arrow-down-s";this.folderButton.innerHTML=``}}updateFolderButton(){const e=this.querySelector(".page_folder");if(!e)return;const t=this.hasChildren||this.folded;if(t&&"SPAN"===e.tagName){const t=this.folded?"arrow-right-s":"arrow-down-s";e.outerHTML=``,this.folderButton?.addEventListener("click",this)}else t||e.tagName!==Se?t&&e.tagName===Se&&this.updateFolderIcon():e.outerHTML=''}get hasChildren(){const e=this.querySelector(`#page_${this.pageId}_children`);return!!e&&e.querySelectorAll(":scope > alchemy-page-node").length>0}get folderButton(){return this.querySelector("button.page_folder")}}customElements.define("alchemy-page-node",xe);class Te extends HTMLElement{connectedCallback(){this.publicField?.addEventListener("click",this.#ge)}disconnectedCallback(){this.publicField?.removeEventListener("click",this.#ge)}#ge=e=>{const t=e.target,s=new Date;t.checked?(this.publicationDateFields.classList.remove("hidden"),this.publicOnPicker.flatpickr.setDate(s)):(this.publicationDateFields.classList.add("hidden"),this.publicOnPicker.flatpickr.clear()),this.publicUntilPicker.flatpickr?.clear()};get publicField(){return this.querySelector("#page_public")}get publicOnPicker(){return this.querySelector("alchemy-datepicker:has(#page_public_on)")}get publicUntilPicker(){return this.querySelector("alchemy-datepicker:has(#page_public_until)")}get publicationDateFields(){return this.querySelector(".page-publication-date-fields")}}customElements.define("alchemy-page-publication-fields",Te);customElements.define("alchemy-page-select",class extends H{get pageId(){return this.selection?JSON.parse(this.selection).id:void 0}_searchQuery(e,t){return{q:{name_cont:e,...JSON.parse(this.queryParams)},page:t}}_parseResponse(e){const t=e.meta;return{results:e.pages,more:t.page*t.per_page\n
    \n \n ${this._hightlightTerm(e.name,t)}\n ${e.site.name}\n
    \n
    \n ${e.url_path}\n ${e.language_code}\n
    \n
    \n `}});class Ae extends HTMLElement{connectedCallback(){this.addEventListener("change",this)}disconnectedCallback(){this.removeEventListener("change",this)}handleEvent(e){if("change"===e.type)this.onChange()}onChange(){const e=new URL(this.getAttribute("url")),t=this.querySelector("select");e.searchParams.set("language_id",t.value),Turbo.visit(e,{frame:"picture_descriptions"})}}customElements.define("alchemy-picture-description-select",Ae);class Fe extends HTMLElement{connectedCallback(){this.cropFromField=this.querySelector("[data-crop-from]"),this.cropSizeField=this.querySelector("[data-crop-size]"),this.pictureIdField=this.querySelector("[data-picture-id]"),this.targetSizeField=this.querySelector("[data-target-size]"),this.imageCropperField=this.querySelector("[data-image-cropper]"),this.image=this.querySelector("img"),this.pictureThumbnail=this.querySelector("alchemy-picture-thumbnail"),this.deleteButton=this.querySelector(".picture_tool.delete"),this.cropLink=this.querySelector(".crop_link"),this.targetSize=this.targetSizeField.dataset.targetSize,this.pictureId=this.pictureIdField.value,this.update=function(e,t){let s;return function(...i){const n=this;clearTimeout(s),s=setTimeout((()=>e.apply(n,i)),t)}}((()=>{this.updateImage(),this.updateCropLink()}),125),this.deleteButton?.addEventListener("click",this.removeImage),this.observer=new MutationObserver(this.mutationCallback),this.observer.observe(this.cropFromField,{attributes:!0}),this.observer.observe(this.cropSizeField,{attributes:!0}),this.observer.observe(this.pictureIdField,{attributes:!0})}disconnectedCallback(){this.observer.disconnect()}mutationCallback=e=>{for(const t of e)"pictureId"in t.target.dataset&&(this.cropFromField.value="",this.cropSizeField.value="",this.pictureId=t.target.value),this.update()};updateImage(){this.pictureId&&(this.pictureThumbnail.loading=!0,Z(Alchemy.routes.url_admin_picture_path(this.pictureId),{crop:this.imageCropperEnabled,crop_from:this.cropFrom,crop_size:this.cropSize,flatten:!0,size:"160x120"}).then((({data:e})=>{this.pictureThumbnail.src=e.url,this.pictureThumbnail.image.alt=e.alt,this.pictureThumbnail.image.title=e.title,this.setElementDirty()})).catch((e=>{console.error(e.message||e),F(e.message||e,"error")})))}removeImage=()=>{this.pictureThumbnail.innerHTML='',this.pictureIdField.value="",this.image=null,this.cropLink.classList.add("disabled"),this.setElementDirty()};setElementDirty(){this.closest(".element-editor").setDirty(this)}updateCropLink(){this.pictureId&&this.imageCropperEnabled&&(this.cropLink.classList.remove("disabled"),this.cropLink.href.match(/(picture_id=)\d+/)?this.cropLink.href=this.cropLink.href.replace(/(picture_id=)\d+/,"$1"+this.pictureId):this.cropLink.href=this.cropLink.href+`&picture_id=${this.pictureId}`)}get cropFrom(){return""===this.cropFromField.value?this.defaultCropFrom.join("x"):this.cropFromField.value}get cropSize(){return""===this.cropSizeField.value?this.defaultCropSize.join("x"):this.cropSizeField.value}get defaultCropSize(){if(!this.imageCropperEnabled)return[];const e=this.targetSize.split("x").map((e=>parseInt(e))),t=(s=e[0]/this.imageFileWidth,i=e[1]/this.imageFileHeight,s>=i?s:i);var s,i;return[Math.round(e[0]/t),Math.round(e[1]/t)]}get defaultCropFrom(){if(!this.imageCropperEnabled)return[];const e=this.defaultCropSize;return[Math.round((this.imageFileWidth-e[0])/2),Math.round((this.imageFileHeight-e[1])/2)]}get imageFileWidth(){return parseInt(this.pictureIdField.dataset.imageFileWidth)}get imageFileHeight(){return parseInt(this.pictureIdField.dataset.imageFileHeight)}get imageCropperEnabled(){return"true"===this.targetSizeField.dataset.imageCropper}}customElements.define("alchemy-picture-editor",Fe);class $e extends HTMLElement{constructor(){super(),this.spinner=new y("small")}handleEvent(e){switch(e.type){case"load":this.#ye();break;case"error":this.#ve(e)}}connectedCallback(){this.classList.add("thumbnail_background"),this.src&&!this.image&&this.start(),this.#be()}disconnectedCallback(){this.image?.removeEventListener("load",this),this.image?.removeEventListener("error",this),this.stop()}createImage(e=this.src,t=this.name){this.image=new Image,this.image.src=e,t&&(this.image.alt=t)}start(e){this.createImage(e),this.image.addEventListener("load",this),this.image.addEventListener("error",this),this.load()}load(){this.image?.complete||(this.setAttribute("loading","loading"),this.innerHTML="",this.spinner.spin(this))}stop(){this.classList.remove("loading"),this.spinner.stop()}#ye(){this.spinner.stop(),this.removeAttribute("loading")}#ve(e){const t=`Could not load ${this.image.src}`,s=this.closest(".ingredient-editor");this.spinner.stop(),this.innerHTML=`\n \n \n \n `,console.error(t,e)}#be(){this.image?.complete?this.replaceChildren(this.image):this.image&&this.append(this.image)}set loading(e){e?this.load():this.stop()}set src(e){this.start(e),this.#be()}get name(){return this.getAttribute("name")}get src(){return this.getAttribute("src")}}customElements.define("alchemy-picture-thumbnail",$e);class Ie extends HTMLElement{connectedCallback(){this.addEventListener("submit",this),document.addEventListener("alchemy:page-dirty",this)}disconnectedCallback(){this.removeEventListener("submit",this),document.removeEventListener("alchemy:page-dirty",this)}handleEvent(e){switch(e.type){case"alchemy:page-dirty":this.markDirty(e.detail);break;case"submit":this.button.loading=!0}}markDirty(e){this.button.variant="primary",this.button.disabled=!1,this.tooltip.content=e.tooltip}get button(){return this.querySelector("sl-button")}get tooltip(){return this.querySelector("sl-tooltip")}}customElements.define("alchemy-publish-page-button",Ie);class qe extends HTMLSelectElement{#fe;connectedCallback(){this.classList.add("alchemy_selectbox"),this.#fe=$(this).select2({minimumResultsForSearch:5,dropdownAutoWidth:!0,allowClear:!!this.allowClear}),this.allowClear||this.multiple||this.#fe.prev(".select2-container").find(".select2-search-choice-close").remove()}disconnectedCallback(){this.#fe?.select2("destroy"),this.#fe=null}enable(){this.removeAttribute("disabled"),this.#Ee()}disable(){this.setAttribute("disabled","disabled"),this.#Ee()}setOptions(e,t=void 0){let s=this.value;this.innerHTML="",t&&this.add(new Option(t,"")),e.forEach((e=>{this.add(new Option(e.text,e.id,!1,e.id===s))})),this.#Ee()}#Ee(){this.#fe.trigger("change")}get allowClear(){return this.dataset.hasOwnProperty("allowClear")||this.multiple}}customElements.define("alchemy-select",qe,{extends:"select"});class Be extends HTMLElement{connectedCallback(){this.searchInput=document.querySelector(".search_input_field"),this.clearButton=document.querySelector("#search_field_clear"),this.resultCounter=document.querySelector("#page_filter_result"),this.setupSearch(),requestAnimationFrame((()=>{this.setupSortables()})),this.observer=new MutationObserver((e=>{e.forEach((e=>{e.addedNodes.forEach((e=>{e.nodeType===Node.ELEMENT_NODE&&(e.classList?.contains("children")&&this.setupSortable(e),e.querySelectorAll(".children").forEach((e=>this.setupSortable(e))))}))}))})),this.observer.observe(this,{childList:!0,subtree:!0})}disconnectedCallback(){this.teardownSearch(),this.observer?.disconnect()}setupSearch(){this.searchInput?.addEventListener("input",this),this.clearButton?.addEventListener("click",this)}teardownSearch(){this.searchInput?.removeEventListener("input",this),this.clearButton?.removeEventListener("click",this)}handleEvent(e){"input"===e.type&&e.target===this.searchInput?this.handleSearch(e):"click"===e.type&&e.target===this.clearButton&&this.handleClearSearch(e)}handleSearch(e){const t=e.target.value.toLowerCase().trim();""!==t?this.filterPages(t):this.clearFilter()}filterPages(e){const t=this.querySelectorAll(".sitemap_page");let s=0,i=null;t.forEach((t=>{(t.getAttribute("name")||"").toLowerCase().includes(e)?(t.classList.add("highlight"),t.classList.remove("no-match"),s++,i||(i=t)):(t.classList.remove("highlight"),t.classList.add("no-match"))})),1===s?(this.resultCounter.textContent=`1 ${h("page_found")}`,this.resultCounter.style.display="block"):s>1?(this.resultCounter.textContent=`${s} ${h("pages_found")}`,this.resultCounter.style.display="block"):this.resultCounter.style.display="none",i&&i.scrollIntoView({behavior:"smooth",block:"center"})}clearFilter(){this.querySelectorAll(".sitemap_page").forEach((e=>{e.classList.remove("highlight","no-match")})),this.resultCounter.style.display="none"}handleClearSearch(e){e.preventDefault(),this.searchInput.value="",this.clearFilter()}setupSortable(e){new i(e,{group:"pages",animation:150,fallbackOnBody:!0,swapThreshold:.65,handle:".page-icon.handle",draggable:"alchemy-page-node",onEnd:e=>this.handleSort(e)})}setupSortables(){this.querySelectorAll(".children").forEach((e=>this.setupSortable(e)))}async handleSort(e){if(e.from===e.to&&e.oldIndex===e.newIndex)return;const t=e.item,s=t.pageId,i=Alchemy.routes.move_admin_page_path(s),n={target_parent_id:e.to.dataset.parentId,new_position:e.newIndex};C(!0);try{const l=await ee(i,n),o=await l.data,r=t.querySelector(`#page_${s}`);if(r){const e=r.querySelector(".sitemap_url");e&&o.url_path&&(e.textContent=o.url_path)}this.updateFolderIcons(e.from,e.to),F(h("Successfully moved page"))}catch(e){F(e.message||e,"error"),window.location.reload()}finally{C(!1)}}updateFolderIcons(e,t){const s=e.closest("alchemy-page-node");if(s?.updateFolderButton(),e!==t){const e=t.closest("alchemy-page-node");e?.updateFolderButton()}}}customElements.define("alchemy-sitemap",Be);const Me={draggable:".element-editor",handle:".element-handle.draggable",ghostClass:"dragged",animation:150,swapThreshold:.65,easing:"cubic-bezier(1, 0, 0, 1)"};function ze(e){const t=e.item.dataset.elementName;document.querySelectorAll(`[data-droppable-elements~="${t}"]`).forEach((e=>e.classList.add("droppable-elements")))}function De(e){const t=e.item,s=e.to.parentElement.closest(".element-editor"),i={element_id:t.dataset.elementId,position:e.newIndex+1};s&&(i.parent_element_id=s.dataset.elementId),e.target===e.to&&te(Alchemy.routes.order_admin_elements_path,i).then((e=>{const s=e.data;F(s.message),s.pageHasUnpublishedChanges&&oe(s),B(),t.updateTitle(s.preview_text)}))}function He(){document.querySelectorAll("[data-droppable-elements]").forEach((e=>e.classList.remove("droppable-elements")))}class Pe extends HTMLElement{connectedCallback(){const e={name:this.dataset.elementName,put:(e,t,s)=>e.el.dataset.droppableElements.split(" ").includes(s.dataset.elementName)};new i(this,{...Me,onStart:ze,onSort:De,onEnd:He,group:e})}}customElements.define("alchemy-sortable-elements",Pe);class Oe extends HTMLElement{connectedCallback(){this.className=`spinner spinner--${this.size}`,this.innerHTML=`\n \n \n \n \n \n `}get size(){return this.getAttribute("size")||"medium"}get color(){return this.getAttribute("color")||"currentColor"}}customElements.define("alchemy-spinner",Oe);class Ne extends HTMLElement{#h=null;async connectedCallback(){await d(),this.isConnected&&(this.classList.add("autocomplete_tag_list"),this.#h=$(this.input).select2(this.select2Config))}disconnectedCallback(){this.#h?.select2("destroy"),this.#h=null}get input(){return this.getElementsByTagName("input")[0]}get select2Config(){return{tags:!0,tokenSeparators:[","],openOnEnter:!1,minimumInputLength:1,createSearchChoice:this.#we,ajax:{url:this.getAttribute("url"),dataType:"json",data:e=>({term:e}),results:e=>({results:e})},initSelection:this.#ke}}#we(e,t){if(0===$(t).filter((function(){return 0===this.text.localeCompare(e)})).length)return{id:e,text:e}}#ke(e,t){const s=[];$(e.val().split(",")).each((function(){s.push({id:this.trim(),text:this})})),t(s)}}customElements.define("alchemy-tags-autocomplete",Ne);const Ue="alchemy-dark",Re="alchemy";class We extends HTMLElement{#Le=null;connectedCallback(){this.className="tinymce_container",this.querySelector(":scope > alchemy-spinner")||this.insertAdjacentHTML("beforeend",''),this.style.minHeight=`${this.minHeight}px`,this.editor.style.display="none";const e={root:document.getElementById("element_area"),rootMargin:"0px",threshold:[.05]};this.tinymceIntersectionObserver=new IntersectionObserver(((e,t)=>{e.forEach((e=>{e.intersectionRatio>0&&(this._initTinymceEditor(),t.unobserve(e.target))}))}),e),this.tinymceIntersectionObserver.observe(this),this._setupThemeChangeListener()}disconnectedCallback(){this.tinymceIntersectionObserver?.disconnect(),this._removeThemeChangeListener(),tinymce.get(this.editorId)?.remove(this.editorId)}_initTinymceEditor(){tinymce.init(this.configuration).then((e=>{e.forEach((e=>this._setupEditor(e)))}))}_setupEditor(e){e.show();const t=this.getElementsByTagName("alchemy-spinner")[0];t&&t.remove(),this.elementEditor&&(e.on("dirty",(e=>{this.elementEditor.setDirty(e.target.editorContainer)})),e.on("click",(()=>this.elementEditor.onClickElement(!1))))}_setupThemeChangeListener(){this.darkModeMediaQuery=window.matchMedia("(prefers-color-scheme: dark)"),this.themeChangeHandler=e=>this._handleThemeChange(e),this.darkModeMediaQuery.addEventListener("change",this.themeChangeHandler)}_removeThemeChangeListener(){this.darkModeMediaQuery&&this.themeChangeHandler&&this.darkModeMediaQuery.removeEventListener("change",this.themeChangeHandler)}_handleThemeChange(e){const t=tinymce.get(this.editorId);if(t){const s=e.matches?Ue:Re,i=e.matches?Ue:Re;t.remove(),tinymce.init({content_css:i,...this.configuration,skin:s}).then((e=>{e.forEach((e=>this._setupEditor(e)))}))}}get configuration(){const e={};this.getAttributeNames().forEach((t=>{if(!["class","id","is","name","style"].includes(t)){const s=this.getAttribute(t),i=t.replaceAll("-","_");if(s===t||""===s)e[i]=!0;else try{e[i]=JSON.parse(s)}catch(t){e[i]=s}}}));const t={content_css:this.preferredTheme,...Alchemy.TinymceDefaults,...e,language:c(),selector:`#${this.editorId}`,skin:this.preferredTheme};return t.height=t.min_height,t}get preferredTheme(){return window.matchMedia("(prefers-color-scheme: dark)").matches?Ue:Re}get editorId(){return this.editor.id}get editor(){return this.getElementsByTagName("textarea")[0]}get elementEditor(){return document.getElementById(this.editorId).closest("alchemy-element-editor")}get minHeight(){return this.#Le||this.configuration.min_height}set minHeight(e){this.#Le=e}}customElements.define("alchemy-tinymce",We);class je extends HTMLElement{async connectedCallback(){const e=new y("small");e.spin(this);try{const e=await fetch(this.url,{credentials:"include"}),t=await e.json();e.ok?this.showStatus(t):this.showError(e)}catch(e){this.showError(e)}finally{e.stop()}}get url(){return this.getAttribute("url")}showStatus(e){"true"==e.status?this.querySelector(".update_available").classList.remove("hidden"):this.querySelector(".up_to_date").classList.remove("hidden")}showError(e){this.querySelector(".error").classList.remove("hidden"),console.error("[alchemy] Error fetching update status",e)}}var Je;customElements.define("alchemy-update-check",je),Je=Handlebars.template,(Handlebars.templates=Handlebars.templates||{})["node_folder.hbs"]=Je({0:function(e,t,s,i,n){return"right"},1:function(e,t,s,i,n){return"down"},compiler:[8,">= 4.3.0"],main:function(e,t,s,i,n){var l,o=e.lambda,r=e.escapeExpression,a=e.lookupProperty||function(e,t){if(Object.prototype.hasOwnProperty.call(e,t))return e[t]};return'\n \n\n'},useData:!0}),n("tooltip.show",{keyframes:[{transform:"translateY(10px)",opacity:"0"},{transform:"translateY(0)",opacity:"1"}],options:{duration:100}}),n("tooltip.hide",{keyframes:[{transform:"translateY(0)",opacity:"1"},{transform:"translateY(10px)",opacity:"0"}],options:{duration:100}}),n("dialog.show",{keyframes:[{transform:"scale(0.98)",opacity:"0"},{transform:"scale(1)",opacity:"1"}],options:{duration:150}}),n("dialog.hide",{keyframes:[{transform:"scale(1)",opacity:"1"},{transform:"scale(0.98)",opacity:"0"}],options:{duration:150}});const Ve=document.querySelector('link[rel="preload"][as="image"]').getAttribute("href"),Xe={"x-lg":"close",caret:"arrow-down-s"},Qe={resolver:e=>`${Ve}#ri-${Xe[e]||e}-line`,mutator:e=>{e.setAttribute("fill","currentColor"),e.setAttribute("viewBox","0 0 24 24")},spriteSheet:!0};function Ke(e,t,s,i){document.querySelectorAll(t).forEach((t=>{t.addEventListener(e,(e=>{const n=Array.from(t.querySelectorAll(s));let l=e.target;for(;l!==t;){if(n.includes(l))return void i.call(l,e);l=l.parentElement}}))}))}l("default",Qe),l("system",Qe);class Ye{#_e=!1;#Ce=null;#Se=null;#xe=null;constructor(e,t){this.image=e,this.defaultBox=t.default_box,this.aspectRatio=t.ratio,this.#Se=document.getElementById(t.crop_from_form_field_id),this.#xe=document.getElementById(t.crop_size_form_field_id),this.elementId=t.element_id,this.dialog=Alchemy.currentDialog(),this.dialog&&(this.dialog.options.closed=()=>this.destroy(),this.bind()),this.init()}get cropperOptions(){return{aspectRatio:this.aspectRatio,viewMode:1,zoomable:!1,checkCrossOrigin:!1,checkOrientation:!1,data:this.box,cropend:()=>{const e=this.#Ce.getData(!0);this.update(e)}}}get cropFrom(){if(this.#Se?.value)return this.#Se.value.split("x").map((e=>parseInt(e)))}get cropSize(){if(this.#xe?.value)return this.#xe.value.split("x").map((e=>parseInt(e)))}get box(){return this.cropFrom&&this.cropSize?{x:this.cropFrom[0],y:this.cropFrom[1],width:this.cropSize[0],height:this.cropSize[1]}:this.defaultBoxSize}get defaultBoxSize(){return{x:this.defaultBox[0],y:this.defaultBox[1],width:this.defaultBox[2],height:this.defaultBox[3]}}init(){this.#_e||(this.#Ce=new o(this.image,this.cropperOptions),this.#_e=!0)}update(e){this.#Se.value=`${e.x}x${e.y}`,this.#Se.dispatchEvent(new Event("change")),this.#xe.value=`${e.width}x${e.height}`,this.#xe.dispatchEvent(new Event("change"))}reset(){this.#Ce.setData(this.defaultBoxSize),this.update(this.defaultBoxSize)}destroy(){return this.#Ce&&this.#Ce.destroy(),this.#_e=!1,!0}bind(){this.dialog.dialog_body.find('button[type="submit"]').on("click",(()=>(document.querySelector(`[data-element-id='${this.elementId}']`).setDirty(),this.dialog.close(),!1))),this.dialog.dialog_body.find('button[type="reset"]').on("click",(()=>(this.reset(),!1)))}}class Ge extends f{constructor(e,t={}){super(e,t)}init(){$(".zoomed-picture-background").on("click",(e=>{if(e.stopPropagation(),"IMG"!==e.target.nodeName)return this.close(),!1})),$(".picture-overlay-handle").on("click",(e=>(this.dialog.toggleClass("hide-form"),!1))),this.$previous=$(".previous-picture"),this.$next=$(".next-picture"),this.#Te(),super.init()}previous(){null!=this.$previous[0]&&this.$previous[0].click()}next(){null!=this.$next[0]&&this.$next[0].click()}build(){this.dialog_container=$('
    '),this.dialog=$('
    '),this.dialog_body=$('
    '),this.close_button=$('\n \n '),this.dialog.append(this.close_button),this.dialog.append(this.dialog_body),this.dialog_container.append(this.dialog),this.overlay=$('
    '),this.$body.append(this.overlay),this.$body.append(this.dialog_container)}#Te(){this.$document.keydown((e=>{if("INPUT"===e.target.nodeName||"TEXTAREA"===e.target.nodeName)return!0;switch(e.which){case 37:return this.previous(),!1;case 39:return this.next(),!1;default:return!0}}))}}function Ze(){return document.querySelectorAll("#picture_archive input:checked")}function et(){const e=document.querySelector("#select_all_pictures"),t=document.querySelector(".selected_item_tools");Ke("click",".toolbar_buttons","a#select_all_pictures",(s=>{s.preventDefault(),e.classList.toggle("active");const i=e.classList.contains("active");!function(e){document.querySelectorAll(".picture_tool.select input[type='checkbox']").forEach((t=>{t.checked=e,t.closest(".picture_thumbnail").classList.toggle("active",e)}))}(i),t.classList.toggle("hidden",!i)})),Ke("change",".picture_tool.select","input",(e=>{t.classList.toggle("hidden",0===Ze().length);const s=e.target.parentElement.classList,i=e.target.checked;s.toggle("visible",i)})),Ke("click",".selected_item_tools","a#edit_multiple_pictures",(e=>{e.preventDefault();k(function(e){const t=new URL(e);return Ze().forEach((e=>t.searchParams.append(e.name,e.value))),t.toString()}(e.target.href),{title:e.target.title,size:"400x295"})}))}function tt(){document.querySelectorAll("li.menu-item").forEach((e=>{const t=e.querySelector(".nodes_tree-left_images"),s=e.querySelector(".children"),i={folded:"true"===e.dataset.folded,id:e.dataset.id,type:e.dataset.type};s.children.length>0||i.folded?t.innerHTML=Handlebars.templates["node_folder.hbs"]({node:i}):t.innerHTML=" "}))}function st(e){ee(Alchemy.routes[e.item.dataset.type].move_api_path(e.item.dataset.id),{target_parent_id:e.to.dataset.recordId,new_position:e.newIndex}).then((()=>{F(Alchemy.t("Successfully moved menu item")),tt()})).catch((e=>{F(e.message||e,"error")}))}function it(){Ke("click",".nodes_tree",".node_folder",(function(){const e=this.dataset.recordId,t=this.closest("li.menu-item"),s=Alchemy.routes[this.dataset.recordType].toggle_folded_api_path(e),i=t.querySelector(".children");ee(s).then((()=>{i.classList.toggle("folded"),t.dataset.folded="true"==t.dataset.folded?"false":"true",tt()})).catch((e=>{F(e.message||e)}))})),tt(),document.querySelectorAll(".nodes_tree ul.children").forEach((e=>{new i(e,{group:"nodes",animation:150,fallbackOnBody:!0,swapThreshold:.65,handle:".node_name",invertSwap:!0,onEnd:st})}))}void 0===window.Alchemy&&(window.Alchemy={}),Object.assign(Alchemy,{closeCurrentDialog:w,currentDialog:E,...x,t:h,FixedElements:A,growl:F,LinkDialog:class extends f{#Ae;constructor(e){const t=new URL(Alchemy.routes.link_admin_pages_path,window.location),s={url:e.url,selected_tab:e.type,link_title:e.title,link_target:e.target};Object.keys(s).forEach((e=>{s[e]&&t.searchParams.set(e,s[e])})),super(t.href,{size:"600x320",title:h("Link")})}replace(e){super.replace(e),this.#a()}open(){return super.open(),new Promise((e=>this.#Ae=e))}#a(){const e=document.querySelector('[data-link-form-type="internal"]'),t=document.querySelector('[data-link-form-type="file"] alchemy-attachment-select');e.addEventListener("Alchemy.RemoteSelect.Change",(e=>{this.#Fe(e.detail.added)})),t.addEventListener("Alchemy.RemoteSelect.Change",(e=>{const t=e.detail.added;document.getElementById("file_link").value=t?t.url:""})),document.querySelectorAll("[data-link-form-type]").forEach((e=>{e.addEventListener("submit",(e=>{e.preventDefault(),this.#$e(e.target.dataset.linkFormType)}))}))}#Fe(e=null){const t=document.getElementById("internal_link"),s=document.querySelector('[data-link-form-type="internal"] alchemy-dom-id-api-select');t.value=e?e.url_path:"",s.page=e?e.id:void 0}#$e(e){const t=document.getElementById("element_anchor");let s=document.getElementById(`${e}_link`).value;if("internal"===e&&""!==t.value)s=s.replace(I,"")+t.value;else if("external"===e&&!s.match(Alchemy.link_url_regexp))return void this.#Ie();this.#Ae({url:s.trim(),title:document.getElementById(`${e}_link_title`).value,target:document.getElementById(`${e}_link_target`)?.value,type:e}),this.close()}#Ie(){const e=document.getElementById("errors");e.querySelector("ul").innerHTML=`
  • ${Alchemy.t("url_validation_failed")}
  • `,e.style.display="block"}},pleaseWaitOverlay:C,Spinner:y,reloadPreview:B}),t.start(),e.config.forms.confirm=_,document.addEventListener("turbo:load",(function(){document.documentElement.classList.remove("no-js"),p(),document.querySelectorAll(".please_wait").forEach((e=>{e.addEventListener("click",C)})),document.querySelectorAll("a.button").forEach((e=>{e.setAttribute("tabindex",0)})),key.filter=function(e){let t=(e.target||e.srcElement).tagName;return key.isPressed("esc")||!("INPUT"===t||"SELECT"===t||"TEXTAREA"===t)}}));export{Ye as ImageCropper,Ge as ImageOverlay,it as NodeTree,H as RemoteSelect,Ke as on,et as pictureSelector}; //# sourceMappingURL=alchemy_admin.min.js.map diff --git a/app/assets/builds/alchemy/alchemy_admin.min.js.map b/app/assets/builds/alchemy/alchemy_admin.min.js.map index 972bea3398..9a0f635ee0 100644 --- a/app/assets/builds/alchemy/alchemy_admin.min.js.map +++ b/app/assets/builds/alchemy/alchemy_admin.min.js.map @@ -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/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 ``\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 = $(`\n

    ${error_header}

    \n

    ${error_body}

    \n
    `)\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 = $('
    ')\n this.dialog = $('
    ')\n this.dialog_body = $('
    ')\n this.dialog_header = $('
    ')\n this.dialog_title = $('
    ')\n this.close_button = $(\n ''\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 = $('
    ')\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 \n ${this.message}\n \n \n \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} 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 ``\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 = `${label}`\n const panel = ``\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 \n ${message}\n \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}\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 `
  • ${Alchemy.t(\"url_validation_failed\")}
  • `\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 = ``\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 { setupSelectLocale } from \"alchemy_admin/i18n\"\n\nexport function hightlightTerm(name, term) {\n return name.replace(new RegExp(term, \"gi\"), (match) => `${match}`)\n}\n\nexport class RemoteSelect extends HTMLElement {\n #select2 = null\n\n async connectedCallback() {\n await setupSelectLocale()\n // Bail out if the element was disconnected while the locale was loading.\n // Otherwise Select2 would leak onto a detached input.\n if (!this.isConnected) return\n\n this.input.classList.add(\"alchemy_selectbox\")\n\n this.#select2 = $(this.input)\n .select2(this.select2Config)\n .on(\"select2-open\", this.#onOpen)\n .on(\"change\", this.#onChange)\n }\n\n disconnectedCallback() {\n if (this.#select2) {\n this.#select2.off(\"select2-open\", this.#onOpen)\n this.#select2.off(\"change\", this.#onChange)\n this.#select2.select2(\"destroy\")\n this.#select2 = null\n }\n }\n\n #onOpen = (evt) => this.onOpen(evt)\n #onChange = (evt) => this.onChange(evt)\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 /**\n * Dispatches a custom event with given name, namespaced under `Alchemy.`.\n * Subclasses may call this to emit their own events.\n * @param {string} name The name of the custom event\n * @param {object} detail Optional event details\n */\n dispatchCustomEvent(name, detail = {}) {\n this.dispatchEvent(\n new CustomEvent(`Alchemy.${name}`, { bubbles: true, detail })\n )\n }\n\n get allowClear() {\n return this.hasAttribute(\"allow-clear\")\n }\n\n get selection() {\n return this.getAttribute(\"selection\")\n }\n\n get placeholder() {\n return this.getAttribute(\"placeholder\") ?? \"\"\n }\n\n get queryParams() {\n return this.getAttribute(\"query-params\") ?? \"{}\"\n }\n\n get url() {\n return this.getAttribute(\"url\") ?? \"\"\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
    \n \n ${this._hightlightTerm(attachment.name, term)}\n
    \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 { translate } from \"alchemy_admin/i18n\"\n\nclass CharCounter extends HTMLElement {\n connectedCallback() {\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)\n }\n }\n\n disconnectedCallback() {\n this.formField?.removeEventListener(\"keyup\", this)\n }\n\n handleEvent(event) {\n if (event.type === \"keyup\") this.countCharacters()\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 = this.querySelector(\":scope > .alchemy-char-counter\")\n if (this.display) return\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 get maxChars() {\n return this.getAttribute(\"max-chars\") ?? 60\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 \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 ? ``\n : ``\n\n return `\n
    \n ${colorIndicator}\n ${object.text}\n
    `\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 { translate, currentLocale } from \"alchemy_admin/i18n\"\nimport flatpickr from \"flatpickr\"\n\nconst locale = currentLocale()\n\nclass Datepicker extends HTMLElement {\n // Load the locales for flatpickr before setting it up.\n async connectedCallback() {\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 // Bail out if the element was disconnected while the locale was loading.\n // Otherwise flatpickr would leak a calendar onto a detached input.\n if (!this.isConnected) return\n\n this.flatpickr = flatpickr(this.inputField, this.flatpickrOptions)\n }\n\n disconnectedCallback() {\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 get inputType() {\n return this.getAttribute(\"input-type\") || \"date\"\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 `${ingredient.errorMessage}`\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
    \n ${option.icon}${option.text}\n
    \n `\n}\n\nconst formatItem = (icon, text, hint) => {\n const description = hint\n ? `
    ${hint}
    `\n : \"\"\n return `\n
    \n ${formatSelection({ icon, text })}\n ${description}\n
    \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 \n ${this.dismissable && this.type === \"error\" ? '' : \"\"}\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 = ``\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 = ''\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 ''\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 = ``\n\n return `\n
    \n \n
    \n \n ${ancestors.length > 0 ? ancestors.join(seperator) + seperator : \"\"}\n \n \n ${this._hightlightTerm(node.name, term)}\n \n
    \n
    \n ${node.url || \"\"}\n
    \n
    \n `\n }\n}\n\ncustomElements.define(\"alchemy-node-select\", NodeSelect)\n","import { formatFileSize } from \"alchemy_admin/utils/format\"\nimport { translate } from \"alchemy_admin/i18n\"\nimport { growl } from \"alchemy_admin/growler\"\n\nexport class FileUpload extends HTMLElement {\n // public — used by callers (Uploader, Progress, tests)\n file = null\n request = null\n progressEventLoaded = 0\n progressEventTotal = 0\n\n // private — backing state for getters/setters\n #valid = true\n #value = 0\n #status = undefined\n #errorMessage = \"\"\n\n connectedCallback() {\n this.innerHTML = `\n \n
    \n ${this.file?.name}\n ${this.loadedSize}\n ${this.errorMessage}\n
    \n \n \n \n `\n\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 * 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 this.status = \"in-progress\"\n\n this.#validateFile()\n this.#addRequestEventListener()\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 * Dispatches a custom event with given name, namespaced under `Alchemy.`.\n * @param {string} name The name of the custom event\n */\n dispatchCustomEvent(name) {\n this.dispatchEvent(new CustomEvent(`Alchemy.${name}`, { bubbles: true }))\n }\n\n /**\n * validate given file with the `Alchemy.uploader_defaults` - configuration\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 */\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 { FileUpload } from \"alchemy_admin/components/uploader/file_upload\"\nimport { formatFileSize } from \"alchemy_admin/utils/format\"\nimport { translate } from \"alchemy_admin/i18n\"\n\nconst template = (buttonLabel, fileCount) => `\n \n
    \n \n\n \n \n \n
    \n
    3 ? 3 : fileCount\n }\">
    \n
    \n`\n\nexport class Progress extends HTMLElement {\n // public — accessed by Uploader and tests\n fileCount = 0\n\n // private — backing state and internals\n #fileUploads = []\n #buttonLabel = translate(\"Cancel all uploads\")\n #actionButton = null\n #visible = false\n #handleFileChange = () => this.#updateView()\n\n connectedCallback() {\n this.innerHTML = template(this.#buttonLabel, this.fileCount)\n this.visible = true\n\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 this.#updateView()\n this.addEventListener(\"Alchemy.FileUpload.Change\", this.#handleFileChange)\n }\n\n disconnectedCallback() {\n this.removeEventListener(\n \"Alchemy.FileUpload.Change\",\n this.#handleFileChange\n )\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 * 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 * 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 /**\n * get all active upload components\n * @returns {FileUpload[]}\n */\n #activeUploads() {\n return this.#fileUploads.filter((upload) => upload.active)\n }\n\n /**\n * replace cancel button to be the close button\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 */\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 */\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 { 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 HTMLElement {\n #dropzoneElement = null\n #isDraggedOver = false\n\n connectedCallback() {\n this.fileInput.addEventListener(\"change\", this.#onFileInputChange)\n if (this.dropzone) {\n this.#setupDropZone()\n }\n this.addEventListener(\"Alchemy.upload.successful\", this)\n }\n\n disconnectedCallback() {\n this.fileInput?.removeEventListener(\"change\", this.#onFileInputChange)\n if (this.#dropzoneElement) {\n this.#dropzoneElement.removeEventListener(\n \"dragleave\",\n this.#onDropzoneDragleave\n )\n this.#dropzoneElement.removeEventListener(\"drop\", this.#onDropzoneDrop)\n this.#dropzoneElement.removeEventListener(\n \"dragover\",\n this.#onDropzoneDragover\n )\n this.#dropzoneElement = null\n }\n }\n\n handleEvent(evt) {\n switch (evt.type) {\n case \"Alchemy.upload.successful\":\n this.#handleUploadComplete()\n break\n }\n }\n\n #onFileInputChange = (event) => {\n this.uploadFiles(Array.from(event.target.files))\n }\n\n #toggleDropzoneClass = (enabled) => {\n if (this.#isDraggedOver !== enabled) {\n this.#isDraggedOver = enabled\n this.#dropzoneElement.classList.toggle(\"dragover\")\n }\n }\n\n #onDropzoneDragleave = () => this.#toggleDropzoneClass(false)\n\n #onDropzoneDrop = async (event) => {\n event.preventDefault()\n this.#toggleDropzoneClass(false)\n\n const files = [...event.dataTransfer.items].map((item) => item.getAsFile())\n\n this.uploadFiles(files)\n }\n\n #onDropzoneDragover = (event) => {\n event.preventDefault() // dragover has to be disabled to use the custom drop event\n this.#toggleDropzoneClass(true)\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 #setupDropZone() {\n this.#dropzoneElement = document.querySelector(this.dropzone)\n if (!this.#dropzoneElement) return\n\n this.#dropzoneElement.addEventListener(\n \"dragleave\",\n this.#onDropzoneDragleave\n )\n this.#dropzoneElement.addEventListener(\"drop\", this.#onDropzoneDrop)\n this.#dropzoneElement.addEventListener(\"dragover\", this.#onDropzoneDragover)\n }\n\n /**\n * @param {File[]} files\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.dispatchEvent(\n new CustomEvent(`Alchemy.upload.${status}`, { bubbles: true })\n )\n }\n\n document.body.append(this.uploadProgress)\n }\n\n get dropzone() {\n return this.getAttribute(\"dropzone\")\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","class Overlay extends HTMLElement {\n connectedCallback() {\n this.innerHTML = `\n \n
    \n ${this.getAttribute(\"text\") ?? \"\"}\n
    \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 = ``\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 = ``\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 = ''\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
    \n
    \n \n ${this._hightlightTerm(page.name, term)}\n ${page.site.name}\n
    \n
    \n ${page.url_path}\n ${page.language_code}\n
    \n
    \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 = ''\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 }\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 \n \n \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 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","class Spinner extends HTMLElement {\n connectedCallback() {\n this.className = `spinner spinner--${this.size}`\n this.innerHTML = `\n \n \n \n \n \n `\n }\n\n get size() {\n return this.getAttribute(\"size\") || \"medium\"\n }\n\n get color() {\n return this.getAttribute(\"color\") || \"currentColor\"\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 { currentLocale } from \"alchemy_admin/i18n\"\n\nconst DARK_THEME = \"alchemy-dark\"\nconst LIGHT_THEME = \"alchemy\"\n\nclass Tinymce extends HTMLElement {\n #min_height = null\n\n /**\n * the observer will initialize Tinymce if the textarea becomes visible\n */\n connectedCallback() {\n this.className = \"tinymce_container\"\n\n // Append the spinner if not already present (idempotent on reconnect/clone)\n if (!this.querySelector(\":scope > alchemy-spinner\")) {\n this.insertAdjacentHTML(\n \"beforeend\",\n ``\n )\n }\n\n // hide the textarea until TinyMCE is ready to show the editor\n this.style.minHeight = `${this.minHeight}px`\n this.editor.style.display = \"none\"\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 disconnectedCallback() {\n this.tinymceIntersectionObserver?.disconnect()\n\n // Remove theme change listener\n this._removeThemeChangeListener()\n\n tinymce.get(this.editorId)?.remove(this.editorId)\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'\\n \\n\\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 = $('
    ')\n this.dialog = $('
    ')\n this.dialog_body = $('
    ')\n this.close_button = $(`\n \n `)\n this.dialog.append(this.close_button)\n this.dialog.append(this.dialog_body)\n this.dialog_container.append(this.dialog)\n this.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 '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","hightlightTerm","term","RegExp","RemoteSelect","isConnected","input","classList","add","select2Config","onOpen","onChange","dispatchCustomEvent","removed","added","allowClear","selection","placeholder","queryParams","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","CharCounter","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","Datepicker","flatpickr","inputField","flatpickrOptions","enableTime","inputType","slice","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","observedAttributes","spriteUrl","render","attributeChangedCallback","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","a","seperator","FileUpload","file","request","progressEventLoaded","progressEventTotal","valid","loadedSize","cancel","reader","FileReader","readAsDataURL","image","Image","result","prepend","initialize","validateFile","addRequestEventListener","finished","abort","config","uploader_defaults","maxFileSize","file_size_limit","allowedFiletypes","allowed_filetypes","alchemy_pictures","alchemy_attachments","upload","onprogress","progressEvent","onload","responseMessage","onerror","errorMessageContainer","progressElement","loaded","total","round","toggleAttribute","isValid","Progress","fileCount","fileUploads","buttonLabel","actionButton","handleFileChange","updateView","onComplete","fileUpload","activeUploads","setupCloseButton","_status","ariaLabel","sumFileProgresses","field","reduce","accumulator","totalProgress","overallProgressValue","overallUploadSize","every","entry","fileProgress","uploadedFileCount","uploadsStatuses","totalSize","ceil","weight","Uploader","dropzoneElement","isDraggedOver","fileInput","onFileInputChange","dropzone","setupDropZone","onDropzoneDragleave","onDropzoneDrop","onDropzoneDragover","handleUploadComplete","uploadFiles","files","toggleDropzoneClass","dataTransfer","getAsFile","redirectUrl","uploadProgress","fileUploadCount","XMLHttpRequest","upload_limit","submitFile","createProgress","formData","FormData","set","setRequestHeader","send","removeChild","Overlay","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","b","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","Tinymce","min_height","insertAdjacentHTML","minHeight","root","rootMargin","threshold","tinymceIntersectionObserver","IntersectionObserver","entries","intersectionRatio","_initTinymceEditor","unobserve","_setupThemeChangeListener","_removeThemeChangeListener","tinymce","editorId","configuration","editors","_setupEditor","editorContainer","darkModeMediaQuery","matchMedia","themeChangeHandler","_handleThemeChange","skin","matches","content_css","customConfig","getAttributeNames","attributeName","replaceAll","preferredTheme","TinymceDefaults","language","UpdateCheck","credentials","responseJSON","showStatus","showError","Handlebars","template","templates","l","r","compiler","main","o","lambda","d","escapeExpression","u","lookupProperty","Object","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":"iWAAA,MAAMA,EAAgB,KAWtB,SAASC,EAAeC,GACtB,MAAMC,EAASC,IACTC,EAAeC,QAAQD,aAE7B,OAAKA,EAKDL,EAAcO,KAAKL,GAlBzB,SAA2BG,EAAcH,GACvC,MAAMM,EAAON,EAAIO,MAAMT,GACjBU,EAAQL,EAAaG,EAAK,IAChC,OAAIE,GACKA,EAAMF,EAAK,KAEbN,CACT,CAYWS,CAAkBN,EAAcH,GAElCG,EAAaH,IAAQA,GAP1BU,QAAQC,KAAK,2BAA2BV,gBACjCD,EAOX,CAEO,SAASE,IACd,OAAIU,SAASC,gBAAgBC,KACpBF,SAASC,gBAAgBC,KAE3B,IACT,CAEO,SAASC,EAAUf,EAAKgB,OAAcC,GAC3C,IAAIC,EAAcnB,EAAeC,GAEjC,OAAIgB,EACKE,EAAYC,QAAQ,UAAWH,GAEjCE,CACT,CAEOE,eAAeC,IACpB,MAAMpB,EAASC,IACA,OAAXD,UAEEqB,OAAO,WAAWrB,QACxBsB,EAAEC,OAAOD,EAAEE,GAAGC,QAAQC,SAAUJ,EAAEE,GAAGC,QAAQE,QAAQ3B,IACvD,CC7CA,MAAM4B,EAAgB,GAEtB,SAASC,EAASC,GAChB,SACGR,EAAEQ,EAAIC,QAAQC,GAAG,oBACiB,MAAnCC,OAAOC,aAAaJ,EAAIK,UAExBC,EAAW,cAAe,CACxBC,MAAOlC,QAAQmC,EAAE,QACjBC,KAAM,aAED,EAIX,CAEe,SAAAC,EAAUC,EAAQ9B,UAE3B8B,aAAiBC,SACnBD,EAAQA,EAAM,IAIZA,IAAU9B,WACZA,SAASgC,oBAAoB,WAAYd,GACzClB,SAASiC,iBAAiB,WAAYf,GACtCD,EAAciB,SAASC,GAAW/C,IAAIgD,OAAOD,MAI/C,MAAME,EAAgBP,EAAMQ,iBAAiB,uBACvCC,EAAsBT,EAAMQ,iBAChC,+CAEFlD,IAAI,SAAS,WAGX,OAFAA,IAAIoD,SAAS,UACbH,EAAcH,SAASO,GAAOA,EAAGC,MAAM,CAAEC,cAAc,OAChD,CACT,IACA1B,EAAc2B,KAAK,SACnBxD,IAAI,MAAO,UAAU,WACnBmD,EAAoBL,SAASO,GAAOA,EAAGI,UACvCR,EAAcH,SAASO,GAAOA,EAAGK,QACnC,IACA7B,EAAc2B,KAAK,OAOnBd,EAAMQ,iBAAiB,yBAAyBJ,SAAQ,SAAUO,GAChE,MAAMN,EAASM,EAAGM,QAAQC,cAC1B5D,IAAI+C,GAAQ,IAAMM,EAAGI,UACrB5B,EAAc2B,KAAKT,EACrB,GACF,CCvDO,SAASc,EAAkBC,GAChC,MAAMC,EAAUnD,SAASoD,cAAc,YAEvC,OADAD,EAAQE,UAAYH,EACbC,EAAQG,QAAQC,SAAS,EAClC,CCPe,IAAAC,EAAA,MACb,WAAAC,CAAY7B,EAAM8B,EAAQ,gBACxBC,KAAK/B,KAAOA,EACZ+B,KAAKD,MAAQA,EACbC,KAAKC,aAAUvD,CACjB,CAKA,MAAIoC,GACF,OAAOkB,KAAKC,OACd,CAIA,IAAAC,CAAKC,GAQH,YAPsB,IAAXA,IACTA,EAAS9D,SAAS+D,MAEpBJ,KAAKC,QAAUX,EACb,0BAA0BU,KAAK/B,gBAAgB+B,KAAKD,6BAEtDI,EAAOE,OAAOL,KAAKC,SACZD,IACT,CAEA,IAAAM,GACMN,KAAKC,UACPD,KAAKC,QAAQM,SACbP,KAAKC,aAAUvD,EAEnB,GC9BF,MAAM8D,EAAiB,GAEjBC,EAAW,CACfC,cAAe,GACfzC,KAAM,UACN0C,SAAS,EACT5C,MAAO,GACP6C,OAAO,EACPC,SAAU,UACVC,MAAO,OACPC,OAAQ,QAGH,MAAMC,EAMX,WAAAlB,CAAYmB,EAAKC,EAAU,IACzBlB,KAAKiB,IAAMA,EACXjB,KAAKkB,QAAU,IAAKT,KAAaS,GACjClB,KAAKmB,UAAYnE,EAAEX,UACnB2D,KAAKoB,QAAUpE,EAAEqE,QACjBrB,KAAKsB,MAAQtE,EAAE,QACf,MAAMiB,EAAO+B,KAAKkB,QAAQjD,KAAKjC,MAAM,KACrCgE,KAAKuB,MAAQC,SAASvD,EAAK,GAAI,IAC/B+B,KAAKyB,OAASD,SAASvD,EAAK,GAAI,IAChC+B,KAAK0B,QACL1B,KAAK2B,QACP,CAGA,IAAAC,GACE5B,KAAK6B,OAAOC,QAAQ,sBACpB9B,KAAK+B,oBACLV,OAAOW,uBAAsB,KAE3B,GADAhC,KAAKiC,iBAAiBC,SAAS,QACX,MAAhBlC,KAAKmC,QACP,OAAOnC,KAAKmC,QAAQD,SAAS,OAC/B,IAEFlC,KAAKsB,MAAMY,SAAS,qBACpB1B,EAAevB,KAAKe,MACpBA,KAAKoC,MACP,CAGA,KAAAC,GAsBE,OArBArC,KAAK6B,OAAOC,QAAQ,uBACpB9B,KAAKmB,UAAUmB,IAAI,WACnBtC,KAAKiC,iBAAiBM,YAAY,QACd,MAAhBvC,KAAKmC,SACPnC,KAAKmC,QAAQI,YAAY,QAE3BvC,KAAKmB,UAAUqB,GACb,oDACA,KAQE,GAPAxC,KAAKmB,UAAUmB,IAAI,oDACnBtC,KAAKiC,iBAAiB1B,SACF,MAAhBP,KAAKmC,SACPnC,KAAKmC,QAAQ5B,SAEfP,KAAKsB,MAAMiB,YAAY,qBACvB/B,EAAeiC,IAAIzC,MACQ,MAAvBA,KAAKkB,QAAQH,OACf,OAAOf,KAAKkB,QAAQH,QACtB,KAGG,CACT,CAGA,IAAAqB,GACEpC,KAAK0C,eACL1F,EAAE2F,IAAI3C,KAAKiB,KAAM2B,IACf5C,KAAKpD,QAAQgG,EAAI,IAChBC,MAAMC,IACP9C,KAAK+C,WAAWD,EAAG,GAEvB,CAGA,MAAAE,GACEhD,KAAKiD,YAAYC,QACjBlD,KAAKoC,MACP,CAGA,OAAAxF,CAAQgG,GACN5C,KAAKmD,iBACLnD,KAAKiD,YAAYG,OACjBpD,KAAKiD,YAAYI,KAAKT,GACtB5C,KAAKsD,OACLtD,KAAK6B,OAAO,GAAG0B,cACb,IAAIC,YAAY,sBAAuB,CACrCC,SAAS,EACTC,OAAQ,CACNtD,KAAMJ,KAAKiD,YAAY,OAIH,MAAtBjD,KAAKkB,QAAQJ,OACfd,KAAKkB,QAAQJ,MAAMd,KAAKiD,aAE1BjD,KAAKiD,YAAYU,MACnB,CAGA,YAAAjB,GACE1C,KAAKC,QAAU,IAAI2D,EAAQ,UAC3B5D,KAAKC,QAAQC,KAAKF,KAAKiD,YAAY,GACrC,CAGA,cAAAE,GACEnD,KAAKC,QAAQK,MACf,CAGA,IAAAgD,GACEpF,EAAQ8B,KAAKiD,aACbjD,KAAK6D,oBACP,CAGA,kBAAAA,GACE,MAAMC,EAAQ9G,EAAE,uBAAwBgD,KAAKiD,aAE7Ca,EAAMtB,GAAG,gBAAiBuB,IACxB,MAAMjB,EAAMiB,EAAML,OAAO,GACJZ,EAAIkB,kBAAkB,gBAC1BC,MAAM,gBAGrBjE,KAAKiD,YAAYI,KAAKP,EAAIoB,cAC1BlE,KAAKsD,OACP,IAGFQ,EAAMtB,GAAG,cAAeuB,IACtB,MAAMI,EAAaJ,EAAML,OAAO,GAC1BZ,EAAMiB,EAAML,OAAO,GACzB1D,KAAK+C,WAAWD,EAAKqB,EAAU,GAEnC,CAGA,UAAApB,CAAWD,EAAKqB,GACd,GAAmB,MAAfrB,EAAIsB,OAGN,OAFApE,KAAKiD,YAAYI,KAAKP,EAAIoB,mBAC1BlE,KAAKsD,OAIP,MAAMe,WAAEA,EAAUC,aAAEA,EAAYC,WAAEA,GAAevE,KAAKwE,eACpD1B,EACAqB,GAGIM,EAAYzH,EAAE,0BAA0BuH,kBACtCD,oBACDD,iCAGPrE,KAAKiD,YAAYI,KAAKoB,EACxB,CAGA,cAAAD,CAAe1B,EAAKqB,GAClB,IAAIE,EACFC,EACAC,EAAa,UAEf,OAAQzB,EAAIsB,QACV,KAAK,EACHE,EAAe,+BACfD,EAAa,qCACb,MACF,KAAK,IACHC,EAAe,0BACfD,EAAa,4BACb,MACF,QACEE,EAAa,QACTJ,GACFG,EAAeH,EACfhI,QAAQuI,MAAM5B,EAAIoB,eAElBI,EAAe,GAAGxB,EAAIqB,eAAerB,EAAIsB,UAE3CC,EAAa,kCAGjB,MAAO,CAAEC,eAAcD,aAAYE,aACrC,CAMA,iBAAAxC,GACE/B,KAAK2E,aAAanC,GAAG,SAAS,KAC5BxC,KAAKqC,OAAK,IAEZrC,KAAKiC,iBAAiBC,SAAS,YAAYM,GAAG,SAAUoC,GAClDA,EAAEnH,SAAWuC,KAAKiC,iBAAiBU,IAAI,KAG3C3C,KAAKqC,SACE,KAETrC,KAAKmB,UAAU0D,SAASD,GACN,KAAZA,EAAE/G,QACJmC,KAAKqC,SACE,IAKb,CAGA,KAAAX,GACE1B,KAAKiC,iBAAmBjF,EAAE,4CAC1BgD,KAAK6B,OAAS7E,EAAE,kCAChBgD,KAAKiD,YAAcjG,EAAE,uCACrBgD,KAAK8E,cAAgB9H,EAAE,yCACvBgD,KAAK+E,aAAe/H,EAAE,wCACtBgD,KAAK2E,aAAe3H,EAClB,4FAEFgD,KAAK+E,aAAaxF,KAAKS,KAAKkB,QAAQnD,OACpCiC,KAAK8E,cAAczE,OAAOL,KAAK+E,cAC/B/E,KAAK8E,cAAczE,OAAOL,KAAK2E,cAC/B3E,KAAK6B,OAAOxB,OAAOL,KAAK8E,eACxB9E,KAAK6B,OAAOxB,OAAOL,KAAKiD,aACxBjD,KAAKiC,iBAAiB5B,OAAOL,KAAK6B,QAC9B7B,KAAKkB,QAAQN,OACfZ,KAAK6B,OAAOK,SAAS,SAEnBlC,KAAKkB,QAAQP,SACfX,KAAKiD,YAAYf,SAAS,UAExBlC,KAAKkB,QAAQN,QACfZ,KAAKmC,QAAUnF,EAAE,0CACjBgD,KAAKsB,MAAMjB,OAAOL,KAAKmC,UAEzBnC,KAAKsB,MAAMjB,OAAOL,KAAKiC,iBACzB,CAIA,MAAAN,GACE,MAAMJ,MAAEA,EAAKE,OAAEA,GAAWzB,KAAKgF,UAE/BhF,KAAK6B,OAAOoD,IAAI,CACd1D,MAAOA,EACP,aAAcE,EACdZ,SAAUb,KAAKkB,QAAQL,WAGK,WAA1Bb,KAAKkB,QAAQL,SACfb,KAAKiD,YAAYgC,IAAI,CACnBxD,OAAQA,EACRZ,SAAU,SAGZb,KAAKiD,YAAYgC,IAAI,CACnB,aAAcxD,EACdZ,SAAU,WAGhB,CAEA,OAAAmE,GACE,MAAMrE,EAAUX,KAAKkB,QAAQP,QAAU,GAAK,EACtCuE,EAAYlF,KAAKoB,QAAQG,QACzB4D,EAAanF,KAAKoB,QAAQK,SAEhC,IAAIF,EAAQvB,KAAKuB,MACbE,EAASzB,KAAKyB,OAUlB,OARIF,GAAS2D,IACX3D,EAAQ2D,EAAYvE,GAGlBc,GAAU0D,IACZ1D,EAAS0D,EAAaxE,EAAUF,EAASC,eAGpC,CAAEa,QAAOE,SAClB,EAIK,SAAS2D,IACd,MAAMC,OAAEA,GAAW7E,EACnB,GAAe,IAAX6E,EAGJ,OAAO7E,EAAe6E,EAAS,EACjC,CAMO,SAASC,EAAmBC,GACjC,MAAM1D,EAASuD,IACf,GAAc,MAAVvD,EAEF,OADAA,EAAOX,QAAQH,OAASwE,EACjB1D,EAAOQ,OAElB,CAGO,SAASvE,EAAWmD,EAAKC,GAC9B,IAAKD,EACH,KAAM,uCAEO,IAAID,EAAOC,EAAKC,GACxBU,MACT,CC5TA,MAAM4D,EACJ,WAAA1F,CAAY2F,EAASvE,EAAU,IAC7BlB,KAAKyF,QAAUA,EACfzF,KAAKkB,QAAU,IAZC,CAElBjD,KAAM,UACNF,MAAOvB,EAAU,kBACjBkJ,SAAUlJ,EAAU,OACpBmJ,aAAcnJ,EAAU,MACxB,KAAAoJ,GAAS,MAM+B1E,GACtClB,MAAK0B,IACL1B,MAAK6F,GACP,CAEA,IAAAjE,GACEI,uBAAsB,KACpBhC,KAAK6B,OAAO8B,MAAI,GAEpB,CAEA,EAAAjC,GACE,MAAMH,EAAQvB,KAAKkB,QAAQjD,KAAKjC,MAAM,KAAK,GAC3CgE,KAAK6B,OAASvC,EAAkB,6BACVU,KAAKkB,QAAQnD,0BAA0BwD,kBACvDvB,KAAKyF,yGAEHzF,KAAKkB,QAAQyE,8GAGb3F,KAAKkB,QAAQwE,yDAIrBrJ,SAAS+D,KAAKC,OAAOL,KAAK6B,OAC5B,CAEA,EAAAgE,GACE7F,KAAK8F,aAAaxH,iBAAiB,SAAUd,IAC3CA,EAAIuI,iBACJ/F,KAAKkB,QAAQ8E,YACbhG,KAAK6B,OAAOuB,MAAI,IAElBpD,KAAKiG,SAAS3H,iBAAiB,SAAUd,IACvCA,EAAIuI,iBACJ/F,KAAKkB,QAAQ0E,QACb5F,KAAK6B,OAAOuB,MAAI,IAGlBpD,KAAK6B,OAAOvD,iBAAiB,oBAAqByF,IACpB,YAAxBA,EAAML,OAAOwC,SACflG,KAAKkB,QAAQ8E,YACbjC,EAAMgC,iBACR,IAGF/F,KAAK6B,OAAOvD,iBAAiB,iBAAiB,KAC5C0B,KAAK6B,OAAOtB,QAAM,GAEtB,CAEA,gBAAIuF,GACF,OAAO9F,KAAK6B,OAAOsE,cAAc,qBACnC,CAEA,YAAIF,GACF,OAAOjG,KAAK6B,OAAOsE,cAAc,sBACnC,EAeK,SAASC,EAAkBX,EAASvE,EAAU,IACnD,OAAO,IAAImF,SAASC,IACH,IAAId,EAAcC,EAAS,IACrCvE,EACH,KAAA0E,GACEU,GAAQ,EACV,EACA,SAAAN,GACEM,GAAQ,EACV,IAEK1E,MAAI,GAEf,CC/Fe,SAAS2E,EAAkB5C,GAAO,GAC/CtH,SAAS8J,cAAc,mBAAmBxC,OAASA,CACrD,CCHA,SAAS6C,EAAmBhH,GAC1B,IAAI+F,EAAW,OAEXvI,EAAEwC,GAAS9B,GAAG,QAChB6H,EAAW,WACT,MAAMzB,EAAQ9G,EACZ,iBAAiBwC,EAAQiH,kDAE3B3C,EAAMzD,OAAOrD,EAAEwC,GAASkH,KAAK,UAC7B5C,EAAM6C,SAAS,QAEfJ,IACAzC,EAAMhC,QAAQ,SAChB,EACS9E,EAAEwC,GAAS9B,GAAG,OACvB6H,EAAW,IAAMqB,MAAMC,MAAMrH,EAAQsH,WAMvC,QAFEzK,SAASsC,iBAAiB,gCAAgC0G,OAAS,KAGnEe,EAAkB5J,EAAU,qBAAsB,CAChDuB,MAAOvB,EAAU,WACjBkJ,SAAUlJ,EAAU,MACpBmJ,aAAcnJ,EAAU,YACvBuK,MAAMC,IACHA,IACF3F,OAAO4F,oBAAiB,EACxB1B,IACF,KAEK,EAGX,CAYA,IAAA2B,EAAe,CACbV,qBACAW,kBAZF,WACE9K,SAASsC,iBAAiB,gBAAgBJ,SAASiB,IACjDA,EAAQlB,iBAAiB,SAAUyF,IAC5ByC,EAAmBzC,EAAMqD,gBAC5BrD,EAAMgC,gBACR,GACD,GAEL,GClCO,SAASsB,EAAUC,GACxB,MAAMC,EAAiBlL,SAASmL,eAAe,kBACzCC,EAAa,iBAAiBH,IAEpCC,EAAepB,cAAc,iBAAiBsB,OAAgBlH,SAC9DgH,EAAepB,cAAc,sBAAsBsB,OAAgBlH,SAEnEgH,EAAe5D,KAAK,wBACtB,+CAtBO,SAAmB2D,EAAYI,GACpC,MAAMH,EAAiBlL,SAASmL,eAAe,kBACzCC,EAAa,iBAAiBH,IAE9BK,EAAM,6BAA6BF,MAAeC,aAClDE,EAAQ,uBAAuBH,6BAErCF,EAAe7H,WAAaiI,EAAMC,EAElCvG,OAAOW,uBAAsB,WAC3BuF,EAAe5D,KAAK8D,EACtB,GACF,gBCDO,SAASI,EAAMpC,EAASqC,EAAQ,WAVvC,SAAerC,EAASsC,GACtB,MAAMC,EAAe3L,SAASmL,eAAe,iBACvCS,EAAe3I,EAAkB,gCACZyI,0BACrBtC,iCAGNuC,EAAa3H,OAAO4H,EACtB,CAGEvG,CAAM+D,EAASqC,EACjB,CCRA,MAAMI,EAAe,aCHrB,MAAMC,UAAsBC,kBAC1BC,GACAC,GACAC,GACAC,GAEA,WAAA1I,GACE2I,QACAzI,KAAK1B,iBAAiB,OAAQ0B,MAC9BA,MAAKwI,EAAuBxI,MAAK0I,EAA2BC,KAAK3I,KACnE,CAEA,WAAA4I,CAAYpL,GACO,SAAbA,EAAIqL,OACN7I,MAAK8I,IACL9I,MAAK+I,IACL/I,MAAKqI,GAAYW,KAAKhJ,KAAMxC,GAEhC,CAEA,EAAAkL,CAA2B3E,GACE,yBAAvBA,EAAMnB,KAAK6C,UACbzF,MAAK8I,IACL9I,MAAK+I,IACL/I,MAAKqI,GAAYW,KAAKhJ,KAAM+D,GAEhC,CAEA,iBAAAkF,GACE,IAAIhI,EAAMjB,KAAKiB,IAEfjB,MAAKkJ,IACL7H,OAAO/C,iBAAiB,UAAW0B,MAAKwI,GAEpCnH,OAAO8H,aAAaC,QAAQ,yBAC9BnI,EAAMI,OAAO8H,aAAaC,QAAQ,uBAClCpJ,KAAKqJ,iBAAiBC,MAAQrI,GAGhCjB,KAAKuJ,QAAQtI,EACf,CAEA,oBAAAuI,GACE/N,IAAIgD,OAAO,SACX4C,OAAOhD,oBAAoB,UAAW2B,MAAKwI,EAC7C,CAEA,WAAAiB,CAAY7G,GACV5C,KAAK0J,cAAcD,YAAY7G,EAAM,IACvC,CAEA,MAAAjB,CAAOJ,GACLvB,KAAK8H,MAAMvG,MAAQ,GAAGA,KACxB,CAEA,OAAAgI,CAAQtI,GAgBN,OAfAjB,MAAK2J,IAGH3J,KAAK4J,IADH3I,GAGSjB,KAAKiB,IAIlBjB,MAAK8I,IACL9I,MAAKuI,EAAesB,YAAW,KAC7B7J,MAAK+I,IACLlB,EAAMrL,EAAU,0BAA2B,UAAS,GACnD,KAEI,IAAI6J,SAASC,IAClBtG,MAAKqI,EAAa/B,CAAA,GAEtB,CAEA,aAAIwD,CAAUC,GACZ/J,KAAK8H,MAAMkC,mBAAqBD,EAAU,OAAS,KACnD/J,KAAK8H,MAAMmC,cAAgBF,EAAU,OAAS,IAChD,CAEA,EAAAb,GACElJ,KAAKkK,cAAc5L,iBAAiB,SAAUd,IAC5CA,EAAIuI,iBACJ/F,KAAKuJ,SAAO,IAGd9N,IAAI,SAAS,IAAMuE,KAAKuJ,YAExBvJ,KAAKmK,WAAW7L,iBAAiB,UAAWd,IAC1C,MACM+D,EADS/D,EAAIC,OACE6L,MAEP,KAAV/H,EACFvB,KAAK8H,MAAMvG,MAAQ,KAEnBvB,KAAK2B,OAAOJ,EACd,IAGFvB,KAAKqJ,kBAAkB/K,iBAAiB,UAAWd,IACjD,MAAMyD,EAAMzD,EAAIC,OAAO6L,MACvBjI,OAAO8H,aAAaiB,QAAQ,sBAAuBnJ,GACnDjB,KAAKuJ,QAAQtI,EAAG,GAEpB,CAEA,EAAA0I,GAEO3J,KAAKkK,aAAaxK,UAAU2K,SAAS,qBACxCrK,MAAKsI,EAActI,KAAKkK,aAAaxK,WAEvCM,KAAKkK,aAAaxK,UAAY,kDAChC,CAEA,EAAAqJ,GACE/I,KAAKkK,aAAaxK,UAAYM,MAAKsI,CACrC,CAEA,EAAAQ,GACM9I,MAAKuI,IACP+B,aAAatK,MAAKuI,GAClBvI,MAAKuI,EAAe,KAExB,CAEA,OAAItH,GACF,OAAOjB,KAAKuK,aAAa,MAC3B,CAEA,cAAIJ,GACF,OAAO9N,SAAS8J,cAAc,sBAChC,CAEA,oBAAIkD,GACF,OAAOhN,SAAS8J,cAAc,qBAChC,CAEA,gBAAI+D,GACF,OAAO7N,SAAS8J,cAAc,yBAChC,EAOK,SAASqE,IACQnO,SAASmL,eAAe,0BAChC+B,SAChB,CAPAkB,eAAeC,OAAO,yBAA0BvC,EAAe,CAC7DwC,QAAS,WCnJI,MAAMC,EACnB,iBAAOC,CAAWC,EAAcC,GAAS,GACvC,MAAMC,EAAmB3O,SAAS8J,cAChC,wBAAwB2E,OAE1B,GAAIE,EAAkB,CACPA,EAAiB7E,cAC5B,6CAEG8E,aAAa,aAAcF,EAAS,OAAS,OACpD,CACF,ECLF,MAAMG,UAAeC,YACnB,WAAArL,GACE2I,QAGAzI,KAAKoL,QAAU,CAIb9F,qBACAkF,gBACAa,mBAAoBhE,EACpBiE,iBAAkBV,EAAqBC,WACvC,qBAAAU,GACEhF,GAAkB,EACpB,EAEJ,CAEA,iBAAA0C,GACE,MAAMuC,EAAOxL,KAAKoL,QAAQpL,KAAKyL,MAE3BD,EACFA,KAAQxL,KAAK0L,QAEbvP,QAAQuI,MAAM,2BAA2B1E,KAAKyL,QAGhDzL,KAAKO,QACP,CAEA,QAAIkL,GACF,OAAOzL,KAAKuK,aAAa,OAC3B,CAEA,UAAImB,GACF,OAAI1L,KAAK2L,aAAa,UACbC,KAAKC,MAAM7L,KAAKuK,aAAa,WAE/B,EACT,EC5CK,SAASuB,EAAeL,EAAMM,GACnC,OAAON,EAAK7O,QAAQ,IAAIoP,OAAOD,EAAM,OAAQ9H,GAAU,OAAOA,UAChE,CD6CAwG,eAAeC,OAAO,iBAAkBQ,GC3CjC,MAAMe,UAAqBd,YAChChO,GAAW,KAEX,uBAAM8L,SACEnM,IAGDkD,KAAKkM,cAEVlM,KAAKmM,MAAMC,UAAUC,IAAI,qBAEzBrM,MAAK7C,EAAWH,EAAEgD,KAAKmM,OACpBhP,QAAQ6C,KAAKsM,eACb9J,GAAG,eAAgBxC,MAAKuM,GACxB/J,GAAG,SAAUxC,MAAKwM,GACvB,CAEA,oBAAAhD,GACMxJ,MAAK7C,IACP6C,MAAK7C,EAASmF,IAAI,eAAgBtC,MAAKuM,GACvCvM,MAAK7C,EAASmF,IAAI,SAAUtC,MAAKwM,GACjCxM,MAAK7C,EAASA,QAAQ,WACtB6C,MAAK7C,EAAW,KAEpB,CAEAoP,GAAW/O,GAAQwC,KAAKuM,OAAO/O,GAC/BgP,GAAahP,GAAQwC,KAAKwM,SAAShP,GAMnC,QAAAgP,CAASzI,GACP/D,KAAKyM,oBAAoB,sBAAuB,CAC9CC,QAAS3I,EAAM2I,QACfC,MAAO5I,EAAM4I,OAEjB,CAMA,MAAAJ,CAAOxI,GAKL8F,YAAW,KACTxN,SAAS8J,cAAc,gCAAgCpH,OAAK,GAC3D,IACL,CAQA,mBAAA0N,CAAoBhB,EAAM/H,EAAS,IACjC1D,KAAKuD,cACH,IAAIC,YAAY,WAAWiI,IAAQ,CAAEhI,SAAS,EAAMC,WAExD,CAEA,cAAIkJ,GACF,OAAO5M,KAAK2L,aAAa,cAC3B,CAEA,aAAIkB,GACF,OAAO7M,KAAKuK,aAAa,YAC3B,CAEA,eAAIuC,GACF,OAAO9M,KAAKuK,aAAa,gBAAkB,EAC7C,CAEA,eAAIwC,GACF,OAAO/M,KAAKuK,aAAa,iBAAmB,IAC9C,CAEA,OAAItJ,GACF,OAAOjB,KAAKuK,aAAa,QAAU,EACrC,CAEA,SAAI4B,GACF,OAAOnM,KAAKgN,qBAAqB,SAAS,EAC5C,CAEA,iBAAIV,GACF,MAAO,CACLQ,YAAa9M,KAAK8M,YAClBF,WAAY5M,KAAK4M,WACjBK,cAAe,CAACC,EAAM3H,KAChBvF,KAAK6M,WACPtH,EAASqG,KAAKC,MAAM7L,KAAK6M,WAC3B,EAEFM,KAAMnN,KAAKoN,WACXC,gBAAkBC,GAAStN,KAAKuN,cAAcD,GAC9CE,aAAc,CAACF,EAAMG,EAAKC,IACxB1N,KAAK2N,iBAAiBL,EAAMI,EAAM3B,MAExC,CAMA,cAAIqB,GACF,MAAO,CACLnM,IAAKjB,KAAKiB,IACV2M,SAAU,OACVC,YAAa,IACbjL,KAAM,CAACmJ,EAAM+B,IAAS9N,KAAK+N,aAAahC,EAAM+B,GAC9CE,QAAUC,GAAajO,KAAKkO,eAAeD,GAE/C,CASA,YAAAF,CAAahC,EAAM+B,GACjB,MAAO,CACLK,EAAG,CACDC,UAAWrC,KACRH,KAAKC,MAAM7L,KAAK+M,cAErBe,KAAMA,EAEV,CAQA,cAAAI,CAAeD,GACb,MAAMI,EAAOJ,EAASI,KACtB,MAAO,CACLL,QAASC,EAASrL,KAClB0L,KAAMD,EAAKP,KAAOO,EAAKE,SAAWF,EAAKG,YAE3C,CAQA,aAAAjB,GACE,MAAM,IAAIkB,MACR,iEAEJ,CASA,gBAAAd,GACE,MAAM,IAAIc,MACR,oEAEJ,CASA,eAAAC,CAAgBjD,EAAMM,GACpB,OAAOD,EAAeL,EAAMM,EAC9B,ECvKFtB,eAAeC,OAAO,4BAtBtB,cAA+BuB,EAC7B,aAAAsB,CAAcD,GACZ,OAAOtN,KAAK2N,iBAAiBL,EAC/B,CASA,gBAAAK,CAAiBgB,EAAY5C,GAC3B,MAAO,oFAEmB4C,EAAWC,6FACkB5O,KAAK0O,gBAAgBC,EAAWlD,KAAMM,+BAG/F,ICnBF,MAAM8C,UAAmB1D,YACvB,iBAAAlC,GAGEjM,EAAEgD,MAAMwC,GAAG,UAAU,SAAUuB,GAG7B,MAAM+K,EAAc,IAAIC,MAAM,SAAU,CACtCtL,SAAS,EACTuL,YAAY,IAGd,OADAjL,EAAMtG,OAAOwR,KAAK1L,cAAcuL,IACzB,CACT,GACF,EAGFrE,eAAeC,OAAO,sBAAuBmE,GCjB7C,MAAMK,UAAeC,kBACnB,iBAAAlG,GACMjJ,KAAKiP,MACPjP,KAAKiP,KAAK3Q,iBAAiB,SAAU0B,MAEL,QAA5BA,KAAKiP,KAAK7P,QAAQgQ,QACpBpP,KAAKiP,KAAK3Q,iBAAiB,gBAAiB0B,MAG9CA,KAAKiP,KAAK3Q,iBAAiB,mBAAoB0B,OAE/C7D,QAAQC,KAAK,4BAA6B4D,KAE9C,CAEA,WAAA4I,CAAY7E,GACV,OAAQA,EAAM8E,MACZ,IAAK,SACkD,aAAlC7I,KAAKuK,aAAa,aAGnCxG,EAAMgC,iBACNhC,EAAMsL,mBAENrP,KAAKsP,UAEP,MACF,IAAK,gBACL,IAAK,mBACHtP,KAAKuP,SAGX,CAEA,OAAAD,GACE,MAAMrP,EAAU,IAAI2D,EAAQ,SACtB4L,EAAOxP,KAAKyP,wBAElBzP,KAAKZ,QAAQsQ,kBAAoB1P,KAAKN,UACtCM,KAAKiL,aAAa,WAAY,YAC9BjL,KAAKiL,aAAa,WAAY,MAC9BjL,KAAKoM,UAAUC,IAAI,YACnBrM,KAAK8H,MAAMvG,MAAQ,GAAGiO,EAAKjO,UAC3BvB,KAAK8H,MAAMrG,OAAS,GAAG+N,EAAK/N,WAC5BzB,KAAKN,UAAY,SAEjBO,EAAQC,KAAKF,KACf,CAEA,MAAAuP,GACEvP,KAAKoM,UAAU7L,OAAO,YACtBP,KAAK2P,gBAAgB,YACrB3P,KAAK2P,gBAAgB,YACrB3P,KAAK8H,MAAMvG,MAAQ,KACnBvB,KAAK8H,MAAMrG,OAAS,KACpBzB,KAAKN,UAAYM,KAAKZ,QAAQsQ,iBAChC,EAGFjF,eAAeC,OAAO,iBAAkBwE,EAAQ,CAAEvE,QAAS,WCxD3D,MAAMiF,UAAoBzE,YACxB,iBAAAlC,GACEjJ,KAAKrD,YAAcH,EAAU,gBAAiBwD,KAAK6P,UACnD7P,KAAK8P,UAAY9P,KAAK+P,eAElB/P,KAAK8P,YACP9P,KAAKgQ,uBACLhQ,KAAKiQ,kBACLjQ,KAAK8P,UAAUxR,iBAAiB,QAAS0B,MAE7C,CAEA,oBAAAwJ,GACExJ,KAAK8P,WAAWzR,oBAAoB,QAAS2B,KAC/C,CAEA,WAAA4I,CAAY7E,GACS,UAAfA,EAAM8E,MAAkB7I,KAAKiQ,iBACnC,CAEA,YAAAF,GACE,MAAMG,EAAalQ,KAAKrB,iBAAiB,mBACzC,OAAOuR,EAAW7K,OAAS,EAAI6K,EAAW,QAAKxT,CACjD,CAEA,oBAAAsT,GACEhQ,KAAKmQ,QAAUnQ,KAAKmG,cAAc,kCAC9BnG,KAAKmQ,UACTnQ,KAAKmQ,QAAU9T,SAASoD,cAAc,SACtCO,KAAKmQ,QAAQC,UAAY,uBACzBpQ,KAAK8P,UAAUO,MAAMrQ,KAAKmQ,SAC5B,CAEA,eAAAF,GACE,MAAMK,EAAatQ,KAAK8P,UAAUxG,MAAMjE,OACxCrF,KAAKmQ,QAAQI,YAAc,GAAGD,KAActQ,KAAKrD,cACjDqD,KAAKmQ,QAAQ/D,UAAUoE,OAAO,WAAYF,EAAatQ,KAAK6P,SAC9D,CAEA,YAAIA,GACF,OAAO7P,KAAKuK,aAAa,cAAgB,EAC3C,EAGFE,eAAeC,OAAO,uBAAwBkF,GC9C9C,MAAMa,UAAwBtF,YAC5B,WAAArL,GACE2I,QAEAzI,KAAKN,UAAY,+DAIjBM,KAAK0Q,UAAY,IAAIC,YAAY3Q,KAAM,CACrCT,KAAM,IACGS,KAAKuK,aAAa,aAI7BvK,KAAK0Q,UAAUlO,GAAG,WAAW,KAC3BqF,EAAM7H,KAAKuK,aAAa,gBAAe,GAE3C,CAEA,oBAAAf,GACExJ,KAAK0Q,UAAUE,SACjB,EAGFnG,eAAeC,OAAO,2BAA4B+F,GC3BlD,MAAMI,EAAcC,IAClB,MAAMC,EAAWD,EAAOtR,QAAQ,GAC1BwR,EAASD,EAAS3R,QAAQ4R,QAAUD,EAASzH,MAMnD,MAAO,kDALgC,iBAAnByH,EAASzH,MAEzB,+CACA,iDAAiD0H,6BAKzCF,EAAOvR,yBAAI,EAIzB,MAAM0R,UAAoB9F,YACxB,iBAAAlC,GACMjJ,KAAKkR,QACPlR,MAAKmR,IACLnU,EAAEgD,KAAKkR,QAAQ1O,GAAG,UAAWuB,GAC3B/D,MAAKoR,EAAiC,iBAAdrN,EAAMsN,SAGhCrR,KAAKsR,YAAYhT,iBAAiB,QAAS0B,MAC3CA,KAAKuR,WAAWjT,iBAAiB,QAAS0B,MAC1CA,MAAKoR,GAAmB,GAE5B,CAEA,WAAAxI,CAAY7E,GACV,OAAQA,EAAMtG,QACZ,KAAKuC,KAAKsR,WACRtR,KAAKuR,UAAUjI,MAAQtJ,KAAKsR,WAAWhI,MACvC,MACF,KAAKtJ,KAAKuR,UACRvR,KAAKsR,WAAWhI,MAAQtJ,KAAKuR,UAAUjI,MAG7C,CAEA,oBAAAE,GACExJ,KAAKsR,YAAYjT,oBAAoB,QAAS2B,MAC9CA,KAAKuR,WAAWlT,oBAAoB,QAAS2B,KAC/C,CAEA,EAAAmR,GACEnR,KAAKkR,OAAO9E,UAAUC,IAAI,qBAC1B,MAAMnL,EAAU,CACdsQ,wBAAyB,GACzBhE,aAAcqD,EACdxD,gBAAiBwD,GAEnB7T,EAAEgD,KAAKkR,QAAQ/T,QAAQ+D,EACzB,CAEA,EAAAkQ,CAAmBK,GAAU,GAC3BzR,KAAKsR,WAAWI,UAAYD,CAC9B,CAEA,cAAIH,GACF,OAAOtR,KAAKmG,cAAc,sBAC5B,CAEA,aAAIoL,GACF,OAAOvR,KAAKmG,cAAc,qBAC5B,CAEA,UAAI+K,GACF,OAAOlR,KAAKmG,cAAc,SAC5B,EAGFsE,eAAeC,OAAO,uBAAwBuG,GCrE9C,MAAMvV,EAASC,IAEf,MAAMgW,UAAmBxG,YAEvB,uBAAMlC,GAEW,OAAXvN,SACIqB,OAAO,aAAarB,QAIvBsE,KAAKkM,cAEVlM,KAAK4R,UAAYA,EAAU5R,KAAK6R,WAAY7R,KAAK8R,kBACnD,CAEA,oBAAAtI,GACExJ,KAAK4R,WAAWhB,SAClB,CAEA,oBAAIkB,GACF,MAAMC,EAAa,OAAOjW,KAAKkE,KAAKgS,WAC9B9Q,EAAU,CAEdxF,OAAQA,EAAOuW,MAAM,EAAG,GACxBC,UAAU,EACVC,UAAW3V,EAAU,WAAWwD,KAAKgS,aACrCI,cAAe,kBACfL,aACAM,WAA+B,SAAnBrS,KAAKgS,UACjBM,UAAW9V,EAAU,qBACrB,aAAA+V,CAAcC,EAAgBC,EAAUC,GACtCA,EAASlT,QACNmT,QAAQ,2BACPC,SAAS5S,KAAK6R,WACpB,GAOF,OAJIE,IACF7Q,EAAQ2R,WAAa,KAGhB3R,CACT,CAEA,cAAI2Q,GACF,OAAO7R,KAAKmG,cAAc,QAC5B,CAEA,aAAI6L,GACF,OAAOhS,KAAKuK,aAAa,eAAiB,MAC5C,EAGFE,eAAeC,OAAO,qBAAsBiH,GCvDrC,MAAMmB,UAAmBC,kBAC9B,WAAAjT,GACE2I,QACAzI,KAAK1B,iBAAiB,QAAS0B,KACjC,CAEA,WAAA4I,CAAYpL,GACLwC,KAAK0R,UACR1R,KAAKlC,aAEPN,EAAIuI,gBACN,CAEA,UAAAjI,GACEkC,KAAK6B,OAAS,IAAIb,EAAOhB,KAAKuK,aAAa,QAASvK,KAAKgT,eACzDhT,KAAK6B,OAAOD,MACd,CAEA,iBAAIoR,GAIF,OAHgBhT,KAAKZ,QAAQ4T,cACzBpH,KAAKC,MAAM7L,KAAKZ,QAAQ4T,eACxB,CAAA,CAEN,CAEA,YAAItB,GACF,OAAO1R,KAAKoM,UAAU6G,SAAS,WACjC,EAGFxI,eAAeC,OAAO,sBAAuBoI,EAAY,CAAEnI,QAAS,MChCpE,MAAMuI,EAAoB,mBACpBC,EAA4B,6BAElC,SAASC,EAAaC,GACpB,MAAgC,QAAzBA,EAAOC,aAChB,CAqBA,SAASC,EAAeF,EAAQzQ,EAAM4Q,GACpC,MAAMC,EAVR,SAAwBD,GACtB,MAAO,CACL,eAAgB,kCAChBE,OAAQF,EACR,mBAAoB,iBACpB,eAAgBG,IAEpB,CAGkBC,CAAeJ,GACzBtS,EAAU,CAAEmS,SAAQI,WAM1B,OAJI7Q,IAASwQ,EAAaC,KACxBnS,EAAQd,KAAOwL,KAAKiI,UAAUjR,IAGzB1B,CACT,CAEO,SAASyS,IAEd,OADgBtX,SAAS8J,cAAc,2BACxB2N,WAAWnU,QAAQ4Q,WACpC,CAEO,SAAS5N,EAAI1B,EAAKyK,GACvB,OAAOyB,GAAK,MAAOlM,EAAKyK,EAC1B,CAEO,SAASqI,GAAM9S,EAAK2B,EAAM4Q,GAC/B,OAAOrG,GAAK,QAASlM,EAAK2B,EAAM4Q,EAClC,CAEO,SAASQ,GAAK/S,EAAK2B,EAAM4Q,EAASN,GACvC,OAAO/F,GAAK,OAAQlM,EAAK2B,EAAM4Q,EACjC,CAEe3W,eAAesQ,GAC5BkG,EACAY,EACArR,EACA4Q,EAASN,GAET,MAAMjF,QAAiBiG,MArDzB,SAAoBD,EAAMrR,EAAMyQ,GAC9B,MAAMpS,EAAM,IAAIkT,IAAI9S,OAAO+S,SAASC,OAASJ,GAM7C,OAJIrR,GAAQwQ,EAAaC,KACvBpS,EAAIqT,OAAS,IAAIC,gBAAgB3R,GAAM4R,YAGlCvT,EAAIuT,UACb,CA8CIC,CAAWR,EAAMrR,EAAMyQ,GACvBE,EAAeF,EAAQzQ,EAAM4Q,IAEzBkB,EAAczG,EAASwF,QAAQ9Q,IAAI,gBACnCgS,EAASD,GAAarK,SAAS6I,GAC/B0B,EAAgBF,GAAarK,SAAS8I,GAE5C,IAAI0B,EAAe,KAWnB,GAVIF,EACFE,QAAqB5G,EAAS6G,OACrBF,IACTC,QAAqB5G,EAAS1O,OAET,oBAAVqH,OACTA,MAAMmO,oBAAoBF,IAI1B5G,EAAS+G,GACX,MAAO,CAAEpS,KAAMiS,EAAczQ,OAAQ6J,EAAS7J,QAE9C,MAAMyQ,GAAgB,IAAIpG,MAAM,2CAEpC,CCjFA,MAAMwG,WAAoB9J,YACxB,QAAA+J,CAASC,GACP,MAAO,CACLC,GAAI,IAAID,IACR5V,KAAM,IAAI4V,IAEd,CAEA,iBAAIE,GACF,OAAOrV,KAAKmG,cAAc,8BAC5B,EAsDFsE,eAAeC,OAAO,4BAnDtB,cAA6BuK,GAC3BK,QAAU5Y,EAEV,iBAAAuM,GACEjJ,KAAK8N,KAAO9N,KAAKuK,aAAa,OAChC,CAEA,OAAMgL,GACJ,MAGMrU,SAHeyB,EAAI9G,QAAQ2Z,OAAOC,qBAAsB,CAC5DC,QAAS1V,MAAKsV,KAEO1S,KAAK+S,YACzBC,QAAQC,GAAeA,EAAWjT,MAAMkT,SACxCC,KAAKF,GAAe7V,KAAKkV,SAASW,EAAWjT,KAAKkT,UAC/CE,EACJ9U,EAAQmE,OAAS,EAAI7I,EAAU,QAAUA,EAAU,oBAErDwD,KAAKqV,cAAcY,WAAW/U,EAAS8U,GACvChW,KAAKqV,cAAc9F,QACrB,CAEA,EAAA2G,GAEElU,uBAAsB,KACpBhC,KAAKqV,cAAc/F,UACnBtP,KAAKqV,cAAcY,WAAW,GAAIzZ,EAAU,uBAAsB,GAEtE,CAEA,QAAIsR,CAAKwH,GACPtV,MAAKsV,EAAUA,EACfA,EAAStV,MAAKuV,IAAiBvV,MAAKkW,GACtC,IAoBFzL,eAAeC,OAAO,gCAjBtB,cAAiCuK,GAC/B,iBAAAhM,GAEEjH,uBAAsB,KACpB,MAAMmU,EAAQ9Z,SAASmL,eAAe,0BAChC4O,EAAWD,EAAME,iBAAiB1X,iBAAiB,SAAW,GACpE,GAAIyX,EAAS/Q,OAAS,EAAG,CACvB,MAAMnE,EAAUoV,MAAMC,KAAKH,GAAUL,KAAKvW,GACjCQ,KAAKkV,SAAS1V,EAAQ4V,MAE/BpV,KAAKqV,cAAcY,WAAW/U,EAAS1E,EAAU,QACnD,IAEJ,IChEK,MAAMga,WAA6BrL,YACxCsL,GAEA,iBAAAxN,GACEjJ,MAAKyW,EAAyBzW,KAAK0W,eAAenM,aAAa,WAC/DvK,KAAK2W,cAAcrY,iBAAiB,QAAS0B,MAC7CA,KAAK4W,SAAStY,iBAAiB,UAAW0B,MAC1CA,KAAK4W,SAAStY,iBAAiB,UAAW0B,KAC5C,CAEA,oBAAAwJ,GACExJ,KAAK2W,cAActY,oBAAoB,QAAS2B,MAChDA,KAAK4W,SAASvY,oBAAoB,UAAW2B,MAC7CA,KAAK4W,SAASvY,oBAAoB,UAAW2B,KAC/C,CAEA,WAAA4I,CAAY7E,GACV,OAAQA,EAAM8E,MACZ,IAAK,QACH7I,KAAK2W,cAAcE,SAAU,EAC7B,MACF,IAAK,UACH7W,KAAK0W,eAAezL,aAAa,UAAW,WAC5C,MACF,IAAK,UACHjL,KAAK0W,eAAezL,aAAa,UAAWjL,MAAKyW,GAGvD,CAEA,iBAAIE,GACF,OAAO3W,KAAKmG,cAAc,2BAC5B,CAEA,YAAIyQ,GACF,OAAO5W,KAAKmG,cAAc,cAC5B,CAEA,kBAAIuQ,GACF,OAAO1W,KAAKmG,cAAc,4BAC5B,EAGFsE,eAAeC,OAAO,iCAAkC8L,ICpCjD,MAAMM,WAA4B3L,YACvC,WAAArL,GACE2I,QACAzI,KAAK+W,QAAQzY,iBAAiB,QAAS0B,KACzC,CAEA,iBAAM4I,GAEJ,SADwBxC,EAAkBpG,KAAKyF,SAChC,CACb,MAAMwI,QAAiBd,GAAK,SAAUnN,KAAKiB,KAC3CjB,MAAKgX,EAAe/I,EAASrL,KAC/B,CACF,CAEA,EAAAoU,CAAepU,GACb,MAAMqU,EAAgBjX,KAAK2S,QAAQ,0BACnCsE,EAAc3Y,iBAAiB,iBAAiB,KAC1C2Y,EAAcC,OAChB7P,EAAU4P,EAAcE,WAE1BF,EAAc1W,QAAM,IAEtB0W,EAAc7K,UAAUC,IAAI,WAC5BxE,EAAMjF,EAAK6C,SACP7C,EAAKwU,2BACPC,GAAuBzU,GAEzB4H,GACF,CAEA,OAAIvJ,GACF,OAAOjB,KAAKuK,aAAa,OAC3B,CAEA,WAAI9E,GACF,OAAOzF,KAAKuK,aAAa,UAC3B,CAEA,UAAIwM,GACF,OAAO/W,KAAKmG,cAAc,SAC5B,ECvCK,SAASkR,GAAuBzU,GACrCvG,SAASkH,cACP,IAAIC,YAAY,qBAAsB,CACpCE,OAAQ,CAAE4T,QAAS1U,EAAK2U,wBAG9B,CDoCA9M,eAAeC,OAAO,gCAAiCoM,IClChD,MAAMU,WAAsBrM,YACjC,WAAArL,GACE2I,QAGAzI,KAAK1B,iBAAiB,QAAS0B,MAE/BA,KAAK1B,iBAAiB,+BAAgC0B,MAEtDA,KAAK1B,iBAAiB,gBAAiB0B,MAIvChD,EAAEgD,KAAKiP,MAAMzM,GAAG,SAAUxC,KAAKwM,UAE/BxM,KAAKyX,QAAQnZ,iBAAiB,YAAY,KACxC0B,KAAKwQ,QAAM,IAEbxQ,KAAK0X,cAAcpZ,iBAAiB,SAAUd,IACtBA,EAAIC,OAAOkV,QAAQ,4BACnB3S,MACpBA,KAAKwQ,QACP,GAEJ,CAEA,iBAAAvH,GAEMjJ,KAAKoM,UAAU6G,SAAS,4BAKxBjT,KAAK2L,aAAa,aACpB3L,KAAK2X,eACL3X,KAAK4X,eAAerO,UAAUxC,MAAK,KACjC/G,KAAK6X,qBAAmB,IAE1B7X,KAAK2P,gBAAgB,WAEzB,CAEA,WAAA/G,CAAY7E,GACV,OAAQA,EAAM8E,MACZ,IAAK,QACmB9E,EAAMtG,OAAOkV,QAAQ,4BACrB3S,MACpBA,KAAK8X,iBAEP,MACF,IAAK,gBACH,GAAI/T,EAAMtG,SAAWuC,KAAKI,KAAM,CAC9B,MAAM0C,EAAMiB,EAAML,OAAO,GACzBK,EAAMsL,kBACNrP,KAAK+X,cAAcjV,EACrB,CACA,MACF,IAAK,+BACE9C,KAAKgY,YAAcjU,EAAMtG,QAAUuC,KAAKiY,YAC3CjY,KAAKkY,SAASnU,EAAML,OAAO3F,OAInC,CAEA,QAAAyO,CAASzI,GACP,MAAMtG,EAASsG,EAAMtG,OAIrB,IAAIA,EAAO2O,UAAU6G,SAAS,mBAK9B,OAFAjT,KAAK2S,QAAQ,0BAA0BC,SAASnV,GAChDsG,EAAMsL,mBACC,CACT,CASA,kBAAMsI,GAEAtb,SAAS8J,cAAc,0BACnBnG,KAAKmY,4BAGPnY,KAAKoY,SACXpY,KAAKqV,eAAc,EACrB,CAEA,mBAAAwC,GACE7X,KAAK4X,eAAenO,YAAY,CAC9BhE,QAAS,uBACT6B,WAAYtH,KAAKmX,WAErB,CAEA,cAAAW,GACE9X,KAAKqV,gBACLrV,KAAK6X,qBACP,CASA,aAAAE,CAAcjV,GACZ,MAAMF,EAAOgJ,KAAKC,MAAM/I,EAAIoB,cAI5B,GAFAlE,KAAKqY,WAEc,MAAfvV,EAAIsB,OAAgB,CACtB,MAAMkU,EAAU1V,EAAK0V,QAGrB1V,EAAK2V,sBAAsBha,SAASsX,IAClC,MAAM7K,EAAmBhL,KAAKmG,cAC5B,wBAAwB0P,EAAWT,QAE/BoD,EAAelZ,EACnB,wBAAwBuW,EAAW4C,wBAErCzN,GAAkB0N,YAAYF,GAC9BxN,GAAkBoB,UAAUC,IAAI,oBAAmB,IAGrDxE,EAAMyQ,EAAS,QACftY,KAAK2Y,cAAcvM,UAAU7L,OAAO,SACtC,MACEsH,EAAMjF,EAAKgW,QACX5Y,KAAK4X,eAAerO,UAAUxC,MAAK,KACjC/G,KAAK6X,qBAAmB,IAE1B7X,KAAK6Y,YAAYjW,EAAKkW,aACtBlW,EAAKmW,kBAAkBxa,SAASya,IAC9BpO,EAAqBC,WAAWmO,EAAOlO,aAAckO,EAAOjO,OAAM,IAEhEnI,EAAKwU,2BACPC,GAAuBzU,EAG7B,CAKA,eAAAqW,GAGEpP,YAAW,KACT7J,KAAKkZ,eAAe,CAClBC,SAAU,UACX,GACA,GACL,CAMA,aAAA9D,CAAc+D,GAAS,GACrB/c,SACGsC,iBAAiB,mCACjBJ,SAASO,IACRA,EAAGsN,UAAU7L,OAAO,WAAU,IAElCc,OAAOW,uBAAsB,KAC3BhC,KAAKoM,UAAUC,IAAI,WAAU,IAE3B+M,GAAQpZ,KAAKiZ,iBACnB,CAOA,mBAAAd,GACE,OAAO,IAAI9R,SAAQ,CAACC,EAAS+S,KAC3B,MAAMC,EAAOjd,SAAS8J,cAAc,mBAC9ByB,EAAQ5H,KAAK2S,QAAQ,gBACvB2G,GAAQ1R,GACV0R,EAAK3V,KAAKiE,EAAM2C,aAAa,SAC7BjE,KAEA+S,EAAO,IAAI5K,MAAM,mBACnB,GAEJ,CAKA,QAAA4J,GACErY,KAAKuZ,OAAQ,EACblY,OAAO4F,eAAiB,KACxBjH,KAAK2Y,cAAcvM,UAAUC,IAAI,UAE7BrM,KAAKgY,YACPhY,KAAKI,KAAKzB,iBAAiB,sBAAsBJ,SAASO,IACxDA,EAAGsN,UAAU7L,OAAO,QAAS,qBAC7BzB,EAAGH,iBAAiB,eAAeJ,SAASqG,GAAMA,EAAErE,UAAQ,GAGlE,CAMA,QAAAqS,CAAS4G,GACHxZ,KAAKgY,aACPhY,KAAKuZ,OAAQ,EAERlY,OAAO4F,iBACV5F,OAAO4F,eAAkBlD,GAAUA,EAAMgC,kBAG3CyT,GAAQ7G,QAAQ,uBAAuBvG,UAAUC,IAAI,SAEzD,CAMA,QAAA6L,CAASna,GACOiC,KAAKmG,cAAc,uCAC3BoK,YAAcxS,CACtB,CAMA,YAAMyS,GACAxQ,KAAKyZ,gBACDzZ,KAAKoY,eAELpY,KAAK0Z,UAEf,CAMA,QAAAA,GACE,GAAI1Z,KAAKyZ,WAAazZ,KAAK2Z,SAAW3Z,KAAKkX,MACzC,OAAO7Q,QAAQC,QAAQ,iCAGzB,MAAMrG,EAAU,IAAIpE,QAAQ+H,QAAQ,SAGpC,OAFA3D,EAAQC,KAAKF,KAAK0X,cAClB1X,KAAK4Z,YAAYxN,WAAWC,IAAI,UACzB2H,GAAKnY,QAAQ2Z,OAAOqE,4BAA4B7Z,KAAKmX,YACzDpQ,MAAMkH,IACL,MAAMrL,EAAOqL,EAASrL,KAMtB,GAJA5C,KAAKyZ,WAAY,EACjBzZ,KAAK0X,cAAczM,aAAa,QAASrI,EAAK7E,OAG1C6E,EAAKkX,iBAAiBzU,OAAQ,CAChC,MAAM0U,EAAWnX,EAAKkX,iBACnB/D,KAAKX,GAAO,YAAYA,MACxB4E,KAAK,MACRha,KAAKrB,iBAAiBob,GAAUxb,SAAS0b,IACvCA,EAAcR,WAAY,EAC1BQ,EAAcvC,cAAczM,aAAa,QAASrI,EAAK7E,MAAK,GAEhE,KAEDmc,OAAOxV,IACNmD,EAAMnD,EAAMe,QAAS,SACrBtJ,QAAQuI,MAAMA,EAAK,IAEpByV,SAAQ,KACPna,KAAK4Z,YAAYxN,WAAW7L,OAAO,UACnCN,EAAQK,MAAI,GAElB,CAMA,MAAA8X,GACE,GAAIpY,KAAKoa,WAAapa,KAAK2Z,QACzB,OAAOtT,QAAQC,QAAQ,gCAGzB,GAAItG,KAAK2Z,SAAW3Z,KAAKqa,oBACvB,OAAOra,KAAKqa,oBAAoBjC,SAC3B,CACL,MAAMnY,EAAU,IAAIpE,QAAQ+H,QAAQ,SAIpC,OAHA3D,EAAQC,KAAKF,KAAK0X,cAClB1X,KAAK4Z,YAAYxN,UAAUC,IAAI,UAExB,IAAIhG,SAAQ,CAACC,EAAS+S,KAC3BrF,GAAKnY,QAAQ2Z,OAAO8E,0BAA0Bta,KAAKmX,YAChDpQ,MAAMkH,IACL,MAAMrL,EAAOqL,EAASrL,KAGtB,GAAIA,EAAK2X,iBAAiBlV,OAAQ,CAChC,MAAM0U,EAAWnX,EAAK2X,iBACnBxE,KAAKX,GAAO,YAAYA,MACxB4E,KAAK,MACR3d,SAASsC,iBAAiBob,GAAUxb,SAASic,IAC3CA,EAAcf,WAAY,EAC1Be,EAAc9C,cAAczM,aAAa,QAASrI,EAAK7E,MAAK,GAEhE,CAEAiC,KAAKyZ,WAAY,EACjBzZ,KAAK0X,cAAczM,aAAa,QAASrI,EAAK7E,OAE9CuI,GAAO,IAER4T,OAAOxV,IACNmD,EAAMnD,EAAMe,QAAS,SACrBtJ,QAAQuI,MAAMA,GACd2U,EAAO3U,EAAK,IAEbyV,SAAQ,KACPna,KAAK4Z,YAAYxN,WAAW7L,OAAO,UACnCN,EAAQK,MAAI,GACb,GAEP,CACF,CAOA,WAAAuY,CAAY9a,GACViC,KAAKkY,SAASna,GACdiC,KAAKuD,cACH,IAAIC,YAAY,+BAAgC,CAC9CC,SAAS,EACTC,OAAQ,CAAE3F,WAGhB,CAMA,aAAI0c,CAAUC,GACRA,EACF1a,KAAKoM,UAAU7L,OAAO,kBAEtBP,KAAKoM,UAAUC,IAAI,iBAEvB,CAMA,aAAIoO,GACF,OAAQza,KAAKoM,UAAU6G,SAAS,SAClC,CAKA,WAAI0G,GACF,OAAwC,OAAjC3Z,KAAKuK,aAAa,UAC3B,CAKA,SAAI2M,GACF,OAAsC,OAA/BlX,KAAKuK,aAAa,QAC3B,CAKA,aAAIkP,CAAUnQ,GACZtJ,KAAKoM,UAAUoE,OAAO,SAAUlH,GAChCtJ,KAAKoM,UAAUoE,OAAO,YAAalH,GACnCtJ,KAAK4Z,aACF5Z,KAAK4Z,WAAWnO,KAAOnC,EAAQ,eAAiB,eACrD,CAKA,aAAImQ,GACF,OAAOzZ,KAAKoM,UAAU6G,SAAS,SACjC,CAKA,YAAImH,GACF,OAAQpa,KAAKyZ,SACf,CAOA,SAAIF,CAAMjQ,GACRtJ,KAAKoM,UAAUoE,OAAO,QAASlH,EACjC,CAOA,SAAIiQ,GACF,OAAOvZ,KAAKoM,UAAU6G,SAAS,QACjC,CAOA,UAAIwE,GACF,OAAOzX,KAAKmG,cAAc,kBAC5B,CAUA,QAAI/F,GACF,OAAOJ,KAAKmG,cAAcnG,KAAK2a,aACjC,CAEA,gBAAIA,GACF,MAAO,IAAI3a,KAAKoV,oBAClB,CAUA,UAAIwF,GACF,OAAO5a,KAAKmG,cAAc,IAAInG,KAAKoV,uBACrC,CAOA,gBAAIsC,GACF,OAAO1X,KAAKmG,cAAc,kBAC5B,CAOA,cAAIyT,GACF,OAAO5Z,KAAK0X,cAAcvR,cAAc,eAC1C,CAOA,iBAAIwS,GACF,OAAO3Y,KAAKI,KAAK+F,cAAc,kBACjC,CAOA,aAAIgR,GACF,OAAOnX,KAAKZ,QAAQ+X,SACtB,CAOA,eAAI0D,GACF,OAAO7a,KAAKZ,QAAQyb,WACtB,CAOA,cAAI7C,GACF,QAAShY,KAAKI,MAAM+F,cAAc,8BACpC,CAOA,eAAI2U,GACF,QAAS9a,KAAKmG,cAAc,mBAC9B,CAOA,cAAI8R,GACF,OAAOjY,KAAKmG,cAAc,yBAC5B,CAOA,QAAI8I,GACF,OAAOjP,KAAKmG,cAAc,oBAC5B,CAOA,uBAAIkU,GACF,OAAOra,KAAKwa,eAAe7H,QAAQ,yBACrC,CAEA,iBAAIiF,GACF,OAAOvb,SAASmL,eAAe,yBACjC,EAGFiD,eAAeC,OAAO,yBAA0B8M,IClkBhD,MAAMnK,GAAmB0N,GAChB,kDAEDA,EAAOC,aAAaD,EAAOxb,8BAiBnC,MAAM0b,WAAsB9P,YAC1B,WAAArL,GACE2I,OACF,CAEA,iBAAAQ,GACE,MAAM+E,EAAUhO,KAAKkB,QACfA,EAAU,CACdsQ,wBAAyB,EACzB0J,mBAAmB,EACnBtY,KAAI,KACK,CAAEoL,YAEXR,aAAc,CAACuN,EAAQtN,EAAK6G,KAC1B,IAAI/U,EAEJ,MAAkB,KAAdwb,EAAO3F,GAAkB2F,EAAOxb,MAElCA,EADkB,KAAhB+U,EAAOvI,KACFD,EAAeiP,EAAOxb,KAAM+U,EAAOvI,MAEnCgP,EAAOxb,KAhCL,EAACyb,EAAMzb,EAAM4b,KAC9B,MAAMC,EAAcD,EAChB,2CAA2CA,UAC3C,GACJ,MAAO,kDAED9N,GAAgB,CAAE2N,OAAMzb,mBACxB6b,mBAAW,EA4BJvK,CAAWkK,EAAOC,KAAMzb,EAAMwb,EAAOI,MAAI,EAElD9N,mBACAP,YAAa9M,KAAK8M,aAEpB9P,EAAEgD,KAAK6R,YAAY1U,QAAQ+D,EAC7B,CAEA,WAAIA,GACF,OAAO0K,KAAKC,MAAM7L,KAAKuK,aAAa,WACtC,CAEA,eAAIuC,GACF,OAAO9M,KAAKuK,aAAa,cAC3B,CAEA,cAAIsH,GACF,OAAO7R,KAAKmG,cAAc,QAC5B,EAGFsE,eAAeC,OAAO,yBAA0BuQ,IChEhD,MAAMI,WAAuBlQ,YAC3BmQ,IAAW,EACXC,GAAc,KAEd,WAAAzb,GACE2I,QACAzI,MAAKkJ,GACP,CAEA,iBAAAD,GACEjJ,KAAK0X,cAAcpZ,iBAAiB,SAAUd,IAC5CA,EAAIuI,iBACJ/F,KAAKwQ,QAAM,IAETnP,OAAO+S,SAASe,MAClBnV,KAAKwb,mBAAmBna,OAAO+S,SAASe,MAE1CnV,KAAK2B,QACP,CAEA,mBAAA8Z,GACEzb,KAAKrB,iBACH,sDACAJ,SAASib,GAAWA,EAAOE,YAC/B,CAEA,MAAAlJ,GACExQ,MAAKsb,EAAWtb,KAAKoD,OAASpD,KAAK2D,MACrC,CAEA,IAAAA,GACEtH,SAAS+D,KAAKgM,UAAUC,IAAI,2BAC5BrM,MAAKsb,GAAW,EAChBtb,KAAK0X,aAAa/E,QAAQ,cAAchT,QAAU9D,QAAQmC,EAAE,iBAC5DgC,KAAK0X,aACFvR,cAAc,gBACd8E,aAAa,OAAQ,eACxBjL,KAAK2B,QACP,CAEA,IAAAyB,GACE/G,SAAS+D,KAAKgM,UAAU7L,OAAO,2BAC/BlE,SAAS+D,KAAK0H,MAAM4T,eAAe,2BACnC1b,MAAKsb,GAAW,EAChBtb,KAAK0X,aAAa/E,QAAQ,cAAchT,QAAU9D,QAAQmC,EAAE,iBAC5DgC,KAAK0X,aACFvR,cAAc,gBACd8E,aAAa,OAAQ,YAC1B,CAEA,MAAAtJ,CAAOJ,QACS7E,IAAV6E,IACFA,EAAQvB,KAAK2b,iBAGXpa,IACFlF,SAAS+D,KAAK0H,MAAM8T,YAAY,0BAA2B,GAAGra,OAC9DlF,SAASwf,OAAS,iCAAiCta,2BAEvD,CAIA,kBAAAia,CAAmB1F,GACjB,MAAMhX,EAAKzC,SAAS8J,cAAc2P,GAE9BhX,aAAc0Y,IAChB1Y,EAAG6Y,gBAAkB7Y,EAAG+Y,qBAE5B,CAEA,kBAAIiE,GACF,OAAO9b,KAAKmG,cAAc,gCAC5B,CAEA,gBAAIuR,GACF,OAAOrb,SAAS8J,cAAc,yBAChC,CAEA,iBAAIyR,GACF,OAAOvb,SAASmL,eAAe,yBACjC,CAEA,cAAI+T,GAIF,OAHKvb,MAAKub,IACRvb,MAAKub,EAAcvb,KAAK2S,QAAQ,gBAE3B3S,MAAKub,CACd,CAEA,mBAAII,GACF,OAAOtf,SAASwf,OACb7f,MAAM,MACN0K,MAAMqV,GAAQA,EAAIC,WAAW,qCAC5BhgB,MAAM,KAAK,EACjB,CAEA,aAAI8N,CAAUC,GACZ/J,KAAKub,WAAWzT,MAAMkC,mBAAqBD,EAAU,OAAS,KAC9D/J,KAAKub,WAAWzT,MAAMmC,cAAgBF,EAAU,OAAS,IAC3D,CAEA,EAAAb,GACElJ,KAAK8b,gBAAgBxd,iBAAiB,SAAS,KAC7C0B,KAAKyb,qBAAmB,IAE1Bpa,OAAO/C,iBAAiB,WAAYyF,IAClC,MAAMnB,EAAOmB,EAAMnB,KACnB,GAAqB,8BAAjBA,GAAM6C,QAAyC,CACjD,MAAMjG,EAAUnD,SAASmL,eAAe,WAAW5E,EAAK0E,cACxDtH,KAAK2D,OACLnE,GAASmY,cACX,KAEFtb,SAAS+D,KAAK9B,iBAAiB,SAAUd,IAClCA,EAAIC,OAAOkV,QAAQ,4BACtB3S,KAAKrB,iBAAiB,0BAA0BJ,SAASib,IACvDA,EAAOpN,UAAU7L,OAAO,WAAU,IAEpCP,KAAK4X,eAAenO,YAAY,CAAEhE,QAAS,yBAC7C,GAEJ,EAGFgF,eAAeC,OAAO,0BAA2B2Q,IC/HjD,MAAMY,WAA6B9Q,YACjC+Q,IAAY,EACZC,GAAkB,KAClBvE,GAAiB,KAEjB,WAAA9X,GACE2I,QAEAzI,KAAK1B,iBAAiB,YAAa0B,MACnCqB,OAAO/C,iBAAiB,YAAa0B,MACrCqB,OAAO/C,iBAAiB,UAAW0B,KACrC,CAEA,WAAA4I,CAAY7E,GACV,OAAQA,EAAM8E,MACZ,IAAK,YACH9E,EAAMsL,kBACNrP,KAAKoc,cACL,MACF,IAAK,UACHpc,KAAKqc,YACL,MACF,IAAK,YACCrc,MAAKkc,GACPlc,KAAKsc,OAAOvY,EAAMwY,OAI1B,CAEA,WAAAH,GACEpc,MAAKkc,GAAY,EACjBlc,KAAKmc,eAAerS,WAAY,EAChC9J,KAAK4X,cAAc9N,WAAY,EAC/B9J,KAAKoM,UAAUC,IAAI,aACrB,CAEA,SAAAgQ,GACErc,MAAKkc,GAAY,EACjBlc,KAAKmc,eAAerS,WAAY,EAChC9J,KAAK4X,cAAc9N,WAAY,EAC/B9J,KAAKoM,UAAU7L,OAAO,aACxB,CAEA,MAAA+b,CAAOC,GACL,MAAMC,EAAqBnb,OAAOob,WAAaF,EAC/Cvc,KAAKmc,eAAexa,OAAO6a,EAC7B,CAEA,kBAAIL,GAIF,OAHKnc,MAAKmc,IACRnc,MAAKmc,EAAkB9f,SAAS8J,cAAc,4BAEzCnG,MAAKmc,CACd,CAEA,iBAAIvE,GAIF,OAHK5X,MAAK4X,IACR5X,MAAK4X,EAAiBvb,SAASmL,eAAe,2BAEzCxH,MAAK4X,CACd,EAGFnN,eAAeC,OAAO,iCAAkCuR,IChExD,MAAMS,WAAmBvR,YACvB,WAAArL,GACE2I,QACAzI,KAAK2c,WAAa3c,KAAKmG,cAAc,qBACrCnG,KAAK4c,SAAW5c,KAAKmG,cAAc,cACnCnG,KAAK6c,SAAW7c,KAAKmG,cAAc,cACnCnG,KAAK8c,YAAc9c,KAAK2c,YAAYvd,QAAQ0d,YAC5C9c,KAAK8P,UAAY9P,KAAKmG,cAAc,IAAInG,KAAK8c,eAC7C9c,KAAK2c,YAAYre,iBAAiB,QAAS0B,KAC7C,CAEA,WAAA4I,CAAY7E,GACS,UAAfA,EAAM8E,MAAkB7I,KAAK+c,aACjChZ,EAAMsL,iBACR,CAEA,UAAA0N,GACE/c,KAAK8P,UAAUxG,MAAQ,GACvBtJ,KAAK4c,SAASld,UAAY,GAC1BM,KAAK6c,SAASnd,UAAY,GAC1BM,KAAK2c,YAAYvQ,UAAUC,IAAI,UAC/BrM,KAAK2S,QAAQ,0BAA0BC,SAAS5S,KAAK8P,UACvD,EAGFrF,eAAeC,OAAO,sBAAuBgS,ICvB7C,MAAMM,WAAmB7R,YACvB8R,GAEA,WAAAnd,GACE2I,QACAzI,MAAKkJ,GACP,CAEA,EAAAA,GACMlJ,KAAKxB,QACP/C,IAAIuE,KAAKxB,QAAQ,KACfwB,KAAKkd,YAAYne,SACV,KAGXiB,KAAKkd,YAAY5e,iBAAiB,SAAS,KACzCgM,aAAatK,MAAKid,GAClBjd,MAAKid,EAAiBpT,YAAW,KAC/B,MAAMkC,EAAO/L,KAAKkd,YAAY5T,MAC9BtJ,KAAKmd,YAAYrV,MAAMsV,WAAarR,EAAO,UAAY,SACvD/L,KAAK4V,OAAO7J,EAAI,GACf/L,KAAKqd,aAAY,IAEtBrd,KAAKmd,YAAY7e,iBAAiB,SAAUsG,IAC1CA,EAAEmB,iBACF/F,KAAKsd,OAAK,IAEZtd,KAAKkd,YAAY5e,iBAAiB,SAAS,IACzC7C,IAAIoD,SAAS,iBAEfpD,IAAI,MAAO,eAAe,KACxBuE,KAAKsd,QACLtd,KAAKkd,YAAY/d,MAAI,GAEzB,CAEA,oBAAAqK,GACMxJ,KAAKxB,QACP/C,IAAIgD,OAAOuB,KAAKxB,QAElB/C,IAAIgD,OAAO,MAAO,cACpB,CAEA,MAAAmX,CAAO7J,GACQ,KAATA,IACF/L,KAAKmd,YAAYrV,MAAMsV,WAAa,UAGtC,MAAMG,EAAe,GACfC,EAAc,IAAIC,IAClBC,EAAY3R,EAAKuH,cAGvBtT,KAAK2d,MAAMpf,SAAS+O,IAClB,MAAM7B,EAAO6B,EAAK/C,aAAavK,KAAK4d,gBAAgBtK,cAEpD,IAAgC,IAA5B7H,EAAKoS,QAAQH,GAAmB,CAClCH,EAAate,KAAKqO,GAClBkQ,EAAYnR,IAAIiB,GAEhB,IAAIwQ,EAAWxQ,EAAKkN,eAAe7H,QAAQ3S,KAAK+d,eAChD,KAAOD,GACLN,EAAYnR,IAAIyR,GAChBA,EAAWA,EAAStD,eAAe7H,QAAQ3S,KAAK+d,cAEpD,KAIF/d,KAAK2d,MAAMpf,SAAS+O,IAClBA,EAAKlB,UAAUoE,OAAO,UAAWgN,EAAYQ,IAAI1Q,GAAK,IAI5B,IAAxBiQ,EAAalY,QACfkY,EAAa,GAAGrE,eAAe,CAAEC,SAAU,SAAU8E,MAAO,WAEhE,CAEA,KAAAX,GACEtd,KAAKkd,YAAY5T,MAAQ,GACzBtJ,KAAKmd,YAAYrV,MAAMsV,WAAa,SACpCpd,KAAK2d,MAAMpf,SAAS+O,GAASA,EAAKlB,UAAU7L,OAAO,WACrD,CAEA,iBAAIqd,GACF,OAAO5d,KAAKuK,aAAa,mBAAqB,MAChD,CAEA,eAAI4S,GACF,OAAOnd,KAAKmG,cAAc,wBAC5B,CAEA,eAAI+W,GACF,OAAOld,KAAKmG,cAAc,qBAC5B,CAEA,SAAIwX,GACF,OAAOthB,SAASsC,iBAAiBqB,KAAK+d,cACxC,CAEA,iBAAIA,GACF,OAAO/d,KAAKuK,aAAa,iBAC3B,CAEA,gBAAI8S,GACF,OAAO7b,SAASxB,KAAKuK,aAAa,mBA5GR,GA6G5B,CAEA,UAAI/L,GACF,OAAOwB,KAAKuK,aAAa,SAC3B,EAGFE,eAAeC,OAAO,sBAAuBsS,IClH7C,MAAMkB,WAAgB/S,YACpB1F,GAEA,WAAA3F,GACE2I,QACAzI,MAAKyF,EAAWzF,KAAKN,WACjBM,KAAKme,aAA6B,UAAdne,KAAK6I,OAC3B7I,KAAK1B,iBAAiB,QAAS0B,KAEnC,CAEA,WAAA4I,CAAY7E,GACS,UAAfA,EAAM8E,MACR7I,KAAKoe,SAET,CAEA,iBAAAnV,GACEjJ,KAAKN,UAAY,+BACOM,KAAKqe,oCACzBre,KAAKme,aAA6B,UAAdne,KAAK6I,KAAmB,6CAA+C,aAC3F7I,MAAKyF,UAELzF,KAAKme,aAA6B,UAAdne,KAAK6I,MAC3BgB,YAAW,KACT7J,KAAKoe,SAAO,GACXpe,KAAKse,aAEZ,CAEA,OAAAF,GACEpe,KAAK1B,iBAAiB,iBAAiB,IAAM0B,KAAKO,WAClDP,KAAKoM,UAAUC,IAAI,YACrB,CAEA,eAAI8R,GACF,OAAOne,KAAK2L,aAAa,cAC3B,CAEA,QAAIqP,GACF,OAAOhb,KAAKuK,aAAa,OAC3B,CAEA,QAAI1B,GACF,OAAO7I,KAAKuK,aAAa,SAAW,QACtC,CAEA,gBAAI+T,GACF,OAAO9c,SACLxB,KAAKue,gBAAgBnf,QAAQof,kBAnDb,IAqDpB,CAEA,YAAIH,GACF,OAAQre,KAAKgb,MAAQhb,KAAK6I,MACxB,IAAK,UACL,IAAK,OACL,IAAK,QACH,MAAO,QACT,IAAK,SACH,MAAO,QACT,IAAK,OACL,IAAK,OACH,MAAO,cACT,IAAK,QACH,MAAO,MACT,QACE,OAAO7I,KAAK6I,KAElB,CAEA,kBAAI0V,GACF,OAAOve,KAAK2S,QAAQ,iBACtB,EAGFlI,eAAeC,OAAO,kBAAmBwT,IC5EzC,MAAMO,WAActT,YAClB,iBAAAlC,GACEpB,EAAM7H,KAAKyF,QAASzF,KAAKuK,aAAa,SAAW,UACjDvK,KAAKO,QACP,CAEA,WAAIkF,GACF,OAAOzF,KAAKuK,aAAa,YAAcvK,KAAKN,SAC9C,EAGF+K,eAAeC,OAAO,gBAAiB+T,ICbvC,MAAMC,WAAavT,YACjB,6BAAWwT,GACT,MAAO,CAAC,OAAQ,OAAQ,aAC1B,CAEA,WAAA7e,GACE2I,QACAzI,KAAK4e,UAAYviB,SACd8J,cAAc,mCACdoE,aAAa,OAClB,CAEA,iBAAAtB,GACEjJ,KAAK6e,QACP,CAEA,wBAAAC,GACE9e,KAAK6e,QACP,CAEA,MAAAA,GACE,MAAME,EAAY/e,KAAK/B,KAAO,UAAU+B,KAAK/B,OAAS,GACtD+B,KAAKN,UAAY,mBAAmBqf,iBAAyB/e,KAAK4e,gBAAgB5e,KAAKqe,WAAWre,KAAK8H,iBACzG,CAEA,QAAI2D,CAAKnC,GACPtJ,KAAKiL,aAAa,OAAQ3B,EAC5B,CAEA,YAAI+U,GACF,OAAOre,KAAKuK,aAAa,OAC3B,CAEA,QAAItM,GACF,OAAO+B,KAAKuK,aAAa,OAC3B,CAEA,SAAIzC,GACF,MAAMwB,EAAQtJ,KAAKuK,aAAa,cAChC,OAAQjB,GACN,IAAK,OACH,MAAO,GACT,KAAK,KACH,MAAO,QACT,QACE,MAAO,IAAIA,IAEjB,EAGFmB,eAAeC,OAAO,eAAgBgU,IClD/B,MAAMM,WAAwBC,mBACnCC,GAAmB,qCAEnB,WAAApf,GACE2I,QAEAzI,KAAK1B,iBAAiB,SAAU0B,MAE5BA,KAAKmf,mBACPnf,KAAK4B,MAAO,EAEhB,CAKA,WAAAgH,GACE,IAAIwW,EAA6Bpf,KAAKqf,iBAElCrf,KAAK4B,MACP5B,KAAK4Z,WAAWnO,KAAO,eAClBzL,KAAKmf,kBAAkBC,EAA2BngB,KAAKe,KAAKoV,MAEjEpV,KAAK4Z,WAAWnO,KAAO,eACvB2T,EAA6BA,EAA2BxJ,QACrDtM,GAAUA,IAAUtJ,KAAKoV,MAI9BjM,aAAaiB,QACXpK,MAAKkf,EACLtT,KAAKiI,UAAUuL,GAEnB,CAEA,oBAAID,GACF,OAAOnf,KAAKqf,iBAAiBhV,SAASrK,KAAKoV,GAC7C,CAEA,oBAAIiK,GACF,MAAM/R,EAAOnE,aAAaC,QAAQpJ,MAAKkf,GAEvC,IAAK5R,EAAM,MAAO,GAElB,IACE,OAAO1B,KAAKC,MAAMyB,EACpB,CAAE,MAAO5I,GAEP,OADAvI,QAAQuI,MAAMA,GACP,EACT,CACF,CAEA,cAAIkV,GACF,OAAO5Z,KAAKmG,cAAc,eAC5B,EAGFsE,eAAeC,OAAO,2BAA4BsU,GAAiB,CACjErU,QAAS,YC1DX,MAAM2U,WAAmBnQ,kBACvB,WAAArP,GACE2I,QACAzI,KAAK1B,iBAAiB,QAAS0B,MAC/BA,KAAKoM,UAAUC,IAAI,eAEnBrM,KAAKiL,aAAa,OAAQ,UAC1BjL,KAAKN,UAAY,0DACnB,CAEA,WAAAkJ,CAAY7E,GACK,IAAIlI,QAAQ0jB,WAAW,CACpCte,IAAKjB,KAAKwf,QACVzhB,MAAOiC,KAAKyf,UACZhiB,OAAQuC,KAAK0f,WACb7W,KAAM7I,KAAK2f,YAEN/d,OAAOmF,MAAM6Y,GAAS5f,KAAK6f,QAAQD,KAC1C7b,EAAMgC,gBACR,CAEA,OAAA8Z,CAAQD,GACW,KAAbA,EAAK3e,KACPjB,KAAKoM,UAAU7L,OAAO,UACtBP,KAAKuD,cAAc,IAAIC,YAAY,iBAAkB,CAAEC,SAAS,OAEhEzD,KAAKoM,UAAUC,IAAI,UACnBrM,KAAKuD,cACH,IAAIC,YAAY,eAAgB,CAC9BC,SAAS,EACTC,OAAQkc,KAIhB,CAEA,WAAIJ,GACF,OAAOxf,KAAK8f,YAAYC,aAAazW,KACvC,CAEA,aAAImW,GACF,OAAOzf,KAAK8f,YAAYE,eAAe1W,KACzC,CAEA,cAAIoW,GACF,OAAO1f,KAAK8f,YAAYG,gBAAgB3W,KAC1C,CAEA,aAAIqW,GACF,OAAO3f,KAAK8f,YAAYI,eAAe5W,KACzC,CAEA,eAAIwW,GACF,OAAO9f,KAAK2S,QAAQ,uBACtB,EAGFlI,eAAeC,OAAO,sBAAuB4U,GAAY,CAAE3U,QAAS,WCzDpE,MAAMwV,WAAqBhR,kBACzB,WAAArP,GACE2I,QACAzI,KAAK1B,iBAAiB,QAAS0B,MAC/BA,KAAKoM,UAAUC,IAAI,eAEnBrM,KAAKiL,aAAa,OAAQ,UAC1BjL,KAAKogB,OAASpgB,KAAKogB,OACnBpgB,KAAKN,UACH,iEACJ,CAEA,WAAAkJ,CAAY7E,GACN/D,KAAKogB,SACPpgB,KAAKogB,QAAS,EACdpgB,KAAKb,OACLa,KAAKuD,cAAc,IAAIC,YAAY,iBAAkB,CAAEC,SAAS,MAElEM,EAAMgC,gBACR,CAEA,UAAIqa,CAAOC,GACLA,GACFrgB,KAAKoM,UAAUxP,QAAQ,WAAY,UACnCoD,KAAK2P,gBAAgB,cAErB3P,KAAKoM,UAAUxP,QAAQ,SAAU,YACjCoD,KAAKiL,aAAa,WAAY,MAElC,CAEA,UAAImV,GACF,OAAOpgB,KAAKoM,UAAU6G,SAAS,SACjC,EAGFxI,eAAeC,OAAO,wBAAyByV,GAAc,CAC3DxV,QAAS,WClCX,MAAM2V,WAAoBnV,YACxB,WAAArL,GACE2I,QACAzI,KAAK1B,iBAAiB,eAAgB0B,MACtCA,KAAK1B,iBAAiB,iBAAkB0B,KAC1C,CAEA,WAAA4I,CAAY7E,GACV,OAAQA,EAAM8E,MACZ,IAAK,eACH7I,KAAK6f,QAAQ9b,EAAML,QACnB,MACF,IAAK,iBACH1D,KAAKugB,aAETxc,EAAMsL,iBACR,CAEA,OAAAwQ,CAAQjd,GACN5C,KAAK+f,aAAazW,MAAQ1G,EAAK3B,IAC/BjB,KAAK+f,aAAaxc,cAAc,IAAIwL,MAAM,WAC1C/O,KAAKggB,eAAe1W,MAAQ1G,EAAK7E,MACjCiC,KAAKkgB,eAAe5W,MAAQ1G,EAAKiG,KACjC7I,KAAKigB,gBAAgB3W,MAAQ1G,EAAKnF,OAElCuC,KAAKwgB,aAAaJ,QAAS,EAC3BpgB,KAAKygB,iBACP,CAEA,UAAAF,GACEvgB,KAAK+f,aAAazW,MAAQ,GAC1BtJ,KAAK+f,aAAaxc,cAAc,IAAIwL,MAAM,WAC1C/O,KAAKggB,eAAe1W,MAAQ,GAC5BtJ,KAAKkgB,eAAe5W,MAAQ,GAC5BtJ,KAAKigB,gBAAgB3W,MAAQ,GAE7BtJ,KAAK0gB,WAAWtU,UAAU7L,OAAO,UACjCP,KAAKwgB,aAAaJ,QAAS,EAE3BpgB,KAAKygB,iBACP,CAEA,eAAAA,GACEzgB,KAAKiX,cAAcrE,SAAS5S,KAC9B,CAEA,cAAI0gB,GACF,OAAO1gB,KAAKmG,cAAc,6BAC5B,CAEA,gBAAIqa,GACF,OAAOxgB,KAAKmG,cAAc,+BAC5B,CAEA,oBAAI6E,GACF,MAAMF,EAAe9K,KAAKZ,QAAQ0L,aAClC,OAAO9K,KAAKwa,cAAc7H,QAAQ,wBAAwB7H,MAC5D,CAEA,iBAAImM,GACF,OAAOjX,KAAK2S,QAAQ,yBACtB,CAEA,gBAAIoN,GACF,OAAO/f,KAAKgL,iBAAiB7E,cAAc,oBAC7C,CAEA,kBAAI6Z,GACF,OAAOhgB,KAAKgL,iBAAiB7E,cAAc,oBAC7C,CAEA,mBAAI8Z,GACF,OAAOjgB,KAAKgL,iBAAiB7E,cAAc,qBAC7C,CAEA,kBAAI+Z,GACF,OAAOlgB,KAAKgL,iBAAiB7E,cAAc,oBAC7C,EAGFsE,eAAeC,OAAO,uBAAwB4V,ICnFvC,SAASK,GAAeC,GAC7B,IAAIC,EAAqB,IAAVD,EAAc,EAAIE,KAAKC,MAAMD,KAAKE,IAAIJ,GAASE,KAAKE,IAAI,OAQvE,OALIH,EAAW,IACbA,EAAW,IAGAD,EAAQE,KAAKG,IAAI,KAAMJ,IAAWK,QAAQ,GACxC,IAAM,CAAC,IAAK,KAAM,KAAM,MAAML,EAC/C,CCqCApW,eAAeC,OAAO,sBA7CtB,cAAyBuB,EACvB,YAAA8B,CAAahC,EAAM+B,GACjB,MAAO,CACL8H,OAAQ,CACNuL,uBAAwBpV,KACrBH,KAAKC,MAAM7L,KAAK+M,cAErBe,KAAMA,EAEV,CAEA,aAAAP,CAAcD,GACZ,OAAOtN,KAAK2N,iBAAiBL,EAC/B,CASA,gBAAAK,CAAiByT,EAAMrV,GACrB,MAAMsV,EAAYD,EAAKC,UAAUtL,KAAKuL,GAAMA,EAAE7V,OACxC8V,EAAY,qDAElB,MAAO,yNAKGF,EAAUhc,OAAS,EAAIgc,EAAUrH,KAAKuH,GAAaA,EAAY,uFAG/DvhB,KAAK0O,gBAAgB0S,EAAK3V,KAAMM,iGAIlCqV,EAAKngB,KAAO,wCAItB,ICxCK,MAAMugB,WAAmBrW,YAE9BsW,KAAO,KACPC,QAAU,KACVC,oBAAsB,EACtBC,mBAAqB,EAGrBC,IAAS,EACTvY,GAAS,EACTlF,QAAU1H,EACV+b,GAAgB,GAEhB,iBAAAxP,GAiBE,GAhBAjJ,KAAKN,UAAY,mCACWM,KAAKsJ,+FAEHtJ,KAAKyhB,MAAMhW,kDACTzL,KAAK8hB,0DACH9hB,KAAKyY,iEAEdjc,EAAU,sEACWA,EAAU,wHAMxDwD,KAAKmG,cAAc,UAAU7H,iBAAiB,SAAS,IAAM0B,KAAK+hB,WAE9D/hB,KAAKyhB,MAAM5Y,KAAKwB,SAAS,SAAU,CACrC,MAAM2X,EAAS,IAAIC,WACnBD,EAAOE,cAAcliB,KAAKyhB,MAC1BO,EAAO1jB,iBAAiB,QAAQ,KAC9B,MAAM6jB,EAAQ,IAAIC,MAClBD,EAAMvY,IAAMoY,EAAOK,OACnBriB,KAAKsiB,QAAQH,EAAK,GAEtB,CACF,CAOA,UAAAI,CAAWd,EAAMC,GACf1hB,KAAKyhB,KAAOA,EACZzhB,KAAK0hB,QAAUA,EACf1hB,KAAK4hB,mBAAqBH,EAAOA,EAAKxjB,KAAO,EAC7C+B,KAAKoE,OAAS,cAEdpE,MAAKwiB,IACLxiB,MAAKyiB,GACP,CAKA,MAAAV,GACO/hB,KAAK0iB,WACR1iB,KAAKoE,OAAS,WACdpE,KAAK0hB,SAASiB,QACd3iB,KAAKyM,oBAAoB,qBAE7B,CAMA,mBAAAA,CAAoBhB,GAClBzL,KAAKuD,cAAc,IAAIC,YAAY,WAAWiI,IAAQ,CAAEhI,SAAS,IACnE,CAKA,EAAA+e,GACE,MAAMI,EAAS/mB,QAAQgnB,kBACjBC,EAAcF,EAAOG,gBAAkBjC,KAAKG,IAAI,KAAM,GAC5D,IAAIxI,EAEAzY,KAAKyhB,MAAMxjB,KAAO6kB,IACpBrK,EAAejc,EAAU,oCAG3B,MAAMwmB,EAAmBhjB,KAAKyhB,MAAM5Y,KAAKwB,SAAS,SAC9CuY,EAAOK,kBAAkBC,iBACzBN,EAAOK,kBAAkBE,oBAG3BH,EAAiB3Y,SAAS,MAC1B2Y,EAAiB3Y,SACfrK,KAAKyhB,MAAM5Y,KAAKjM,QAAQ,uBAAwB,SAIlD6b,EAAejc,EAAU,0BAGvBic,IACFzY,KAAK6hB,OAAQ,EACb7hB,KAAKyY,aAAeA,EAExB,CAKA,EAAAgK,GAEOziB,KAAK0hB,UAKV1hB,KAAK0hB,QAAQ0B,OAAOC,WAAcC,IAChCtjB,KAAKsjB,cAAgBA,CAAA,EAIvBtjB,KAAK0hB,QAAQ6B,OAAS,KAChBvjB,KAAK0hB,QAAQtd,OAAS,KACxBpE,KAAKoE,OAAS,aACdyD,EAAM7H,KAAKwjB,mBAEXxjB,KAAKoE,OAAS,SACdpE,KAAKyY,aAAezY,KAAKwjB,iBAE3BxjB,KAAKyM,oBAAoB,oBAAmB,EAI9CzM,KAAK0hB,QAAQ+B,QAAU,KACrBzjB,KAAKyY,aAAejc,EAAU,2CAA0C,EAE5E,CAKA,UAAIuO,GACF,OAAO/K,KAAK6hB,OAAyB,aAAhB7hB,KAAKoE,MAC5B,CAKA,gBAAIqU,GACF,OAAOzY,MAAKyY,GAAiB,EAC/B,CAKA,gBAAIA,CAAahT,GACfzF,MAAKyY,EAAgBhT,EACrB,MAAMie,EAAwB1jB,KAAKmG,cAAc,kBAC7Cud,IACFA,EAAsBnT,YAAc9K,GAEtCoC,EAAMpC,EAAS,QACjB,CAKA,YAAIid,GACF,MAAO,CAAC,WAAY,aAAc,UAAUrY,SAASrK,KAAKoE,OAC5D,CAMA,cAAI0d,GACF,MAAO,GAAGnB,GAAe3gB,KAAK2hB,0BAA0BhB,GACtD3gB,KAAK4hB,qBAET,CAKA,mBAAI+B,GACF,OAAO3jB,KAAKmG,cAAc,kBAC5B,CAKA,iBAAImd,CAAcA,GAChBtjB,KAAK2hB,oBAAsB2B,EAAcM,OACzC5jB,KAAK4hB,mBAAqB0B,EAAcO,MAExC7jB,KAAKsJ,MAAQwX,KAAKgD,MAAOR,EAAcM,OAASN,EAAcO,MAAS,KACvE7jB,KAAKmG,cAAc,gBAAgBoK,YAAcvQ,KAAK8hB,UACxD,CAKA,mBAAI0B,GACF,IAEE,OADiB5X,KAAKC,MAAM7L,KAAK0hB,QAAQxd,cAChB,OAC3B,CAAE,MAAOQ,GACP,MAAO,GAAG1E,KAAK0hB,QAAQtd,WAAWpE,KAAK0hB,QAAQvd,YACjD,CACF,CAKA,UAAIC,GACF,OAAOpE,MAAKoE,CACd,CAKA,UAAIA,CAAOA,GACTpE,MAAKoE,EAAUA,EACfpE,KAAKoQ,UAAYhM,EAEjBpE,KAAK2jB,iBAAiBI,gBACpB,gBACW,oBAAX3f,EAEJ,CAKA,SAAIyd,GACF,OAAO7hB,MAAK6hB,CACd,CAKA,SAAIA,CAAMmC,GACRhkB,MAAK6hB,EAASmC,EACdhkB,KAAKoM,UAAUoE,OAAO,WAAYwT,EACpC,CAMA,SAAI1a,GACF,OAAOtJ,MAAKsJ,CACd,CAKA,SAAIA,CAAMA,GACRtJ,MAAKsJ,EAASA,EACVtJ,KAAK2jB,kBACP3jB,KAAK2jB,gBAAgBra,MAAQA,GAGjB,MAAVA,IACFtJ,KAAKoE,OAAS,mBAGhBpE,KAAKyM,oBAAoB,oBAC3B,EAGFhC,eAAeC,OAAO,sBAAuB8W,IC/PtC,MAAMyC,WAAiB9Y,YAE5B+Y,UAAY,EAGZC,GAAe,GACfC,GAAe5nB,EAAU,sBACzB6nB,GAAgB,KAChB/I,IAAW,EACXgJ,GAAoB,IAAMtkB,MAAKukB,IAE/B,iBAAAtb,GA5Be,IAACmb,EAAaF,EA6B3BlkB,KAAKN,WA7BS0kB,EA6BYpkB,MAAKokB,EA7BJF,EA6BkBlkB,KAAKkkB,UA7BT,+JAKlBE,sDACqBA,mKAM5CF,EAAY,EAAI,EAAIA,sEAkBpBlkB,KAAKsb,SAAU,EAEftb,MAAKqkB,EAAgBrkB,KAAKmG,cAAc,UACxCnG,MAAKqkB,EAAc/lB,iBAAiB,SAAS,KACvC0B,KAAK0iB,SACP1iB,KAAKwkB,WAAWxkB,KAAKoE,QAErBpE,KAAK+hB,QACP,IAGF/hB,MAAKmkB,EAAa5lB,SAASkmB,IACzBzkB,KAAKmG,cAAc,mBAAmB9F,OAAOokB,EAAU,IAGzDzkB,MAAKukB,IACLvkB,KAAK1B,iBAAiB,4BAA6B0B,MAAKskB,EAC1D,CAEA,oBAAA9a,GACExJ,KAAK3B,oBACH,4BACA2B,MAAKskB,EAET,CAMA,UAAA/B,CAAW4B,EAAc,IACvBnkB,MAAKmkB,EAAeA,EACpBnkB,KAAKkkB,UAAYC,EAAY9e,MAC/B,CAKA,MAAA0c,GACE/hB,MAAK0kB,IAAiBnmB,SAAS6kB,IAC7BA,EAAOrB,QAAM,IAEf/hB,MAAK2kB,GACP,CAOA,UAAAH,CAAWI,GAAU,CAMrB,EAAAF,GACE,OAAO1kB,MAAKmkB,EAAavO,QAAQwN,GAAWA,EAAOrY,QACrD,CAKA,EAAA4Z,GACE3kB,MAAKokB,EAAe5nB,EAAU,SAC9BwD,MAAKqkB,EAAcQ,UAAY7kB,MAAKokB,EACpCpkB,MAAKqkB,EAAc7J,cAAc7a,QAAUK,MAAKokB,CAClD,CAMA,EAAAU,CAAmBC,GACjB,OAAO/kB,MAAK0kB,IAAiBM,QAC3B,CAACC,EAAa7B,IAAWA,EAAO2B,GAASE,GACzC,EAEJ,CAKA,EAAAV,GACE,MAAMngB,EAASpE,KAAKoE,OACpBpE,KAAKoQ,UAAYhM,EAGjBpE,KAAK2jB,gBAAgBra,MAAQtJ,KAAKklB,cAClCllB,KAAK2jB,gBAAgBI,gBACnB,gBACW,oBAAX3f,GAIFpE,KAAKmG,cAAc,kCAAkCoK,YACnDvQ,KAAKmlB,qBACPnlB,KAAKmG,cAAc,yBAAyBoK,YAC1CvQ,KAAKolB,kBAEHplB,KAAK0iB,UACP1iB,MAAK2kB,IACL3kB,KAAKwkB,WAAWpgB,IAEhBpE,KAAKsb,SAAU,CAEnB,CAKA,YAAIoH,GACF,OAAO1iB,MAAK0kB,IAAiBW,OAAOC,GAAUA,EAAM5C,UACtD,CAKA,qBAAI0C,GACwBplB,MAAK0kB,IAAiB9O,QAC7C2P,GAAiBA,EAAajc,OAAS,MACxCjE,OAKF,OAHErF,KAAKklB,cACsBllB,MAAK0kB,IAAiBrf,OAE5C,GAAGsb,GACR3gB,MAAK8kB,EAAmB,6BACnBnE,GAAe3gB,MAAK8kB,EAAmB,wBAChD,CAKA,wBAAIK,GACF,MAAMK,EAAoBxlB,MAAK0kB,IAAiB9O,QAC7C2P,GAAiBA,EAAajc,OAAS,MACxCjE,OACF,MAAO,GAAGrF,KAAKklB,mBAAmBM,OAChCxlB,MAAK0kB,IAAiBrf,SAE1B,CAKA,mBAAIse,GACF,OAAO3jB,KAAKmG,cAAc,kBAC5B,CAMA,UAAI/B,GACF,MAAMqhB,EAAkBzlB,MAAK0kB,IAAiB3O,KAC3CqN,GAAWA,EAAOhT,YAIrB,OAAIqV,EAAgBpb,SAAS,UACpB,SAIsB,IAA3Bob,EAAgBpgB,OACX,WAILogB,EAAgBJ,OAAOC,GAAUA,IAAUG,EAAgB,KACtDA,EAAgB,GAGlB,aACT,CAKA,iBAAIP,GACF,MAAMQ,EAAY1lB,MAAK0kB,IAAiBM,QACtC,CAACC,EAAa7B,IAAW6B,EAAc7B,EAAO3B,KAAKxjB,MACnD,GAEF,IAAIinB,EAAgBpE,KAAK6E,KACvB3lB,MAAK0kB,IAAiBM,QAAO,CAACC,EAAa7B,KACzC,MAAMwC,EAASxC,EAAO3B,KAAKxjB,KAAOynB,EAClC,OAAOtC,EAAO9Z,MAAQsc,EAASX,IAC9B,IAML,OAHIC,EAAgB,MAClBA,EAAgB,KAEXA,CACT,CAKA,WAAI5J,GACF,OAAOtb,MAAKsb,CACd,CAKA,WAAIA,CAAQA,GACVtb,KAAKoM,UAAUoE,OAAO,UAAW8K,GACjCtb,MAAKsb,EAAWA,CAClB,EAGF7Q,eAAeC,OAAO,0BAA2BuZ,IC9O1C,MAAM4B,WAAiB1a,YAC5B2a,GAAmB,KACnBC,IAAiB,EAEjB,iBAAA9c,GACEjJ,KAAKgmB,UAAU1nB,iBAAiB,SAAU0B,MAAKimB,GAC3CjmB,KAAKkmB,UACPlmB,MAAKmmB,IAEPnmB,KAAK1B,iBAAiB,4BAA6B0B,KACrD,CAEA,oBAAAwJ,GACExJ,KAAKgmB,WAAW3nB,oBAAoB,SAAU2B,MAAKimB,GAC/CjmB,MAAK8lB,IACP9lB,MAAK8lB,EAAiBznB,oBACpB,YACA2B,MAAKomB,GAEPpmB,MAAK8lB,EAAiBznB,oBAAoB,OAAQ2B,MAAKqmB,GACvDrmB,MAAK8lB,EAAiBznB,oBACpB,WACA2B,MAAKsmB,GAEPtmB,MAAK8lB,EAAmB,KAE5B,CAEA,WAAAld,CAAYpL,GACV,GACO,8BADCA,EAAIqL,KAER7I,MAAKumB,GAGX,CAEAN,GAAsBliB,IACpB/D,KAAKwmB,YAAYlQ,MAAMC,KAAKxS,EAAMtG,OAAOgpB,OAAM,EAGjDC,GAAwBjV,IAClBzR,MAAK+lB,IAAmBtU,IAC1BzR,MAAK+lB,EAAiBtU,EACtBzR,MAAK8lB,EAAiB1Z,UAAUoE,OAAO,YACzC,EAGF4V,GAAuB,IAAMpmB,MAAK0mB,GAAqB,GAEvDL,GAAkBxpB,MAAOkH,IACvBA,EAAMgC,iBACN/F,MAAK0mB,GAAqB,GAE1B,MAAMD,EAAQ,IAAI1iB,EAAM4iB,aAAahJ,OAAO5H,KAAKzI,GAASA,EAAKsZ,cAE/D5mB,KAAKwmB,YAAYC,EAAK,EAGxBH,GAAuBviB,IACrBA,EAAMgC,iBACN/F,MAAK0mB,GAAqB,EAAI,EAGhC,EAAAH,GACE1c,YAAW,KACT,MAAM5I,EAAMjB,KAAK6mB,YACXtL,EAAavb,KAAK2S,QAAQ,eAChC3S,KAAK8mB,eAAexL,SAAU,EAEzBra,IAEDsa,GACFA,EAAWtQ,aAAa,MAAOhK,GAC/Bsa,EAAWvY,UAEX4D,MAAMC,MAAM5F,GACd,GACC,IACL,CAMA,EAAAklB,GACEnmB,MAAK8lB,EAAmBzpB,SAAS8J,cAAcnG,KAAKkmB,UAC/ClmB,MAAK8lB,IAEV9lB,MAAK8lB,EAAiBxnB,iBACpB,YACA0B,MAAKomB,GAEPpmB,MAAK8lB,EAAiBxnB,iBAAiB,OAAQ0B,MAAKqmB,GACpDrmB,MAAK8lB,EAAiBxnB,iBAAiB,WAAY0B,MAAKsmB,GAC1D,CAKA,WAAAE,CAAYC,GAEV,IAAIM,EAAkB,EAEtB,MAAM5C,EAAcsC,EAAM1Q,KAAK0L,IAC7B,MAAMC,EAAU,IAAIsF,eACdvC,EAAa,IAAIjD,GAWvB,OAVAiD,EAAWlC,WAAWd,EAAMC,GAExB7lB,QAAQgnB,kBAAkBoE,aAAe,EAAIF,GAC/CtC,EAAW5C,OAAQ,EACnB4C,EAAWhM,aAAejc,EAAU,qCAC3BioB,EAAW5C,QACpBkF,IACA/mB,MAAKknB,EAAYxF,EAASD,IAGrBgD,KAGTzkB,MAAKmnB,EAAgBhD,EACvB,CAOA,EAAA+C,CAAYxF,EAASD,GACnB,MAAMxS,EAAOjP,KAAKmG,cAAc,QAC1BihB,EAAW,IAAIC,SAASpY,GAC9BmY,EAASE,IAAItnB,KAAKgmB,UAAUva,KAAMgW,GAClCC,EAAQ9f,KAAK,OAAQqN,EAAKxI,QAC1Bib,EAAQ6F,iBAAiB,eAAgB5T,KACzC+N,EAAQ6F,iBAAiB,mBAAoB,kBAC7C7F,EAAQ6F,iBAAiB,SAAU,oBACnC7F,EAAQ8F,KAAKJ,EACf,CAOA,EAAAD,CAAgBhD,GACVnkB,KAAK8mB,iBACP9mB,KAAK8mB,eAAe/E,SACpB1lB,SAAS+D,KAAKqnB,YAAYznB,KAAK8mB,iBAEjC9mB,KAAK8mB,eAAiB,IAAI7C,GAC1BjkB,KAAK8mB,eAAevE,WAAW4B,GAC/BnkB,KAAK8mB,eAAetC,WAAcpgB,IAChCpE,KAAKuD,cACH,IAAIC,YAAY,kBAAkBY,IAAU,CAAEX,SAAS,IAC/D,EAGIpH,SAAS+D,KAAKC,OAAOL,KAAK8mB,eAC5B,CAEA,YAAIZ,GACF,OAAOlmB,KAAKuK,aAAa,WAC3B,CAKA,aAAIyb,GACF,OAAOhmB,KAAKmG,cAAc,qBAC5B,CAEA,eAAI0gB,GACF,OAAO7mB,KAAKuK,aAAa,eAC3B,EAGFE,eAAeC,OAAO,mBAAoBmb,ICzL1C,MAAM6B,WAAgBvc,YACpB,iBAAAlC,GACEjJ,KAAKN,UAAY,mHAGaM,KAAKuK,aAAa,SAAW,+BAG7D,CAEA,QAAI5G,CAAK2F,GACPtJ,KAAKoM,UAAUoE,OAAO,UAAWlH,EACnC,EAGFmB,eAAeC,OAAO,kBAAmBgd,ICXzC,MAAMC,GAAS,SAOR,MAAMC,WAAwBzc,YACnC,iBAAAlC,GACEjJ,KAAKsV,OAAStV,KAAKuK,aAAa,WAChCvK,KAAK6nB,OAAS7nB,KAAK2L,aAAa,UAEhC3L,KAAK8nB,cAAcxpB,iBAAiB,QAAS0B,KAC/C,CAEA,oBAAAwJ,GACExJ,KAAK8nB,cAAczpB,oBAAoB,QAAS2B,KAClD,CAEA,iBAAM4I,CAAY7E,GACG,UAAfA,EAAM8E,YACF7I,KAAK+nB,kBAAkBhkB,EAEjC,CAEA,uBAAMgkB,CAAkBhkB,GACtBA,EAAMgC,iBACNhC,EAAMsL,kBAEN,MAAMyY,EAAe/jB,EAAMqD,cAC3B0gB,EAAapoB,UAAY,GACzB,MAAMO,EAAU,IAAI2D,EAAQ,SAC5B3D,EAAQC,KAAK4nB,GAEb,UACQ/T,GACJlY,QAAQ2Z,OAAOwS,qBAAqBhoB,KAAKsV,QACzC,KACA,8BAGFtV,KAAK6nB,QAAU7nB,KAAK6nB,OACpB7nB,KAAK+jB,gBAAgB,SAAU/jB,KAAK6nB,QACpC7nB,KAAKioB,iBACLjoB,KAAKkoB,kBACP,CAAE,MAAOxjB,GACPmD,EAAMnD,EAAMe,SAAWf,EAAO,SAC9B1E,KAAKkoB,kBACP,CAAC,QACCjoB,EAAQK,MACV,CACF,CAEA,cAAA2nB,GACE,MAAME,EAAoBnoB,KAAKmG,cAC7B,SAASnG,KAAKsV,mBAEZ6S,GACFA,EAAkB/b,UAAUoE,OAAO,SAAUxQ,KAAK6nB,OAEtD,CAEA,gBAAAK,GACE,GAAIloB,KAAK8nB,aAAc,CACrB,MAAMzJ,EAAWre,KAAK6nB,OAAS,gBAAkB,eACjD7nB,KAAK8nB,aAAapoB,UAAY,uBAAuB2e,oBACvD,CACF,CAMA,kBAAA+J,GACE,MAAMC,EAAgBroB,KAAKmG,cAAc,gBACzC,IAAKkiB,EAAe,OAEpB,MAAMC,EAAmBtoB,KAAK8a,aAAe9a,KAAK6nB,OAElD,GAAIS,GA9EK,SA8EeD,EAAcE,QAAkB,CAEtD,MAAMlK,EAAWre,KAAK6nB,OAAS,gBAAkB,eACjDQ,EAAcG,UAAY,yEACFnK,sCAIxBre,KAAK8nB,cAAcxpB,iBAAiB,QAAS0B,KAC/C,MAAYsoB,GAAoBD,EAAcE,UAAYZ,GAG/CW,GAAoBD,EAAcE,UAAYZ,IAEvD3nB,KAAKkoB,mBAHLG,EAAcG,UAAY,mCAK9B,CAEA,eAAI1N,GACF,MAAMqN,EAAoBnoB,KAAKmG,cAC7B,SAASnG,KAAKsV,mBAEhB,QAAK6S,GAGHA,EAAkBxpB,iBAAiB,8BAA8B0G,OACjE,CAEJ,CAEA,gBAAIyiB,GACF,OAAO9nB,KAAKmG,cAAc,qBAC5B,EAGFsE,eAAeC,OAAO,oBAAqBkd,ICrHpC,MAAMa,WAA8Btd,YACzC,iBAAAlC,GACE,MAAMyf,EAAmB1oB,KAAKmG,cAC5B,2CAEIwiB,EAAsB3oB,KAAKmG,cAC/B,8CAEIyiB,EAA0B5oB,KAAKmG,cACnC,iCAEI0iB,EAAe7oB,KAAKmG,cAAc,gBAEnC0iB,GAELA,EAAavqB,iBAAiB,SAAS,SAAUd,GAC/C,MAAMsrB,EAAWtrB,EAAIC,OACfsrB,EAAM,IAAIC,KAEZF,EAASG,SACXL,EAAwBxc,UAAU7L,OAAO,UACzCmoB,EAAiB9W,UAAUsX,QAAQH,KAEnCH,EAAwBxc,UAAUC,IAAI,UACtCqc,EAAiB9W,UAAU0L,SAE7BqL,EAAoB/W,WAAW0L,OACjC,GACF,EAGF7S,eAAeC,OAAO,kCAAmC+d,IC2BzDhe,eAAeC,OAAO,sBAzDtB,cAAyBuB,EACvB,UAAIqJ,GACF,OAAOtV,KAAK6M,UAAYjB,KAAKC,MAAM7L,KAAK6M,WAAe,QAAInQ,CAC7D,CAEA,YAAAqR,CAAahC,EAAM+B,GACjB,MAAO,CACLK,EAAG,CACDC,UAAWrC,KACRH,KAAKC,MAAM7L,KAAK+M,cAErBe,KAAMA,EAEV,CAEA,cAAAI,CAAeD,GACb,MAAMI,EAAOJ,EAASI,KACtB,MAAO,CACLL,QAASC,EAASkb,MAClB7a,KAAMD,EAAKP,KAAOO,EAAKE,SAAWF,EAAKG,YAE3C,CAQA,aAAAjB,CAAcO,GACZ,OAAOA,EAAKvO,MAAQuO,EAAKrC,IAC3B,CASA,gBAAAkC,CAAiBG,EAAM/B,GACrB,MAAO,0LAIsC/L,KAAK0O,gBAAgBZ,EAAKrC,KAAMM,6DAChC+B,EAAKsb,KAAK3d,6HAGPqC,EAAKub,uEACJvb,EAAKwb,0DAIxD,ICxDF,MAAMC,WAAiCpe,YACrC,WAAArL,GACE2I,QACAzI,KAAK1B,iBAAiB,SAAU0B,KAClC,CAEA,WAAA4I,CAAY7E,GACV,GACO,WADCA,EAAM8E,KAEV7I,KAAKwM,UAGX,CAEA,QAAAA,GACE,MAAMvL,EAAM,IAAIkT,IAAInU,KAAKuK,aAAa,QAChC2G,EAASlR,KAAKmG,cAAc,UAClClF,EAAIuoB,aAAalC,IAAI,cAAepW,EAAO5H,OAC3C1C,MAAMC,MAAM5F,EAAK,CAAEkV,MAAO,wBAC5B,EAGF1L,eAAeC,OACb,qCACA6e,ICfK,MAAME,WAAsBte,YACjC,WAAArL,GACE2I,QAEAzI,KAAK0pB,cAAgB1pB,KAAKmG,cAAc,oBACxCnG,KAAK2pB,cAAgB3pB,KAAKmG,cAAc,oBACxCnG,KAAK4pB,eAAiB5pB,KAAKmG,cAAc,qBACzCnG,KAAK6pB,gBAAkB7pB,KAAKmG,cAAc,sBAC1CnG,KAAK8pB,kBAAoB9pB,KAAKmG,cAAc,wBAC5CnG,KAAKmiB,MAAQniB,KAAKmG,cAAc,OAChCnG,KAAK+pB,iBAAmB/pB,KAAKmG,cAAc,6BAC3CnG,KAAKgqB,aAAehqB,KAAKmG,cAAc,wBACvCnG,KAAKiqB,SAAWjqB,KAAKmG,cAAc,cAEnCnG,KAAKkqB,WAAalqB,KAAK6pB,gBAAgBzqB,QAAQ8qB,WAC/ClqB,KAAKmqB,UAAYnqB,KAAK4pB,eAAetgB,MAIrCtJ,KAAKoqB,OC5BM,SAAU5e,EAAM6e,GAC7B,IAAIC,EAEJ,OAAO,YAAaC,GAClB,MAAMC,EAAOxqB,KAEbsK,aAAaggB,GACbA,EAAUzgB,YAAW,IAAM2B,EAAKif,MAAMD,EAAMD,IAAOF,EACrD,CACF,CDmBkBK,EAAS,KACrB1qB,KAAK2qB,cACL3qB,KAAK4qB,gBAAc,GAzBJ,KA4BjB5qB,KAAKgqB,cAAc1rB,iBAAiB,QAAS0B,KAAK6qB,YAAYliB,KAAK3I,MACrE,CAEA,iBAAAiJ,GACEjJ,KAAK8qB,SAAW,IAAIC,iBAAiB/qB,KAAKgrB,iBAAiBriB,KAAK3I,OAEhEA,KAAK8qB,SAASG,QAAQjrB,KAAK0pB,cAAe,CAAE5V,YAAY,IACxD9T,KAAK8qB,SAASG,QAAQjrB,KAAK2pB,cAAe,CAAE7V,YAAY,IACxD9T,KAAK8qB,SAASG,QAAQjrB,KAAK4pB,eAAgB,CAAE9V,YAAY,GAC3D,CAEA,oBAAAtK,GACExJ,KAAK8qB,SAASI,YAChB,CAEA,gBAAAF,CAAiBG,GACf,IAAK,MAAMC,KAAYD,EACjB,cAAeC,EAAS3tB,OAAO2B,UACjCY,KAAK0pB,cAAcpgB,MAAQ,GAC3BtJ,KAAK2pB,cAAcrgB,MAAQ,GAC3BtJ,KAAKmqB,UAAYiB,EAAS3tB,OAAO6L,OAEnCtJ,KAAKoqB,QAET,CAEA,WAAAO,GACO3qB,KAAKmqB,YAEVnqB,KAAK+pB,iBAAiBlT,SAAU,EAChClU,EAAI9G,QAAQ2Z,OAAO6V,uBAAuBrrB,KAAKmqB,WAAY,CACzDmB,KAAMtrB,KAAKurB,oBACXC,UAAWxrB,KAAKyrB,SAChBC,UAAW1rB,KAAK2rB,SAChBC,SAAS,EACT3tB,KA7DiB,YA+DhB8I,MAAK,EAAGnE,WACP5C,KAAK+pB,iBAAiBngB,IAAMhH,EAAK3B,IACjCjB,KAAK+pB,iBAAiB5H,MAAM0J,IAAMjpB,EAAKipB,IACvC7rB,KAAK+pB,iBAAiB5H,MAAMpkB,MAAQ6E,EAAK7E,MACzCiC,KAAKygB,iBAAe,IAErBvG,OAAOxV,IACNvI,QAAQuI,MAAMA,EAAMe,SAAWf,GAC/BmD,EAAMnD,EAAMe,SAAWf,EAAO,QAAO,IAE3C,CAEA,WAAAmmB,GACE7qB,KAAK+pB,iBAAiBrqB,UA7EA,uDA8EtBM,KAAK4pB,eAAetgB,MAAQ,GAC5BtJ,KAAKmiB,MAAQ,KACbniB,KAAKiqB,SAAS7d,UAAUC,IAAI,YAC5BrM,KAAKygB,iBACP,CAEA,eAAAA,GACEzgB,KAAK2S,QAAQ,mBAAmBC,SAAS5S,KAC3C,CAEA,cAAA4qB,GACO5qB,KAAKmqB,WAAcnqB,KAAKurB,sBAE7BvrB,KAAKiqB,SAAS7d,UAAU7L,OAAO,YAE3BP,KAAKiqB,SAAS6B,KAAK7nB,MAAM,oBAC3BjE,KAAKiqB,SAAS6B,KAAO9rB,KAAKiqB,SAAS6B,KAAKlvB,QACtC,mBACA,KAAOoD,KAAKmqB,WAGdnqB,KAAKiqB,SAAS6B,KAAO9rB,KAAKiqB,SAAS6B,KAAO,eAAe9rB,KAAKmqB,YAElE,CAEA,YAAIsB,GACF,MAAiC,KAA7BzrB,KAAK0pB,cAAcpgB,MACdtJ,KAAK+rB,gBAAgB/R,KAAK,KAE5Bha,KAAK0pB,cAAcpgB,KAC5B,CAEA,YAAIqiB,GACF,MAAiC,KAA7B3rB,KAAK2pB,cAAcrgB,MACdtJ,KAAKgsB,gBAAgBhS,KAAK,KAE5Bha,KAAK2pB,cAAcrgB,KAC5B,CAEA,mBAAI0iB,GACF,IAAKhsB,KAAKurB,oBAAqB,MAAO,GAEtC,MAAMU,EAAOjsB,KAAKkqB,WAAWluB,MAAM,KAAK+Z,KAAKmW,GAAM1qB,SAAS0qB,KACtDC,GE/He7K,EFgInB2K,EAAK,GAAKjsB,KAAKosB,eEhIOC,EFiItBJ,EAAK,GAAKjsB,KAAKssB,gBEhIZhL,GAAK+K,EAAI/K,EAAI+K,GADP,IAAU/K,EAAG+K,EFoIxB,MAAO,CAACvL,KAAKgD,MAAMmI,EAAK,GAAKE,GAAOrL,KAAKgD,MAAMmI,EAAK,GAAKE,GAC3D,CAEA,mBAAIJ,GACF,IAAK/rB,KAAKurB,oBAAqB,MAAO,GAEtC,MAAMgB,EAAavsB,KAAKgsB,gBAExB,MAAO,CACLlL,KAAKgD,OAAO9jB,KAAKosB,eAAiBG,EAAW,IAAM,GACnDzL,KAAKgD,OAAO9jB,KAAKssB,gBAAkBC,EAAW,IAAM,GAExD,CAEA,kBAAIH,GACF,OAAO5qB,SAASxB,KAAK4pB,eAAexqB,QAAQgtB,eAC9C,CAEA,mBAAIE,GACF,OAAO9qB,SAASxB,KAAK4pB,eAAexqB,QAAQktB,gBAC9C,CAEA,uBAAIf,GACF,MAAqD,SAA9CvrB,KAAK6pB,gBAAgBzqB,QAAQotB,YACtC,EAGF/hB,eAAeC,OAAO,yBAA0B+e,IG1JjC,MAAMgD,WAAyBthB,YAC5C,WAAArL,GACE2I,QAEAzI,KAAKoM,UAAUC,IAAI,wBACnBrM,KAAKC,QAAU,IAAI2D,EAAQ,SAEvB5D,KAAK4J,KACP5J,KAAK0sB,OAET,CAEA,WAAA9jB,CAAYpL,GACV,OAAQA,EAAIqL,MACV,IAAK,OACH7I,MAAK2sB,KACL,MACF,IAAK,QACH3sB,MAAK4sB,GAASpvB,GAKpB,CAEA,iBAAAyL,GACEjJ,MAAK6sB,IACP,CAEA,oBAAArjB,GACExJ,KAAKmiB,OAAO9jB,oBAAoB,OAAQ2B,MACxCA,KAAKmiB,OAAO9jB,oBAAoB,QAAS2B,MACzCA,KAAKM,MACP,CAEA,WAAAwsB,CAAYljB,EAAM5J,KAAK4J,IAAKiiB,EAAM7rB,KAAKyL,MACrCzL,KAAKmiB,MAAQ,IAAIC,MACjBpiB,KAAKmiB,MAAMvY,IAAMA,EACbiiB,IACF7rB,KAAKmiB,MAAM0J,IAAMA,EAErB,CAEA,KAAAa,CAAM9iB,GACJ5J,KAAK8sB,YAAYljB,GACjB5J,KAAKmiB,MAAM7jB,iBAAiB,OAAQ0B,MACpCA,KAAKmiB,MAAM7jB,iBAAiB,QAAS0B,MACrCA,KAAKoC,MACP,CAEA,IAAAA,GACMpC,KAAKmiB,OAAO4K,WAGhB/sB,KAAKiL,aAAa,UAAW,WAC7BjL,KAAKN,UAAY,GACjBM,KAAKC,QAAQC,KAAKF,MACpB,CAEA,IAAAM,GACEN,KAAKoM,UAAU7L,OAAO,WACtBP,KAAKC,QAAQK,MACf,CAEA,GAAAqsB,GACE3sB,KAAKC,QAAQK,OACbN,KAAK2P,gBAAgB,UACvB,CAEA,GAAAid,CAASpvB,GACP,MAAMiI,EAAU,kBAAkBzF,KAAKmiB,MAAMvY,MACvCojB,EAAQhtB,KAAK2S,QAAQ,sBAC3B3S,KAAKC,QAAQK,OACbN,KAAKN,UAAY,gCACQ+F,MAAYunB,EAAQ,QAAU,mGAIvD7wB,QAAQuI,MAAMe,EAASjI,EACzB,CAEA,GAAAqvB,GACM7sB,KAAKmiB,OAAO4K,SACd/sB,KAAKitB,gBAAgBjtB,KAAKmiB,OACjBniB,KAAKmiB,OACdniB,KAAKK,OAAOL,KAAKmiB,MAErB,CAEA,WAAItL,CAAQvN,GACVA,EAAQtJ,KAAKoC,OAASpC,KAAKM,MAC7B,CAEA,OAAIsJ,CAAIA,GACN5J,KAAK0sB,MAAM9iB,GACX5J,MAAK6sB,IACP,CAEA,QAAIphB,GACF,OAAOzL,KAAKuK,aAAa,OAC3B,CAEA,OAAIX,GACF,OAAO5J,KAAKuK,aAAa,MAC3B,EAGFE,eAAeC,OAAO,4BAA6B+hB,IChHnD,MAAMS,WAA0B/hB,YAC9B,WAAArL,GACE2I,QACAzI,KAAK1B,iBAAiB,SAAU0B,KAClC,CAEA,iBAAAiJ,GACE5M,SAASiC,iBAAiB,qBAAsB0B,KAClD,CAEA,oBAAAwJ,GACEnN,SAASgC,oBAAoB,qBAAsB2B,KACrD,CAEA,WAAA4I,CAAY7E,GACV,OAAQA,EAAM8E,MACZ,IAAK,qBACH7I,KAAKmtB,UAAUppB,EAAML,QACrB,MACF,IAAK,SACH1D,KAAK+W,OAAOF,SAAU,EAG5B,CAEA,SAAAsW,CAAUzpB,GACR1D,KAAK+W,OAAOqW,QAAU,UACtBptB,KAAK+W,OAAOrF,UAAW,EACvB1R,KAAKsX,QAAQ3X,QAAU+D,EAAO4T,OAChC,CAEA,UAAIP,GACF,OAAO/W,KAAKmG,cAAc,YAC5B,CAEA,WAAImR,GACF,OAAOtX,KAAKmG,cAAc,aAC5B,EAGFsE,eAAeC,OAAO,8BAA+BwiB,ICxCrD,MAAMG,WAAeC,kBACnBC,IAEA,iBAAAtkB,GACEjJ,KAAKoM,UAAUC,IAAI,qBAEnBrM,MAAKutB,GAAkBvwB,EAAEgD,MAAM7C,QAAQ,CACrCqU,wBAAyB,EACzB0J,mBAAmB,EACnBtO,aAAc5M,KAAK4M,aAKhB5M,KAAK4M,YAAe5M,KAAKwtB,UAC5BxtB,MAAKutB,GACFE,KAAK,sBACL/mB,KAAK,gCACLnG,QAEP,CAEA,MAAAgP,GACEvP,KAAK2P,gBAAgB,YACrB3P,MAAK0tB,IACP,CAEA,OAAApe,GACEtP,KAAKiL,aAAa,WAAY,YAC9BjL,MAAK0tB,IACP,CAEA,UAAAzX,CAAWrT,EAAMoT,OAAStZ,GACxB,IAAIixB,EAAgB3tB,KAAKsJ,MAGzBtJ,KAAKN,UAAY,GACbsW,GACFhW,KAAKqM,IAAI,IAAIuhB,OAAO5X,EAAQ,KAI9BpT,EAAKrE,SAAS+O,IACZtN,KAAKqM,IAAI,IAAIuhB,OAAOtgB,EAAK/N,KAAM+N,EAAK8H,IAAI,EAAO9H,EAAK8H,KAAOuY,GAAc,IAG3E3tB,MAAK0tB,IACP,CAKA,GAAAA,GACE1tB,MAAKutB,GAAgBzrB,QAAQ,SAC/B,CAEA,cAAI8K,GACF,OAAO5M,KAAKZ,QAAQyuB,eAAe,eAAiB7tB,KAAKwtB,QAC3D,EAGF/iB,eAAeC,OAAO,iBAAkB2iB,GAAQ,CAAE1iB,QAAS,WCnDpD,MAAMmjB,WAAuB3iB,YAClC,iBAAAlC,GACEjJ,KAAK+tB,YAAc1xB,SAAS8J,cAAc,uBAC1CnG,KAAKmd,YAAc9gB,SAAS8J,cAAc,uBAC1CnG,KAAKguB,cAAgB3xB,SAAS8J,cAAc,uBAE5CnG,KAAKiuB,cAGLjsB,uBAAsB,KACpBhC,KAAKkuB,gBAAc,IAIrBluB,KAAK8qB,SAAW,IAAIC,kBAAkBoD,IACpCA,EAAU5vB,SAAS6sB,IACjBA,EAASgD,WAAW7vB,SAAS6iB,IACvBA,EAAKiN,WAAaC,KAAKC,eAGvBnN,EAAKhV,WAAW6G,SAAS,aAC3BjT,KAAKwuB,cAAcpN,GAKrBA,EACGziB,iBAAiB,aACjBJ,SAASO,GAAOkB,KAAKwuB,cAAc1vB,KAAG,GAC1C,GACF,IAIHkB,KAAK8qB,SAASG,QAAQjrB,KAAM,CAC1ByuB,WAAW,EACXC,SAAS,GAEb,CAEA,oBAAAllB,GACExJ,KAAK2uB,iBACL3uB,KAAK8qB,UAAUI,YACjB,CAEA,WAAA+C,GACEjuB,KAAK+tB,aAAazvB,iBAAiB,QAAS0B,MAC5CA,KAAKmd,aAAa7e,iBAAiB,QAAS0B,KAC9C,CAEA,cAAA2uB,GACE3uB,KAAK+tB,aAAa1vB,oBAAoB,QAAS2B,MAC/CA,KAAKmd,aAAa9e,oBAAoB,QAAS2B,KACjD,CAEA,WAAA4I,CAAY7E,GACS,UAAfA,EAAM8E,MAAoB9E,EAAMtG,SAAWuC,KAAK+tB,YAClD/tB,KAAK4uB,aAAa7qB,GACM,UAAfA,EAAM8E,MAAoB9E,EAAMtG,SAAWuC,KAAKmd,aACzDnd,KAAK6uB,kBAAkB9qB,EAE3B,CAEA,YAAA6qB,CAAa7qB,GACX,MAAMgI,EAAOhI,EAAMtG,OAAO6L,MAAMgK,cAAcwb,OAEjC,KAAT/iB,EAKJ/L,KAAK+uB,YAAYhjB,GAJf/L,KAAKgvB,aAKT,CAEA,WAAAD,CAAYhjB,GACV,MAAMkjB,EAAWjvB,KAAKrB,iBAAiB,iBACvC,IAAIuwB,EAAa,EACbC,EAAa,KAEjBF,EAAS1wB,SAAS6wB,KACCA,EAAY7kB,aAAa,SAAW,IAExC+I,cAAcjJ,SAAS0B,IAClCqjB,EAAYhjB,UAAUC,IAAI,aAC1B+iB,EAAYhjB,UAAU7L,OAAO,YAC7B2uB,IACKC,IAAYA,EAAaC,KAE9BA,EAAYhjB,UAAU7L,OAAO,aAC7B6uB,EAAYhjB,UAAUC,IAAI,YAC5B,IAKiB,IAAf6iB,GACFlvB,KAAKguB,cAAczd,YAAc,KAAK/T,EAAU,gBAChDwD,KAAKguB,cAAclmB,MAAMqI,QAAU,SAC1B+e,EAAa,GACtBlvB,KAAKguB,cAAczd,YAAc,GAAG2e,KAAc1yB,EAAU,iBAC5DwD,KAAKguB,cAAclmB,MAAMqI,QAAU,SAEnCnQ,KAAKguB,cAAclmB,MAAMqI,QAAU,OAIjCgf,GACFA,EAAWjW,eAAe,CAAEC,SAAU,SAAU8E,MAAO,UAE3D,CAEA,WAAA+Q,GACmBhvB,KAAKrB,iBAAiB,iBAC9BJ,SAAS6wB,IAChBA,EAAYhjB,UAAU7L,OAAO,YAAa,WAAU,IAGtDP,KAAKguB,cAAclmB,MAAMqI,QAAU,MACrC,CAEA,iBAAA0e,CAAkB9qB,GAChBA,EAAMgC,iBACN/F,KAAK+tB,YAAYzkB,MAAQ,GACzBtJ,KAAKgvB,aACP,CAEA,aAAAR,CAAca,GACZ,IAAIC,EAASD,EAAW,CACtBpzB,MAAO,QACPszB,UAAW,IACXC,gBAAgB,EAChBC,cAAe,IACfC,OAAQ,oBACRC,UAAW,oBACXC,MAAQpyB,GAAQwC,KAAK6vB,WAAWryB,IAEpC,CAEA,cAAA0wB,GACoBluB,KAAKrB,iBAAiB,aAC9BJ,SAASO,GAAOkB,KAAKwuB,cAAc1vB,IAC/C,CAEA,gBAAM+wB,CAAWryB,GAEf,GAAIA,EAAI+Y,OAAS/Y,EAAIsyB,IAAMtyB,EAAIuyB,WAAavyB,EAAIwyB,SAC9C,OAIF,MAAMC,EAAWzyB,EAAI8P,KACfgI,EAAS2a,EAAS3a,OAClBrU,EAAMpF,QAAQ2Z,OAAO0a,qBAAqB5a,GAC1C1S,EAAO,CACXutB,iBAAkB3yB,EAAIsyB,GAAG1wB,QAAQgxB,SACjCC,aAAc7yB,EAAIwyB,UAGpBzpB,GAAkB,GAElB,IACE,MAAM0H,QAAiB8F,GAAM9S,EAAK2B,GAC5B0tB,QAAiBriB,EAASrL,KAG1B2tB,EAASN,EAAS9pB,cAAc,SAASmP,KAC/C,GAAIib,EAAQ,CACV,MAAMC,EAAYD,EAAOpqB,cAAc,gBACnCqqB,GAAaF,EAASjH,WACxBmH,EAAUjgB,YAAc+f,EAASjH,SAErC,CAGArpB,KAAKywB,kBAAkBjzB,EAAI+Y,KAAM/Y,EAAIsyB,IAErCjoB,EAAMrL,EAAU,2BAClB,CAAE,MAAOkI,GACPmD,EAAMnD,EAAMe,SAAWf,EAAO,SAE9BrD,OAAO+S,SAASpR,QAClB,CAAC,QACCuD,GAAkB,EACpB,CACF,CAEA,iBAAAkqB,CAAkBC,EAAeC,GAE/B,MAAMC,EAAaF,EAAc/d,QAAQ,qBAIzC,GAHAie,GAAYxI,qBAGRsI,IAAkBC,EAAa,CACjC,MAAME,EAAWF,EAAYhe,QAAQ,qBACrCke,GAAUzI,oBACZ,CACF,EAGF3d,eAAeC,OAAO,kBAAmBojB,IC3MzC,MAAMgD,GAAmB,CACvBnB,UAAW,kBACXD,OAAQ,4BACRqB,WAAY,UACZxB,UAAW,IACXE,cAAe,IACfuB,OAAQ,4BAGV,SAASC,GAAQltB,GACf,MAAM0H,EAAO1H,EAAMuJ,KAAKlO,QAAQyb,YAChCxe,SACGsC,iBAAiB,8BAA8B8M,OAC/ClN,SAAS2nB,GAAaA,EAAS9Z,UAAUC,IAAI,uBAClD,CAEA,SAAS6kB,GAAOntB,GACd,MAAMuJ,EAAOvJ,EAAMuJ,KACbkN,EAAgBzW,EAAM+rB,GAAGtV,cAAc7H,QAAQ,mBAC/CjH,EAAS,CACbpE,WAAYgG,EAAKlO,QAAQ+X,UACzBga,SAAUptB,EAAMisB,SAAW,GAGzBxV,IACF9O,EAAO0lB,kBAAoB5W,EAAcpb,QAAQ+X,WAM/CpT,EAAMtG,SAAWsG,EAAM+rB,IACzB9b,GAAKnY,QAAQ2Z,OAAO6b,0BAA2B3lB,GAAQ3E,MAAMkH,IAC3D,MAAMrL,EAAOqL,EAASrL,KACtBiF,EAAMjF,EAAK6C,SACP7C,EAAKwU,2BACPC,GAAuBzU,GAEzB4H,IACA8C,EAAKuL,YAAYjW,EAAK0uB,aAAY,GAGxC,CAEA,SAAS1B,KACWvzB,SAASsC,iBAAiB,6BAClCJ,SAAS2nB,GACjBA,EAAS9Z,UAAU7L,OAAO,uBAE9B,CAEA,MAAMgxB,WAAyBpmB,YAC7B,iBAAAlC,GACE,MAAMhN,EAAQ,CACZwP,KAAMzL,KAAKZ,QAAQyb,YACnB2W,IAAG,CAAC1B,EAAI2B,EAAOnkB,IACNwiB,EAAGhxB,GAAGM,QAAQsyB,kBAClB11B,MAAM,KACNqO,SAASiD,EAAKlO,QAAQyb,cAG7B,IAAIyU,EAAStvB,KAAM,IACd8wB,GACHG,WACAC,UACAtB,SACA3zB,SAEJ,EAGFwO,eAAeC,OAAO,4BAA6B6mB,IC7EnD,MAAM3tB,WAAgBuH,YACpB,iBAAAlC,GACEjJ,KAAKoQ,UAAY,oBAAoBpQ,KAAK/B,OAC1C+B,KAAKN,UAAY,yEACiDM,KAAKD,4eAezE,CAEA,QAAI9B,GACF,OAAO+B,KAAKuK,aAAa,SAAW,QACtC,CAEA,SAAIxK,GACF,OAAOC,KAAKuK,aAAa,UAAY,cACvC,EAGFE,eAAeC,OAAO,kBAAmB9G,IC5BzC,MAAM+tB,WAAyBxmB,YAC7B,uBAAMlC,SACEnM,IAENkD,KAAKoM,UAAUC,IAAI,yBACnBrP,EAAEgD,KAAKmM,OAAOhP,QAAQ6C,KAAKsM,cAC7B,CAEA,SAAIH,GACF,OAAOnM,KAAKgN,qBAAqB,SAAS,EAC5C,CAEA,iBAAIV,GACF,MAAO,CACLslB,MAAM,EACNC,gBAAiB,CAAC,KAClBC,aAAa,EACbC,mBAAoB,EACpBC,mBAAoBhyB,MAAKgyB,GACzB7kB,KAAM,CACJlM,IAAKjB,KAAKuK,aAAa,OACvB0nB,SAAU,OACVrvB,KAAOmJ,IACE,CAAEA,SAEXiC,QAAUpL,IACD,CAAEoL,QAASpL,KAGtBqK,cAAejN,MAAKiN,GAExB,CAEA,GAAA+kB,CAAoBjmB,EAAMnJ,GACxB,GAGgB,IAFd5F,EAAE4F,GAAMgT,QAAO,WACb,OAAyC,IAAlC5V,KAAKT,KAAK2yB,cAAcnmB,EACjC,IAAG1G,OAEH,MAAO,CACL+P,GAAIrJ,EACJxM,KAAMwM,EAGZ,CAEA,GAAAkB,CAAezN,EAAS+F,GACtB,MAAM3C,EAAO,GACb5F,EAAEwC,EAAQ6R,MAAMrV,MAAM,MAAMm2B,MAAK,WAC/BvvB,EAAK3D,KAAK,CACRmW,GAAIpV,KAAK8uB,OACTvvB,KAAMS,MAEV,IACAuF,EAAS3C,EACX,EAGF6H,eAAeC,OAAO,4BAA6BinB,ICzDnD,MAAMS,GAAa,eACbC,GAAc,UAEpB,MAAMC,WAAgBnnB,YACpBonB,IAAc,KAKd,iBAAAtpB,GACEjJ,KAAKoQ,UAAY,oBAGZpQ,KAAKmG,cAAc,6BACtBnG,KAAKwyB,mBACH,YACA,oDAKJxyB,KAAK8H,MAAM2qB,UAAY,GAAGzyB,KAAKyyB,cAC/BzyB,KAAKwZ,OAAO1R,MAAMqI,QAAU,OAE5B,MAUMjP,EAAU,CACdwxB,KAAMr2B,SAASmL,eAAe,gBAC9BmrB,WAAY,MACZC,UAAW,CAAC,MAGd5yB,KAAK6yB,4BAA8B,IAAIC,sBAhBd,CAACC,EAASjI,KACjCiI,EAAQx0B,SAAS+mB,IACXA,EAAM0N,kBAAoB,IAC5BhzB,KAAKizB,qBAELnI,EAASoI,UAAU5N,EAAM7nB,QAC3B,GACD,GAWDyD,GAEFlB,KAAK6yB,4BAA4B5H,QAAQjrB,MAGzCA,KAAKmzB,2BACP,CAKA,oBAAA3pB,GACExJ,KAAK6yB,6BAA6B3H,aAGlClrB,KAAKozB,6BAELC,QAAQ1wB,IAAI3C,KAAKszB,WAAW/yB,OAAOP,KAAKszB,SAC1C,CAMA,kBAAAL,GACEI,QAAQ/vB,KAAKtD,KAAKuzB,eAAexsB,MAAMysB,IACrCA,EAAQj1B,SAASib,GAAWxZ,KAAKyzB,aAAaja,IAAO,GAEzD,CAOA,YAAAia,CAAaja,GAIXA,EAAO7V,OAGP,MAAM1D,EAAUD,KAAKgN,qBAAqB,mBAAmB,GACzD/M,GACFA,EAAQM,SAINP,KAAKiX,gBACPuC,EAAOhX,GAAG,SAAUhF,IAClBwC,KAAKiX,cAAcrE,SAASpV,EAAIC,OAAOi2B,gBAAe,IAExDla,EAAOhX,GAAG,SAAS,IAAMxC,KAAKiX,cAAca,gBAAe,KAE/D,CAMA,yBAAAqb,GACEnzB,KAAK2zB,mBAAqBtyB,OAAOuyB,WAAW,gCAC5C5zB,KAAK6zB,mBAAsB9vB,GAAU/D,KAAK8zB,mBAAmB/vB,GAC7D/D,KAAK2zB,mBAAmBr1B,iBAAiB,SAAU0B,KAAK6zB,mBAC1D,CAMA,0BAAAT,GACMpzB,KAAK2zB,oBAAsB3zB,KAAK6zB,oBAClC7zB,KAAK2zB,mBAAmBt1B,oBACtB,SACA2B,KAAK6zB,mBAGX,CAOA,kBAAAC,CAAmB/vB,GACjB,MAAMyV,EAAS6Z,QAAQ1wB,IAAI3C,KAAKszB,UAChC,GAAI9Z,EAAQ,CACV,MAAMua,EAAOhwB,EAAMiwB,QAAU5B,GAAaC,GACpC4B,EAAclwB,EAAMiwB,QAAU5B,GAAaC,GAGjD7Y,EAAOjZ,SACP8yB,QACG/vB,KAAK,CACJ2wB,iBACGj0B,KAAKuzB,cACRQ,SAEDhtB,MAAMysB,IACLA,EAAQj1B,SAASib,GAAWxZ,KAAKyzB,aAAaja,IAAO,GAE3D,CACF,CAEA,iBAAI+Z,GACF,MAAMW,EAAe,CAAA,EAGrBl0B,KAAKm0B,oBAAoB51B,SAAS61B,IAChC,IAAK,CAAC,QAAS,KAAM,KAAM,OAAQ,SAAS/pB,SAAS+pB,GAAgB,CACnE,MAAMxR,EAAS5iB,KAAKuK,aAAa6pB,GAC3B34B,EAAM24B,EAAcC,WAAW,IAAK,KAG1C,GAAIzR,IAAWwR,GAA4B,KAAXxR,EAC9BsR,EAAaz4B,IAAO,OAEpB,IACEy4B,EAAaz4B,GAAOmQ,KAAKC,MAAM+W,EACjC,CAAE,MAAOhe,GAEPsvB,EAAaz4B,GAAOmnB,CACtB,CAEJ,KAGF,MAAMA,EAAS,CACbqR,YAAaj0B,KAAKs0B,kBACfz4B,QAAQ04B,mBACRL,EACHM,SAAU74B,IACVoe,SAAU,IAAI/Z,KAAKszB,WACnBS,KAAM/zB,KAAKs0B,gBAQb,OAFA1R,EAAOnhB,OAASmhB,EAAO2P,WAEhB3P,CACT,CAEA,kBAAI0R,GACF,OAAOjzB,OAAOuyB,WAAW,gCAAgCI,QACrD5B,GACAC,EACN,CAEA,YAAIiB,GACF,OAAOtzB,KAAKwZ,OAAOpE,EACrB,CAEA,UAAIoE,GACF,OAAOxZ,KAAKgN,qBAAqB,YAAY,EAC/C,CAEA,iBAAIiK,GACF,OAAO5a,SACJmL,eAAexH,KAAKszB,UACpB3gB,QAAQ,yBACb,CAEA,aAAI8f,GACF,OAAOzyB,MAAKuyB,IAAevyB,KAAKuzB,cAAchB,UAChD,CAEA,aAAIE,CAAUnpB,GACZtJ,MAAKuyB,GAAcjpB,CACrB,EAGFmB,eAAeC,OAAO,kBAAmB4nB,ICxNzC,MAAMmC,WAAoBtpB,YACxB,uBAAMlC,GACJ,MAAMhJ,EAAU,IAAI2D,EAAQ,SAC5B3D,EAAQC,KAAKF,MAEb,IACE,MAAMiO,QAAiBiG,MAAMlU,KAAKiB,IAAK,CAAEyzB,YAAa,YAChDC,QAAqB1mB,EAAS6G,OAEhC7G,EAAS+G,GACXhV,KAAK40B,WAAWD,GAEhB30B,KAAK60B,UAAU5mB,EAEnB,CAAE,MAAOvJ,GACP1E,KAAK60B,UAAUnwB,EACjB,CAAC,QACCzE,EAAQK,MACV,CACF,CAEA,OAAIW,GACF,OAAOjB,KAAKuK,aAAa,MAC3B,CAEA,UAAAqqB,CAAWD,GACqB,QAA1BA,EAAqB,OACvB30B,KAAKmG,cAAc,qBAAqBiG,UAAU7L,OAAO,UAEzDP,KAAKmG,cAAc,eAAeiG,UAAU7L,OAAO,SAEvD,CAEA,SAAAs0B,CAAUnwB,GACR1E,KAAKmG,cAAc,UAAUiG,UAAU7L,OAAO,UAC9CpE,QAAQuI,MAAM,yCAA0CA,EAC1D,ECtCF,IAAUwnB,GDyCVzhB,eAAeC,OAAO,uBAAwB+pB,ICzCpCvI,GAAE4I,WAAWC,UAAUD,WAAWE,UAAUF,WAAWE,WAAW,CAAA,GAAI,mBAAmB9I,GAAE,CAAC,EAAE,SAASA,EAAEtnB,EAAEqwB,EAAE3T,EAAE4T,GAAG,MAAM,OAAO,EAAE,EAAE,SAAShJ,EAAEtnB,EAAEqwB,EAAE3T,EAAE4T,GAAG,MAAM,MAAM,EAAEC,SAAS,CAAC,EAAE,YAAYC,KAAK,SAASlJ,EAAEtnB,EAAEqwB,EAAE3T,EAAE4T,GAAG,IAAIG,EAAEr3B,EAAEkuB,EAAEoJ,OAAOC,EAAErJ,EAAEsJ,iBAAiBC,EAAEvJ,EAAEwJ,gBAAgB,SAASxJ,EAAEtnB,GAAG,GAAG+wB,OAAOC,UAAU/H,eAAe7kB,KAAKkjB,EAAEtnB,GAAG,OAAOsnB,EAAEtnB,EAAE,EAAE,MAAM,0CAA0C2wB,EAAEv3B,EAAE,OAAOq3B,EAAE,MAAMzwB,EAAE6wB,EAAE7wB,EAAE,QAAQA,GAAG6wB,EAAEJ,EAAE,MAAMA,EAAEzwB,IAAI,uBAAuB2wB,EAAEv3B,EAAE,OAAOq3B,EAAE,MAAMzwB,EAAE6wB,EAAE7wB,EAAE,QAAQA,GAAG6wB,EAAEJ,EAAE,QAAQA,EAAEzwB,IAAI,oCAAoC,OAAOywB,EAAEI,EAAER,EAAE,MAAMjsB,KAAK,MAAMpE,EAAEA,EAAEsnB,EAAE2J,aAAa,CAAA,EAAG,OAAOR,EAAE,MAAMzwB,EAAE6wB,EAAE7wB,EAAE,QAAQA,GAAG6wB,EAAEJ,EAAE,UAAUA,EAAE,CAAC5pB,KAAK,KAAK0J,KAAK,CAAA,EAAGjY,GAAGgvB,EAAE4J,QAAQ,EAAEZ,EAAE,GAAGa,QAAQ7J,EAAE4J,QAAQ,EAAEZ,EAAE,GAAGtyB,KAAKsyB,EAAEc,IAAI,CAACtJ,MAAM,CAACuJ,KAAK,EAAEC,OAAO,IAAIC,IAAI,CAACF,KAAK,EAAEC,OAAO,QAAQb,EAAE,IAAI,6BAA6B,EAAEe,SAAQ,ICGx0BC,EAAoB,eAAgB,CAClCC,UAAW,CACT,CAAEC,UAAW,mBAAoBC,QAAS,KAC1C,CAAED,UAAW,gBAAiBC,QAAS,MAEzCt1B,QAAS,CACPu1B,SAAU,OAIdJ,EAAoB,eAAgB,CAClCC,UAAW,CACT,CAAEC,UAAW,gBAAiBC,QAAS,KACvC,CAAED,UAAW,mBAAoBC,QAAS,MAE5Ct1B,QAAS,CACPu1B,SAAU,OAKdJ,EAAoB,cAAe,CACjCC,UAAW,CACT,CAAEC,UAAW,cAAeC,QAAS,KACrC,CAAED,UAAW,WAAYC,QAAS,MAEpCt1B,QAAS,CACPu1B,SAAU,OAIdJ,EAAoB,cAAe,CACjCC,UAAW,CACT,CAAEC,UAAW,WAAYC,QAAS,KAClC,CAAED,UAAW,cAAeC,QAAS,MAEvCt1B,QAAS,CACPu1B,SAAU,OAId,MAAM7X,GAAYviB,SACf8J,cAAc,mCACdoE,aAAa,QAEVmsB,GAAU,CACd,OAAQ,QACRC,MAAO,gBAGHz1B,GAAU,CACd01B,SAAWnrB,GAAS,GAAGmT,SAAgB8X,GAAQjrB,IAASA,SACxDorB,QAAUC,IACRA,EAAI7rB,aAAa,OAAQ,gBACzB6rB,EAAI7rB,aAAa,UAAW,YAAW,EAEzC8rB,aAAa,GC3DR,SAASv0B,GAAGw0B,EAAWC,EAAcC,EAAgB3xB,GAC1DlJ,SAASsC,iBAAiBs4B,GAAc14B,SAAS44B,IAC/CA,EAAS74B,iBAAiB04B,GAAYx5B,IACpC,MAAM45B,EAAU9gB,MAAMC,KAAK4gB,EAASx4B,iBAAiBu4B,IACrD,IAAIG,EAAc75B,EAAIC,OAEtB,KAAO45B,IAAgBF,GAAU,CAC/B,GAAIC,EAAQ/sB,SAASgtB,GAEnB,YADA9xB,EAASyD,KAAKquB,EAAa75B,GAG7B65B,EAAcA,EAAY7c,aAC5B,IACD,GAEL,CD+CA8c,EAAoB,UAAWp2B,IAC/Bo2B,EAAoB,SAAUp2B,IE7Df,MAAMq2B,GACnBC,KAAe,EACfC,IAAW,KACX/N,IAAiB,KACjBC,IAAiB,KAEjB,WAAA7pB,CAAYqiB,EAAOuV,GACjB13B,KAAKmiB,MAAQA,EACbniB,KAAK23B,WAAaD,EAASE,YAC3B53B,KAAK63B,YAAcH,EAASI,MAC5B93B,MAAK0pB,GAAiBrtB,SAASmL,eAC7BkwB,EAASK,yBAEX/3B,MAAK2pB,GAAiBttB,SAASmL,eAC7BkwB,EAASM,yBAEXh4B,KAAKmX,UAAYugB,EAASpwB,WAC1BtH,KAAK6B,OAAShG,QAAQuJ,gBAClBpF,KAAK6B,SACP7B,KAAK6B,OAAOX,QAAQH,OAAS,IAAMf,KAAK4Q,UACxC5Q,KAAK2I,QAEP3I,KAAKsD,MACP,CAEA,kBAAI20B,GACF,MAAO,CACLJ,YAAa73B,KAAK63B,YAClBK,SAAU,EACVC,UAAU,EACVC,kBAAkB,EAClBC,kBAAkB,EAClBz1B,KAAM5C,KAAKs4B,IACXC,QAAS,KACP,MAAM31B,EAAO5C,MAAKy3B,GAASe,SAAQ,GACnCx4B,KAAKoqB,OAAOxnB,EAAI,EAGtB,CAEA,YAAI6oB,GACF,GAAIzrB,MAAK0pB,IAAgBpgB,MACvB,OAAOtJ,MAAK0pB,GAAepgB,MAAMtN,MAAM,KAAK+Z,KAAK0iB,GAAMj3B,SAASi3B,IAEpE,CAEA,YAAI9M,GACF,GAAI3rB,MAAK2pB,IAAgBrgB,MACvB,OAAOtJ,MAAK2pB,GAAergB,MAAMtN,MAAM,KAAK+Z,KAAK0iB,GAAMj3B,SAASi3B,IAEpE,CAEA,OAAIH,GACF,OAAIt4B,KAAKyrB,UAAYzrB,KAAK2rB,SACjB,CACL+M,EAAG14B,KAAKyrB,SAAS,GACjBkN,EAAG34B,KAAKyrB,SAAS,GACjBlqB,MAAOvB,KAAK2rB,SAAS,GACrBlqB,OAAQzB,KAAK2rB,SAAS,IAGjB3rB,KAAK44B,cAEhB,CAEA,kBAAIA,GACF,MAAO,CACLF,EAAG14B,KAAK23B,WAAW,GACnBgB,EAAG34B,KAAK23B,WAAW,GACnBp2B,MAAOvB,KAAK23B,WAAW,GACvBl2B,OAAQzB,KAAK23B,WAAW,GAE5B,CAEA,IAAAr0B,GACOtD,MAAKw3B,KACRx3B,MAAKy3B,GAAW,IAAIoB,EAAQ74B,KAAKmiB,MAAOniB,KAAKi4B,gBAC7Cj4B,MAAKw3B,IAAe,EAExB,CAEA,MAAApN,CAAO0O,GACL94B,MAAK0pB,GAAepgB,MAAQ,GAAGwvB,EAAOJ,KAAKI,EAAOH,IAClD34B,MAAK0pB,GAAenmB,cAAc,IAAIwL,MAAM,WAC5C/O,MAAK2pB,GAAergB,MAAQ,GAAGwvB,EAAOv3B,SAASu3B,EAAOr3B,SACtDzB,MAAK2pB,GAAepmB,cAAc,IAAIwL,MAAM,UAC9C,CAEA,KAAAmH,GACElW,MAAKy3B,GAASsB,QAAQ/4B,KAAK44B,gBAC3B54B,KAAKoqB,OAAOpqB,KAAK44B,eACnB,CAEA,OAAAhoB,GAKE,OAJI5Q,MAAKy3B,IACPz3B,MAAKy3B,GAAS7mB,UAEhB5Q,MAAKw3B,IAAe,GACb,CACT,CAEA,IAAA7uB,GACE3I,KAAK6B,OAAOoB,YAAYyD,KAAK,yBAAyBlE,GAAG,SAAS,KAC1CnG,SAAS8J,cAC7B,qBAAqBnG,KAAKmX,eAEdvE,WACd5S,KAAK6B,OAAOQ,SACL,KAETrC,KAAK6B,OAAOoB,YAAYyD,KAAK,wBAAwBlE,GAAG,SAAS,KAC/DxC,KAAKkW,SACE,IAEX,EClHa,MAAM8iB,WAAqBh4B,EACxC,WAAAlB,CAAYmB,EAAKC,EAAU,IACzBuH,MAAMxH,EAAKC,EACb,CAEA,IAAAoC,GACEtG,EAAE,8BAA8BwF,GAAG,SAAUoC,IAE3C,GADAA,EAAEyK,kBACwB,QAAtBzK,EAAEnH,OAAOw7B,SAIb,OADAj5B,KAAKqC,SACE,KAETrF,EAAE,2BAA2BwF,GAAG,SAAUoC,IACxC5E,KAAK6B,OAAOq3B,YAAY,cACjB,KAETl5B,KAAKm5B,UAAYn8B,EAAE,qBACnBgD,KAAKo5B,MAAQp8B,EAAE,iBACfgD,MAAKq5B,KACL5wB,MAAMnF,MACR,CAEA,QAAAg2B,GAC2B,MAArBt5B,KAAKm5B,UAAU,IACjBn5B,KAAKm5B,UAAU,GAAGj6B,OAEtB,CAEA,IAAAq6B,GACuB,MAAjBv5B,KAAKo5B,MAAM,IACbp5B,KAAKo5B,MAAM,GAAGl6B,OAElB,CAEA,KAAAwC,GACE1B,KAAKiC,iBAAmBjF,EAAE,mDAC1BgD,KAAK6B,OAAS7E,EAAE,gDAChBgD,KAAKiD,YAAcjG,EAAE,8CACrBgD,KAAK2E,aAAe3H,EAAE,iHAGtBgD,KAAK6B,OAAOxB,OAAOL,KAAK2E,cACxB3E,KAAK6B,OAAOxB,OAAOL,KAAKiD,aACxBjD,KAAKiC,iBAAiB5B,OAAOL,KAAK6B,QAClC7B,KAAKmC,QAAUnF,EAAE,yCACjBgD,KAAKsB,MAAMjB,OAAOL,KAAKmC,SACvBnC,KAAKsB,MAAMjB,OAAOL,KAAKiC,iBACzB,CAEA,GAAAo3B,GACEr5B,KAAKmB,UAAU0D,SAASD,IACtB,GAA0B,UAAtBA,EAAEnH,OAAOw7B,UAA8C,aAAtBr0B,EAAEnH,OAAOw7B,SAC5C,OAAO,EAET,OAAQr0B,EAAE/G,OACR,KAAK,GAEH,OADAmC,KAAKs5B,YACE,EACT,KAAK,GAEH,OADAt5B,KAAKu5B,QACE,EACT,QACE,OAAO,EACjB,GAEE,ECzDF,SAASC,KACP,OAAOn9B,SAASsC,iBAAiB,iCACnC,CAee,SAAS86B,KACtB,MAAMC,EAAkBr9B,SAAS8J,cAAc,wBACzCwzB,EAAoBt9B,SAAS8J,cAAc,wBAEjD3D,GAAG,QAAS,mBAAoB,yBAA0BuB,IACxDA,EAAMgC,iBAEN2zB,EAAgBttB,UAAUoE,OAAO,UAEjC,MAAMopB,EAAQF,EAAgBttB,UAAU6G,SAAS,WAnCrD,SAA0B2mB,GACxBv9B,SACGsC,iBAAiB,+CACjBJ,SAASuqB,IACRA,EAASG,QAAU2Q,EACnB9Q,EAASnW,QAAQ,sBAAsBvG,UAAUoE,OAAO,SAAUopB,EAAK,GAE7E,CA8BIC,CAAiBD,GAEjBD,EAAkBvtB,UAAUoE,OAAO,UAAWopB,EAAK,IAIrDp3B,GAAG,SAAU,uBAAwB,SAAUuB,IAC7C41B,EAAkBvtB,UAAUoE,OAAO,SAAqC,IAA3BgpB,KAAgBn0B,QAE7D,MAAMy0B,EAAyB/1B,EAAMtG,OAAO+c,cAAcpO,UACpD6c,EAAUllB,EAAMtG,OAAOwrB,QAE7B6Q,EAAuBtpB,OAAO,UAAWyY,EAAO,IAIlDzmB,GAAG,QAAS,uBAAwB,4BAA6BuB,IAC/DA,EAAMgC,iBAINjI,EA7CJ,SAAiCguB,GAC/B,MAAM7qB,EAAM,IAAIkT,IAAI2X,GAMpB,OAJA0N,KAAgBj7B,SAAS+mB,GACvBrkB,EAAIuoB,aAAanpB,OAAOilB,EAAM7Z,KAAM6Z,EAAMhc,SAGrCrI,EAAIuT,UACb,CAmCgBulB,CAAwBh2B,EAAMtG,OAAOquB,MAEjC,CACd/tB,MAAOgG,EAAMtG,OAAOM,MACpBE,KAAM,WACP,GAEL,CC7DA,SAAS+7B,KACP39B,SAASsC,iBAAiB,gBAAgBJ,SAASO,IACjD,MAAMm7B,EAAen7B,EAAGqH,cAAc,2BAChC+zB,EAAOp7B,EAAGqH,cAAc,aACxBib,EAAO,CACXyG,OAA8B,SAAtB/oB,EAAGM,QAAQyoB,OACnBzS,GAAItW,EAAGM,QAAQgW,GACfvM,KAAM/J,EAAGM,QAAQyJ,MAGfqxB,EAAKt6B,SAASyF,OAAS,GAAK+b,EAAKyG,OACnCoS,EAAav6B,UAAYo1B,WAAWE,UAAU,mBAAmB,CAC/D5T,KAAMA,IAGR6Y,EAAav6B,UAAY,QAC3B,GAEJ,CAEA,SAASy6B,GAAiB38B,GASxBuW,GARYlY,QAAQ2Z,OAAOhY,EAAI8P,KAAKlO,QAAQyJ,MAAMuxB,cAChD58B,EAAI8P,KAAKlO,QAAQgW,IAEN,CACX+a,iBAAkB3yB,EAAIsyB,GAAG1wB,QAAQi7B,SACjChK,aAAc7yB,EAAIwyB,WAIjBjpB,MAAK,KAEJc,EADgBhM,QAAQmC,EAAE,iCAE1Bg8B,IAAkB,IAEnB9f,OAAOxV,IACNmD,EAAMnD,EAAMe,SAAWf,EAAO,QAAO,GAE3C,CAuBe,SAAS41B,KApBtB93B,GAAG,QAAS,cAAe,gBAAgB,WACzC,MAAM+3B,EAASv6B,KAAKZ,QAAQi7B,SACtBG,EAAYx6B,KAAK2S,QAAQ,gBACzB1R,EACJpF,QAAQ2Z,OAAOxV,KAAKZ,QAAQq7B,YAAYC,uBAAuBH,GAC3DL,EAAOM,EAAUr0B,cAAc,aAErC4N,GAAM9S,GACH8F,MAAK,KACJmzB,EAAK9tB,UAAUoE,OAAO,UACtBgqB,EAAUp7B,QAAQyoB,OACY,QAA5B2S,EAAUp7B,QAAQyoB,OAAmB,QAAU,OACjDmS,IAAkB,IAEnB9f,OAAOxV,IACNmD,EAAMnD,EAAMe,SAAWf,EAAK,GAElC,IAKAs1B,KAEA39B,SAASsC,iBAAiB,2BAA2BJ,SAASO,IAC5D,IAAIwwB,EAASxwB,EAAI,CACf7C,MAAO,QACPszB,UAAW,IACXC,gBAAgB,EAChBC,cAAe,IACfC,OAAQ,aACRiL,YAAY,EACZ/K,MAAOuK,IACR,GAEL,MClD8B,IAAnB94B,OAAOxF,UAChBwF,OAAOxF,QAAU,CAAA,GAInB85B,OAAOiF,OAAO/+B,QAAS,CACrByJ,qBACAF,mBACG8B,EACHlJ,EAAGxB,EACHq+B,gBACAhzB,QACA0X,W3DhCK,cAAyBve,EAC9B85B,IAEA,WAAAh7B,CAAY8f,GACV,MAAM3e,EAAM,IAAIkT,IAAItY,QAAQ2Z,OAAOulB,sBAAuB15B,OAAO+S,UAC3D4mB,EAAmB,CACvB/5B,IAAK2e,EAAK3e,IACVg6B,aAAcrb,EAAK/W,KACnBqyB,WAAYtb,EAAK7hB,MACjBo9B,YAAavb,EAAKniB,QAIpBk4B,OAAO55B,KAAKi/B,GAAkBz8B,SAAS9C,IACjCu/B,EAAiBv/B,IACnBwF,EAAIuoB,aAAalC,IAAI7rB,EAAKu/B,EAAiBv/B,GAC7C,IAGFgN,MAAMxH,EAAI6qB,KAAM,CACd7tB,KAAM,UACNF,MAAOvB,EAAU,SAErB,CAKA,OAAAI,CAAQgG,GAEN6F,MAAM7L,QAAQgG,GACd5C,MAAKkJ,GACP,CAOA,IAAAtH,GAEE,OADA6G,MAAM7G,OACC,IAAIyE,SAASC,GAAatG,MAAK86B,GAAgBx0B,GACxD,CAKA,EAAA4C,GAEE,MAAMkyB,EAAe/+B,SAAS8J,cAC5B,oCAEIk1B,EAAmBh/B,SAAS8J,cAChC,0DAGFi1B,EAAa98B,iBAAiB,+BAAgCsG,IAC5D5E,MAAKs7B,GAAY12B,EAAElB,OAAOiJ,MAAK,IAGjC0uB,EAAiB/8B,iBAAiB,+BAAgCsG,IAChE,MAAM+J,EAAa/J,EAAElB,OAAOiJ,MAC5BtQ,SAASmL,eAAe,aAAa8B,MAAQqF,EACzCA,EAAW1N,IACX,EAAA,IAGN5E,SAASsC,iBAAiB,yBAAyBJ,SAAS0Q,IAC1DA,EAAK3Q,iBAAiB,UAAWsG,IAC/BA,EAAEmB,iBACF/F,MAAKu7B,GAAY32B,EAAEnH,OAAO2B,QAAQo8B,aAAY,GAC/C,GAEL,CAMA,GAAAF,CAAYxtB,EAAO,MACjB,MAAM2tB,EAAep/B,SAASmL,eAAe,iBACvCk0B,EAAcr/B,SAAS8J,cAC3B,8DAGFs1B,EAAanyB,MAAQwE,EAAOA,EAAKub,SAAW,GAC5CqS,EAAY5tB,KAAOA,EAAOA,EAAKsH,QAAK1Y,CACtC,CAMA,GAAA6+B,CAAYI,GACV,MAAMC,EAAgBv/B,SAASmL,eAAe,kBAC9C,IAAIvG,EAAM5E,SAASmL,eAAe,GAAGm0B,UAAiBryB,MAEtD,GAAiB,aAAbqyB,GAAmD,KAAxBC,EAActyB,MAE3CrI,EAAMA,EAAIrE,QAAQsL,EAAc,IAAM0zB,EAActyB,WAC/C,GAAiB,aAAbqyB,IAA4B16B,EAAIgD,MAAMpI,QAAQggC,iBAGvD,YADA77B,MAAK87B,KAKP97B,MAAK86B,GAAc,CACjB75B,IAAKA,EAAI6tB,OACT/wB,MAAO1B,SAASmL,eAAe,GAAGm0B,gBAAuBryB,MACzD7L,OAAQpB,SAASmL,eAAe,GAAGm0B,kBAAyBryB,MAC5DT,KAAM8yB,IAER37B,KAAKqC,OACP,CAKA,GAAAy5B,GACE,MAAMC,EAAS1/B,SAASmL,eAAe,UACvCu0B,EAAO51B,cAAc,MAAMzG,UACzB,OAAO7D,QAAQmC,EAAE,gCACnB+9B,EAAOj0B,MAAMqI,QAAU,OACzB,G2D3FA5J,oBACF3C,QAAEA,EACA4G,kBAGFwxB,EAAMtP,QACN9lB,EAAMgc,OAAOqZ,MAAMC,QAAU91B,EAC7B/J,SAASiC,iBAAiB,cChDX,WAEbjC,SAASC,gBAAgB8P,UAAU7L,OAAO,SAG1CrC,IAGA7B,SAASsC,iBAAiB,gBAAgBJ,SAASiB,IACjDA,EAAQlB,iBAAiB,QAASiI,EAAiB,IAIrDlK,SAASsC,iBAAiB,YAAYJ,SAASwY,IAC7CA,EAAO9L,aAAa,WAAY,EAAC,IAInCxP,IAAIma,OAAS,SAAU7R,GACrB,IAAIwkB,GAAWxkB,EAAMtG,QAAUsG,EAAMo4B,YAAY5T,QACjD,OACE9sB,IAAI2gC,UAAU,UACA,UAAZ7T,GAAmC,WAAZA,GAAoC,aAAZA,EAErD,CACF"} \ No newline at end of file +{"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/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 ``\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 = $(`\n

    ${error_header}

    \n

    ${error_body}

    \n
    `)\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 = $('
    ')\n this.dialog = $('
    ')\n this.dialog_body = $('
    ')\n this.dialog_header = $('
    ')\n this.dialog_title = $('
    ')\n this.close_button = $(\n ''\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 = $('
    ')\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 \n ${this.message}\n \n \n \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} 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 ``\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 = `${label}`\n const panel = ``\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 \n ${message}\n \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}\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 `
  • ${Alchemy.t(\"url_validation_failed\")}
  • `\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\n handleEvent(evt) {\n if (evt.type === \"load\") {\n this.#clearLoadTimeout()\n this.#stopSpinner()\n this.#afterLoad?.call(this, evt)\n }\n }\n\n #onPreviewReady = (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.addEventListener(\"load\", this)\n this.#attachEvents()\n window.addEventListener(\"message\", this.#onPreviewReady)\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 this.removeEventListener(\"load\", this)\n key.unbind(\"alt+r\")\n window.removeEventListener(\"message\", this.#onPreviewReady)\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 = ``\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 { setupSelectLocale } from \"alchemy_admin/i18n\"\n\nexport function hightlightTerm(name, term) {\n return name.replace(new RegExp(term, \"gi\"), (match) => `${match}`)\n}\n\nexport class RemoteSelect extends HTMLElement {\n #select2 = null\n\n async connectedCallback() {\n await setupSelectLocale()\n // Bail out if the element was disconnected while the locale was loading.\n // Otherwise Select2 would leak onto a detached input.\n if (!this.isConnected) return\n\n this.input.classList.add(\"alchemy_selectbox\")\n\n this.#select2 = $(this.input)\n .select2(this.select2Config)\n .on(\"select2-open\", this.#onOpen)\n .on(\"change\", this.#onChange)\n }\n\n disconnectedCallback() {\n if (this.#select2) {\n this.#select2.off(\"select2-open\", this.#onOpen)\n this.#select2.off(\"change\", this.#onChange)\n this.#select2.select2(\"destroy\")\n this.#select2 = null\n }\n }\n\n #onOpen = (evt) => this.onOpen(evt)\n #onChange = (evt) => this.onChange(evt)\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 /**\n * Dispatches a custom event with given name, namespaced under `Alchemy.`.\n * Subclasses may call this to emit their own events.\n * @param {string} name The name of the custom event\n * @param {object} detail Optional event details\n */\n dispatchCustomEvent(name, detail = {}) {\n this.dispatchEvent(\n new CustomEvent(`Alchemy.${name}`, { bubbles: true, detail })\n )\n }\n\n get allowClear() {\n return this.hasAttribute(\"allow-clear\")\n }\n\n get selection() {\n return this.getAttribute(\"selection\")\n }\n\n get placeholder() {\n return this.getAttribute(\"placeholder\") ?? \"\"\n }\n\n get queryParams() {\n return this.getAttribute(\"query-params\") ?? \"{}\"\n }\n\n get url() {\n return this.getAttribute(\"url\") ?? \"\"\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
    \n \n ${this._hightlightTerm(attachment.name, term)}\n
    \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\", this.#onChange)\n }\n\n disconnectedCallback() {\n $(this).off(\"change\", this.#onChange)\n }\n\n #onChange = (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\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 { translate } from \"alchemy_admin/i18n\"\n\nclass CharCounter extends HTMLElement {\n connectedCallback() {\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)\n }\n }\n\n disconnectedCallback() {\n this.formField?.removeEventListener(\"keyup\", this)\n }\n\n handleEvent(event) {\n if (event.type === \"keyup\") this.countCharacters()\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 = this.querySelector(\":scope > .alchemy-char-counter\")\n if (this.display) return\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 get maxChars() {\n return this.getAttribute(\"max-chars\") ?? 60\n }\n}\n\ncustomElements.define(\"alchemy-char-counter\", CharCounter)\n","import \"clipboard\"\nimport { growl } from \"alchemy_admin/growler\"\n\nclass ClipboardButton extends HTMLElement {\n connectedCallback() {\n this.innerHTML = ''\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 ? ``\n : ``\n\n return `\n
    \n ${colorIndicator}\n ${object.text}\n
    `\n}\n\nclass ColorSelect extends HTMLElement {\n #select2 = null\n\n connectedCallback() {\n if (this.select) {\n this.#initializeSelect2()\n this.#select2.on(\"change\", this.#onSelectChange)\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 if (this.#select2) {\n this.#select2.off(\"change\", this.#onSelectChange)\n this.#select2.select2(\"destroy\")\n this.#select2 = null\n }\n }\n\n #onSelectChange = (event) => {\n this.#toggleColorPicker(event.val === \"custom_color\")\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.#select2 = $(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 { translate, currentLocale } from \"alchemy_admin/i18n\"\nimport flatpickr from \"flatpickr\"\n\nconst locale = currentLocale()\n\nclass Datepicker extends HTMLElement {\n // Load the locales for flatpickr before setting it up.\n async connectedCallback() {\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 // Bail out if the element was disconnected while the locale was loading.\n // Otherwise flatpickr would leak a calendar onto a detached input.\n if (!this.isConnected) return\n\n this.flatpickr = flatpickr(this.inputField, this.flatpickrOptions)\n }\n\n disconnectedCallback() {\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 get inputType() {\n return this.getAttribute(\"input-type\") || \"date\"\n }\n}\n\ncustomElements.define(\"alchemy-datepicker\", Datepicker)\n","import { Dialog } from \"alchemy_admin/dialog\"\n\nexport class DialogLink extends HTMLAnchorElement {\n connectedCallback() {\n this.addEventListener(\"click\", this)\n }\n\n disconnectedCallback() {\n this.removeEventListener(\"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 connectedCallback() {\n this.button?.addEventListener(\"click\", this)\n }\n\n disconnectedCallback() {\n this.button?.removeEventListener(\"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 #form = null\n #header = null\n #toggleButton = null\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 // 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 = this.form\n if (this.#form) {\n $(this.#form).on(\"change\", this.onChange)\n }\n\n this.#header = this.header\n this.#header?.addEventListener(\"dblclick\", this.#onHeaderDblclick)\n\n this.#toggleButton = this.toggleButton\n this.#toggleButton?.addEventListener(\"click\", this.#onToggleClick)\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 disconnectedCallback() {\n this.removeEventListener(\"click\", this)\n this.removeEventListener(\"alchemy:element-update-title\", this)\n this.removeEventListener(\"ajax:complete\", this)\n if (this.#form) {\n $(this.#form).off(\"change\", this.onChange)\n this.#form = null\n }\n this.#header?.removeEventListener(\"dblclick\", this.#onHeaderDblclick)\n this.#header = null\n this.#toggleButton?.removeEventListener(\"click\", this.#onToggleClick)\n this.#toggleButton = null\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.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 `${ingredient.errorMessage}`\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 #onHeaderDblclick = () => {\n this.toggle()\n }\n\n #onToggleClick = (evt) => {\n const elementEditor = evt.target.closest(\"alchemy-element-editor\")\n if (elementEditor === this) {\n this.toggle()\n }\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
    \n ${option.icon}${option.text}\n
    \n `\n}\n\nconst formatItem = (icon, text, hint) => {\n const description = hint\n ? `
    ${hint}
    `\n : \"\"\n return `\n
    \n ${formatSelection({ icon, text })}\n ${description}\n
    \n `\n}\n\nclass ElementSelect extends HTMLElement {\n #select2 = null\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.#select2 = $(this.inputField).select2(options)\n }\n\n disconnectedCallback() {\n this.#select2?.select2(\"destroy\")\n this.#select2 = null\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 connectedCallback() {\n this.toggleButton?.addEventListener(\"click\", this.#onToggleClick)\n this.collapseButton?.addEventListener(\"click\", this.#onCollapseClick)\n window.addEventListener(\"message\", this.#onWindowMessage)\n document.body.addEventListener(\"click\", this.#onBodyClick)\n if (window.location.hash) {\n this.focusElementEditor(window.location.hash)\n }\n this.resize()\n }\n\n disconnectedCallback() {\n this.toggleButton?.removeEventListener(\"click\", this.#onToggleClick)\n this.collapseButton?.removeEventListener(\"click\", this.#onCollapseClick)\n window.removeEventListener(\"message\", this.#onWindowMessage)\n document.body.removeEventListener(\"click\", this.#onBodyClick)\n }\n\n #onToggleClick = (evt) => {\n evt.preventDefault()\n this.toggle()\n }\n\n #onCollapseClick = () => {\n this.collapseAllElements()\n }\n\n #onWindowMessage = (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\n #onBodyClick = (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 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\ncustomElements.define(\"alchemy-elements-window\", ElementsWindow)\n","class ElementsWindowHandle extends HTMLElement {\n #dragging = false\n #elementsWindow = null\n #previewWindow = null\n\n connectedCallback() {\n this.addEventListener(\"mousedown\", this)\n window.addEventListener(\"mousemove\", this)\n window.addEventListener(\"mouseup\", this)\n }\n\n disconnectedCallback() {\n this.removeEventListener(\"mousedown\", this)\n window.removeEventListener(\"mousemove\", this)\n window.removeEventListener(\"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 connectedCallback() {\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 disconnectedCallback() {\n this.deleteLink?.removeEventListener(\"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 #filterField = null\n #clearButton = null\n\n connectedCallback() {\n if (this.hotkey) {\n key(this.hotkey, this.#onHotkey)\n }\n this.#filterField = this.filterField\n this.#filterField.addEventListener(\"keyup\", this.#onKeyup)\n this.#filterField.addEventListener(\"focus\", this.#onFocus)\n\n this.#clearButton = this.clearButton\n this.#clearButton.addEventListener(\"click\", this.#onClearClick)\n\n key(\"esc\", \"list_filter\", this.#onEscape)\n }\n\n disconnectedCallback() {\n clearTimeout(this.#debounceTimer)\n this.#filterField?.removeEventListener(\"keyup\", this.#onKeyup)\n this.#filterField?.removeEventListener(\"focus\", this.#onFocus)\n this.#filterField = null\n this.#clearButton?.removeEventListener(\"click\", this.#onClearClick)\n this.#clearButton = null\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 #onHotkey = () => {\n this.filterField.focus()\n return false\n }\n\n #onKeyup = () => {\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\n #onFocus = () => key.setScope(\"list_filter\")\n\n #onClearClick = (e) => {\n e.preventDefault()\n this.clear()\n }\n\n #onEscape = () => {\n this.clear()\n this.filterField.blur()\n }\n}\n\ncustomElements.define(\"alchemy-list-filter\", ListFilter)\n","const DISMISS_DELAY = 5000\n\nclass Message extends HTMLElement {\n #dismissTimeoutId = null\n\n handleEvent(event) {\n if (event.type === \"click\") {\n this.dismiss()\n }\n }\n\n connectedCallback() {\n if (!this.querySelector(\":scope > alchemy-icon\")) {\n const closeIcon =\n this.dismissable && this.type === \"error\"\n ? ''\n : \"\"\n this.insertAdjacentHTML(\n \"afterbegin\",\n `${closeIcon}`\n )\n }\n if (this.dismissable || this.type === \"error\") {\n this.addEventListener(\"click\", this)\n }\n if (this.dismissable && this.type !== \"error\") {\n this.#dismissTimeoutId = setTimeout(() => {\n this.dismiss()\n }, this.dismissDelay)\n }\n }\n\n disconnectedCallback() {\n if (this.#dismissTimeoutId !== null) {\n clearTimeout(this.#dismissTimeoutId)\n this.#dismissTimeoutId = null\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 = ``\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 connectedCallback() {\n this.addEventListener(\"toggle\", this)\n\n if (this.isInLocalStorage) {\n this.open = true\n }\n }\n\n disconnectedCallback() {\n this.removeEventListener(\"toggle\", this)\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 connectedCallback() {\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 = ''\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 connectedCallback() {\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 ''\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 connectedCallback() {\n this.addEventListener(\"alchemy:link\", this)\n this.addEventListener(\"alchemy:unlink\", this)\n }\n\n disconnectedCallback() {\n this.removeEventListener(\"alchemy:link\", this)\n this.removeEventListener(\"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 = ``\n\n return `\n
    \n \n
    \n \n ${ancestors.length > 0 ? ancestors.join(seperator) + seperator : \"\"}\n \n \n ${this._hightlightTerm(node.name, term)}\n \n
    \n
    \n ${node.url || \"\"}\n
    \n
    \n `\n }\n}\n\ncustomElements.define(\"alchemy-node-select\", NodeSelect)\n","import { formatFileSize } from \"alchemy_admin/utils/format\"\nimport { translate } from \"alchemy_admin/i18n\"\nimport { growl } from \"alchemy_admin/growler\"\n\nexport class FileUpload extends HTMLElement {\n // public — used by callers (Uploader, Progress, tests)\n file = null\n request = null\n progressEventLoaded = 0\n progressEventTotal = 0\n\n // private — backing state for getters/setters\n #valid = true\n #value = 0\n #status = undefined\n #errorMessage = \"\"\n\n connectedCallback() {\n this.innerHTML = `\n \n
    \n ${this.file?.name}\n ${this.loadedSize}\n ${this.errorMessage}\n
    \n \n \n \n `\n\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 * 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 this.status = \"in-progress\"\n\n this.#validateFile()\n this.#addRequestEventListener()\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 * Dispatches a custom event with given name, namespaced under `Alchemy.`.\n * @param {string} name The name of the custom event\n */\n dispatchCustomEvent(name) {\n this.dispatchEvent(new CustomEvent(`Alchemy.${name}`, { bubbles: true }))\n }\n\n /**\n * validate given file with the `Alchemy.uploader_defaults` - configuration\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 */\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 { FileUpload } from \"alchemy_admin/components/uploader/file_upload\"\nimport { formatFileSize } from \"alchemy_admin/utils/format\"\nimport { translate } from \"alchemy_admin/i18n\"\n\nconst template = (buttonLabel, fileCount) => `\n \n
    \n \n\n \n \n \n
    \n
    3 ? 3 : fileCount\n }\">
    \n
    \n`\n\nexport class Progress extends HTMLElement {\n // public — accessed by Uploader and tests\n fileCount = 0\n\n // private — backing state and internals\n #fileUploads = []\n #buttonLabel = translate(\"Cancel all uploads\")\n #actionButton = null\n #visible = false\n #handleFileChange = () => this.#updateView()\n\n connectedCallback() {\n this.innerHTML = template(this.#buttonLabel, this.fileCount)\n this.visible = true\n\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 this.#updateView()\n this.addEventListener(\"Alchemy.FileUpload.Change\", this.#handleFileChange)\n }\n\n disconnectedCallback() {\n this.removeEventListener(\n \"Alchemy.FileUpload.Change\",\n this.#handleFileChange\n )\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 * 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 * 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 /**\n * get all active upload components\n * @returns {FileUpload[]}\n */\n #activeUploads() {\n return this.#fileUploads.filter((upload) => upload.active)\n }\n\n /**\n * replace cancel button to be the close button\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 */\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 */\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 { 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 HTMLElement {\n #dropzoneElement = null\n #isDraggedOver = false\n\n connectedCallback() {\n this.fileInput.addEventListener(\"change\", this.#onFileInputChange)\n if (this.dropzone) {\n this.#setupDropZone()\n }\n this.addEventListener(\"Alchemy.upload.successful\", this)\n }\n\n disconnectedCallback() {\n this.fileInput?.removeEventListener(\"change\", this.#onFileInputChange)\n if (this.#dropzoneElement) {\n this.#dropzoneElement.removeEventListener(\n \"dragleave\",\n this.#onDropzoneDragleave\n )\n this.#dropzoneElement.removeEventListener(\"drop\", this.#onDropzoneDrop)\n this.#dropzoneElement.removeEventListener(\n \"dragover\",\n this.#onDropzoneDragover\n )\n this.#dropzoneElement = null\n }\n }\n\n handleEvent(evt) {\n switch (evt.type) {\n case \"Alchemy.upload.successful\":\n this.#handleUploadComplete()\n break\n }\n }\n\n #onFileInputChange = (event) => {\n this.uploadFiles(Array.from(event.target.files))\n }\n\n #toggleDropzoneClass = (enabled) => {\n if (this.#isDraggedOver !== enabled) {\n this.#isDraggedOver = enabled\n this.#dropzoneElement.classList.toggle(\"dragover\")\n }\n }\n\n #onDropzoneDragleave = () => this.#toggleDropzoneClass(false)\n\n #onDropzoneDrop = async (event) => {\n event.preventDefault()\n this.#toggleDropzoneClass(false)\n\n const files = [...event.dataTransfer.items].map((item) => item.getAsFile())\n\n this.uploadFiles(files)\n }\n\n #onDropzoneDragover = (event) => {\n event.preventDefault() // dragover has to be disabled to use the custom drop event\n this.#toggleDropzoneClass(true)\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 #setupDropZone() {\n this.#dropzoneElement = document.querySelector(this.dropzone)\n if (!this.#dropzoneElement) return\n\n this.#dropzoneElement.addEventListener(\n \"dragleave\",\n this.#onDropzoneDragleave\n )\n this.#dropzoneElement.addEventListener(\"drop\", this.#onDropzoneDrop)\n this.#dropzoneElement.addEventListener(\"dragover\", this.#onDropzoneDragover)\n }\n\n /**\n * @param {File[]} files\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.dispatchEvent(\n new CustomEvent(`Alchemy.upload.${status}`, { bubbles: true })\n )\n }\n\n document.body.append(this.uploadProgress)\n }\n\n get dropzone() {\n return this.getAttribute(\"dropzone\")\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","class Overlay extends HTMLElement {\n connectedCallback() {\n this.innerHTML = `\n \n
    \n ${this.getAttribute(\"text\") ?? \"\"}\n
    \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 = ``\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 = ``\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 = ''\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 this.publicField?.addEventListener(\"click\", this.#onClick)\n }\n\n disconnectedCallback() {\n this.publicField?.removeEventListener(\"click\", this.#onClick)\n }\n\n #onClick = (evt) => {\n const checkbox = evt.target\n const now = new Date()\n\n if (checkbox.checked) {\n this.publicationDateFields.classList.remove(\"hidden\")\n this.publicOnPicker.flatpickr.setDate(now)\n } else {\n this.publicationDateFields.classList.add(\"hidden\")\n this.publicOnPicker.flatpickr.clear()\n }\n this.publicUntilPicker.flatpickr?.clear()\n }\n\n get publicField() {\n return this.querySelector(\"#page_public\")\n }\n\n get publicOnPicker() {\n return this.querySelector(\"alchemy-datepicker:has(#page_public_on)\")\n }\n\n get publicUntilPicker() {\n return this.querySelector(\"alchemy-datepicker:has(#page_public_until)\")\n }\n\n get publicationDateFields() {\n return this.querySelector(\".page-publication-date-fields\")\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
    \n
    \n \n ${this._hightlightTerm(page.name, term)}\n ${page.site.name}\n
    \n
    \n ${page.url_path}\n ${page.language_code}\n
    \n
    \n `\n }\n}\n\ncustomElements.define(\"alchemy-page-select\", PageSelect)\n","class PictureDescriptionSelect extends HTMLElement {\n connectedCallback() {\n this.addEventListener(\"change\", this)\n }\n\n disconnectedCallback() {\n this.removeEventListener(\"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 = ''\nconst THUMBNAIL_SIZE = \"160x120\"\n\nexport class PictureEditor extends HTMLElement {\n connectedCallback() {\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)\n\n this.observer = new MutationObserver(this.mutationCallback)\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.spinner = new Spinner(\"small\")\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.classList.add(\"thumbnail_background\")\n if (this.src && !this.image) {\n this.start()\n }\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 }\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 \n \n \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 connectedCallback() {\n this.addEventListener(\"submit\", this)\n document.addEventListener(\"alchemy:page-dirty\", this)\n }\n\n disconnectedCallback() {\n this.removeEventListener(\"submit\", this)\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 disconnectedCallback() {\n this.#select2Element?.select2(\"destroy\")\n this.#select2Element = null\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 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","class Spinner extends HTMLElement {\n connectedCallback() {\n this.className = `spinner spinner--${this.size}`\n this.innerHTML = `\n \n \n \n \n \n `\n }\n\n get size() {\n return this.getAttribute(\"size\") || \"medium\"\n }\n\n get color() {\n return this.getAttribute(\"color\") || \"currentColor\"\n }\n}\n\ncustomElements.define(\"alchemy-spinner\", Spinner)\n","import { setupSelectLocale } from \"alchemy_admin/i18n\"\n\nclass TagsAutocomplete extends HTMLElement {\n #select2 = null\n\n async connectedCallback() {\n await setupSelectLocale()\n if (!this.isConnected) return\n\n this.classList.add(\"autocomplete_tag_list\")\n this.#select2 = $(this.input).select2(this.select2Config)\n }\n\n disconnectedCallback() {\n this.#select2?.select2(\"destroy\")\n this.#select2 = null\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 { currentLocale } from \"alchemy_admin/i18n\"\n\nconst DARK_THEME = \"alchemy-dark\"\nconst LIGHT_THEME = \"alchemy\"\n\nclass Tinymce extends HTMLElement {\n #min_height = null\n\n /**\n * the observer will initialize Tinymce if the textarea becomes visible\n */\n connectedCallback() {\n this.className = \"tinymce_container\"\n\n // Append the spinner if not already present (idempotent on reconnect/clone)\n if (!this.querySelector(\":scope > alchemy-spinner\")) {\n this.insertAdjacentHTML(\n \"beforeend\",\n ``\n )\n }\n\n // hide the textarea until TinyMCE is ready to show the editor\n this.style.minHeight = `${this.minHeight}px`\n this.editor.style.display = \"none\"\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 disconnectedCallback() {\n this.tinymceIntersectionObserver?.disconnect()\n\n // Remove theme change listener\n this._removeThemeChangeListener()\n\n tinymce.get(this.editorId)?.remove(this.editorId)\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'
    \\n \\n\\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 = $('
    ')\n this.dialog = $('
    ')\n this.dialog_body = $('
    ')\n this.close_button = $(`\n \n `)\n this.dialog.append(this.close_button)\n this.dialog.append(this.dialog_body)\n this.dialog_container.append(this.dialog)\n this.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 '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","handleEvent","type","clearLoadTimeout","stopSpinner","call","onPreviewReady","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","super","actions","removeFixedElement","updateAnchorIcon","hidePleaseWaitOverlay","func","name","params","hasAttribute","JSON","parse","hightlightTerm","term","RegExp","RemoteSelect","isConnected","input","classList","add","select2Config","onOpen","onChange","dispatchCustomEvent","removed","added","allowClear","selection","placeholder","queryParams","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","CharCounter","maxChars","formField","getFormField","createDisplayElement","countCharacters","formFields","display","className","after","charLength","textContent","toggle","ClipboardButton","clipboard","ClipboardJS","destroy","formatItem","object","optionEl","swatch","ColorSelect","select","initializeSelect2","onSelectChange","colorInput","textInput","toggleColorPicker","val","minimumResultsForSearch","enabled","disabled","Datepicker","flatpickr","inputField","flatpickrOptions","enableTime","inputType","slice","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","onHeaderDblclick","onToggleClick","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","collapseButton","onCollapseClick","onWindowMessage","onBodyClick","focusElementEditor","collapseAllElements","removeProperty","widthFromCookie","setProperty","cookie","row","startsWith","ElementsWindowHandle","dragging","elementsWindow","onMouseDown","onMouseUp","onDrag","pageX","elementWindowWidth","innerWidth","FileEditor","deleteLink","fileIcon","fileName","formFieldId","removeFile","ListFilter","debounceTimer","filterField","clearButton","onHotkey","onKeyup","onFocus","onClearClick","onEscape","visibility","matchedItems","itemsToShow","Set","lowerTerm","items","nameAttribute","indexOf","ancestor","itemsSelector","has","block","clear","debounceTime","Message","dismissTimeoutId","dismiss","closeIcon","dismissable","insertAdjacentHTML","iconName","dismissDelay","noticesWrapper","autoDismissDelay","Growl","Icon","observedAttributes","spriteUrl","render","attributeChangedCallback","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","a","seperator","FileUpload","file","request","progressEventLoaded","progressEventTotal","valid","loadedSize","cancel","reader","FileReader","readAsDataURL","image","Image","result","prepend","initialize","validateFile","addRequestEventListener","finished","abort","config","uploader_defaults","maxFileSize","file_size_limit","allowedFiletypes","allowed_filetypes","alchemy_pictures","alchemy_attachments","upload","onprogress","progressEvent","onload","responseMessage","onerror","errorMessageContainer","progressElement","loaded","total","round","toggleAttribute","isValid","Progress","fileCount","fileUploads","buttonLabel","actionButton","handleFileChange","updateView","onComplete","fileUpload","activeUploads","setupCloseButton","_status","ariaLabel","sumFileProgresses","field","reduce","accumulator","totalProgress","overallProgressValue","overallUploadSize","every","entry","fileProgress","uploadedFileCount","uploadsStatuses","totalSize","ceil","weight","Uploader","dropzoneElement","isDraggedOver","fileInput","onFileInputChange","dropzone","setupDropZone","onDropzoneDragleave","onDropzoneDrop","onDropzoneDragover","handleUploadComplete","uploadFiles","files","toggleDropzoneClass","dataTransfer","getAsFile","redirectUrl","uploadProgress","fileUploadCount","XMLHttpRequest","upload_limit","submitFile","createProgress","formData","FormData","set","setRequestHeader","send","removeChild","Overlay","BUTTON","AlchemyPageNode","folded","folderButton","handleFolderClick","fold_admin_page_path","toggleChildren","updateFolderIcon","childrenContainer","updateFolderButton","folderElement","shouldShowButton","tagName","outerHTML","PagePublicationFields","publicField","onClick","checkbox","now","Date","checked","publicationDateFields","publicOnPicker","setDate","publicUntilPicker","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","b","imageFileHeight","dimensions","imageCropper","PictureThumbnail","onLoaded","onError","start","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","Tinymce","min_height","minHeight","root","rootMargin","threshold","tinymceIntersectionObserver","IntersectionObserver","entries","intersectionRatio","_initTinymceEditor","unobserve","_setupThemeChangeListener","_removeThemeChangeListener","tinymce","editorId","configuration","editors","_setupEditor","editorContainer","darkModeMediaQuery","matchMedia","themeChangeHandler","_handleThemeChange","skin","matches","content_css","customConfig","getAttributeNames","attributeName","replaceAll","preferredTheme","TinymceDefaults","language","UpdateCheck","credentials","responseJSON","showStatus","showError","Handlebars","template","templates","l","r","compiler","main","o","lambda","d","escapeExpression","u","lookupProperty","Object","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","bind","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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"} \ No newline at end of file diff --git a/app/javascript/alchemy_admin/components/auto_submit.js b/app/javascript/alchemy_admin/components/auto_submit.js index a12d8bf262..88c36a4e4a 100644 --- a/app/javascript/alchemy_admin/components/auto_submit.js +++ b/app/javascript/alchemy_admin/components/auto_submit.js @@ -4,16 +4,22 @@ class AutoSubmit extends HTMLElement { connectedCallback() { // Still using jQuery here, because select2 does not emit // the event from the original select element. - $(this).on("change", function (event) { - // We need to dispatch a submit event, so that Turbo that listens - // to it submits the search form us. - const submitEvent = new Event("submit", { - bubbles: true, - cancelable: true - }) - event.target.form.dispatchEvent(submitEvent) - return false + $(this).on("change", this.#onChange) + } + + disconnectedCallback() { + $(this).off("change", this.#onChange) + } + + #onChange = (event) => { + // We need to dispatch a submit event, so that Turbo that listens + // to it submits the search form us. + const submitEvent = new Event("submit", { + bubbles: true, + cancelable: true }) + event.target.form.dispatchEvent(submitEvent) + return false } } diff --git a/app/javascript/alchemy_admin/components/clipboard_button.js b/app/javascript/alchemy_admin/components/clipboard_button.js index 675e6233d9..65ddddc38a 100644 --- a/app/javascript/alchemy_admin/components/clipboard_button.js +++ b/app/javascript/alchemy_admin/components/clipboard_button.js @@ -2,12 +2,8 @@ import "clipboard" import { growl } from "alchemy_admin/growler" class ClipboardButton extends HTMLElement { - constructor() { - super() - - this.innerHTML = ` - - ` + connectedCallback() { + this.innerHTML = '' this.clipboard = new ClipboardJS(this, { text: () => { diff --git a/app/javascript/alchemy_admin/components/color_select.js b/app/javascript/alchemy_admin/components/color_select.js index e170f2065a..114fcf49e6 100644 --- a/app/javascript/alchemy_admin/components/color_select.js +++ b/app/javascript/alchemy_admin/components/color_select.js @@ -14,12 +14,12 @@ const formatItem = (object) => { } class ColorSelect extends HTMLElement { + #select2 = null + connectedCallback() { if (this.select) { this.#initializeSelect2() - $(this.select).on("change", (event) => - this.#toggleColorPicker(event.val === "custom_color") - ) + this.#select2.on("change", this.#onSelectChange) } else { this.colorInput?.addEventListener("input", this) this.textInput?.addEventListener("input", this) @@ -41,6 +41,15 @@ class ColorSelect extends HTMLElement { disconnectedCallback() { this.colorInput?.removeEventListener("input", this) this.textInput?.removeEventListener("input", this) + if (this.#select2) { + this.#select2.off("change", this.#onSelectChange) + this.#select2.select2("destroy") + this.#select2 = null + } + } + + #onSelectChange = (event) => { + this.#toggleColorPicker(event.val === "custom_color") } #initializeSelect2() { @@ -50,7 +59,7 @@ class ColorSelect extends HTMLElement { formatResult: formatItem, formatSelection: formatItem } - $(this.select).select2(options) + this.#select2 = $(this.select).select2(options) } #toggleColorPicker(enabled = true) { diff --git a/app/javascript/alchemy_admin/components/dialog_link.js b/app/javascript/alchemy_admin/components/dialog_link.js index 792a201a6c..e71d837941 100644 --- a/app/javascript/alchemy_admin/components/dialog_link.js +++ b/app/javascript/alchemy_admin/components/dialog_link.js @@ -1,11 +1,14 @@ import { Dialog } from "alchemy_admin/dialog" export class DialogLink extends HTMLAnchorElement { - constructor() { - super() + connectedCallback() { this.addEventListener("click", this) } + disconnectedCallback() { + this.removeEventListener("click", this) + } + handleEvent(evt) { if (!this.disabled) { this.openDialog() diff --git a/app/javascript/alchemy_admin/components/element_editor.js b/app/javascript/alchemy_admin/components/element_editor.js index 6375b8b81e..b7ef2f769c 100644 --- a/app/javascript/alchemy_admin/components/element_editor.js +++ b/app/javascript/alchemy_admin/components/element_editor.js @@ -15,8 +15,15 @@ export function dispatchPageDirtyEvent(data) { } export class ElementEditor extends HTMLElement { - constructor() { - super() + #form = null + #header = null + #toggleButton = null + + connectedCallback() { + // The placeholder while be being dragged is empty. + if (this.classList.contains("ui-sortable-placeholder")) { + return + } // Add event listeners this.addEventListener("click", this) @@ -27,24 +34,16 @@ export class ElementEditor extends HTMLElement { // Dirty observer still needs to be jQuery // in order to support select2. - $(this.form).on("change", this.onChange) + this.#form = this.form + if (this.#form) { + $(this.#form).on("change", this.onChange) + } - this.header?.addEventListener("dblclick", () => { - this.toggle() - }) - this.toggleButton?.addEventListener("click", (evt) => { - const elementEditor = evt.target.closest("alchemy-element-editor") - if (elementEditor === this) { - this.toggle() - } - }) - } + this.#header = this.header + this.#header?.addEventListener("dblclick", this.#onHeaderDblclick) - connectedCallback() { - // The placeholder while be being dragged is empty. - if (this.classList.contains("ui-sortable-placeholder")) { - return - } + this.#toggleButton = this.toggleButton + this.#toggleButton?.addEventListener("click", this.#onToggleClick) // When newly created, focus the element and refresh the preview if (this.hasAttribute("created")) { @@ -56,6 +55,20 @@ export class ElementEditor extends HTMLElement { } } + disconnectedCallback() { + this.removeEventListener("click", this) + this.removeEventListener("alchemy:element-update-title", this) + this.removeEventListener("ajax:complete", this) + if (this.#form) { + $(this.#form).off("change", this.onChange) + this.#form = null + } + this.#header?.removeEventListener("dblclick", this.#onHeaderDblclick) + this.#header = null + this.#toggleButton?.removeEventListener("click", this.#onToggleClick) + this.#toggleButton = null + } + handleEvent(event) { switch (event.type) { case "click": @@ -79,7 +92,7 @@ export class ElementEditor extends HTMLElement { } } - onChange(event) { + onChange = (event) => { const target = event.target // SortableJS fires a native change event :/ // and we do not want to set the element editor dirty @@ -87,7 +100,7 @@ export class ElementEditor extends HTMLElement { if (target.classList.contains("nested-elements")) { return } - this.closest("alchemy-element-editor").setDirty(target) + this.setDirty(target) event.stopPropagation() return false } @@ -576,6 +589,17 @@ export class ElementEditor extends HTMLElement { get previewWindow() { return document.getElementById("alchemy_preview_window") } + + #onHeaderDblclick = () => { + this.toggle() + } + + #onToggleClick = (evt) => { + const elementEditor = evt.target.closest("alchemy-element-editor") + if (elementEditor === this) { + this.toggle() + } + } } customElements.define("alchemy-element-editor", ElementEditor) diff --git a/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js b/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js index 6c6e825cb1..e1a25c36a8 100644 --- a/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +++ b/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js @@ -6,11 +6,14 @@ import { openConfirmDialog } from "alchemy_admin/confirm_dialog" import { dispatchPageDirtyEvent } from "alchemy_admin/components/element_editor" export class DeleteElementButton extends HTMLElement { - constructor() { - super() + connectedCallback() { this.button?.addEventListener("click", this) } + disconnectedCallback() { + this.button?.removeEventListener("click", this) + } + async handleEvent() { const confirmed = await openConfirmDialog(this.message) if (confirmed) { diff --git a/app/javascript/alchemy_admin/components/element_select.js b/app/javascript/alchemy_admin/components/element_select.js index 4dc708d895..c3bafb1c9d 100644 --- a/app/javascript/alchemy_admin/components/element_select.js +++ b/app/javascript/alchemy_admin/components/element_select.js @@ -21,9 +21,7 @@ const formatItem = (icon, text, hint) => { } class ElementSelect extends HTMLElement { - constructor() { - super() - } + #select2 = null connectedCallback() { const results = this.options @@ -48,7 +46,12 @@ class ElementSelect extends HTMLElement { formatSelection, placeholder: this.placeholder } - $(this.inputField).select2(options) + this.#select2 = $(this.inputField).select2(options) + } + + disconnectedCallback() { + this.#select2?.select2("destroy") + this.#select2 = null } get options() { diff --git a/app/javascript/alchemy_admin/components/elements_window.js b/app/javascript/alchemy_admin/components/elements_window.js index cff65bcdf4..1aa6ce6cd6 100644 --- a/app/javascript/alchemy_admin/components/elements_window.js +++ b/app/javascript/alchemy_admin/components/elements_window.js @@ -4,22 +4,51 @@ class ElementsWindow extends HTMLElement { #visible = true #turboFrame = null - constructor() { - super() - this.#attachEvents() - } - connectedCallback() { - this.toggleButton?.addEventListener("click", (evt) => { - evt.preventDefault() - this.toggle() - }) + this.toggleButton?.addEventListener("click", this.#onToggleClick) + this.collapseButton?.addEventListener("click", this.#onCollapseClick) + window.addEventListener("message", this.#onWindowMessage) + document.body.addEventListener("click", this.#onBodyClick) if (window.location.hash) { this.focusElementEditor(window.location.hash) } this.resize() } + disconnectedCallback() { + this.toggleButton?.removeEventListener("click", this.#onToggleClick) + this.collapseButton?.removeEventListener("click", this.#onCollapseClick) + window.removeEventListener("message", this.#onWindowMessage) + document.body.removeEventListener("click", this.#onBodyClick) + } + + #onToggleClick = (evt) => { + evt.preventDefault() + this.toggle() + } + + #onCollapseClick = () => { + this.collapseAllElements() + } + + #onWindowMessage = (event) => { + const data = event.data + if (data?.message == "Alchemy.focusElementEditor") { + const element = document.getElementById(`element_${data.element_id}`) + this.show() + element?.focusElement() + } + } + + #onBodyClick = (evt) => { + if (!evt.target.closest("alchemy-element-editor")) { + this.querySelectorAll("alchemy-element-editor").forEach((editor) => { + editor.classList.remove("selected") + }) + this.previewWindow?.postMessage({ message: "Alchemy.blurElements" }) + } + } + collapseAllElements() { this.querySelectorAll( "alchemy-element-editor:not([compact]):not([fixed])" @@ -101,28 +130,6 @@ class ElementsWindow extends HTMLElement { this.turboFrame.style.transitionProperty = dragged ? "none" : null this.turboFrame.style.pointerEvents = dragged ? "none" : null } - - #attachEvents() { - this.collapseButton?.addEventListener("click", () => { - this.collapseAllElements() - }) - window.addEventListener("message", (event) => { - const data = event.data - if (data?.message == "Alchemy.focusElementEditor") { - const element = document.getElementById(`element_${data.element_id}`) - this.show() - element?.focusElement() - } - }) - document.body.addEventListener("click", (evt) => { - if (!evt.target.closest("alchemy-element-editor")) { - this.querySelectorAll("alchemy-element-editor").forEach((editor) => { - editor.classList.remove("selected") - }) - this.previewWindow?.postMessage({ message: "Alchemy.blurElements" }) - } - }) - } } customElements.define("alchemy-elements-window", ElementsWindow) diff --git a/app/javascript/alchemy_admin/components/elements_window_handle.js b/app/javascript/alchemy_admin/components/elements_window_handle.js index c4ed990215..99e63ca60a 100644 --- a/app/javascript/alchemy_admin/components/elements_window_handle.js +++ b/app/javascript/alchemy_admin/components/elements_window_handle.js @@ -3,14 +3,18 @@ class ElementsWindowHandle extends HTMLElement { #elementsWindow = null #previewWindow = null - constructor() { - super() - + connectedCallback() { this.addEventListener("mousedown", this) window.addEventListener("mousemove", this) window.addEventListener("mouseup", this) } + disconnectedCallback() { + this.removeEventListener("mousedown", this) + window.removeEventListener("mousemove", this) + window.removeEventListener("mouseup", this) + } + handleEvent(event) { switch (event.type) { case "mousedown": diff --git a/app/javascript/alchemy_admin/components/file_editor.js b/app/javascript/alchemy_admin/components/file_editor.js index 3af79d3e79..fc4c0a94f6 100644 --- a/app/javascript/alchemy_admin/components/file_editor.js +++ b/app/javascript/alchemy_admin/components/file_editor.js @@ -1,6 +1,5 @@ class FileEditor extends HTMLElement { - constructor() { - super() + connectedCallback() { this.deleteLink = this.querySelector(".remove_file_link") this.fileIcon = this.querySelector(".file_icon") this.fileName = this.querySelector(".file_name") @@ -9,6 +8,10 @@ class FileEditor extends HTMLElement { this.deleteLink?.addEventListener("click", this) } + disconnectedCallback() { + this.deleteLink?.removeEventListener("click", this) + } + handleEvent(event) { if (event.type === "click") this.removeFile() event.stopPropagation() diff --git a/app/javascript/alchemy_admin/components/ingredient_group.js b/app/javascript/alchemy_admin/components/ingredient_group.js index f6821594e0..e6d7e1e15f 100644 --- a/app/javascript/alchemy_admin/components/ingredient_group.js +++ b/app/javascript/alchemy_admin/components/ingredient_group.js @@ -1,9 +1,7 @@ export class IngredientGroup extends HTMLDetailsElement { #localStorageKey = "Alchemy.expanded_ingredient_groups" - constructor() { - super() - + connectedCallback() { this.addEventListener("toggle", this) if (this.isInLocalStorage) { @@ -11,6 +9,10 @@ export class IngredientGroup extends HTMLDetailsElement { } } + disconnectedCallback() { + this.removeEventListener("toggle", this) + } + /** * Toggle visibility of the ingredient fields in this group */ diff --git a/app/javascript/alchemy_admin/components/link_buttons.js b/app/javascript/alchemy_admin/components/link_buttons.js index 14d609c063..a081b07841 100644 --- a/app/javascript/alchemy_admin/components/link_buttons.js +++ b/app/javascript/alchemy_admin/components/link_buttons.js @@ -2,12 +2,16 @@ import "alchemy_admin/components/link_buttons/link_button" import "alchemy_admin/components/link_buttons/unlink_button" class LinkButtons extends HTMLElement { - constructor() { - super() + connectedCallback() { this.addEventListener("alchemy:link", this) this.addEventListener("alchemy:unlink", this) } + disconnectedCallback() { + this.removeEventListener("alchemy:link", this) + this.removeEventListener("alchemy:unlink", this) + } + handleEvent(event) { switch (event.type) { case "alchemy:link": diff --git a/app/javascript/alchemy_admin/components/link_buttons/link_button.js b/app/javascript/alchemy_admin/components/link_buttons/link_button.js index 5bad7824bd..503eb220e0 100644 --- a/app/javascript/alchemy_admin/components/link_buttons/link_button.js +++ b/app/javascript/alchemy_admin/components/link_buttons/link_button.js @@ -1,6 +1,5 @@ class LinkButton extends HTMLButtonElement { - constructor() { - super() + connectedCallback() { this.addEventListener("click", this) this.classList.add("icon_button") // Prevent accidental form submits if this component is wrapped inside a form diff --git a/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js b/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js index f7d51ba2dc..3be9481b22 100644 --- a/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js +++ b/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js @@ -1,6 +1,5 @@ class UnlinkButton extends HTMLButtonElement { - constructor() { - super() + connectedCallback() { this.addEventListener("click", this) this.classList.add("icon_button") // Prevent accidental form submits if this component is wrapped inside a form diff --git a/app/javascript/alchemy_admin/components/list_filter.js b/app/javascript/alchemy_admin/components/list_filter.js index 77d9fd28fa..1e9a2f47de 100644 --- a/app/javascript/alchemy_admin/components/list_filter.js +++ b/app/javascript/alchemy_admin/components/list_filter.js @@ -2,41 +2,30 @@ const DEFAULT_DEBOUNCE_TIME = 150 class ListFilter extends HTMLElement { #debounceTimer + #filterField = null + #clearButton = null - constructor() { - super() - this.#attachEvents() - } - - #attachEvents() { + connectedCallback() { if (this.hotkey) { - key(this.hotkey, () => { - this.filterField.focus() - return false - }) + key(this.hotkey, this.#onHotkey) } - this.filterField.addEventListener("keyup", () => { - clearTimeout(this.#debounceTimer) - this.#debounceTimer = setTimeout(() => { - const term = this.filterField.value - this.clearButton.style.visibility = term ? "visible" : "hidden" - this.filter(term) - }, this.debounceTime) - }) - this.clearButton.addEventListener("click", (e) => { - e.preventDefault() - this.clear() - }) - this.filterField.addEventListener("focus", () => - key.setScope("list_filter") - ) - key("esc", "list_filter", () => { - this.clear() - this.filterField.blur() - }) + this.#filterField = this.filterField + this.#filterField.addEventListener("keyup", this.#onKeyup) + this.#filterField.addEventListener("focus", this.#onFocus) + + this.#clearButton = this.clearButton + this.#clearButton.addEventListener("click", this.#onClearClick) + + key("esc", "list_filter", this.#onEscape) } disconnectedCallback() { + clearTimeout(this.#debounceTimer) + this.#filterField?.removeEventListener("keyup", this.#onKeyup) + this.#filterField?.removeEventListener("focus", this.#onFocus) + this.#filterField = null + this.#clearButton?.removeEventListener("click", this.#onClearClick) + this.#clearButton = null if (this.hotkey) { key.unbind(this.hotkey) } @@ -112,6 +101,32 @@ class ListFilter extends HTMLElement { get hotkey() { return this.getAttribute("hotkey") } + + #onHotkey = () => { + this.filterField.focus() + return false + } + + #onKeyup = () => { + clearTimeout(this.#debounceTimer) + this.#debounceTimer = setTimeout(() => { + const term = this.filterField.value + this.clearButton.style.visibility = term ? "visible" : "hidden" + this.filter(term) + }, this.debounceTime) + } + + #onFocus = () => key.setScope("list_filter") + + #onClearClick = (e) => { + e.preventDefault() + this.clear() + } + + #onEscape = () => { + this.clear() + this.filterField.blur() + } } customElements.define("alchemy-list-filter", ListFilter) diff --git a/app/javascript/alchemy_admin/components/message.js b/app/javascript/alchemy_admin/components/message.js index 31ab55ff29..d3fed2a5d3 100644 --- a/app/javascript/alchemy_admin/components/message.js +++ b/app/javascript/alchemy_admin/components/message.js @@ -1,15 +1,7 @@ const DISMISS_DELAY = 5000 class Message extends HTMLElement { - #message - - constructor() { - super() - this.#message = this.innerHTML - if (this.dismissable || this.type === "error") { - this.addEventListener("click", this) - } - } + #dismissTimeoutId = null handleEvent(event) { if (event.type === "click") { @@ -18,18 +10,33 @@ class Message extends HTMLElement { } connectedCallback() { - this.innerHTML = ` - - ${this.dismissable && this.type === "error" ? '' : ""} - ${this.#message} - ` + if (!this.querySelector(":scope > alchemy-icon")) { + const closeIcon = + this.dismissable && this.type === "error" + ? '' + : "" + this.insertAdjacentHTML( + "afterbegin", + `${closeIcon}` + ) + } + if (this.dismissable || this.type === "error") { + this.addEventListener("click", this) + } if (this.dismissable && this.type !== "error") { - setTimeout(() => { + this.#dismissTimeoutId = setTimeout(() => { this.dismiss() }, this.dismissDelay) } } + disconnectedCallback() { + if (this.#dismissTimeoutId !== null) { + clearTimeout(this.#dismissTimeoutId) + this.#dismissTimeoutId = null + } + } + dismiss() { this.addEventListener("transitionend", () => this.remove()) this.classList.add("dismissed") diff --git a/app/javascript/alchemy_admin/components/page_publication_fields.js b/app/javascript/alchemy_admin/components/page_publication_fields.js index 66a31a75dd..9f84c64397 100644 --- a/app/javascript/alchemy_admin/components/page_publication_fields.js +++ b/app/javascript/alchemy_admin/components/page_publication_fields.js @@ -1,32 +1,41 @@ // Handles the page publication date fields export class PagePublicationFields extends HTMLElement { connectedCallback() { - const public_on_picker = this.querySelector( - "alchemy-datepicker:has(#page_public_on)" - ) - const public_until_picker = this.querySelector( - "alchemy-datepicker:has(#page_public_until)" - ) - const publication_date_fields = this.querySelector( - ".page-publication-date-fields" - ) - const public_field = this.querySelector("#page_public") - - if (!public_field) return - - public_field.addEventListener("click", function (evt) { - const checkbox = evt.target - const now = new Date() - - if (checkbox.checked) { - publication_date_fields.classList.remove("hidden") - public_on_picker.flatpickr.setDate(now) - } else { - publication_date_fields.classList.add("hidden") - public_on_picker.flatpickr.clear() - } - public_until_picker.flatpickr?.clear() - }) + this.publicField?.addEventListener("click", this.#onClick) + } + + disconnectedCallback() { + this.publicField?.removeEventListener("click", this.#onClick) + } + + #onClick = (evt) => { + const checkbox = evt.target + const now = new Date() + + if (checkbox.checked) { + this.publicationDateFields.classList.remove("hidden") + this.publicOnPicker.flatpickr.setDate(now) + } else { + this.publicationDateFields.classList.add("hidden") + this.publicOnPicker.flatpickr.clear() + } + this.publicUntilPicker.flatpickr?.clear() + } + + get publicField() { + return this.querySelector("#page_public") + } + + get publicOnPicker() { + return this.querySelector("alchemy-datepicker:has(#page_public_on)") + } + + get publicUntilPicker() { + return this.querySelector("alchemy-datepicker:has(#page_public_until)") + } + + get publicationDateFields() { + return this.querySelector(".page-publication-date-fields") } } diff --git a/app/javascript/alchemy_admin/components/picture_description_select.js b/app/javascript/alchemy_admin/components/picture_description_select.js index 852d321dc3..34d97a8979 100644 --- a/app/javascript/alchemy_admin/components/picture_description_select.js +++ b/app/javascript/alchemy_admin/components/picture_description_select.js @@ -1,9 +1,12 @@ class PictureDescriptionSelect extends HTMLElement { - constructor() { - super() + connectedCallback() { this.addEventListener("change", this) } + disconnectedCallback() { + this.removeEventListener("change", this) + } + handleEvent(event) { switch (event.type) { case "change": diff --git a/app/javascript/alchemy_admin/components/picture_editor.js b/app/javascript/alchemy_admin/components/picture_editor.js index 23e9059ad9..5add76cb6a 100644 --- a/app/javascript/alchemy_admin/components/picture_editor.js +++ b/app/javascript/alchemy_admin/components/picture_editor.js @@ -8,9 +8,7 @@ const IMAGE_PLACEHOLDER = '' const THUMBNAIL_SIZE = "160x120" export class PictureEditor extends HTMLElement { - constructor() { - super() - + connectedCallback() { this.cropFromField = this.querySelector("[data-crop-from]") this.cropSizeField = this.querySelector("[data-crop-size]") this.pictureIdField = this.querySelector("[data-picture-id]") @@ -31,12 +29,9 @@ export class PictureEditor extends HTMLElement { this.updateCropLink() }, UPDATE_DELAY) - this.deleteButton?.addEventListener("click", this.removeImage.bind(this)) - } - - connectedCallback() { - this.observer = new MutationObserver(this.mutationCallback.bind(this)) + this.deleteButton?.addEventListener("click", this.removeImage) + this.observer = new MutationObserver(this.mutationCallback) this.observer.observe(this.cropFromField, { attributes: true }) this.observer.observe(this.cropSizeField, { attributes: true }) this.observer.observe(this.pictureIdField, { attributes: true }) @@ -46,7 +41,7 @@ export class PictureEditor extends HTMLElement { this.observer.disconnect() } - mutationCallback(mutationsList) { + mutationCallback = (mutationsList) => { for (const mutation of mutationsList) { if ("pictureId" in mutation.target.dataset) { this.cropFromField.value = "" @@ -80,7 +75,7 @@ export class PictureEditor extends HTMLElement { }) } - removeImage() { + removeImage = () => { this.pictureThumbnail.innerHTML = IMAGE_PLACEHOLDER this.pictureIdField.value = "" this.image = null diff --git a/app/javascript/alchemy_admin/components/picture_thumbnail.js b/app/javascript/alchemy_admin/components/picture_thumbnail.js index c31833dd26..a98de2207d 100644 --- a/app/javascript/alchemy_admin/components/picture_thumbnail.js +++ b/app/javascript/alchemy_admin/components/picture_thumbnail.js @@ -7,12 +7,7 @@ export default class PictureThumbnail extends HTMLElement { constructor() { super() - this.classList.add("thumbnail_background") this.spinner = new Spinner("small") - - if (this.src) { - this.start() - } } handleEvent(evt) { @@ -29,6 +24,10 @@ export default class PictureThumbnail extends HTMLElement { } connectedCallback() { + this.classList.add("thumbnail_background") + if (this.src && !this.image) { + this.start() + } this.#setImage() } diff --git a/app/javascript/alchemy_admin/components/preview_window.js b/app/javascript/alchemy_admin/components/preview_window.js index 3a340c195a..7a5c0393c7 100644 --- a/app/javascript/alchemy_admin/components/preview_window.js +++ b/app/javascript/alchemy_admin/components/preview_window.js @@ -5,13 +5,6 @@ class PreviewWindow extends HTMLIFrameElement { #afterLoad #reloadIcon #loadTimeout - #previewReadyHandler - - constructor() { - super() - this.addEventListener("load", this) - this.#previewReadyHandler = this.#handlePreviewReadyMessage.bind(this) - } handleEvent(evt) { if (evt.type === "load") { @@ -21,7 +14,7 @@ class PreviewWindow extends HTMLIFrameElement { } } - #handlePreviewReadyMessage(event) { + #onPreviewReady = (event) => { if (event.data.message === "Alchemy.previewReady") { this.#clearLoadTimeout() this.#stopSpinner() @@ -32,8 +25,9 @@ class PreviewWindow extends HTMLIFrameElement { connectedCallback() { let url = this.url + this.addEventListener("load", this) this.#attachEvents() - window.addEventListener("message", this.#previewReadyHandler) + window.addEventListener("message", this.#onPreviewReady) if (window.localStorage.getItem("alchemy-preview-url")) { url = window.localStorage.getItem("alchemy-preview-url") @@ -44,8 +38,9 @@ class PreviewWindow extends HTMLIFrameElement { } disconnectedCallback() { + this.removeEventListener("load", this) key.unbind("alt+r") - window.removeEventListener("message", this.#previewReadyHandler) + window.removeEventListener("message", this.#onPreviewReady) } postMessage(data) { diff --git a/app/javascript/alchemy_admin/components/publish_page_button.js b/app/javascript/alchemy_admin/components/publish_page_button.js index 2244b4826e..66106db2b4 100644 --- a/app/javascript/alchemy_admin/components/publish_page_button.js +++ b/app/javascript/alchemy_admin/components/publish_page_button.js @@ -1,14 +1,11 @@ class PublishPageButton extends HTMLElement { - constructor() { - super() - this.addEventListener("submit", this) - } - connectedCallback() { + this.addEventListener("submit", this) document.addEventListener("alchemy:page-dirty", this) } disconnectedCallback() { + this.removeEventListener("submit", this) document.removeEventListener("alchemy:page-dirty", this) } diff --git a/app/javascript/alchemy_admin/components/select.js b/app/javascript/alchemy_admin/components/select.js index bf56d7def7..af0bed4a2d 100644 --- a/app/javascript/alchemy_admin/components/select.js +++ b/app/javascript/alchemy_admin/components/select.js @@ -20,6 +20,11 @@ class Select extends HTMLSelectElement { } } + disconnectedCallback() { + this.#select2Element?.select2("destroy") + this.#select2Element = null + } + enable() { this.removeAttribute("disabled") this.#updateSelect2() diff --git a/app/javascript/alchemy_admin/components/tags_autocomplete.js b/app/javascript/alchemy_admin/components/tags_autocomplete.js index 795da6cb97..aa4f514248 100644 --- a/app/javascript/alchemy_admin/components/tags_autocomplete.js +++ b/app/javascript/alchemy_admin/components/tags_autocomplete.js @@ -1,11 +1,19 @@ import { setupSelectLocale } from "alchemy_admin/i18n" class TagsAutocomplete extends HTMLElement { + #select2 = null + async connectedCallback() { await setupSelectLocale() + if (!this.isConnected) return this.classList.add("autocomplete_tag_list") - $(this.input).select2(this.select2Config) + this.#select2 = $(this.input).select2(this.select2Config) + } + + disconnectedCallback() { + this.#select2?.select2("destroy") + this.#select2 = null } get input() { diff --git a/app/views/alchemy/_menubar.html.erb b/app/views/alchemy/_menubar.html.erb index 031b41e62a..aa999de8be 100644 --- a/app/views/alchemy/_menubar.html.erb +++ b/app/views/alchemy/_menubar.html.erb @@ -114,14 +114,10 @@