PageSourceSearch

https://www.kotmo.ca/cdn/shop/t/4/assets/kotmo-qty-rules.js?v=80383753823824659351759693403

js kotmo.ca collected 2026-10-06 19:33:06 UTC 5,234 bytes, 2 lines download raw bytes

1(()=>{const qtyInputs=()=>Array.from(document.querySelectorAll('input[name="quantity"]')),addButtons=()=>Array.from(document.querySelectorAll('form[action^="/cart/add"] [type="submit"], button[name="add"], button.add-to-cart')),plusButtons=()=>Array.from(document.querySelectorAll('button[aria-label*="Augmenter"], button[aria-label*="Increase"], button[name="plus"], .quantity__button--plus, [data-action*="increase"], [data-action*="plus"]')),minusButtons=()=>Array.from(document.querySelectorAll('button[aria-label*="Diminuer"], button[aria-label*="Decrease"], button[name="minus"], .quantity__button--minus, [data-action*="decrease"], [data-action*="minus"]')),rulesEl=document.getElementById("kotmo-qty-rules");if(!rulesEl)return;let rules;try{rules=JSON.parse(rulesEl.textContent)}catch{rules={}}const MIN=Number.isInteger(rules.min)?rules.min:1,STEP=Number.isInteger(rules.step)?rules.step:1,MAX=Number.isInteger(rules.max)?rules.max:null,listeners=[],on=(el,ev,fn,opts)=>{el&&(el.addEventListener(ev,fn,opts),listeners.push(()=>el.removeEventListener(ev,fn,opts)))},cleanup=()=>{for(;listeners.length;)try{listeners.pop()()}catch{}},showMessage=(msg,type="error")=>{let box=document.querySelector("#kotmo-qty-message");box||(box=document.createElement("div"),box.id="kotmo-qty-message",box.style.marginTop="8px",box.style.fontSize="0.9rem",box.style.lineHeight="1.3",box.style.padding="8px 10px",box.style.borderRadius="8px",box.style.border="1px solid",(qtyInputs()[0]?.closest("form")||document.body).appendChild(box)),box.textContent=msg,type==="error"?(box.style.background="#fff3f3",box.style.borderColor="#f2b3b3",box.style.color="#8a1f1f"):(box.style.background="#f2fff3",box.style.borderColor="#b3f2b9",box.style.color="#1f8a36")},clearMessage=()=>{const b=document.querySelector("#kotmo-qty-message");b&&b.remove()},applyNativeAttrs=input=>{input.setAttribute("min",String(MIN)),input.setAttribute("step",String(STEP)),MAX&&input.setAttribute("max",String(MAX))},clampDownToGrid=val=>{let v=parseInt(val,10);isNaN(v)&&(v=MIN),v<MIN&&(v=MIN),MAX&&v>MAX&&(v=MAX);const k=Math.floor((v-MIN)/STEP);return MIN+k*STEP},normalizeOnCommit=input=>{const commit=()=>{const before=input.value,after=clampDownToGrid(before);String(after)!==String(before)&&(input.value=after),clearMessage(),MAX&&Number(after)>=MAX&&showMessage(`Quantit\xE9 maximale atteinte : ${MAX} unit\xE9s.`,"info"),qtyInputs().forEach(i=>{i!==input&&(i.value=input.value)})};on(input,"blur",commit),on(input,"change",commit),on(input,"input",()=>{const v=parseInt(input.value,10);clearMessage(),!isNaN(v)&&MAX&&v>MAX&&showMessage(`Maximum ${MAX} unit\xE9s.`,"error")})},prevValueMap=new WeakMap,recordPrevOnPointerDown=btn=>{on(btn,"pointerdown",e=>{const input=btn.closest("form, .product, .product__sticky-cart")?.querySelector('input[name="quantity"]')||qtyInputs()[0];if(!input)return;const prev=clampDownToGrid(input.value);prevValueMap.set(input,prev)},!0)},bindPlusMinus=()=>{plusButtons().forEach(recordPrevOnPointerDown),minusButtons().forEach(recordPrevOnPointerDown),plusButtons().forEach(btn=>{on(btn,"click",()=>{setTimeout(()=>{const input=btn.closest("form, .product, .product__sticky-cart")?.querySelector('input[name="quantity"]')||qtyInputs()[0];if(!input)return;const prev=prevValueMap.get(input);let next=(Number.isInteger(prev)?prev:clampDownToGrid(input.value))+STEP;MAX&&next>MAX&&(next=MAX),input.value=next,input.dispatchEvent(new Event("change",{bubbles:!0})),qtyInputs().forEach(i=>{i!==input&&(i.value=input.value)})},0)},!0)}),minusButtons().forEach(btn=>{on(btn,"click",()=>{setTimeout(()=>{const input=btn.closest("form, .product, .product__sticky-cart")?.querySelector('input[name="quantity"]')||qtyInputs()[0];if(!input)return;const prev=prevValueMap.get(input);let next=(Number.isInteger(prev)?prev:clampDownToGrid(input.value))-STEP;next<MIN&&(next=MIN),input.value=next,input.dispatchEvent(new Event("change",{bubbles:!0})),qtyInputs().forEach(i=>{i!==input&&(i.value=input.value)})},0)},!0)})},bindATC=()=>{addButtons().forEach(btn=>{on(btn,"click",e=>{clearMessage();const input=btn.closest("form")?.querySelector('input[name="quantity"]')||qtyInputs()[0];if(!input)return;let v=parseInt(input.value,10);if(isNaN(v)||v<MIN){e.preventDefault(),input.value=MIN,showMessage(`Quantit\xE9 minimale : ${MIN} unit\xE9s.`),input.focus();return}if(MAX&&v>MAX){e.preventDefault(),input.value=MAX,showMessage(`Quantit\xE9 maximale autoris\xE9e : ${MAX} unit\xE9s.`,"error"),input.focus();return}if((v-MIN)%STEP!==0){e.preventDefault();const suggestion=clampDownToGrid(v);input.value=suggestion,showMessage(`Merci d\u2019utiliser des incr\xE9ments de ${STEP} (ex.: ${MIN}, ${MIN+STEP}, \u2026). Quantit\xE9 ajust\xE9e : ${suggestion}.`),input.focus();return}})})},initAll=()=>{cleanup();
1const inputs=qtyInputs();inputs.length&&(inputs.forEach(input=>{applyNativeAttrs(input);const start=parseInt(input.value,10);input.value=isNaN(start)?MIN:Math.max(MIN,clampDownToGrid(start)),normalizeOnCommit(input)}),bindPlusMinus(),bindATC())};initAll(),new MutationObserver(()=>initAll()).observe(document.body,{childList:!0,subtree:!0})})();
2//# sourceMappingURL=/cdn/shop/t/4/assets/kotmo-qty-rules.js.map?v=80383753823824659351759693403

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.