1import{loadSwiper}from"./swiper-loader.js";import{PopupModal}from"./popup-modal.js";import{Filter}from"https://cdn.jsdelivr.net/npm/[email protected]/dist/profanity-check.esm.js";const profanityFilter=new Filter,encodeAttr=value=>encodeURIComponent(JSON.stringify(value)),decodeAttr=value=>JSON.parse(decodeURIComponent(value)),formatPxToRem=value=>{if(value==null||value==="")return null;const numericValue=Number(value);return!Number.isFinite(numericValue)||numericValue===0?null:`calc(${numericValue} / 16 * 1rem)`},optionGroupFingerprint=(step,options)=>{if(!step||!options?.length)return null;const labels=options.map(option=>option.label).join("\0");return`${step}:${labels}`},getSharedOptionGroupFingerprints=(currentFingerprints,destinationFingerprints)=>{const destinationSet=new Set(destinationFingerprints);return currentFingerprints.filter(fingerprint=>destinationSet.has(fingerprint))},findImageOption=(type,predicate,categoryIndex=null)=>{const numericCategoryIndex=Number(categoryIndex);if(Number.isFinite(numericCategoryIndex)&&type?.categories?.[numericCategoryIndex]){const scopedOption=type.categories[numericCategoryIndex].options?.find(predicate);if(scopedOption)return scopedOption}for(const category of type?.categories??[]){const option=category.options?.find(predicate);if(option)return option}return null};class PersonalizationState{constructor(data){this.data=data,this.confirmedPersonalizations=[],this.currentSelections={product:data.personalizations[0],placement:null,type:null,details:null},this.currentPersonalizationProduct=null,this.editingConfirmedIndex=null,this.steps={current:1,total:4},this.currentCartItems=null,this.cartEditGroupKeys=null,this.cartEditQuantities=null}getConfirmedDraftStorageKey(){return`calpak_personalization_${this.data.base_product.id}`}persistDraft(){const key=this.getConfirmedDraftStorageKey();if(!this.confirmedPersonalizations?.length){this.clearDraft();return}typeof window.setStorageItem=="function"&&window.setStorageItem(key,{confirmedPersonalizations:this.confirmedPersonalizations},7)}loadDraft(){if(typeof window.expireStorageItem!="function")return;const value=window.expireStorageItem(this.getConfirmedDraftStorageKey());value?.confirmedPersonalizations?.length&&(this.confirmedPersonalizations=value.confirmedPersonalizations,this.editingConfirmedIndex=null,this.renumberLocationKeys(),PersonalizationCart.syncFromState(this,{includeCurrentDraft:!1}))}clearDraft(){const key=this.getConfirmedDraftStorageKey();typeof window.storageAvailable=="function"&&window.storageAvailable()&&localStorage.removeItem(key)}resetAll(){this.confirmedPersonalizations=[],this.currentSelections={product:this.data.personalizations[0],placement:null,type:null,details:null},this.currentPersonalizationProduct=null,this.editingConfirmedIndex=null,this.steps.current=1,this.cartEditGroupKeys=null,this.cartEditQuantities=null,PersonalizationCart.syncFromState(this,{includeCurrentDraft:!1})}resetInProgress(){this.currentSelections={product:this.currentSelections.product,placement:null,type:null,details:null},this.currentPersonalizationProduct=null,this.editingConfirmedIndex=null,this.steps.current=1,PersonalizationCart.syncFromState(this,{includeCurrentDraft:!1})}renumberLocationKeys(){this.confirmedPersonalizations.forEach((entry,index)=>{entry.locationKey=`Location_${index+1}`})}buildConfirmedEntry(){const selections={product:this.currentSelections.product,placement:this.currentSelections.placement,type:this.currentSelections.type,details:{...this.currentSelections.details}};return{locationKey:this.editingConfirmedIndex!==null?this.confirmedPersonalizations[this.editingConfirmedIndex].locationKey:`Location_${this.confirmedPersonalizations.length+1}`,productLabel:selections.product?.label??null,placementLabel:selections.placement?.label,selections,personalizationVariantId:this.currentPersonalizationProduct.id,price:this.currentPersonalizationProduct.price,basePropertyValue:PersonalizationCart.formatBaseProperties(selections,selections.details),personalizationProperties:PersonalizationCart.formatPersonalizationProperties(selections,selections.details)}}hasUnconfiguredPlacements(product=this.currentSelections.product){return product.placements.some(placement=>!PersonalizationStepPlacement.isPlacementConfigured(this,placement,product))}hasAnyUnconfiguredPlacements(){return this.data.personalizations.some(product=>this.hasUnconfiguredPlacements(product))}getFirstProductWithUnconfiguredPlacements(){return this.data.personalizations.find(product=>this.hasUnconfiguredPlacements(product))??this.data.personalizations[0]}commitCurrentDraft(){const entry=this.buildConfirmedEntry();this.editingConfirmedIndex!==null?(this.confirmedPersonalizations[this.editingConfirmedIndex]=entry,this.editingConfirmedIndex=null):this.confirmedPersonalizations.push(entry),this.renumberLocationKeys()}}class PersonalizationCart extends HTMLElement{static CART_EDIT_STORAGE_KEY="calpak_personalization_cart_edit";static formatBaseProperties(selections,details){let baseProperties=`${selections.product?.label?`Product: ${selections.product.label} | `:""}Location: ${selections.placement?.label} | Type: ${selections.type?.label}`;if(selections.type?.type=="text")baseProperties+=` | ${details?.text?`Characters: ${details.text} | `:""}${details?.icon?`Icon: ${details.icon} | `:""}${details?.font?`Font: ${details.font.name} | `:""}${details?.color?`Color: ${details.color.name} - ${details.color.thread}`:""}`;else{const detailKey=selections.type?.label.toLowerCase();baseProperties+=` | ${selections.type?.label}: ${details?.[detailKey]?.label??""}`}return baseProperties}static formatPersonalizationProperties(selections,details){const productProperty=selections.product?.label?{product:selections.product.label}:{};return selections.type?.type=="text"?{location:selections.placement?.label,...productProperty,...details?.text?{Initials:details.text}:{}
1,...details?.icon?{Icon:details.icon}:{},...details?.font?{Font:details.font.name}:{},...details?.color?{Color:details.color.name}:{}}:{location:selections.placement?.label,...productProperty}}static formatConfirmationSummary(selections){const{product,placement,type,details}=selections,label=`${product?.label?`${product.label} \u2013 `:""}${placement?.label} \u2013 ${type?.label}`;if(type?.type=="text"){const detailParts=[details?.text?`"${details.text}"`:"",details?.icon??"",details?.font?.name??"",details?.color?.name??""].filter(Boolean);return`${label} \u2013 ${detailParts.join(" \u2013 ")}`}return`${label} \u2013 ${details?.[type?.label.toLowerCase()]?.label??""}`}static shouldShowConfirmationDraft(state){return state.steps.current===state.steps.total&&PersonalizationStepDetails.isDraftComplete(state)&&!PersonalizationStepPlacement.isCurrentPlacementConfirmed(state)&&state.currentPersonalizationProduct?.price}static getConfirmationPricing(state){let personalizationTotal=0;const rows=[],showCurrentDraft=PersonalizationCart.shouldShowConfirmationDraft(state);for(const[index,entry]of state.confirmedPersonalizations.entries())showCurrentDraft&&state.editingConfirmedIndex===index||(personalizationTotal+=entry.price,rows.push({summary:PersonalizationCart.formatConfirmationSummary(entry.selections),price:entry.price,entryIndex:index,isDraft:!1}));return showCurrentDraft&&(personalizationTotal+=state.currentPersonalizationProduct.price,rows.push({summary:PersonalizationCart.formatConfirmationSummary(state.currentSelections),price:state.currentPersonalizationProduct.price,entryIndex:state.editingConfirmedIndex,isDraft:!0})),{rows,subtotal:state.data.base_product.price+personalizationTotal}}static syncFromState(state,{includeCurrentDraft=!1}={}){state.renumberLocationKeys();const groupId=crypto.randomUUID(),baseProperties={"_final-sale":!0,_LPROP:"Final Sale",_personalization_id:groupId},feeProperties=properties=>({"_final-sale":!0,_LPROP:"Final Sale",_personalization_id:groupId,_personalization_fee:!0,...properties}),personalizationProducts=[],useDraftOverlay=includeCurrentDraft&&PersonalizationStepDetails.isDraftComplete(state)&&!PersonalizationStepPlacement.isCurrentPlacementConfirmed(state)&&state.currentPersonalizationProduct?.id;for(const[index,entry]of state.confirmedPersonalizations.entries())useDraftOverlay&&state.editingConfirmedIndex===index?(baseProperties[entry.locationKey]=PersonalizationCart.formatBaseProperties(state.currentSelections,state.currentSelections.details),personalizationProducts.push({id:state.currentPersonalizationProduct.id,quantity:1,properties:feeProperties(PersonalizationCart.formatPersonalizationProperties(state.currentSelections,state.currentSelections.details))})):(baseProperties[entry.locationKey]=entry.basePropertyValue,personalizationProducts.push({id:entry.personalizationVariantId,quantity:1,properties:feeProperties(entry.personalizationProperties)}));if(useDraftOverlay&&state.editingConfirmedIndex===null){const draftKey=`Location_${state.confirmedPersonalizations.length+1}`;baseProperties[draftKey]=PersonalizationCart.formatBaseProperties(state.currentSelections,state.currentSelections.details),personalizationProducts.push({id:state.currentPersonalizationProduct.id,quantity:1,properties:feeProperties(PersonalizationCart.formatPersonalizationProperties(state.currentSelections,state.currentSelections.details))})}state.currentCartItems={baseProduct:{id:state.data.base_product.id,quantity:1,properties:baseProperties},personalizationProducts}}static getLineItems(state){const lineItems=[],{baseProduct,personalizationProducts}=state.currentCartItems??{};baseProduct?.id&&lineItems.push({id:Number(baseProduct.id),quantity:baseProduct.quantity??1,properties:baseProduct.properties??{}});for(const item of personalizationProducts??[])item?.id&&lineItems.push({id:Number(item.id),quantity:item.quantity??1,properties:item.properties??{}});return lineItems}static commitDraftIfComplete(state){!PersonalizationStepDetails.isDraftComplete(state)||PersonalizationStepPlacement.isCurrentPlacementConfirmed(state)||state.commitCurrentDraft()}static canAddToCart(state){return state.confirmedPersonalizations.length>0||PersonalizationStepDetails.isDraftComplete(state)&&!PersonalizationStepPlacement.isCurrentPlacementConfirmed(state)}static parseBasePropertyValue(value){const parsed={};if(!value)return parsed;for(const part of value.split(" | ")){const colonIndex=part.indexOf(":");colonIndex!==-1&&(parsed[part.slice(0,colonIndex).trim()]=part.slice(colonIndex+1).trim())}return parsed}static findProductAndPlacement(data,locationLabel,productLabel=null){const products=productLabel?data.personalizations.filter(product=>product.label===productLabel):data.personalizations;for(const product of products){const placement=product.placements.find(entry=>entry.label===locationLabel);if(placement)return{product,placement}}return productLabel?PersonalizationCart.findProductAndPlacement(data,locationLabel):null}static findFeeForLocation(feeItems,locationLabel,productLabel,usedFeeKeys){return feeItems.find(entry=>!usedFeeKeys.has(entry.key)&&entry.properties?.location===locationLabel&&(productLabel==null||entry.properties?.product===productLabel||!entry.properties?.product))}static buildDetailsFromCart(type,parsed,fee){if(type.type==="text"){const details={},text=fee?.properties?.Initials??parsed.Characters;text&&(details.text=text);const icon=fee?.properties?.Icon??parsed.Icon;icon&&(details.icon=icon);const fontName=fee?.properties?.Font??parsed.Font;if(fontName&&type.properties?.fonts){const font=type.properties.fonts.find(entry=>entry.name===fontName);font&&(details.font=font)}const colorName=fee?.properties?.Color??parsed.Color?.split(" - ")[0]?.trim();if(colorName&&type.properties?.colors){const color=type.properties.colors.find(entry=>entry.name===colorName);color&&(details.color=color)}return details}const detailKey=type.label.toLowerCase(),optionLabel=parsed[type.label],option=findImageOption(type,entry=>entry.label===optionLabel||entry.value==fee?.variant_id);return option?{[detailKey]:option}:{}}static hydrateStateFromCart(state,{baseItem,feeItems}){const locationEntries=Object.entries(baseItem.properties??{}).filter(([key])=>
1/^Location_\d+$/.test(key)).sort(([a],[b])=>Number(a.split("_")[1])-Number(b.split("_")[1])),confirmedPersonalizations=[],usedFeeKeys=new Set;for(const[locationKey,basePropertyValue]of locationEntries){const parsed=PersonalizationCart.parseBasePropertyValue(basePropertyValue),locationLabel=parsed.Location,productLabel=parsed.Product??null;if(!locationLabel){console.warn("Cart personalization edit: missing location in",locationKey);continue}const match=PersonalizationCart.findProductAndPlacement(state.data,locationLabel,productLabel);if(!match){console.warn("Cart personalization edit: placement not found",locationLabel,productLabel);continue}const{product,placement}=match,type=placement.types.find(entry=>entry.label===parsed.Type);if(!type){console.warn("Cart personalization edit: type not found",parsed.Type);continue}const fee=PersonalizationCart.findFeeForLocation(feeItems,locationLabel,product?.label??productLabel,usedFeeKeys);fee?.key&&usedFeeKeys.add(fee.key);const details=PersonalizationCart.buildDetailsFromCart(type,parsed,fee),selections={product,placement,type,details},personalizationVariantId=fee?.variant_id;if(!personalizationVariantId){console.warn("Cart personalization edit: missing fee variant",locationLabel);continue}confirmedPersonalizations.push({locationKey,productLabel:product?.label??null,placementLabel:placement.label,selections,personalizationVariantId,price:fee?.price??0,basePropertyValue,personalizationProperties:PersonalizationCart.formatPersonalizationProperties(selections,details)})}state.confirmedPersonalizations=confirmedPersonalizations,state.currentSelections={product:state.getFirstProductWithUnconfiguredPlacements(),placement:null,type:null,details:null},state.currentPersonalizationProduct=null,state.editingConfirmedIndex=null,state.steps.current=1,state.renumberLocationKeys(),PersonalizationCart.syncFromState(state,{includeCurrentDraft:!1})}static clearSavedDesigns(variantId){const draftKey=`calpak_personalization_${variantId}`;typeof window.storageAvailable=="function"&&window.storageAvailable()&&(localStorage.removeItem(draftKey),(typeof window.expireStorageItem=="function"?window.expireStorageItem(PersonalizationCart.CART_EDIT_STORAGE_KEY):null)?.variantId===variantId&&localStorage.removeItem(PersonalizationCart.CART_EDIT_STORAGE_KEY));const modal=document.getElementById("personalization-modal");modal?.data?.base_product?.id===variantId&&typeof modal.clearSavedDesigns=="function"&&modal.clearSavedDesigns()}static startCartEdit({baseItem,feeItems,groupKeys,quantities}){const payload={baseItem,feeItems,groupKeys,quantities,variantId:baseItem.variant_id},modal=document.getElementById("personalization-modal");if(modal?.state&&modal.data?.base_product?.id===baseItem.variant_id){modal.state.resetAll(),PersonalizationCart.hydrateStateFromCart(modal.state,{baseItem,feeItems}),modal.state.cartEditGroupKeys=groupKeys,modal.state.cartEditQuantities=quantities??null,document.querySelector("cart-drawer")?.close?.(),modal.setAttribute("open","true");return}typeof window.setStorageItem=="function"&&baseItem.url&&(window.setStorageItem(PersonalizationCart.CART_EDIT_STORAGE_KEY,payload,1),window.location.href=baseItem.url)}static async addToCart(state,actionEl){PersonalizationCart.commitDraftIfComplete(state),PersonalizationCart.syncFromState(state,{includeCurrentDraft:!1});const items=PersonalizationCart.getLineItems(state);if(!items.length)return!1;actionEl.disabled=!0;try{if(state.cartEditGroupKeys?.length){const updates=Object.fromEntries(state.cartEditGroupKeys.map(key=>[key,state.cartEditQuantities?.[key]??0])),removeResponse=await window.updateCart({updates},!1);if(removeResponse?.status)return console.error("Personalization cart edit replace failed",removeResponse),!1;state.cartEditGroupKeys=null,state.cartEditQuantities=null}const response=await window.addToCart({items});return response?.status?(console.error("Personalization add to cart failed",response),!1):!0}finally{actionEl.disabled=!1}}#handleAddBound=null;#modal=null;connectedCallback(){this.#modal=this.closest("personalization-modal"),this.#render(),this.#handleAddBound=this.#handleAdd.bind(this),this.querySelector('[data-action="add"]')?.addEventListener("click",this.#handleAddBound)}disconnectedCallback(){this.querySelector('[data-action="add"]')?.removeEventListener("click",this.#handleAddBound),this.#modal=null}#render(){const state=this.#modal?.state;if(!state)return;const canAdd=PersonalizationCart.canAddToCart(state);this.innerHTML=` 2 <button 3 type="button" 4 class="personalization-modal--action cart btn--primary" 5 data-action="add" 6 ${canAdd?"":"disabled"} 7 >Accept Terms & Add to Bag</button> 8 `}async#handleAdd(ev){const state=this.#modal?.state,actionEl=ev.currentTarget;if(!state)return;await PersonalizationCart.addToCart(state,actionEl)&&this.#modal?.dispatchEvent(new CustomEvent("personalization-added",{bubbles:!0}))}}class PersonalizationTextInput extends HTMLElement{static isProfane(text){return text?.trim()?profanityFilter.isProfane(text):!1}static sanitizeTextInput(value,properties){const sanitized=value.replace(/[^A-Za-z1-9 ]/g,"");return properties?.uppercase?sanitized.toUpperCase():sanitized}static getCharacterLimit(properties){return properties.icons?properties.character_limit+1:properties.character_limit}static getTextCharacterLimit(properties,details){const baseLimit=PersonalizationTextInput.getCharacterLimit(properties);return details?.icon?Math.max(0,baseLimit-1):baseLimit}static enforceTextLength(text,limit){return text&&text.slice(0,limit)}
8static syncDetailsTextToLimit(state){const type=state.currentSelections.type,text=state.currentSelections.details?.text;if(!text||type?.type!=="text"||!type?.properties)return;const normalized=PersonalizationTextInput.enforceTextLength(PersonalizationTextInput.sanitizeTextInput(text,type.properties),PersonalizationTextInput.getTextCharacterLimit(type.properties,state.currentSelections.details));normalized!==text&&(normalized?state.currentSelections.details.text=normalized:delete state.currentSelections.details.text)}connectedCallback(){const{properties,details}=decodeAttr(this.getAttribute("context")),textLimit=PersonalizationTextInput.getTextCharacterLimit(properties,details),text=details?.text,hasProfanity=PersonalizationTextInput.isProfane(text),isUppercase=properties?.uppercase===!0;this.innerHTML=` 9 <div class="personalization-modal--input${hasProfanity?" personalization-modal--input--error":""}"> 10 <label for="personalization_details_text" class="personalization-modal--input-label"> 11 Text \u2014 ${isUppercase?"Letters A\u2013Z":"Letters a\u2013z, A\u2013Z"}, Numbers 1\u20139* 12 </label> 13 <div class="input--text"> 14 <input 15 type="text" 16 id="personalization_details_text" 17 name="personalization_details_text" 18 maxlength="${textLimit}" 19 placeholder="${isUppercase?"ABC":"Abc"}" 20 pattern="${isUppercase?"[A-Z1-9 ]+":"[A-Za-z1-9 ]+"}" 21 title="${isUppercase?"Uppercase letters A\u2013Z, numbers 1\u20139, and spaces only":"Letters A\u2013Z, a\u2013z, numbers 1\u20139, and spaces only"}" 22 ${text?`value="${text}"`:""} 23 data-step="details" 24 data-input="text" 25 ${hasProfanity?'aria-invalid="true" aria-describedby="personalization_details_text_error"':""} 26 required 27 > 28 <span class="input--text-characters">${text?.length||0}/${textLimit}</span> 29 </div> 30 ${hasProfanity?` 31 <p id="personalization_details_text_error" class="personalization-modal--input-error" role="alert"> 32 Please revise your text 33 </p> 34 `:""} 35 </div> 36 `}updateFromDetails(properties,details){const textLimit=PersonalizationTextInput.getTextCharacterLimit(properties,details),text=details?.text??"",hasProfanity=PersonalizationTextInput.isProfane(text),elWrapper=this.querySelector(".personalization-modal--input"),elInput=this.querySelector("#personalization_details_text"),elCounter=this.querySelector(".input--text-characters");elCounter&&(elCounter.textContent=`${text.length}/${textLimit}`),elInput&&(elInput.maxLength=textLimit),elWrapper?.classList.toggle("personalization-modal--input--error",hasProfanity),elInput&&(hasProfanity?(elInput.setAttribute("aria-invalid","true"),elInput.setAttribute("aria-describedby","personalization_details_text_error")):(elInput.removeAttribute("aria-invalid"),elInput.removeAttribute("aria-describedby")));let elError=this.querySelector("#personalization_details_text_error");hasProfanity?elError||(elError=document.createElement("p"),elError.id="personalization_details_text_error",elError.className="personalization-modal--input-error",elError.setAttribute("role","alert"),elError.textContent="Please revise your text",elWrapper?.appendChild(elError)):elError&&elError.remove()}}class PersonalizationIconPicker extends HTMLElement{static isIconDisabled(details,properties){const text=details?.text;return!!(text&&properties&&text.length===PersonalizationTextInput.getCharacterLimit(properties))}static getIconFocusIndex(icons,details,properties){const selectedIndex=icons.findIndex(icon=>details?.icon===icon.name);return selectedIndex>=0&&!PersonalizationIconPicker.isIconDisabled(details,properties)?selectedIndex:icons.findIndex(()=>!PersonalizationIconPicker.isIconDisabled(details,properties))}static handleKeydown(ev){const elInput=ev.target.closest('input[data-input="icon"]');if(!elInput)return;const elGroup=elInput.closest("[data-icon-options]");if(!elGroup)return;const elInputs=[...elGroup.querySelectorAll('input[data-input="icon"]:not(:disabled)')];if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End"].includes(ev.key)){ev.preventDefault();const currentIndex=elInputs.indexOf(elInput);if(currentIndex===-1)return;let nextIndex=currentIndex;ev.key==="Home"?nextIndex=0:ev.key==="End"?nextIndex=elInputs.length-1:ev.key==="ArrowRight"||ev.key==="ArrowDown"?nextIndex=(currentIndex+1)%elInputs.length:nextIndex=(currentIndex-1+elInputs.length)%elInputs.length,elInput.tabIndex=-1,elInputs[nextIndex].tabIndex=0,elInputs[nextIndex].focus({preventScroll:!0});return}if(ev.key==="Enter"||ev.key===" "){if(ev.preventDefault(),elInput.disabled)return;elInput.checked=!elInput.checked,elInput.dispatchEvent(new Event("change",{bubbles:!0}))}}connectedCallback(){const{icons,details,properties}=decodeAttr(this.getAttribute("context")),focusIndex=PersonalizationIconPicker.getIconFocusIndex(icons,details,properties),isDisabled=PersonalizationIconPicker.isIconDisabled(details,properties);this.innerHTML=` 37 <fieldset class="personalization-modal--input"${isDisabled?' aria-describedby="personalization_details_icon_hint"':""}> 38 <legend class="personalization-modal--input-label"> 39 Icon 40 </legend> 41 ${isDisabled?` 42 <p id="personalization_details_icon_hint" class="personalization-modal--input-hint small"> 43 Icons are unavailable when your text reaches the character limit. 44 </p> 45 `:""} 46 <ul class="personalization-modal--options" data-icon-options> 47 ${icons.map((icon,index)=>{const inputId=`personalization_details_icon_${icon.name}`;return` 48 <li class="personalization-modal--option input--icon"> 49 <input 50 id="${inputId}" 51 type="checkbox" 52 name="personalization_details_icon" 53 value="${icon.name}" 54 data-step="details" 55 data-input="icon" 56 tabindex="${index===focusIndex?0:-1}" 57 ${details?.icon==icon.name?"checked":""} 58 ${isDisabled?"disabled":""} 59 > 60 <label for="${inputId}">
61 <span class="visually-hidden">${icon.name}</span> 62 <span role="presentation">${icon.svg}</span> 63 </label> 64 </li> 65 `}).join("")} 66 </ul> 67 </fieldset> 68 `}updateDisabledState(details,properties){const isDisabled=PersonalizationIconPicker.isIconDisabled(details,properties),elFieldset=this.querySelector("fieldset.personalization-modal--input");let elHint=this.querySelector("#personalization_details_icon_hint");elFieldset&&(isDisabled?elFieldset.setAttribute("aria-describedby","personalization_details_icon_hint"):elFieldset.removeAttribute("aria-describedby")),isDisabled&&!elHint?(elHint=document.createElement("p"),elHint.id="personalization_details_icon_hint",elHint.className="personalization-modal--input-hint small",elHint.textContent="Icons are unavailable when your text reaches the character limit.",elFieldset?.insertBefore(elHint,elFieldset.querySelector("[data-icon-options]"))):!isDisabled&&elHint&&elHint.remove();for(const elInput of this.querySelectorAll('input[data-input="icon"]'))elInput.disabled=isDisabled}}class PersonalizationFontPicker extends HTMLElement{#swiper=null;#swiperInitId=0;#resizeObserver=null;connectedCallback(){const{fonts,details}=decodeAttr(this.getAttribute("context"));this.innerHTML=` 69 <fieldset class="personalization-modal--input"> 70 <legend class="personalization-modal--input-label"> 71 <span>Font*</span> 72 </legend> 73 <div class="personalization-modal--options-navigation"> 74 <button 75 type="button" 76 class="personalization-modal--options-navigation-prev btn--arrow prev" 77 data-navigation="prev" 78 aria-label="Previous font" 79 ></button> 80 <button 81 type="button" 82 class="personalization-modal--options-navigation-next btn--arrow next" 83 data-navigation="next" 84 aria-label="Next font" 85 ></button> 86 </div> 87 <div class="personalization-modal--options-carousel swiper" data-options-carousel> 88 <ul class="swiper-wrapper"> 89 ${fonts.map(font=>{const inputId=`personalization_details_font_${font.handle}`,baselineAdjustment=formatPxToRem(font.baseline_adjustment),fontSampleStyles=["font-size: calc(1em * var(--scale))","line-height: 1",baselineAdjustment?`--baseline-adjustment: ${baselineAdjustment}`:""].filter(Boolean).join("; ");return` 90 <li class="personalization-modal--option input--radio swiper-slide" style="font-family: var(--font-${font.handle}); --scale: var(--size-${font.handle});"> 91 <input 92 type="radio" 93 id="${inputId}" 94 name="personalization_details_font" 95 value="${font.name}" 96 data-step="details" 97 data-input="font" 98 ${details?.font?.name==font.name?"checked":""} 99 required 100 > 101 <label for="${inputId}"> 102 <span class="visually-hidden">${font.name}</span> 103 <span role="presentation" class="personalization-modal--font-sample" style="${fontSampleStyles}"> 104 AaBbCc 105 </span> 106 </label> 107 </li> 108 `}).join("")} 109 </ul> 110 </div> 111 </fieldset> 112 `,this.#initSwiper()}async#initSwiper(){const elCarousel=this.querySelector("[data-options-carousel]");
112if(!elCarousel)return;const initId=++this.#swiperInitId,Swiper=await loadSwiper({waitForLoad:!1});!this.isConnected||initId!==this.#swiperInitId||(this.#swiper?.destroy(!0,!0),this.#swiper=null,this.#resizeObserver?.disconnect(),this.#swiper=new Swiper(elCarousel,{slidesPerView:4,spaceBetween:12,watchOverflow:!0,observer:!0,observeParents:!0,navigation:{nextEl:this.querySelector('[data-navigation="next"]'),prevEl:this.querySelector('[data-navigation="prev"]')},breakpoints:{1024:{slidesPerView:6,spaceBetween:12}}}),elCarousel.setAttribute("data-swiper-ready",""),this.#resizeObserver=new ResizeObserver(()=>{initId!==this.#swiperInitId||!this.#swiper||(this.#swiper.updateSize(),this.#swiper.update())}),this.#resizeObserver.observe(elCarousel),requestAnimationFrame(()=>{initId!==this.#swiperInitId||!this.#swiper||(this.#swiper.updateSize(),this.#swiper.update())}))}disconnectedCallback(){this.#swiperInitId+=1,this.#resizeObserver?.disconnect(),this.#resizeObserver=null,this.#swiper?.destroy(),this.#swiper=null}}class PersonalizationColorPicker extends HTMLElement{connectedCallback(){const{colors,details}=decodeAttr(this.getAttribute("context"));this.innerHTML=` 113 <fieldset class="personalization-modal--input"> 114 <legend class="personalization-modal--input-label"> 115 Color* 116 </legend> 117 <ul class="personalization-modal--colors"> 118 ${colors.map(color=>{const inputId=`personalization_details_color_${color.name}`,isChecked=details?.color?.name==color.name;return` 119 <li> 120 <input 121 type="radio" 122 id="${inputId}" 123 name="personalization_details_color" 124 value="${color.name}" 125 data-step="details" 126 data-input="color" 127 class="visually-hidden" 128 ${isChecked?"checked":""} 129 > 130 <label for="${inputId}" class="personalization-modal--color"> 131 <span role="presentation" class="input--swatch" style="--swatch: ${color.swatch};"></span> 132 <span class="visually-hidden">${color.name}</span> 133 </label> 134 </li> 135 `}).join("")} 136 </ul> 137 </fieldset> 138 `}}class PersonalizationImagePicker extends HTMLElement{#categories=[];#key="";#selectedValue=null;#selectedCategoryIndex=0;#swiper=null;#swiperInitId=0;static#getOptionsMarkup(options,key,selectedValue){return options.map(option=>{const inputId=`personalization_details_${key}_${option.label.toLowerCase()}`;return` 139 <li class="personalization-modal--option input--image transparent"> 140 <input 141 id="${inputId}" 142 type="radio" 143 name="personalization_details_${key}" 144 value="${option.value}" 145 data-step="details" 146 data-input="${key}" 147 ${selectedValue==option.value?"checked":""} 148 required 149 /> 150 <label for="${inputId}" class="transparent"> 151 <span class="visually-hidden">${option.label}</span> 152 <span role="presentation" class="input--image-imag transparent">${option.image}</span> 153 </label> 154 </li> 155 `}).join("")}#getCategoryIndexForValue(value){if(value==null)return 0;const index=this.#categories.findIndex(category=>category.options?.some(option=>option.value==value));return index>=0?index:0}#renderOptions(){const elOptions=this.querySelector("[data-image-options]"),category=this.#categories[this.#selectedCategoryIndex];!elOptions||!category||(elOptions.dataset.categoryIndex=String(this.#selectedCategoryIndex),elOptions.innerHTML=PersonalizationImagePicker.#getOptionsMarkup(category.options??[],this.#key,this.#selectedValue),window.initLazyLoad?.())}#updateCategoryButtons(){this.querySelectorAll("button[data-category-index]").forEach(elButton=>{const isSelected=Number(elButton.dataset.categoryIndex)===this.#selectedCategoryIndex;elButton.classList.toggle("btn--primary",isSelected),elButton.classList.toggle("btn--secondary",!isSelected),elButton.setAttribute("aria-pressed",isSelected?"true":"false")})}#handleCategoryClick=ev=>{const elButton=ev.target.closest("button[data-category-index]");if(!elButton||!this.contains(elButton))return;const index=Number(elButton.dataset.categoryIndex);!Number.isFinite(index)||index===this.#selectedCategoryIndex||(this.#selectedCategoryIndex=index,this.#updateCategoryButtons(),this.#renderOptions(),this.dispatchEvent(new CustomEvent("personalization-image-category-change",{bubbles:!0,detail:{categoryIndex:index}})))};#handleChange=ev=>{const elInput=ev.target.closest(`input[data-input="${this.#key}"]`);!elInput?.checked||!this.contains(elInput)||(this.#selectedValue=elInput.value)};connectedCallback(){const{type,details,selectedCategoryIndex}=decodeAttr(this.getAttribute("context"));this.#categories=type.categories??[],this.#key=type.label.toLowerCase(),this.#selectedValue=details?.[this.#key]?.value??null,selectedCategoryIndex!=null&&Number.isFinite(Number(selectedCategoryIndex))&&this.#categories[selectedCategoryIndex]?this.#selectedCategoryIndex=Number(selectedCategoryIndex):this.#selectedCategoryIndex=this.#getCategoryIndexForV
155alue(this.#selectedValue);const selectedCategory=this.#categories[this.#selectedCategoryIndex];this.innerHTML=` 156 <fieldset class="personalization-modal--input"> 157 <legend class="personalization-modal--input-label"> 158 <span>${type.label}*</span> 159 </legend> 160 ${this.#categories.length?` 161 <div class="personalization-modal--options-navigation"> 162 <button 163 type="button" 164 class="personalization-modal--options-navigation-prev btn--arrow prev" 165 data-navigation="prev" 166 aria-label="Previous category" 167 ></button> 168 <button 169 type="button" 170 class="personalization-modal--options-navigation-next btn--arrow next" 171 data-navigation="next" 172 aria-label="Next category" 173 ></button> 174 </div> 175 <div 176 class="personalization-modal--image-categories personalization-modal--options-carousel swiper scrollbar--none" 177 data-category-carousel 178 role="group" 179 aria-label="${type.label} categories" 180 > 181 <ul class="swiper-wrapper"> 182 ${this.#categories.map((category,index)=>{const isSelected=index===this.#selectedCategoryIndex;return` 183 <li class="swiper-slide"> 184 <button 185 type="button" 186 class="${isSelected?"btn--primary":"btn--secondary"}" 187 data-category-index="${index}" 188 aria-pressed="${isSelected?"true":"false"}" 189 > 190 ${category.name} 191 </button> 192 </li> 193 `}).join("")} 194 </ul> 195 </div> 196 `:""} 197 <ul 198 class="personalization-modal--options personlization-modal--options-images" 199 data-image-options 200 data-category-index="${this.#selectedCategoryIndex}" 201 > 202 ${PersonalizationImagePicker.#getOptionsMarkup(selectedCategory?.options??[],this.#key,this.#selectedValue)} 203 </ul> 204 </fieldset> 205 `,this.addEventListener("click",this.#handleCategoryClick),this.addEventListener("change",this.#handleChange),window.initLazyLoad?.(),this.#initCategorySwiper()}
205async#initCategorySwiper(){if(!this.querySelector("[data-category-carousel]"))return;const initId=++this.#swiperInitId,Swiper=await loadSwiper({waitForLoad:!1});if(!this.isConnected||initId!==this.#swiperInitId)return;const elCarousel=this.querySelector("[data-category-carousel]");if(!elCarousel)return;const slideCount=elCarousel.querySelectorAll(".swiper-slide").length;this.#swiper?.destroy(!0,!0),this.#swiper=new Swiper(elCarousel,{enabled:slideCount>1,slidesPerView:"auto",spaceBetween:12,watchOverflow:!0,observer:!0,observeParents:!0,navigation:{nextEl:this.querySelector('[data-navigation="next"]'),prevEl:this.querySelector('[data-navigation="prev"]')}}),elCarousel.setAttribute("data-swiper-ready",""),requestAnimationFrame(()=>{initId!==this.#swiperInitId||!this.#swiper||(this.#swiper.updateSize(),this.#swiper.update())})}disconnectedCallback(){this.#swiperInitId+=1,this.removeEventListener("click",this.#handleCategoryClick),this.removeEventListener("change",this.#handleChange),this.#swiper?.destroy(),this.#swiper=null}}class PersonalizationRadioGroup extends HTMLElement{connectedCallback(){const{step,options,selectedLabel,configuredLabels=[],required=!0,legend,legendClass="personalization-modal--heading"}=decodeAttr(this.getAttribute("context")),optionsHtml=` 206 <ul class="personalization-modal--options"> 207 ${options.map((option,index)=>{const inputName=`personalization_${step}`,inputId=`personalization_${step}_${index}`,isChecked=selectedLabel?selectedLabel==option.label:!1,isConfigured=configuredLabels.includes(option.label);return` 208 <li class="personalization-modal--option input--radio${isConfigured?" personalization-modal--option--configured":""}"> 209 <input 210 id="${inputId}" 211 type="radio" 212 name="${inputName}" 213 value="${option.label}" 214 data-step="${step}" 215 ${isChecked?"checked":""} 216 ${required?"required":""} 217 > 218 <label for="${inputId}"> 219 ${option.label} 220 ${isConfigured?'<span class="visually-hidden">, design added</span>':""} 221 </label> 222 </li> 223 `}).join("")} 224 </ul> 225 `;this.innerHTML=legend?` 226 <fieldset class="personalization-modal--field"> 227 <legend class="${legendClass}">${legend}</legend> 228 ${optionsHtml} 229 </fieldset> 230 `:optionsHtml}}class PersonalizationProgress extends HTMLElement{connectedCallback(){const{current,total}=decodeAttr(this.getAttribute("context"));this.innerHTML=` 231 <progress id="personalization_progress" class="personalization-modal--progress-bar" value="${current}" max="${total}"></progress> 232 <label for="personalization_progress" class="personalization-modal--progress-label small"> 233 <span class="visually-hidden">You are viewing step ${current} of ${total}</span> 234 <span role="presentation">${current}/${total} Steps</span> 235 </label> 236 `}}class PersonalizationPreviewImage extends HTMLElement{static PRELOAD_LINK_DATA_ATTR="data-personalization-preload";static#PREVIEW_LETTERS="ABCDEFGHIJKLMNOPQRSTUVWXYZ";static#PREVIEW_NUMBERS="123456789";static#iconViewBoxCache=new Map;static#formatCoordinate(value){if(value==null||value==="")return"50%";const stringValue=String(value);
236return stringValue.includes("%")?stringValue:`${stringValue}%`}static#getPxValue(value){if(value==null||value==="")return null;const numericValue=Number(String(value).trim().replace(/px$/i,""));return Number.isFinite(numericValue)?numericValue:null}static#escapeHtml(value){return String(value).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}static#parseImageMarkup(markup){const parser=document.createElement("div");parser.innerHTML=markup;const img=parser.querySelector("img");return img?{src:img.getAttribute("src"),srcset:img.getAttribute("srcset"),sizes:img.getAttribute("sizes")}:null}static preloadImage(src,srcset,sizes){if(!src)return;const selector=`link[${PersonalizationPreviewImage.PRELOAD_LINK_DATA_ATTR}="${CSS.escape(src)}"]`;if(document.head.querySelector(selector))return;const elLink=document.createElement("link");elLink.rel="preload",elLink.as="image",elLink.href=src,srcset&&elLink.setAttribute("imagesrcset",srcset),sizes&&elLink.setAttribute("imagesizes",sizes),elLink.setAttribute(PersonalizationPreviewImage.PRELOAD_LINK_DATA_ATTR,src),document.head.appendChild(elLink)}static preloadImages(personalizations){const parser=document.createElement("div");for(const product of personalizations)for(const placement of product.placements){parser.innerHTML=placement.image;const img=parser.querySelector("img");img&&PersonalizationPreviewImage.preloadImage(img.getAttribute("src"),img.getAttribute("srcset"),img.getAttribute("sizes"))}}static getActivePlacement(currentSelections,confirmedPersonalizations=[]){if(currentSelections.placement)return currentSelections.placement;const productLabel=currentSelections.product?.label??null,productConfirmed=confirmedPersonalizations.filter(entry=>entry.productLabel===productLabel),lastConfirmed=productConfirmed[productConfirmed.length-1];return lastConfirmed?.selections?.placement?lastConfirmed.selections.placement:currentSelections.product?.placements?.[0]??null}static getBaseImage(currentSelections,confirmedPersonalizations=[]){return PersonalizationPreviewImage.getActivePlacement(currentSelections,confirmedPersonalizations)?.image??""}static#getSelectionOverlayMarkup(selections){const{placement,type,details}=selections;if(!placement)return"";if(!type)return PersonalizationPreviewImage.#getPlacementIndicatorMarkup(placement);const positionStyles=PersonalizationPreviewImage.#getPositionStyles(placement,type);if(type.type==="text"){const properties=type.properties??{},icon=properties.icons?.find(entry=>entry.name===details?.icon),hasText=!!details?.text,hasIcon=!!icon;return!hasText&&!hasIcon?PersonalizationPreviewImage.#getTextOverlayMarkup(placement,type,positionStyles,{text:PersonalizationPreviewImage.#getPreviewPlaceholderText(properties),font:details?.font??properties.fonts?.[0],color:details?.color??properties.colors?.[0]}):PersonalizationPreviewImage.#getTextOverlayMarkup(placement,type,positionStyles,{text:hasText?details.text:"",font:details?.font,color:details?.color,icon:hasIcon?icon:null})}if(type.type==="image"){const detailKey=type.label.toLowerCase(),categoryIndex=selections.imageCategoryIndex,categories=type.categories??[],activeCategory=Number.isFinite(Number(categoryIndex))?categories[Number(categoryIndex)]:categories[0],selectedOption=details?.[detailKey]??activeCategory?.options?.[0];return selectedOption?.image?PersonalizationPreviewImage.#getImageOverlayMarkup(placement,type,positionStyles,selectedOption):PersonalizationPreviewImage.#getPlacementIndicatorMarkup(placement,type)}return""}static getOverlayMarkup(currentSelections,confirmedPersonalizations=[],{editingConfirmedIndex=null}={}){const activePlacement=PersonalizationPreviewImage.getActivePlacement(currentSelections,confirmedPersonalizations);if(!activePlacement)return"";const activeViewImage=activePlacement.image,productLabel=currentSelections.product?.label??null,overlays=[];for(const[index,entry]of confirmedPersonalizations.entries()){if(entry.productLabel!==productLabel)continue;const{placement}=entry.selections??{};if(!placement||placement.image!==activeViewImage||editingConfirmedIndex===index)continue;const overlay=PersonalizationPreviewImage.#getSelectionOverlayMarkup(entry.selections);overlay&&overlays.push(overlay)}if(currentSelections.placement?.image===activeViewImage){const draftOverlay=PersonalizationPreviewImage.#getSelectionOverlayMarkup(currentSelections);draftOverlay&&overlays.push(draftOverlay)}return overlays.join("")}static#getScaleValue(value){const scale=Number(value);return Number.isFinite(scale)&&scale>0?scale:1}static#getRotationValue(value){const rotation=Number(value);return Number.isFinite(rotation)?rotation:0}static#getPositionStyles(placement,type=null){const coordinates=placement.coordinates??{},fontSizes=placement.font_size??{},placementRotation=PersonalizationPreviewImage.#getRotationValue(placement.rotation),typeRotation=type?PersonalizationPreviewImage.#getRotationValue(type.rotation):0,mobileFontSize=Per
236sonalizationPreviewImage.#getPxValue(fontSizes.mobile),desktopFontSize=PersonalizationPreviewImage.#getPxValue(fontSizes.desktop);return[`--x-mobile: ${PersonalizationPreviewImage.#formatCoordinate(coordinates.mobile?.x)}`,`--y-mobile: ${PersonalizationPreviewImage.#formatCoordinate(coordinates.mobile?.y)}`,`--x-desktop: ${PersonalizationPreviewImage.#formatCoordinate(coordinates.desktop?.x)}`,`--y-desktop: ${PersonalizationPreviewImage.#formatCoordinate(coordinates.desktop?.y)}`,mobileFontSize===null?"":`--font-size-mobile-px: ${mobileFontSize}`,desktopFontSize===null?"":`--font-size-desktop-px: ${desktopFontSize}`,`--placement-rotation: ${placementRotation}deg`,`--type-rotation: ${typeRotation}deg`].filter(Boolean).join("; ")}static#getPlacementIndicatorMarkup(placement,type=null){return` 237 <div class="personalization-preview__overlay personalization-preview__overlay--indicator" style="${PersonalizationPreviewImage.#getPositionStyles(placement,type)}"> 238 <span class="personalization-preview__overlay-indicator" role="presentation"></span> 239 </div> 240 `}static#takeFromPool(pool,count){return pool.repeat(Math.ceil(count/pool.length)).slice(0,count)}static#getPreviewLetters(count,isUppercase){const letters=PersonalizationPreviewImage.#takeFromPool(PersonalizationPreviewImage.#PREVIEW_LETTERS,count);return isUppercase?letters:`${letters.slice(0,1)}${letters.slice(1).toLowerCase()}`}static#getPreviewPlaceholderText(properties){const limit=Math.max(1,Math.trunc(Number(properties?.character_limit))||1),isUppercase=properties?.uppercase===!0;if(limit<4)return PersonalizationPreviewImage.#getPreviewLetters(limit,isUppercase);const letterCount=Math.ceil((limit+1)/2),numberCount=limit-letterCount,letters=PersonalizationPreviewImage.#getPreviewLetters(letterCount,isUppercase),numbers=PersonalizationPreviewImage.#takeFromPool(PersonalizationPreviewImage.#PREVIEW_NUMBERS,numberCount);return`${letters}${numbers}`}static#getTextOverlayMarkup(placement,type,positionStyles,{text,font,color,icon}){const fontHandle=font?.handle,threadColor=color?.thread,typeScale=PersonalizationPreviewImage.#getScaleValue(type?.scale),fontScale=PersonalizationPreviewImage.#getScaleValue(font?.scale),overlayStyles=[positionStyles,`--type-scale: ${typeScale}`,threadColor?`--color: #${threadColor}`:""].filter(Boolean).join("; "),baselineAdjustment=PersonalizationPreviewImage.#getPxValue(font?.baseline_adjustment),contentStyles=[`--font-scale: ${fontScale}`,fontHandle?`--font-family: var(--font-${fontHandle})`:"",baselineAdjustment?`--baseline-adjustment-px: ${baselineAdjustment}`:""].filter(Boolean).join("; ");return` 241 <div class="personalization-preview__overlay personalization-preview__overlay--text" style="${overlayStyles}"> 242 <span class="personalization-preview__overlay-content"${contentStyles?` style="${contentStyles}"`:""}> 243 ${text?`<span class="personalization-preview__overlay-text">${PersonalizationPreviewImage.#escapeHtml(text)}</span>`:""} 244 ${icon?`<span class="personalization-preview__overlay-icon" role="presentation">${icon.svg}</span>`:""} 245 </span> 246 </div> 247 `}static#getImageOverlayMarkup(placement,type,positionStyles,option){const imageMarkup=PersonalizationPreviewImage.#parseImageMarkup(option.image),typeScale=PersonalizationPreviewImage.#getScaleValue(type?.scale),imageStyles=[positionStyles,`--type-scale: ${typeScale}`].join("; ");return imageMarkup&&PersonalizationPreviewImage.preloadImage(imageMarkup.src,imageMarkup.srcset,imageMarkup.sizes),` 248 <div class="personalization-preview__overlay personalization-preview__overlay--image transparent" style="${imageStyles}"> 249 <span role="presentation" class="personalization-preview__overlay-image transparent">${option.image}</span> 250 </div> 251 `}static#getStrokeInset(elSvg){const shapes=elSvg.querySelectorAll("path, circle, rect, polygon, polyline, line, ellipse");let widest=0;for(const shape of shapes){const{stroke,strokeWidth}=getComputedStyle(shape);!stroke||stroke==="none"||(widest=Math.max(widest,parseFloat(strokeWidth)||0))}return widest/2}static#applyIconViewBox(elSvg,viewBox){elSvg.removeAttribute("width"),elSvg.removeAttribute("height"),elSvg.setAttribute("viewBox",viewBox)}static#normaliseIconViewBoxes(elRoot){for(const elSvg of elRoot.querySelectorAll(".personalization-preview__overlay-icon svg")){const cacheKey=elSvg.outerHTML,cached=PersonalizationPreviewImage.#iconViewBoxCache.get(cacheKey);if(cached){PersonalizationPreviewImage.#applyIconViewBox(elSvg,cac
251hed);continue}let bounds=null;try{bounds=elSvg.getBBox()}catch{continue}if(!bounds||bounds.width<=0||bounds.height<=0)continue;const inset=PersonalizationPreviewImage.#getStrokeInset(elSvg),viewBox=[bounds.x-inset,bounds.y-inset,bounds.width+inset*2,bounds.height+inset*2].map(value=>Number(value.toFixed(3))).join(" ");PersonalizationPreviewImage.#iconViewBoxCache.set(cacheKey,viewBox),PersonalizationPreviewImage.#applyIconViewBox(elSvg,viewBox)}}static#ensurePreviewImagesVisible(elRoot){for(const elImg of elRoot.querySelectorAll(".personalization-preview img"))elImg.loading="eager",elImg.classList.add("in-view");window.initLazyLoad?.()}connectedCallback(){const selections=decodeAttr(this.getAttribute("selections")),confirmedPersonalizations=decodeAttr(this.getAttribute("confirmed-personalizations"))??[],editingConfirmedIndexAttr=this.getAttribute("editing-confirmed-index"),editingConfirmedIndex=editingConfirmedIndexAttr===null||editingConfirmedIndexAttr===""?null:Number(editingConfirmedIndexAttr),personalizationsAttr=this.getAttribute("personalizations");personalizationsAttr&&PersonalizationPreviewImage.preloadImages(decodeAttr(personalizationsAttr));const baseImage=PersonalizationPreviewImage.getBaseImage(selections,confirmedPersonalizations),overlayMarkup=PersonalizationPreviewImage.getOverlayMarkup(selections,confirmedPersonalizations,{editingConfirmedIndex});this.innerHTML=` 252 <div class="personalization-preview" aria-hidden="true"> 253 <div class="personalization-preview__base transparent">${baseImage}</div> 254 ${overlayMarkup} 255 </div> 256 `,PersonalizationPreviewImage.#ensurePreviewImagesVisible(this),PersonalizationPreviewImage.#normaliseIconViewBoxes(this),requestAnimationFrame(()=>{this.isConnected&&PersonalizationPreviewImage.#normaliseIconViewBoxes(this)}),PersonalizationPreviewImage.#markDraftOverlay(this,selections)}static#markDraftOverlay(element,selections){const elPreview=element.querySelector(".personalization-preview");if(!elPreview||!selections.placement)return;const elDraft=[...elPreview.querySelectorAll(".personalization-preview__overlay")].at(-1);if(!elDraft)return;elDraft.setAttribute("data-personalization-draft","");const details=selections.details??{},properties=selections.type?.properties??{},hasText=!!details.text,hasIcon=!!properties.icons?.find(entry=>entry.name===details.icon);elDraft.dataset.placeholder=!hasText&&!hasIcon?"true":"false"}static patchDraftOverlay(element,selections){const elPreview=element.querySelector(".personalization-preview");if(!elPreview||selections.type?.type!=="text")return;const details=selections.details??{},properties=selections.type?.properties??{},hasText=!!details.text,hasIcon=!!properties.icons?.find(entry=>entry.name===details.icon),usesPlaceholder=!hasText&&!hasIcon;let elDraft=elPreview.querySelector("[data-personalization-draft]");if(elDraft||(elDraft=[...elPreview.querySelectorAll(".personalization-preview__overlay")].at(-1)??null,elDraft&&elDraft.setAttribute("data-personalization-draft","")),elDraft&&!usesPlaceholder&&hasText){const elText=elDraft.querySelector(".personalization-preview__overlay-text"),elIcon=elDraft.querySelector(".personalization-preview__overlay-icon");if(elText&&elDraft.dataset.placeholder!=="true"&&!!elIcon===hasIcon){elText.textContent=details.text;return}}const draftMarkup=PersonalizationPreviewImage.#getSelectionOverlayMarkup(selections);if(elDraft&&elDraft.remove(),!draftMarkup)return;const parser=document.createElement("div");parser.innerHTML=draftMarkup.trim();const elNewDraft=parser.firstElementChild;elNewDraft&&(elNewDraft.setAttribute("data-personalization-draft",""),elNewDraft.dataset.placeholder=usesPlaceholder?"true":"false",elPreview.appendChild(elNewDraft),PersonalizationPreviewImage.#normaliseIconViewBoxes(element))}}class PersonalizationStepActions extends HTMLElement{connectedCallback(){const{current,total,canNext,showSave}=decodeAttr(this.getAttribute("context"));this.innerHTML=` 257 ${current>1?'<button type="button" class="personalization-modal--action prev btn--secondary" data-action="prev">Back</button>':""} 258 ${current<total?`<button type="button" class="personalization-modal--action next btn--primary" data-action="next" ${canNext?"":"disabled"}>Next</button>`:""} 259 ${showSave?'<button type="button" class="personalization-modal--action save btn--tertiary border" data-action="save">Add More Designs</button>':""} 260 `}updateCanNext(canNext){const elNext=this.querySelector('[data-action="next"]');elNext&&(elNext.disabled=!canNext)}}class PersonalizationStepPlacement extends HTMLElement{static findConfirmedEntryIndex(state,placement,product=state.currentSelections.product){return state.confirmedPersonalizations.findIndex(entry=>entry.placementLabel===placement.label&&entry.productLabel===(product?.label??null))}static isPlacementConfigured(state,placement,product=state.currentSelections.product){return PersonalizationStepPlacement.findConfirmedEntryIndex(state,placement,product)!==-1}static beginPlacementEdit(state,placement,entryIndex){const entry=state.confirmedPersonalizations[entryIndex],{product,type,details}=entry.selections;state.editingConfirmedIndex=entryIndex,state.currentSelections.product=product??state.currentSelections.product,state.currentSelections.placement=placement,state.currentSelections.type=type??null,state.currentSelections.details=details?{...details}:null,state.currentPersonalizationProduct={id:entry.personalizationVariantId,price:entry.price}}static isCurrentPlacementConfirmed(state){const{placement,product}=state.currentSelections;if(!placement)return!1;if(state.editingConfirmedIndex!==null){const editingEntry=state.confirmedPersonalizations[state.editingConfirmedIndex];if(editingEntry?.placementLabel===placement.label&&editingEntry?.productLabel===(product?.label??null))return!1}return PersonalizationStepPlacement.isPlacementConfigured(state,placement,product)}static isComplete(state){return!!state.currentSelections.placement}static handleSelection(state,data,target){const{value,dataset}=target,{step}=dataset;let valueObject={},clearDownstream=null,currentPersonalizationProduct;
260if(step==="product")valueObject=data.personalizations.find(product=>product.label==value),valueObject?.label!==state.currentSelections.product?.label&&(clearDownstream=["placement","type","details"],currentPersonalizationProduct=null,state.editingConfirmedIndex=null);else if(step==="placement"){valueObject=state.currentSelections.product.placements.find(placement=>placement.label==value);const entryIndex=PersonalizationStepPlacement.findConfirmedEntryIndex(state,valueObject,state.currentSelections.product);entryIndex!==-1?(PersonalizationStepPlacement.beginPlacementEdit(state,valueObject,entryIndex),currentPersonalizationProduct=state.currentPersonalizationProduct):(state.editingConfirmedIndex=null,clearDownstream=["type","details"],currentPersonalizationProduct=null)}return{step,valueObject,clearDownstream,currentPersonalizationProduct,syncCart:{includeCurrentDraft:!1}}}static beforeRender(){}static getOptionGroupFingerprints(state,data){const fingerprints=[];return data.personalizations.length>1&&fingerprints.push(optionGroupFingerprint("product",data.personalizations)),state.currentSelections.product?.placements?.length&&fingerprints.push(optionGroupFingerprint("placement",state.currentSelections.product.placements)),fingerprints.filter(Boolean)}connectedCallback(){const{data,currentSelections,confirmedPersonalizations}=decodeAttr(this.getAttribute("context")),hasMultipleProducts=data.personalizations.length>1,state={confirmedPersonalizations,currentSelections},configuredLabels=currentSelections.product.placements.filter(placement=>PersonalizationStepPlacement.isPlacementConfigured(state,placement,currentSelections.product)).map(placement=>placement.label),placementHeading=currentSelections.product.placements.length>1&&configuredLabels.length>0?"Select Another Placement":"Select Your Placement";this.innerHTML=` 261 ${hasMultipleProducts?` 262 <personalization-radio-group context="${encodeAttr({step:"product",options:data.personalizations,selectedLabel:currentSelections.product?.label,legend:"Select Your Product"})}"></personalization-radio-group> 263 `:""} 264 <personalization-radio-group context="${encodeAttr({step:"placement",options:currentSelections.product.placements,selectedLabel:currentSelections.placement?.label,configuredLabels,legend:placementHeading})}"></personalization-radio-group> 265 `}}class PersonalizationStepType extends HTMLElement{static isComplete(state){return!!state.currentSelections.type}static handleSelection(state,data,target){const{value}=target,valueObject=state.currentSelections.placement.types.find(type=>type.label==value);let clearDownstream=null,currentPersonalizationProduct=valueObject.type=="text"?valueObject.properties.product:null;return valueObject.type!==state.currentSelections.type?.type&&(clearDownstream=["details"]),{step:"type",valueObject,clearDownstream,currentPersonalizationProduct,syncCart:{includeCurrentDraft:!1}}}static beforeRender(){}static getOptionGroupFingerprints(state){const types=state.currentSelections.placement?.types;if(!types?.length)return[];const fingerprint=optionGroupFingerprint("type",types);return fingerprint?[fingerprint]:[]}connectedCallback(){const{currentSelections}=decodeAttr(this.getAttribute("context")),textTypes=currentSelections.placement?currentSelections.placement.types.filter(type=>type.type=="text"):[];this.innerHTML=` 266 ${currentSelections.placement?` 267 <personalization-radio-group context="${encodeAttr({step:"type",options:currentSelections.placement.types,selectedLabel:currentSelections.type?.label,legend:"Choose Personalization Type"})}"></personalization-radio-group> 268 `:""} 269 ${textTypes.length?` 270 <div class="personalization-modal--note small"> 271 ${textTypes.map(type=>`${type.label} \u2013 Max ${PersonalizationTextInput.getCharacterLimit(type.properties)} characters.`).join("<br />")} 272 </div> 273 `:""} 274 `}}class PersonalizationStepDetails extends HTMLElement{static isDraftComplete(state){const{placement,type,details}=state.currentSelections;if(!placement||!type)return!1;switch(type.type){case"text":return details?.text&&PersonalizationTextInput.isProfane(details.text)?!1:!!details?.text||!!detail
274s?.icon;case"image":return!!details?.[type.label.toLowerCase()];default:return!1}}static isComplete(state){return PersonalizationStepDetails.isDraftComplete(state)}static handleSelection(state,data,target){const{value,dataset}=target,{step,input:selection}=dataset;if(step==="type")return PersonalizationStepType.handleSelection(state,data,target);let valueObject={...state.currentSelections.details},sanitizedSelectionStart=null,sanitizedSelectionEnd=null,currentPersonalizationProduct,imageCategoryIndex;if(selection==="text"){const limit=state.currentSelections.type?.properties?PersonalizationTextInput.getTextCharacterLimit(state.currentSelections.type.properties,valueObject):null,properties=state.currentSelections.type?.properties;let detailValue=PersonalizationTextInput.sanitizeTextInput(value,properties);if(limit!==null&&(detailValue=PersonalizationTextInput.enforceTextLength(detailValue,limit)),detailValue!==value&&typeof target.selectionStart=="number"){let beforeCursor=PersonalizationTextInput.sanitizeTextInput(value.slice(0,target.selectionStart),properties);limit!==null&&(beforeCursor=PersonalizationTextInput.enforceTextLength(beforeCursor,limit)),sanitizedSelectionStart=beforeCursor.length,sanitizedSelectionEnd=beforeCursor.length}detailValue?valueObject.text=detailValue:delete valueObject.text}else if(selection==="icon")if(target.checked){if(valueObject.icon=value,valueObject.text&&state.currentSelections.type?.properties){const limit=PersonalizationTextInput.getTextCharacterLimit(state.currentSelections.type.properties,valueObject);valueObject.text=PersonalizationTextInput.enforceTextLength(PersonalizationTextInput.sanitizeTextInput(valueObject.text,state.currentSelections.type.properties),limit)}}else delete valueObject.icon;else if(selection===state.currentSelections.type?.label.toLowerCase()){imageCategoryIndex=Number(target.closest("[data-image-options]")?.dataset.categoryIndex);const resolvedCategoryIndex=Number.isFinite(imageCategoryIndex)?imageCategoryIndex:state.currentSelections.imageCategoryIndex;valueObject[selection]=findImageOption(state.currentSelections.type,option=>option.value==value,resolvedCategoryIndex),valueObject[selection]||delete valueObject[selection]}else{const propertyKey=selection==="color"?"colors":selection==="font"?"fonts":selection;valueObject[selection]=state.currentSelections.type?.properties?.[propertyKey]?.find(option=>option.name==value)}return state.currentSelections.type?.type=="image"&&(currentPersonalizationProduct=valueObject[selection]?.product),{step:"details",valueObject,currentPersonalizationProduct,imageCategoryIndex:Number.isFinite(imageCategoryIndex)?imageCategoryIndex:void 0,sanitizedSelectionStart,sanitizedSelectionEnd,syncCart:{includeCurrentDraft:!0}}}static beforeRender(state){if(state.steps.current!==3)return;const properties=state.currentSelections.type?.properties;if(state.currentSelections.type?.type!=="text"||!properties)return;const fonts=properties.fonts,colors=properties.colors;if(!fonts?.length&&!colors?.length)return;state.currentSelections.details||(state.currentSelections.details={});const details=state.currentSelections.details;fonts?.length&&!details.font&&(details.font=fonts[0]),colors?.length&&!details.color&&(details.color=colors[0]),PersonalizationTextInput.syncDetailsTextToLimit(state)}static handleKeydown(ev){PersonalizationIconPicker.handleKeydown(ev)}static getOptionGroupFingerprints(state){return PersonalizationStepType.getOptionGroupFingerprints(state)}connectedCallback(){const{currentSelections}=decodeAttr(this.getAttribute("context")),type=currentSelections.type,properties=type?.properties,details=currentSelections.details??{};let detailPickers="";type?.type=="text"&&properties?detailPickers=` 275 <personalization-text-input context="${encodeAttr({properties,details})}"></personalization-text-input> 276 ${properties.icons?`<personalization-icon-picker context="${encodeAttr({icons:properties.icons,details,properties})}"></personalization-icon-picker>`:""} 277 ${properties.fonts?`<personalization-font-picker context="${encodeAttr({fonts:properties.fonts,details})}"></personalization-font-picker>`:""} 278 ${properties.colors?`<personalization-color-picker context="${encodeAttr({colors:properties.colors,details})}"></personalization-color-picker>`:""} 279 `:type?.type=="image"&&(detailPickers=`<personalization-image-picker context="${encodeAttr({type,details,selectedCategoryIndex:currentSelections.imageCategoryIndex??null})}"></personalization-image-picker>`),this.innerHTML=` 280 <div class="personalization-modal--field"> 281 <p class="personalization-modal--heading">Add Details</p>
282 <span class="personalization-modal--note small">* Required</span> 283 ${currentSelections.placement?` 284 <personalization-radio-group context="${encodeAttr({step:"type",options:currentSelections.placement.types,selectedLabel:currentSelections.type?.label,legend:"Choose Personalization Type",legendClass:"visually-hidden"})}"></personalization-radio-group> 285 `:""} 286 </div> 287 ${detailPickers} 288 `}}class PersonalizationStepConfirmation extends HTMLElement{static isComplete(){return!0}static handleSelection(){return null}static beforeRender(){}static getOptionGroupFingerprints(){return[]}static commitUnsavedDraft(state,{persistDraft}={}){return!PersonalizationStepDetails.isDraftComplete(state)||PersonalizationStepPlacement.isCurrentPlacementConfirmed(state)?!1:(state.commitCurrentDraft(),state.currentSelections={product:state.currentSelections.product,placement:null,type:null,details:null},state.currentPersonalizationProduct=null,persistDraft?.(),!0)}static beginEntryEdit(state,entryIndex,{persistDraft}={}){PersonalizationStepConfirmation.commitUnsavedDraft(state,{persistDraft});const placement=state.confirmedPersonalizations[entryIndex]?.selections?.placement;return placement?(PersonalizationStepPlacement.beginPlacementEdit(state,placement,entryIndex),state.steps.current=1,{syncCart:{includeCurrentDraft:!0}}):null}static beginDraftEdit(state){return state.steps.current=1,{syncCart:{includeCurrentDraft:!0}}}static handleAction(state,actionEl,{persistDraft}={}){const{confirmationTarget,entryIndex:entryIndexAttr}=actionEl.dataset;if(confirmationTarget==="draft")return PersonalizationStepConfirmation.beginDraftEdit(state);const entryIndex=Number(entryIndexAttr);return Number.isNaN(entryIndex)?null:PersonalizationStepConfirmation.beginEntryEdit(state,entryIndex,{persistDraft})}static#clearInProgressSelections(state){state.currentSelections={product:state.currentSelections.product,placement:null,type:null,details:null},state.currentPersonalizationProduct=null,state.editingConfirmedIndex=null}static#hasUncommittedConfirmationDraft(state){return PersonalizationStepDetails.isDraftComplete(state)&&!PersonalizationStepPlacement.isCurrentPlacementConfirmed(state)}static#setStepAfterRemoval(state){const hasRemainingPersonalizations=state.confirmedPersonalizations.length>0||PersonalizationStepConfirmation.#hasUncommittedConfirmationDraft(state);state.steps.current=hasRemainingPersonalizations?state.steps.total:1}static#clearDraftForRemovedEntry(state,removedEntry){const{placement,product}=state.currentSelections;!placement||placement.label!==removedEntry.placementLabel||(product?.label??null)!==removedEntry.productLabel||PersonalizationStepConfirmation.#clearInProgressSelections(state)}static removeConfirmedEntry(state,entryIndex,{persistDraft}={}){if(entryIndex<0||entryIndex>=state.confirmedPersonalizations.length)return null;const removedEntry=state.confirmedPersonalizations[entryIndex];return state.confirmedPersonalizations.splice(entryIndex,1),state.renumberLocationKeys(),state.editingConfirmedIndex===entryIndex?PersonalizationStepConfirmation.#clearInProgressSelections(state):state.editingConfirmedIndex>entryIndex?state.editingConfirmedIndex-=1:PersonalizationStepConfirmation.#clearDraftForRemovedEntry(state,removedEntry),PersonalizationStepConfirmation.#setStepAfterRemoval(state),persistDraft?.(),{syncCart:{includeCurrentDraft:PersonalizationStepConfirmation.#hasUncommittedConfirmationDraft(state)}}}static removeDraft(state,{persistDraft}={}){return state.editingConfirmedIndex!==null?PersonalizationStepConfirmation.removeConfirmedEntry(state,state.editingConfirmedIndex,{persistDraft}):(PersonalizationStepConfirmation.#clearInProgressSelections(state),PersonalizationStepConfirmation.#setStepAfterRemoval(state),persistDraft?.(),{syncCart:{includeCurrentDraft:!1}})}static handleRemove(state,actionEl,{persistDraft}={}){const{confirmationTarget,entryIndex:entryIndexAttr}=actionEl.dataset;if(confirmationTarget==="draft")return PersonalizationStepConfirmation.removeDraft(state,{persistDraft});const entryIndex=Number(entryIndexAttr);return Number.isNaN(entryIndex)?null:PersonalizationStepConfirmation.removeConfirmedEntry(state,entryIndex,{persistDraft})}static showAddMoreDesigns(state){return state.steps.current!==state.steps.total||!state.hasAnyUnconfiguredPlacements()?!1:PersonalizationStepDetails.isDraftComplete(state)?!PersonalizationStepPlacement.isCurrentPlacementConfirmed(state):!state.currentSelections.placement}static save(state,{persistDraft,render}){if(state.steps.current!==state.steps.total)return;const draftComplete=PersonalizationStepDetails.isDraftComplete(state);draftComplete&&PersonalizationStepPlacement.isCurrentPlacementConfirmed(state)||(draftComplete&&(state.commitCurrentDraft(),PersonalizationCart.syncFromState(state,{includeCurrentDraft:!1})),state.currentSelections={product:state.getFirstProductWithUnconfiguredPlacements(),placement:null,type:null,details:null},state.currentPersonalizationProduct=null,state.editingConfirmedIndex=null,state.steps.current=state.hasAnyUnconfiguredPlacements()?1:state.steps.total,persistDraft(),render())}connectedCallback(){const{data,currentSelections,confirmedPersonalizations,editingConfirmedIndex,steps,currentPersonalizationProduct,confirmationHeading,confirmationDescription,termsHtml}=decodeAttr(this.getAttribute("context")),state={data,currentSelections,confirmedPersonalizations,editingConfirmedIndex:editingConfirmedIndex??null,steps,currentPersonalizationProduct},{rows,subtotal}=PersonalizationCart.getConfirmationPricing(state);this.innerHTML=` 289 <div class="personalization-modal--field"> 290 <h3 class="personalization-modal--heading">${confirmationHeading}</h3> 291 <div class="personalization-modal--confirmation-description"> 292 ${confirmationDescription} 293 </div> 294 </div> 295 <table class="personalization-modal--confirmation-items"> 296 <thead class="visually-hidden"> 297 <tr> 298 <th>Product</th> 299 <th>Price</th> 300 <th>Actions</th> 301 </tr> 302 </thead> 303 <tbody> 304 <tr> 305 <td class="personalization-modal--confirmation-product">${data.base_product.title}</td> 306 <td class="personalization-modal--confirmation-price">${window.formatMoney(data.base_product.price)}</td> 307 <td class="personalization-modal--confirmation-actions"></td> 308 </tr> 309 ${rows.map(row=>{const editAttrs=row.isDraft&&row.entryIndex===null?'data-action="edit" data-confirmation-target="draft"':`data-action="edit" data-entry-index="${row.entryIndex}"`,removeAttrs=row.isDraft&&row.entryIndex===null?'data-action="remove" data-confirmation-target="draft"':`data-action="remove" data-entry-index="${row.entryIndex}"`;return` 310 <tr> 311 <td class="personalization-modal--confirmation-product">${row.summary}</td> 312 <td class="personalization-modal--confirmation-price">${window.formatMoney(row.price)}</td> 313 <td class="personalization-modal--confirmation-actions"> 314 <button 315 type="button" 316 class="personalization-modal--confirmation-action btn--edit" 317 ${editAttrs} 318 aria-label="Edit ${row.summary}" 319 ></button> 320 <button 321 type="button" 322 class="personalization-modal--confirmation-action btn--remove" 323 ${removeAttrs} 324 aria-label="Remove ${row.summary}" 325 ></button> 326 </td> 327 </tr> 328 `}).join("")} 329 </tbody> 330 <tfoot> 331 <tr> 332 <td class="personalization-modal--confirmation-product">Subtotal</td> 333 <td class="personalization-modal--confirmation-price">${window.formatMoney(subtotal)}</td> 334 <td class="personalization-modal--confirmation-actions"></td> 335 </tr> 336 </tfoot> 337 </table> 338 ${termsHtml??""} 339 `}}const STEP_CLASSES=[PersonalizationStepPlacement,PersonalizationStepType,PersonalizationStepDetails,PersonalizationStepConfirmation],STEP_TAGS=["personalization-step-placement","personalization-step-type","personalization-step-detail
339s","personalization-step-confirmation"],STEP_NAMES=["placement","type","details","confirmation"];if(!customElements.get("personalization-modal")){customElements.define("personalization-text-input",PersonalizationTextInput),customElements.define("personalization-icon-picker",PersonalizationIconPicker),customElements.define("personalization-font-picker",PersonalizationFontPicker),customElements.define("personalization-color-picker",PersonalizationColorPicker),customElements.define("personalization-image-picker",PersonalizationImagePicker),customElements.define("personalization-radio-group",PersonalizationRadioGroup),customElements.define("personalization-progress",PersonalizationProgress),customElements.define("personalization-preview-image",PersonalizationPreviewImage),customElements.define("personalization-step-actions",PersonalizationStepActions),customElements.define("personalization-cart",PersonalizationCart),customElements.define("personalization-step-placement",PersonalizationStepPlacement),customElements.define("personalization-step-type",PersonalizationStepType),customElements.define("personalization-step-details",PersonalizationStepDetails),customElements.define("personalization-step-confirmation",PersonalizationStepConfirmation);class PersonalizationModal extends PopupModal{#isRendering=!1;#handleChangeBound=null;#updateCurrentStepBound=null;#handleSelectKeydownBound=null;#handleIconKeydownBound=null;#handlePersonalizationAddedBound=null;#handleImageCategoryChangeBound=null;#preserveSelectOpenOnRender=!1;#preserveScrollOnRender=!1;#scrollAnchorOnRender=null;static#SCROLL_PRESERVE_SELECTORS=[".personalization-modal--main",".personlization-modal--options-images"];constructor(){super(),this.confirmationHeading=this.getAttribute("confirmation-heading"),this.confirmationDescription=this.getAttribute("confirmation-description")}connectedCallback(){this.elParentSection=this.closest("section")||document.body,this.elTriggers=this.elParentSection.querySelectorAll("[data-personalization-trigger]"),this.elTriggers.forEach(el=>el.addEventListener("click",()=>this.setAttribute("open",!0))),this.elData=this.querySelector("[data-personalization-data]"),this.data=JSON.parse(this.elData.textContent),this.state=new PersonalizationState(this.data),this.state.resetAll();const cartEdit=typeof window.expireStorageItem=="function"?window.expireStorageItem(PersonalizationCart.CART_EDIT_STORAGE_KEY):null;let shouldOpenFromCartEdit=!1;cartEdit?.variantId===this.data.base_product.id?(PersonalizationCart.hydrateStateFromCart(this.state,cartEdit),this.state.cartEditGroupKeys=cartEdit.groupKeys,this.state.cartEditQuantities=cartEdit.quantities??null,typeof window.storageAvailable=="function"&&window.storageAvailable()&&localStorage.removeItem(PersonalizationCart.CART_EDIT_STORAGE_KEY),shouldOpenFromCartEdit=!0):this.state.loadDraft(),this.focusElement=null,this.focusSelectionStart=null,this.focusSelectionEnd=null,this.elContent=this.querySelector("[data-template]"),this.elTerms=this.querySelector("[data-terms]"),this.#handleChangeBound=this.#handleChange.bind(this),this.#updateCurrentStepBound=this.#updateCurrentStep.bind(this),this.#handleSelectKeydownBound=this.#handleSelectKeydown.bind(this),this.#handleIconKeydownBound=this.#handleKeydown.bind(this),this.#handlePersonalizationAddedBound=this.#handlePersonalizationAdded.bind(this),this.#handleImageCategoryChangeBound=this.#handleImageCategoryChange.bind(this),this.elContent.addEventListener("change",this.#handleChangeBound),this.elContent.addEventListener("input",this.#handleChangeBound),this.elContent.addEventListener("click",this.#updateCurrentStepBound),this.elContent.addEventListener("keydown",this.#handleSelectKeydownBound,!0),this.elContent.addEventListener("keydown",this.#handleIconKeydownBound,!0),this.elContent.addEventListener("personalization-image-category-change",this.#handleImageCategoryChangeBound),this.addEventListener("personalization-added",this.#handlePersonalizationAddedBound),this.#render(),shouldOpenFromCartEdit&&this.setAttribute("open","true")}attributeChangedCallback(name,oldValue,newValue){name==="open"&&newValue==="true"&&(!this.state.cartEditGroupKeys?.length&&this.state.confirmedPersonalizations?.length&&!this.state.hasAnyUnconfiguredPlacements()&&!PersonalizationStepDetails.isDraftComplete(this.state)&&(this.state.steps.current=this.state.steps.total),this.#render()),super.attributeChangedCallback(name,oldValue,newValue)}close(){this.state.persistDraft(),this.state.resetInProgress(),super.close()}clearSavedDesigns(){this.state.clearDraft(),this.state.resetAll(),this.state.cartEditGroupKeys=null,this.getAttribute("open")==="true"&&this.#render()}updateData(data){this.data?.base_product?.id!==data.base_product.id&&this.state?.persistDraft(),this.data=data,this.elData&&(this.elData.textContent=JSON.stringify(data)),this.state=new PersonalizationState(this.data),this.state.resetAll(),this.state.loadDraft(),this.getAttribute("open")==="true"&&this.#render()}#getStepContext(){return{data:this.data,currentSelections:this.state.currentSelections,confirmedPersonalizations:this.state.confirmedPersonalizations,editingConfirmedIndex:this.state.editingConfirmedIndex,steps:this.state.steps,currentPersonalizationProduct:this.state.currentPersonalizationProduct,confirmationHeading:this.confirmationHeading,confirmationDescription:this.confirmationDescription,termsHtml:this.elTerms?this.elTerms.innerHTML:""}}#handlePersonalizationAdded(){const elStatus=this.querySelector("[data-personalization-status]");elStatus&&(elStatus.textContent="Personalization added to cart."),window.setTimeout(()=>{this.state.clearDraft(),this.state.resetAll(),super.close(),elStatus&&(elStatus.textContent="")},150)}#render(){this.#isRendering=!0,STEP_CLASSES[this.state.steps.current-1].beforeRender?.(this.state);
339const renderState=this.#captureRenderState();this.elContent.innerHTML=this.#template(),this.#restoreRenderState(renderState),this.focusElement=null,this.focusSelectionStart=null,this.focusSelectionEnd=null,this.#preserveSelectOpenOnRender=!1,this.#preserveScrollOnRender=!1,this.#scrollAnchorOnRender=null,this.#isRendering=!1}#getScrollContainers(){const containers=[];this.elContent&&containers.push({selector:null,el:this.elContent});for(const selector of PersonalizationModal.#SCROLL_PRESERVE_SELECTORS){const el=this.elContent?.querySelector(selector);el&&containers.push({selector,el})}return containers}#findRadioGroupByFingerprint(fingerprint){if(!fingerprint)return null;for(const el of this.elContent?.querySelectorAll("personalization-radio-group")??[]){const{step,options}=decodeAttr(el.getAttribute("context"));if(optionGroupFingerprint(step,options)===fingerprint)return el}return null}#captureScrollAnchor(fingerprint){const anchor=this.#findRadioGroupByFingerprint(fingerprint);if(!anchor)return null;const offsets=[];for(const{selector,el}of this.#getScrollContainers()){if(!el.contains(anchor))continue;const scrollerRect=el.getBoundingClientRect(),anchorRect=anchor.getBoundingClientRect();offsets.push({selector,visualOffset:anchorRect.top-scrollerRect.top})}return offsets.length?{fingerprint,offsets}:null}#restoreScrollAnchor(anchorState){if(!anchorState?.offsets?.length)return;requestAnimationFrame(()=>{const anchor=this.#findRadioGroupByFingerprint(anchorState.fingerprint);if(anchor)for(const{selector,visualOffset}of anchorState.offsets){const el=selector?this.elContent?.querySelector(selector):this.elContent;if(!el?.contains(anchor))continue;const scrollerRect=el.getBoundingClientRect(),delta=anchor.getBoundingClientRect().top-scrollerRect.top-visualOffset;delta!==0&&(el.scrollTop+=delta)}})}#captureScrollPositions(){const positions=[{selector:null,scrollTop:this.elContent?.scrollTop??0,scrollLeft:this.elContent?.scrollLeft??0}];for(const selector of PersonalizationModal.#SCROLL_PRESERVE_SELECTORS){const el=this.elContent?.querySelector(selector);el&&positions.push({selector,scrollTop:el.scrollTop,scrollLeft:el.scrollLeft})}return positions}#restoreScrollPositions(positions){if(!positions?.length)return;requestAnimationFrame(()=>{for(const{selector,scrollTop,scrollLeft}of positions){const el=selector?this.elContent?.querySelector(selector):this.elContent;el&&(el.scrollTop=scrollTop,el.scrollLeft=scrollLeft)}})}#captureRenderState(){const openSelectToggleIds=this.#preserveSelectOpenOnRender?[...this.querySelectorAll('custom-select[open="true"]')].map(el=>el.querySelector("[data-toggle]")?.id).filter(Boolean):[];let focusId=this.focusElement,selectionStart=this.focusSelectionStart,selectionEnd=this.focusSelectionEnd;if(!focusId){const elFocused=this.contains(document.activeElement)?document.activeElement:null;elFocused?.id&&(focusId=elFocused.id,typeof elFocused.selectionStart=="number"&&(selectionStart=elFocused.selectionStart,selectionEnd=elFocused.selectionEnd))}return{preserveSelectOpen:this.#preserveSelectOpenOnRender,preserveScroll:this.#preserveScrollOnRender,scrollPositions:this.#preserveScrollOnRender?this.#captureScrollPositions():null,scrollAnchor:this.#scrollAnchorOnRender,openSelectToggleIds,focusId,selectionStart,selectionEnd}}#restoreRenderState({preserveSelectOpen,preserveScroll,scrollPositions,scrollAnchor,openSelectToggleIds,focusId,selectionStart,selectionEnd}){const toggleIdsToOpen=new Set(openSelectToggleIds);if(preserveSelectOpen&&focusId){const toggleId=this.querySelector(`#${CSS.escape(focusId)}`)?.closest("custom-select")?.querySelector("[data-toggle]")?.id;toggleId&&toggleIdsToOpen.add(toggleId)}for(const toggleId of toggleIdsToOpen)this.querySelector(`#${CSS.escape(toggleId)}`)?.closest("custom-select")?.setAttribute("open","true");if(focusId){const newFocusElement=this.querySelector(`#${CSS.escape(focusId)}`);newFocusElement&&(newFocusElement.focus({preventScroll:!0}),typeof selectionStart=="number"&&typeof newFocusElement.setSelectionRange=="function"&&newFocusElement.setSelectionRange(selectionStart,selectionEnd))}preserveScroll?this.#restoreScrollPositions(scrollPositions):scrollAnchor&&this.#restoreScrollAnchor(scrollAnchor)}#handleSelectKeydown(ev){ev.target.closest('custom-select input[type="radio"]')&&["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End"].includes(ev.key)&&(this.#preserveSelectOpenOnRender=!0)}#handleKeydown(ev){STEP_CLASSES[this.state.steps.current-1].handleKeydown?.(ev)}#handleImageCategoryChange(ev){const{categoryIndex}=ev.detail??{};Number.isFinite(categoryIndex)&&(this.state.currentSelections.imageCategoryIndex=categoryIndex)}#handleChange(ev){if(this.#isRendering)return;const elTarget=ev.target;if(!(ev.type==="input"&&elTarget.matches('input[type="radio"], input[type="checkbox"]'))){if(elTarget.matches('input[data-input="text"]')){if(ev.type==="change")return;this.#applyTextInputSelection(elTarget);return}this.#applySelection(elTarget)}}#applyTextInputSelection(target){const result=PersonalizationStepDetails.handleSelection(this.state,this.data,target);if(!result)return;const{step,valueObject,syncCart}=result;this.state.currentSelections[step]=valueObject,PersonalizationCart.syncFromState(this.state,syncCart);const sanitizedText=valueObject.text??"";target.value!==sanitizedText&&(target.value=sanitizedText,result.sanitizedSelectionStart!==null&&result.sanitizedSelectionStart!==void 0&&target.setSelectionRange(result.sanitizedSelectionStart,result.sanitizedSelectionEnd)),this.#patchTextDetailsUI()}#patchTextDetailsUI(){const properties=this.state.currentSelections.type?.properties,details=this.state.currentSelections.details??{};this.querySelector("personalization-text-input")?.updateFromDetails(properties,details),this.querySelector("personalization-icon-picker")?.updateDisabledState(details,properties);const elPreview=this.querySelector("personalization-preview-image");elPreview&&PersonalizationPreviewImage.patchDraftOverlay(elPreview,this.state.currentSelections),this.querySelector("personalization-step-actions")?.updateCanNext(PersonalizationStepDetails.isDraftComplete(this.state))}#applySelection(target){const result=STEP_CLASSES[this.state.steps.current-1].handleSelection(this.state,this.data,target);if(!result)return;const{step,valueObject,clearDownstream,currentPersonalizationProduct,syncCart,imageCategoryIndex}=result;if(clearDownstream){for(const key of clearDownstream)this.state.currentSelections[key]=null;
339clearDownstream.some(key=>["details","type","placement"].includes(key))&&delete this.state.currentSelections.imageCategoryIndex}currentPersonalizationProduct!==void 0&&(this.state.currentPersonalizationProduct=currentPersonalizationProduct),imageCategoryIndex!==void 0&&(this.state.currentSelections.imageCategoryIndex=imageCategoryIndex);const elCustomSelect=target.closest("custom-select");elCustomSelect&&this.#preserveSelectOpenOnRender?this.focusElement=target.id:elCustomSelect?this.focusElement=elCustomSelect.querySelector("[data-toggle]")?.id??target.id:this.focusElement=target.id,result.sanitizedSelectionStart!==null&&result.sanitizedSelectionStart!==void 0?(this.focusSelectionStart=result.sanitizedSelectionStart,this.focusSelectionEnd=result.sanitizedSelectionEnd):typeof target.selectionStart=="number"&&(this.focusSelectionStart=target.selectionStart,this.focusSelectionEnd=target.selectionEnd),this.state.currentSelections[step]=valueObject,PersonalizationCart.syncFromState(this.state,syncCart),this.#preserveScrollOnRender=!0,this.#render()}#updateCurrentStep(ev){const actionEl=ev.target.closest("[data-action]");if(!actionEl||this.#isRendering)return;const direction=actionEl.dataset.action;if(direction==="edit"||direction==="remove"){const StepClass=STEP_CLASSES[this.state.steps.current-1],result=(direction==="remove"?StepClass.handleRemove:StepClass.handleAction)?.(this.state,actionEl,{persistDraft:()=>this.state.persistDraft()});result&&(result.syncCart&&PersonalizationCart.syncFromState(this.state,result.syncCart),this.#render());return}if(direction==="save"){PersonalizationStepConfirmation.save(this.state,{persistDraft:()=>this.state.persistDraft(),render:()=>this.#render()});return}if(direction!=="add"){if(direction==="prev"){const currentStepIndex=this.state.steps.current-1,destinationStepIndex=currentStepIndex-1,sharedFingerprints=this.#getSharedStepFingerprints(currentStepIndex,destinationStepIndex);sharedFingerprints.length&&(this.#scrollAnchorOnRender=this.#captureScrollAnchor(sharedFingerprints[0]));const currentStep=Object.keys(this.state.currentSelections)[this.state.steps.current];this.state.currentSelections[currentStep]=null,this.state.steps.current-=1}else if(direction==="next"){const currentStepIndex=this.state.steps.current-1,destinationStepIndex=currentStepIndex+1,sharedFingerprints=this.#getSharedStepFingerprints(currentStepIndex,destinationStepIndex);sharedFingerprints.length&&(this.#scrollAnchorOnRender=this.#captureScrollAnchor(sharedFingerprints[0])),this.state.steps.current+=1,this.state.steps.current===this.state.steps.total&&PersonalizationStepDetails.isDraftComplete(this.state)&&PersonalizationCart.syncFromState(this.state,{includeCurrentDraft:!0})}this.#render()}}#getSharedStepFingerprints(currentStepIndex,destinationStepIndex){if(destinationStepIndex<0||destinationStepIndex>=STEP_CLASSES.length)return[];const CurrentStepClass=STEP_CLASSES[currentStepIndex],DestinationStepClass=STEP_CLASSES[destinationStepIndex],currentFingerprints=CurrentStepClass.getOptionGroupFingerprints?.(this.state,this.data)??[],destinationFingerprints=DestinationStepClass.getOptionGroupFingerprints?.(this.state,this.data)??[];return getSharedOptionGroupFingerprints(currentFingerprints,destinationFingerprints)}#template(){const stepIndex=this.state.steps.current-1,activeStep=STEP_NAMES[stepIndex],stepTag=STEP_TAGS[stepIndex],StepClass=STEP_CLASSES[stepIndex],stepContext=encodeAttr(this.#getStepContext());return` 340 <form class="personalization-modal--form" data-form> 341 <div class="personalization-modal--main"> 342 <div class="personalization-modal--progress"> 343 <personalization-progress context="${encodeAttr({current:this.state.steps.current,total:this.state.steps.total})}"></personalization-progress> 344 </div> 345 <div class="personalization-modal--image"> 346 <personalization-preview-image 347 selections="${encodeAttr(this.state.currentSelections)}" 348 confirmed-personalizations="${encodeAttr(this.state.confirmedPersonalizations)}" 349 editing-confirmed-index="${this.state.editingConfirmedIndex??""}" 350 personalizations="${encodeAttr(this.data.personalizations)}" 351 ></personalization-preview-image> 352 </div> 353 <${stepTag} context="${stepContext}" class="personalization-modal--step ${activeStep} ${this.state.currentSelections.type?.type=="image"?"image":""}"></${stepTag}> 354 </div> 355 <div class="personalization-modal--actions"> 356 <personalization-step-actions context="${encodeAttr({current:this.state.steps.current,total:this.state.steps.total,canNext:StepClass.isComplete(this.state),showSave:PersonalizationStepConfirmation.showAddMoreDesigns(this.state)})}"></personalization-step-actions> 357 ${this.state.steps.current===this.state.steps.total?` 358 <personalization-cart></personalization-cart> 359 `:""} 360 </div> 361 </form> 362 `}}customElements.define("personalization-modal",PersonalizationModal)}window.startPersonalizationCartEdit=payload=>PersonalizationCart.startCartEdit(payload),window.clearPersonalizationDraft=variantId=>PersonalizationCart.clearSavedDesigns(variantId); 363//# sourceMappingURL=/cdn/shop/t/3471/assets/personalization-modal.js.map?v=77062502030212783981790184691
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.