PageSourceSearch

https://nominalx.com/cdn/shop/t/417/assets/ab19.js?v=80213340448644581111790728175

js nominalx.com collected 2026-10-01 11:36:57 UTC 16,965 bytes, 211 lines download raw bytes

1(function(){"use strict";const CONFIG={MAX_LOAD_ATTEMPTS:50,POLL_INTERVAL:100,MOBILE_BREAKPOINT:768,TITLE_TEXT:"Make Your Jewelry Perfect",IMAGE_FALLBACK_BG:"#F9F6EC",MIN_VALIDATION_ATTEMPTS:3};function detectAddonSystem(){const boldOptions=document.querySelector(".bold_options"),boldCheckboxes=boldOptions?.querySelectorAll(".bold_option_checkbox"),nativeOptions=document.querySelector("#fieldset-section-2.fieldset"),nativeCheckboxes=nativeOptions?.querySelectorAll('input.product-custom-option[type="checkbox"]'),boldHasCheckboxes=boldCheckboxes&&boldCheckboxes.length>0;return nativeCheckboxes&&nativeCheckboxes.length>0?{type:"native",container:nativeOptions}:boldHasCheckboxes?{type:"bold",container:boldOptions}:{type:"none",container:null}}function validateBoldOptionsComplete(container){if(!container.classList.contains("bold_options_loaded"))return!1;const checkboxes=container.querySelectorAll(".bold_option_checkbox");if(checkboxes.length===0)return!1;let foundValidAddon=!1;for(const element of checkboxes){const handle=element.querySelector('input[type="checkbox"]')?.getAttribute("data-product-handle"),title=element.querySelector(".bold_option_title")?.textContent.trim(),price=element.querySelector(".bold_option_value_price")?.textContent.trim();if(handle){if(!title||!price)return!1;foundValidAddon=!0}}return foundValidAddon}function validateNativeOptionsComplete(container){const checkboxes=container.querySelectorAll('input.product-custom-option[type="checkbox"]');if(checkboxes.length===0)return!1;let foundValidAddon=!1;for(const checkbox of checkboxes){const fieldChoice=checkbox.closest(".field.choice");if(!fieldChoice)continue;const label=fieldChoice.querySelector("label.label");if(!label)continue;const titleSpan=label.querySelector("span:first-child"),priceNotice=label.querySelector(".price-notice"),productSku=checkbox.getAttribute("product-sku"),hasTitle=titleSpan?.textContent.trim(),hasPrice=priceNotice?.textContent.trim()||checkbox.getAttribute("price");hasTitle&&hasPrice&&productSku&&(foundValidAddon=!0)}return foundValidAddon}function injectCustomCSS(){const style=document.createElement("style");style.id="nominal-addon-redesign-styles",style.textContent=`
2      .addon-checkbox-new {
3        -webkit-appearance: none;
4        -moz-appearance: none;
5        appearance: none;
6        width: 16px;
7        height: 16px;
8        border-radius: 2.5px;
9        border: 1px solid #767676;
10        background: #FFFFFF;
11        cursor: pointer;
12        margin: 0;
13        position: relative;
14        transition: all 0.2s ease;
15      }
16
17      .addon-checkbox-new:checked {
18        background-color: #000000 !important;
19        border-color: #000000 !important;
20      }
21
22      .addon-checkbox-new:checked::after {
23        content: '';
24        position: absolute;
25        left: 5px;
26        top: 2px;
27        width: 4px;
28        height: 8px;
29        border: solid white;
30        border-width: 0 1.5px 1.5px 0;
31        transform: rotate(45deg);
32      }
33
34      .addon-card {
35        transition: opacity 0.2s ease;
36      }
37
38      .addon-card:hover {
39        opacity: 0.85;
40      }
41
42      /* Hide Bold Options total/summary */
43      .bold_options .bold_option_total {
44        display: none !important;
45      }
46
47      /* Hide native options container and all child elements */
48      #fieldset-section-2.fieldset.nominal-hidden,
49      #fieldset-section-2.fieldset.nominal-hidden * {
50        display: none !important;
51        visibility: hidden !important;
52        opacity: 0 !important;
53        height: 0 !important;
54        width: 0 !important;
55        margin: 0 !important;
56        padding: 0 !important;
57        overflow: hidden !important;
58      }
59
60      @media (max-width: ${CONFIG.MOBILE_BREAKPOINT}px) {
61        .nominal-addon-redesign {
62          width: 342px !important;
63        }
64      }
65    `,document.getElementById("nominal-addon-redesign-styles")||document.head.appendChild(style)}function waitForAddons(callback,maxAttempts=CONFIG.MAX_LOAD_ATTEMPTS){let done=!1;const STABILITY_MS=100,TIMEOUT_MS=maxAttempts*CONFIG.POLL_INTERVAL;let stabilityTimer=null;function tryValidate(){if(done)return;
65const system=detectAddonSystem();let isValid=!1;system.type==="bold"&&(isValid=validateBoldOptionsComplete(system.container)),system.type==="native"&&(isValid=validateNativeOptionsComplete(system.container)),isValid&&(done=!0,clearTimeout(stabilityTimer),clearTimeout(fallbackTimer),observer.disconnect(),console.log(`[Nominal A/B Test] ${system.type} system fully loaded and validated (mutation-driven)`),callback(system))}function scheduleValidate(){clearTimeout(stabilityTimer),stabilityTimer=setTimeout(tryValidate,STABILITY_MS)}const observer=new MutationObserver(scheduleValidate);observer.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["class"]});const fallbackTimer=setTimeout(()=>{done||(done=!0,observer.disconnect(),console.warn("[Nominal A/B Test] Add-ons did not load within expected time"))},TIMEOUT_MS);tryValidate()}function extractBoldAddons(boldContainer){const addons=[];return boldContainer.querySelectorAll(".bold_option_checkbox").forEach(element=>{const checkbox=element.querySelector('input[type="checkbox"]'),hiddenInput=element.querySelector('input[type="hidden"]');if(!checkbox)return;const productHandle=checkbox.getAttribute("data-product-handle"),title=element.querySelector(".bold_option_title")?.textContent.trim(),price=element.querySelector(".bold_option_value_price")?.textContent.trim(),tooltip=element.querySelector(".bold_tooltip")?.textContent.trim()||"",checkboxClass=checkbox.className,propertyName=hiddenInput?.name.replace("properties[","").replace("]","")||"";!productHandle||!price||addons.push({system:"bold",element,checkbox,hiddenInput,productHandle,productSku:null,title,price,tooltip,checkboxClass,propertyName,imageUrl:null,disabled:checkbox.disabled||!1})}),addons}function extractNativeAddons(nativeContainer){const addons=[];return nativeContainer.querySelectorAll('input.product-custom-option[type="checkbox"]').forEach(checkbox=>{const fieldChoice=checkbox.closest(".field.choice"),dynamicOption=checkbox.closest('[id^="dynamic_option_id_"]');if(!fieldChoice||!dynamicOption)return;const labelElement=fieldChoice.querySelector("label.label");if(!labelElement)return;const title=labelElement.querySelector("span:first-child")?.textContent.trim();let price=labelElement.querySelector(".price-notice")?.textContent.trim();if(!price){const priceAttr=checkbox.getAttribute("price");priceAttr&&(price=`+$${parseFloat(priceAttr).toFixed(0)}`)}const productSku=checkbox.getAttribute("product-sku"),disabled=checkbox.disabled||labelElement.textContent.includes("out of stock"),tooltip=dynamicOption.querySelector("label:first-child")?.textContent.trim()||"";!title||!price||addons.push({system:"native",element:fieldChoice,checkbox,hiddenInput:null,productHandle:null,productSku,title,price,tooltip:tooltip!==title?tooltip:"",checkboxClass:checkbox.className,propertyName:checkbox.name,imageUrl:null,disabled})}),addons}async function fetchProductImages(addons){const skuToHandleMap={"GIFT BOX":"nominal-gift-box","Chain-N-Ext-G":"chain-extension","JC-SPRY-W":"jewelry-cleaner-kit","RING GIFT BOX":"ring-gift-box","NECKLACE GIFT BOX":"necklace-gift-box","CUFF GIFT BOX":"cuff-gift-box"},promises=addons.map(async addon=>{if(addon.system==="bold"&&addon.productHandle)try{const response=await fetch(`/products/${addon.productHandle}.js`);if(!response.ok)throw new Error(`HTTP ${response.status}`);const productData=await response.json();addon.imageUrl=productData.featured_image||productData.images&&productData.images[0]||null}catch(error){console.warn(`[Nominal A/B Test] Failed to fetch image for ${addon.productHandle}:`,error),addon.imageUrl=null}if(addon.system==="native"&&addon.productSku)try{let handle=skuToHandleMap[addon.productSku];handle||(handle=addon.productSku.toLowerCase().replace(/\s+/g,"-"));const response=await fetch(`/products/${handle}.js`);if(!response.ok)throw new Error(`HTTP ${response.status}`);const productData=await response.json();addon.imageUrl=productData.featured_image||productData.images&&productData.images[0]||null}catch{addon.imageUrl=null}});return await Promise.all(promises),addons}function generateNewHTML(addons){let html=`
66      <div class="nominal-addon-redesign" style="
67        display: flex;
68        flex-direction: column;
69        align-items: flex-start;
70        padding: 0;
71        gap: 12px;
72        width: ${window.innerWidth<=CONFIG.MOBILE_BREAKPOINT?"342px":"441px"};
73        max-width: 100%;
74        margin-bottom: 20px;
75      ">
76        <div class="addon-title" style="
77          font-family: 'Aktiv Grotesk Corp', 'Aktiv Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
78          font-style: normal;
79          font-weight: 500;
80          font-size: 12px;
81          line-height: 14px;
82          display: flex;
83          align-items: center;
84          text-transform: capitalize;
85          color: #000000;
86        ">
87          ${CONFIG.TITLE_TEXT}
88        </div>
89    `;return addons.forEach((addon,index)=>{const imageHTML=addon.imageUrl?`<img src="${addon.imageUrl}" alt="${addon.title}" style="width: 40px; height: 40px; object-fit: cover; background: ${CONFIG.IMAGE_FALLBACK_BG}; display: block;">`:`<div style="width: 40px; height: 40px; background: ${CONFIG.IMAGE_FALLBACK_BG};"></div>`,tooltipHTML=addon.tooltip?`<div class="addon-tooltip" style="
90             position: relative;
91             width: 14px;
92             height: 14px;
93             flex-shrink: 0;
94             margin-left: 4px;
95           ">
96             <div class="tooltip-icon" style="
97               width: 14px;
98               height: 14px;
99               border-radius: 50%;
100               border: 1px solid rgba(0, 0, 0, 0.4);
101               display: flex;
102               align-items: center;
103               justify-content: center;
104               font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
105               font-size: 10px;
106               font-weight: 600;
107               color: rgba(0, 0, 0, 0.6);
108               cursor: help;
109               background: transparent;
110             ">i</div>
111             <div class="tooltip-content" style="
112               display: none;
113               position: absolute;
114               bottom: calc(100% + 8px);
115               left: 50%;
116               transform: translateX(-50%);
117               background: #2d2d2d;
118               color: white;
119               padding: 8px 12px;
120               border-radius: 4px;
121               font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
122               font-size: 11px;
123               line-height: 14px;
124               white-space: nowrap;
125               z-index: 1000;
126               pointer-events: none;
127             ">
128               ${addon.tooltip}
129               <div style="
130                 position: absolute;
131                 top: 100%;
132                 left: 50%;
133                 transform: translateX(-50%);
134                 width: 0;
135                 height: 0;
136                 border-left: 5px solid transparent;
137                 border-right: 5px solid transparent;
138                 border-top: 5px solid #2d2d2d;
139               "></div>
140             </div>
141           </div>`:"",priceText=addon.disabled&&!addon.price.includes("out of stock")?`${addon.price} (out of stock)`:addon.price,disabledAttr=addon.disabled?"disabled":"",curs
141orStyle=addon.disabled?"cursor: not-allowed;":"cursor: pointer;",opacityStyle=addon.disabled?"opacity: 0.5;":"";html+=`
142        <div class="addon-card" data-addon-index="${index}" style="
143          display: flex;
144          flex-direction: row;
145          align-items: center;
146          padding: 0;
147          gap: 8px;
148          width: 100%;
149          height: 40px;
150          ${cursorStyle}
151          ${opacityStyle}
152        ">
153          <div class="addon-checkbox-wrapper" style="
154            width: 16px;
155            height: 16px;
156            flex-shrink: 0;
157          ">
158            <input
159              type="checkbox"
160              class="addon-checkbox-new"
161              data-addon-index="${index}"
162              data-original-class="${addon.checkboxClass}"
163              aria-label="Select ${addon.title}"
164              ${disabledAttr}
165            />
166          </div>
167
168          <div class="addon-image" style="flex-shrink: 0;">
169            ${imageHTML}
170          </div>
171
172          <div class="addon-info" style="
173            display: flex;
174            flex-direction: column;
175            justify-content: center;
176            align-items: flex-start;
177            padding: 0;
178            gap: 4px;
179            flex-grow: 1;
180          ">
181            <div style="
182              display: flex;
183              align-items: center;
184              gap: 4px;
185            ">
186              <div class="addon-name" style="
187                font-family: 'AktivGrotesk-Medium', 'Aktiv Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
188                font-style: normal;
189                font-weight: 400;
190                font-size: 14.4px;
191                line-height: 17px;
192                color: #000000;
193              ">
194                ${addon.title}
195              </div>
196              ${tooltipHTML}
197            </div>
198            <div class="addon-price" style="
199              font-family: 'Aktiv Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
200              font-style: normal;
201              font-weight: 400;
202              font-size: 12px;
203              line-height: 15px;
204              color: rgba(0, 0, 0, 0.6);
205            ">
206              ${priceText}
207            </div>
208          </div>
209        </div>
210      `}),html+="</div>",html}function replaceUI(container,newHTML,addons,systemType){if(systemType==="bold"){addons.forEach(addon=>{addon.element.style.display="none"});const boldTotal=container.querySelector(".bold_option_total");boldTotal&&(boldTotal.style.display="none"),container.insertAdjacentHTML("afterend",newHTML)}else systemType==="native"&&(container.classList.add("nominal-hidden"),container.insertAdjacentHTML("afterend",newHTML));return document.querySelector(".nominal-addon-redesign")}function bindEventListeners(addons,newContainer){addons.forEach((addon,index)=>{const newCheckbox=newContainer.querySelector(`.addon-checkbox-new[data-addon-index="${index}"]`),cardElement=newContainer.querySelector(`.addon-card[data-addon-index="${index}"]`);if(!newCheckbox||!addon.checkbox)return;newCheckbox.checked=addon.checkbox.checked,newCheckbox.addEventListener("change",e=>{addon.checkbox.checked=e.target.checked;
210const changeEvent=new Event("change",{bubbles:!0});addon.checkbox.dispatchEvent(changeEvent);const clickEvent=new Event("click",{bubbles:!0});addon.checkbox.dispatchEvent(clickEvent)}),addon.disabled||cardElement.addEventListener("click",e=>{e.target===newCheckbox||e.target.closest(".addon-tooltip")||e.target.closest(".tooltip-icon")||newCheckbox.click()}),addon.checkbox.addEventListener("change",()=>{newCheckbox.checked=addon.checkbox.checked});const tooltipIcon=cardElement.querySelector(".tooltip-icon"),tooltipContent=cardElement.querySelector(".tooltip-content");tooltipIcon&&tooltipContent&&(tooltipIcon.addEventListener("mouseenter",()=>{tooltipContent.style.display="block"}),tooltipIcon.addEventListener("mouseleave",()=>{tooltipContent.style.display="none"}),tooltipIcon.addEventListener("click",e=>{e.stopPropagation();const isVisible=tooltipContent.style.display==="block";tooltipContent.style.display=isVisible?"none":"block"}))})}function init(){if(document.querySelector(".nominal-addon-redesign")){console.log("[Nominal A/B Test] Already initialized");return}injectCustomCSS(),waitForAddons(async system=>{try{let addons;if(system.type==="bold")addons=extractBoldAddons(system.container);else if(system.type==="native")addons=extractNativeAddons(system.container);else{console.log("[Nominal A/B Test] No add-ons found on this product");return}if(addons.length===0){console.log("[Nominal A/B Test] No add-ons extracted");return}console.log(`[Nominal A/B Test] Found ${addons.length} add-ons using ${system.type} system`),await fetchProductImages(addons);const newHTML=generateNewHTML(addons),newContainer=replaceUI(system.container,newHTML,addons,system.type);bindEventListeners(addons,newContainer),console.log(`[Nominal A/B Test] Redesign applied successfully (${addons.length} add-ons, ${system.type} system)`)}catch(error){console.error("[Nominal A/B Test] Error during initialization:",error),system.type==="bold"?extractBoldAddons(system.container).forEach(addon=>{addon.element&&(addon.element.style.display="")}):system.type==="native"&&system.container.classList.remove("nominal-hidden")}})}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",init):init()})();
211//# sourceMappingURL=/cdn/shop/t/417/assets/ab19.js.map?v=80213340448644581111790728175

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.