@thg-agentic/hyper-personalisation-web 3.24.1 → 3.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -25
- package/dist/hp.min.js +3 -3
- package/dist/onsite-answers.min.js +1 -1
- package/dist/onsite-audiences.min.js +1 -0
- package/dist/onsite-render.min.js +2 -2
- package/dist/onsite-wheel.min.js +1 -1
- package/dist/onsite.min.js +1 -1
- package/package.json +4 -3
- package/src/bootstrap.js +114 -49
- package/src/consent-gate.js +8 -5
- package/src/context.js +2 -1
- package/src/device-storage.js +4 -4
- package/src/index.js +142 -10
- package/src/onsite/api.js +22 -5
- package/src/onsite/audiences-loader.js +6 -0
- package/src/onsite/counters.js +2 -2
- package/src/onsite/index.js +52 -1
- package/src/onsite/render.js +49 -18
- package/src/onsite/rules.js +20 -5
- package/src/onsite/visit-audiences.js +94 -0
- package/src/onsite/wheel.js +61 -13
- package/src/settings.js +30 -2
- package/src/shape.js +4 -0
- package/src/social-proof.js +5 -1
- package/src/spec-version.js +1 -1
- package/src/storage-scope.js +8 -2
- package/src/transport.js +22 -2
- package/src/version.js +1 -1
- package/src/visit-campaign.js +168 -0
- package/types/bootstrap.d.ts +10 -2
- package/types/events.d.ts +30 -2
- package/types/hp.d.ts +3 -2
- package/types/index.d.ts +10 -3
- package/types/onsite.d.ts +9 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(()=>{async function Re(
|
|
1
|
+
(()=>{async function Re(i,o,a){let l=m=>Array.from(new Uint8Array(m),v=>v.toString(16).padStart(2,"0")).join(""),f=new TextEncoder,x=i.maxnumber??1e6;for(let m=0;m<=x;m+=256){let v=[];for(let T=m;T<m+256&&T<=x;T++)v.push(crypto.subtle.digest("SHA-256",f.encode(i.salt+T)));let h=(await Promise.all(v)).findIndex(T=>l(T)===i.challenge);if(h>=0)return m+h;if(o&&await o(),a?.aborted)return null}return null}var kt=()=>globalThis.scheduler?.postTask?.(()=>{},{priority:"user-visible"})??new Promise(i=>setTimeout(i,0)),De=(i,o)=>Re(i,kt,o);function At(i,o){let a=`onmessage=async(e)=>postMessage(await(${Re})(e.data))`,l=URL.createObjectURL(new Blob([a],{type:"text/javascript"}));return new Promise((f,x)=>{let m;try{m=new Worker(l)}catch(h){URL.revokeObjectURL(l),x(h);return}let v=(h,T)=>{m.terminate(),URL.revokeObjectURL(l),h(T)};o.addEventListener("abort",()=>v(f,null),{once:!0}),m.onmessage=h=>v(f,h.data),m.onerror=h=>{h.preventDefault?.(),v(x,h)},m.postMessage({challenge:i.challenge,salt:i.salt,maxnumber:i.maxnumber})})}var St=i=>i&&i.algorithm==="SHA-256"&&typeof i.challenge=="string"&&typeof i.salt=="string"&&typeof i.signature=="string";async function Ue(i){if(!St(i)||typeof crypto?.subtle?.digest!="function")return null;let o=Date.now(),a=new AbortController,l,f=new Promise(v=>{l=setTimeout(()=>v(null),2e4)}),x=typeof Worker=="function"&&typeof Blob=="function"?At(i,a.signal).catch(()=>De(i,a.signal)):De(i,a.signal),m=await Promise.race([x,f]);return clearTimeout(l),a.abort(),typeof m!="number"?null:btoa(JSON.stringify({algorithm:i.algorithm,challenge:i.challenge,number:m,salt:i.salt,signature:i.signature,took:Date.now()-o}))}function B(i){if(typeof i!="string")return null;try{let o=new URL(i);return o.protocol==="https:"?o.href:null}catch{return null}}function G(i){if(typeof i!="string")return null;try{let o=new URL(i);return o.protocol==="https:"||o.protocol==="http:"&&o.hostname==="localhost"?o.href:null}catch{return null}}function pe(i,o,a,l){if(typeof o=="string"){i.append(a.createTextNode(o));return}if(l>6||o===null)return;if(Array.isArray(o)){for(let v of o)pe(i,v,a,l+1);return}if(typeof o!="object")return;let f="b"in o?"b":"i"in o?"i":"a"in o?"a":null;if(!f)return;let x=f==="a"?B(o.href):null,m=f==="a"&&!x?i:a.createElement(f);x&&(m.setAttribute("href",x),m.setAttribute("rel","noopener")),pe(m,o[f],a,l+1),m!==i&&i.append(m)}function We(i,o=document){let a=o.createDocumentFragment();return pe(a,i,o,0),a}var Et=i=>i!==null&&typeof i=="object"&&!Array.isArray(i);function V(i,o,a){if(typeof a=="string"&&a)return a;let l=Et(i?.next?.answers)?i.next.answers:{};for(let[f,x]of Object.entries(l)){let m=f.indexOf("="),v=o[f.slice(0,m)];if(m>0&&v?.includes(f.slice(m+1)))return x}return typeof i?.next?.default=="string"?i.next.default:null}var ue={AT:"43",AU:"61",BE:"32",CA:"1",CH:"41",DE:"49",DK:"45",ES:"34",FI:"358",FR:"33",GB:"44",IE:"353",IT:"39",NL:"31",NO:"47",NZ:"64",PL:"48",PT:"351",SE:"46",US:"1"};function Fe(i,o){let a=String(i??"").replace(/[\s.()-]/g,"");a.startsWith("00")&&(a=`+${a.slice(2)}`);let l=ue[String(o??"").toUpperCase()];return!a.startsWith("+")&&l&&(a=`+${l}${a.replace(/^0/,"")}`),/^\+[1-9]\d{7,14}$/.test(a)?a:null}var Ye=i=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(i);function fe(i,o,a,l,f){let x=i("select","i");x.setAttribute("aria-label",o);for(let[m,v]of[["",a],...l]){let h=i("option","",v);h.value=m,h.selected=m===f,x.append(h)}return x}var he={system:"system-ui,sans-serif","neo-grotesque":'Inter,Roboto,"Helvetica Neue",Arial,sans-serif',humanist:'Seravek,"Gill Sans Nova",Ubuntu,Calibri,sans-serif',"geometric-humanist":"Avenir,Montserrat,Corbel,sans-serif","rounded-sans":'ui-rounded,Quicksand,Comfortaa,"Arial Rounded MT Bold",sans-serif',industrial:'Bahnschrift,"DIN Alternate","Franklin Gothic Medium",sans-serif',transitional:'Charter,"Sitka Text",Cambria,serif',"old-style":'"Iowan Old Style","Palatino Linotype",P052,serif',didone:'Didot,"Bodoni MT","Noto Serif Display",serif',"slab-serif":'Rockwell,"Roboto Slab","DejaVu Serif",serif'},me="cubic-bezier(.16,1,.3,1)",be=`
|
|
2
2
|
:host{all:initial}
|
|
3
3
|
:host([preview]){display:block;position:relative;width:100%;height:100%;overflow:hidden;contain:layout paint}
|
|
4
4
|
.s{--a:var(--hp-accent,#111);--b:var(--hp-bg,#fff);--t:var(--hp-text,#111);--r:var(--hp-radius,16px);--oa:var(--hp-on-accent,#fff);--ot:var(--hp-on-accent-text,var(--oa));--fl:var(--hp-field,color-mix(in srgb,var(--t) 5%,var(--b)));--ft:var(--hp-field-text,var(--t));--mu:color-mix(in srgb,var(--t) 74%,var(--b));--ln:color-mix(in srgb,var(--t) 18%,var(--b));--br:min(12px,var(--r));--ir:min(var(--r),8px);font:16px/1.5 var(--hp-font,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif);color:var(--t);-webkit-font-smoothing:antialiased;box-sizing:border-box;margin:0;padding:0;border:0;background:none;max-width:none;max-height:none;overflow:visible}
|
|
@@ -179,4 +179,4 @@ hr{width:100%;margin:4px 0;border:0;border-top:1px solid var(--ln)}
|
|
|
179
179
|
@keyframes hp-down{from{transform:translateY(-100%)}}
|
|
180
180
|
@keyframes hp-out{to{opacity:0;transform:translateY(12px) scale(.98)}}
|
|
181
181
|
@media (prefers-reduced-motion:reduce){.s .w,.s .ov,.f>*{animation:hp-fade .15s ease both!important}.btn{transition:none}}
|
|
182
|
-
`;var $t=/^#(?:[0-9a-f]{3}){1,2}$/i,He=["popup","fullscreen"],_t=["ok","verify","already"],zt=["stack","split","overlay","image-only"],Be={bar:["bottom","top"],sheet:["bottom-right","bottom-left","bottom"],teaser:["bottom-left","bottom-right","left","right"]},Tt=["xs","sm","md","lg","xl","display"],Ge={xs:4,sm:8,md:16,lg:28,xl:44},Ve={sm:380,md:480,lg:880},Ct=["left","center","right"],Xe={secondary:" b2",link:" no"},Lt=220,Mt=["verify","already","held","fallback","success"],Ot=[500,1500],It=600,Pt=1800,$={email:"Enter a valid email address.",phone:"Enter a valid mobile number.",intl:"Enter a valid mobile number, starting with + and its country code.",consent:"Tick the box to sign up.",failed:"Something went wrong. Please try again.",busy:"Too many tries. Please wait a moment and try again.",spin:"Sign up first to spin.",yours:"Your code",held:"You\u2019re already on the list",resent:"We\u2019ve sent your code to your inbox again.",inbox:"Your code is in the email we sent when you first signed up.",shop:"Continue shopping",closeOffer:"Close offer",code:"We\u2019ll email your code to you.",claimed:"This email address has already claimed its code.",copied:"Copied",thanks:"Thanks, you\u2019re signed up",unavailable:"Not available right now.",won:"You won"},P=n=>n!==null&&typeof n=="object"&&!Array.isArray(n),F=(n,o,a)=>n.includes(o)?o:a,I=(n,o,a)=>Number.isFinite(n)?Math.min(Math.max(n,o),a):null,ge=n=>n.type==="date"||n.type==="number",xe=()=>globalThis.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches===!0;function Ze(n){let o=P(n.layout)?n.layout:{},a=P(o.media)?o.media:null,l=F(zt,o.template,"stack"),u=a?.side==="right"?"right":"left";return!a&&P(o.image)&&o.image.side!=="none"&&(a=o.image,o.image.side!=="top"&&(l="split",u=o.image.side==="right"?"right":"left")),(!a||!G(a.asset))&&(a=null,l="stack"),{template:l,media:a,side:u,scrim:I(o.scrim,0,.9)??.45,padding:F(["none","sm","md","lg"],o.padding,"md")}}var Je=(n,o)=>F(["sm","md","lg","full"],n.size,null)??(o.template==="split"||o.template==="overlay"?"lg":"md"),Qe=(n,o,a=1)=>{if(n==="fullscreen"||o==="full")return"100vw";let l=n==="teaser"?220:n==="sheet"?400:Ve[o]??Ve.md;return`(max-width: 639px) 100vw, ${Math.round(l*a)}px`},et=(n,o,a)=>{if(!a||!Array.isArray(n.widths)||!o.endsWith(".webp"))return null;let l=n.widths.filter(u=>Number.isInteger(u)&&u>0&&u<a).sort((u,x)=>u-x);return l.length===0?null:[...l.map(u=>`${o.slice(0,-5)}-${u}w.webp ${u}w`),`${o} ${a}w`].join(",")},tt=(n,o)=>n.format==="inline"?"inline":o.format&&o.format!=="inline"?o.format:n.format,ve=n=>n.kind==="video"||n.t==="video",we={webm:"video/webm",mp4:"video/mp4",m4v:"video/mp4",mov:"video/quicktime",ogv:"video/ogg"},ke=n=>we[n.split(/[?#]/)[0].split(".").pop().toLowerCase()],Nt=(n,o)=>[...new Set([o,...Array.isArray(n.sources)?n.sources.map(G):[]])].filter(Boolean).sort((a,l)=>(ke(l)===we.webm)-(ke(a)===we.webm));function Ae(n,o,a,l=new Set){let u=o.steps.find(k=>k.id===a);if(!u)return Promise.resolve();let x=tt(n,o),m=Ze(u),v=x==="teaser"&&u===o.steps[0],h=x==="teaser"&&!v?"popup":x,T=Je(o.theme,m),S=v?[]:u.blocks.filter(k=>P(k)&&(k.t==="image"||k.t==="video")).map(k=>[k,1]);return m.media&&!(v&&m.template!=="image-only")&&S.push([m.media,m.template==="split"?.46:1]),Promise.all(S.map(([k,Z])=>{let C=G(ve(k)?k.poster:k.asset);if(!C)return null;let L=document.createElement("img"),U=!ve(k)&&et(k,C,I(k.width,1,8192));U&&(L.sizes=Qe(h,T,Z),L.srcset=U);let y=`${L.sizes} ${U||C}`;return l.has(y)?null:(l.add(y),L.src=C,L.decode?.().catch(()=>{}))}))}var jt=0,ye=n=>`hp-${n}-${++jt}`;function f(n,o,a){let l=document.createElement(n);return o&&(l.className=o),a!==void 0&&l.append(We(a)),l}var nt=n=>{let o=n.length===4?[...n.slice(1)].map(a=>a+a).join(""):n.slice(1);return[0,2,4].map(a=>Number.parseInt(o.slice(a,a+2),16))},K=n=>{let[o,a,l]=nt(n).map(u=>{let x=u/255;return x<=.04045?x/12.92:((x+.055)/1.055)**2.4});return .2126*o+.7152*a+.0722*l},Ke=(n,o=4.5)=>{let a=K(n)+.05,l=1.05/a;return l>=o?"#fff":a/(K("#111")+.05)>l?"#111":"#fff"};function qt(n,o){let a=(S,k)=>k&&n.style.setProperty(S,k),l=S=>$t.test(S??"")?S:null;a("--hp-accent",l(o.accent)),a("--hp-bg",l(o.background)),a("--hp-text",l(o.text));let u=I(o.radius,0,48);u!==null&&a("--hp-radius",`${u}px`),a("--hp-font",o.font==="site"?n.ownerDocument.defaultView.getComputedStyle(n.parentElement??n.ownerDocument.body).fontFamily:Object.hasOwn(he,o.font)?he[o.font]:null),l(o.accent)&&(a("--hp-on-accent",Ke(o.accent,3)),a("--hp-on-accent-text",Ke(o.accent))),l(o.background)&&K(o.background)<.18&&(a("--hp-field","#fff"),a("--hp-field-text","#111"));let x=P(o.overlay)?o.overlay:{},m=[l(o.text),l(o.background)].filter(Boolean).sort((S,k)=>K(S)-K(k))[0],[v,h,T]=nt(l(x.colour)??m??"#0b0b0f");a("--hp-ov",`rgb(${v} ${h} ${T} / ${I(x.opacity,0,1)??.6})`)}function Dt(n){try{let o=new CSSStyleSheet;o.replaceSync(be),n.adoptedStyleSheets=[o]}catch{let o=document.createElement("style");o.textContent=be,n.append(o)}}function it(n){let{form:o,creative:a,decision:l,access:u,client:x,api:m,counters:v}=n,h=n.preview??null,T=e=>{let t=e.blocks.find(r=>r?.t==="email"||r?.t==="phone");return t?.t==="phone"||!t&&!o.consent_text?o.sms_consent_text:o.consent_text},S=tt(o,a),k=a.theme,Z=F(Be[S]??[],k.position,Be[S]?.[0]),C=document.createElement("hp-onsite");h&&C.setAttribute("preview",""),qt(C,k);let L=C.attachShadow({mode:"open"});Dt(L);let U=S!=="inline"&&!h,y=f(U?"dialog":S==="inline"?"section":"div"),Se=f("div","ov"),E=f("div","w"),W=f("div","m");h&&(W.dataset.hpMedia="");let Ee=()=>{let e=f("form","f");return e.noValidate=!0,e.addEventListener("submit",gt),e.addEventListener("input",()=>{M.textContent&&A.getAttribute("aria-busy")!=="true"&&N(null)}),e},A=Ee(),M=f("p","e");M.id=ye("err"),M.setAttribute("aria-live","assertive");let ie=f("div","hd");ie.setAttribute("aria-hidden","true"),E.append(ie,W,A),y.append(Se,E);let Y=S!=="inline"&&f("button","x");Y&&(Y.type="button",Y.addEventListener("click",()=>j("close")),E.append(Y),Se.addEventListener("click",()=>j("close"))),L.append(y);let i={step:null,mode:S,size:"md",modal:!1,mobile:h?.device==="mobile",answers:{},sent:new Set,receipt:null,claim:n.coupon?{code:n.coupon}:h?{code:"WELCOME15"}:null,claiming:null,touched:!1,toWarm:new Set,warmed:n.warmed??new Set,warming:!1,owed:!1,submitted:!1,pow:null,wheel:null,timers:[],codes:[],videos:[],closed:!1},J=o.locale||document.documentElement.lang,ot=o.questions.some(ge)&&n.loadAnswers().catch(()=>null),$e=new WeakMap,_e=()=>{let e=P(k.button)?k.button:{};y.className=`s ${i.mode}${Z?` p-${Z}`:""} b-${F(["pill","rounded","square"],e.shape,"rounded")}${i.mobile?" mob":""}${h?" pv":""}${e.style==="outline"?" bo":""}${k.caps===!0?" uc":""}`},X=h?null:globalThis.matchMedia?.("(max-width: 639px)"),ze=()=>{i.mobile=X?.matches===!0,ae(),_e()};X&&(i.mobile=X.matches===!0,X.addEventListener?.("change",ze));let Q=(e,t)=>{h||!l.decision_id||!u.analytics()||x.slotEngaged({decision_id:l.decision_id,slot:l.slot,arm:l.arm??null,engagement:e,step:t??i.step?.id??null})},N=(e,t)=>{M.className="e",M.textContent=e??"";for(let s of A.querySelectorAll("[aria-invalid]"))s.removeAttribute("aria-invalid");let r=t?.closest("label,fieldset");r&&A.contains(r)?r.after(M):A.append(M),t&&(t.setAttribute("aria-invalid","true"),t.setAttribute("aria-describedby",M.id),t.focus(),t.scrollIntoView?.({block:"nearest"}))},H=e=>{A.setAttribute("aria-busy",String(e));for(let t of A.querySelectorAll("button"))t.disabled=e},Te=()=>h?Promise.resolve(null):(i.pow??(i.pow=m.challenge().then(e=>e.status===200?Ue(e.body):null).catch(()=>null)),i.pow),oe=()=>{for(let e of i.videos)try{document.hidden||xe()?e.pause():e.play()?.catch?.(()=>{})}catch{}};document.addEventListener("visibilitychange",oe);function Ce(e,t,r,s){let c=G(e.asset);if(!c)return null;let p=typeof e.alt=="string"?e.alt:"",d;if(ve(e)){d=f("video",t),d.muted=d.defaultMuted=d.loop=d.playsInline=!0;let w=G(e.poster);w&&(d.poster=w),p?d.setAttribute("aria-label",p):d.setAttribute("aria-hidden","true"),d.autoplay=!xe()&&!h,d.preload=d.autoplay?"auto":w?"none":"metadata";let _=Nt(e,c);if(_.length>1)for(let z of _){let O=f("source");O.src=z;let q=ke(z);q&&(O.type=q),d.append(O)}else d.src=c;i.videos.push(d)}else{d=f("img",t),d.alt=p,d.decoding="async",d.loading=r?"eager":"lazy",r&&(d.fetchPriority="high");let w=et(e,c,I(e.width,1,8192));w&&(d.sizes=Qe(i.mode,i.size,s),d.srcset=w),d.src=c}let b=I(e.width,1,8192),g=I(e.height,1,8192);return b&&g&&(d.width=b,d.height=g),P(e.focal)&&(d.style.objectPosition=`${I(e.focal.x,0,100)??50}% ${I(e.focal.y,0,100)??50}%`),d}function Le(e,t){let r=f("label","l");r.append(f("span","lt",e.label??(t==="email"?"Email":"Mobile number")));let s=f("input","i");if(s.type=t,s.name=t==="email"?"email":"phone",s.autocomplete=s.inputMode=t,s.required=!0,typeof e.placeholder=="string"&&(s.placeholder=e.placeholder),s.setAttribute("aria-describedby",M.id),s.addEventListener("focus",Te,{once:!0}),t!=="tel")return r.append(s),r;s.id=`${M.id}-p`,r.htmlFor=s.id;let c=fe(f,"Country code","+",Object.entries(ue).map(([d,b])=>[d,`${d} +${b}`]),(e.country||/-(\w\w)$/.exec(J)?.[1]||"").toUpperCase());c.classList.add("cc"),c.name="hp_cc";let p=f("span","ph");return p.append(c,s),r.append(p),r}function rt(e){let t=o.questions.find(b=>b.key===e.question);if(!t||!ge(t)&&!Array.isArray(t.options))return null;let r=f("fieldset");r.dataset.question=t.key,r.append(f("legend","",t.label??""));let s=i.answers[t.key]??[];if(ge(t))return ot.then(b=>{if(!b)return r.append(f("p","sm",$.unavailable));b.answerFields(r,t,f,s[0],J),$e.set(r,()=>b.answerOf(r,t,void 0,J))}),r;let c=t.type==="many",p=F(["list","dropdown"],e.display,"buttons"),d=t.options.filter(b=>P(b)&&typeof b.value=="string");if(p==="dropdown"&&!c)return r.append(fe(f,t.label??t.key,"\u2014",d.map(b=>[b.value,b.label??b.value]),s[0])),r;p!=="buttons"&&(r.className="ls");for(let b of d){let g=f("input");g.value=b.value,g.checked=s.includes(b.value),g.type=c?"checkbox":"radio",g.name=`q_${t.key}`;let w=f("label",p==="buttons"?"ch":"c");w.append(g,f("span","",b.label??b.value)),r.append(w)}return r}function re(){for(let e of i.codes){if(e.replaceChildren(),!i.claim)continue;if(!i.claim.code){e.append(f("p","",i.claim.held?$.claimed:$.code));continue}i.claim.prize?.label&&e.append(f("p","",i.claim.prize.label));let t=f("output");t.textContent=i.claim.code;let r=f("button","btn cp");r.type="button",r.textContent="Copy code",r.addEventListener("click",()=>{navigator.clipboard?.writeText(i.claim.code).then(()=>{r.textContent=$.copied}).catch(()=>{})}),e.append(t,r)}}function st(e){if(i.claim&&!i.claim.code)return null;let t=f("p","tm");t.setAttribute("role","timer");let r=Date.now()+Math.max(0,Number(e.minutes)||0)*6e4,s=()=>{let p=Math.max(0,Math.round((r-Date.now())/1e3));t.textContent=`${String(Math.floor(p/60)).padStart(2,"0")}:${String(p%60).padStart(2,"0")}`,p===0&&clearInterval(c)},c=setInterval(s,1e3);return i.timers.push(c),s(),t}function se(e,t){return e.href=t,e.rel="noopener",e.addEventListener("click",r=>{h?r.preventDefault():Q("click")}),e}function at(e){let t=e.label??"Continue";if(e.action==="link"){let s=B(e.href);return s?se(f("a",`btn${Xe[e.variant]??""}`,t),s):null}let r=f("button",`btn${Xe[e.variant]??""}`,t);return r.type=e.action==="submit"?"submit":"button",r.type==="button"?r.addEventListener("click",()=>te(e)):r.dataset.submit="",r}let Me=(e,t,r)=>(e.classList.add(`x-${F(Tt,t.size,r)}`),Ct.includes(t.align)&&e.classList.add(`a-${t.align}`),e);function Oe(e){let t=Ce(e,"bi",i.step===a.steps[0]);if(!t)return null;t.style.objectFit=e.fit==="contain"?"contain":"cover";let r=I(e.radius,0,48);r!==null&&(t.style.borderRadius=`${r}px`);let s=I(e.max_height,40,800);if(s!==null&&(t.style.maxHeight=`${s}px`),!t.width&&e.fit!=="contain"&&(t.style.aspectRatio="16 / 9"),e.t==="video"||!B(e.href))return t;let c=se(f("a","bl"),B(e.href));return c.append(t),c}function lt(e){let t=I(e.threshold,.01,1e7);if(!t)return null;let r=f("div","pg"),s=f("p"),c=f("div","tr"),p=f("i");c.append(p),c.setAttribute("role","progressbar"),c.setAttribute("aria-valuemin","0"),c.setAttribute("aria-valuemax",String(t)),r.append(s,c);let d=g=>g.toFixed(2);try{let g=new Intl.NumberFormat(J||void 0,{style:"currency",currency:e.currency});d=w=>g.format(w)}catch{}let b=()=>{let g=Math.max(0,Number(n.basket?.())||0),w=t-g;s.textContent=w>0?String(e.text??"").replace("{amount}",d(w)):e.done??"",p.style.transform=`scaleX(${Math.min(1,g/t)})`,c.setAttribute("aria-valuenow",String(Math.min(g,t))),s.id||(s.id=ye("pg")),c.setAttribute("aria-labelledby",s.id)};return b(),i.timers.push(setInterval(b,2e3)),r}let Ie={heading(e){let t=Me(f("h2","",e.text),e,"xl");return t.id=ye("h"),t.tabIndex=-1,y.hasAttribute("aria-labelledby")||(y.setAttribute("aria-labelledby",t.id),i.step===a.steps[0]&&He.includes(i.mode)&&!h&&(t.autofocus=!0)),t},text:e=>Me(f("p","",e.text),e,"md"),email:e=>Le(e,"email"),phone:e=>Le(e,"tel"),consent(e){let t=T(i.step)?.text;if(typeof t!="string")return null;if(e.required!==!0)return f("p","sm",t);let r=f("label","c"),s=f("input");return s.type="checkbox",s.name="hp_consent",s.required=!0,s.setAttribute("aria-describedby",M.id),r.append(s,f("span","",t)),r},question:rt,wheel(){let e=f("div","wh");return n.loadWheel().then(t=>{i.closed||(i.wheel=t.mountWheel(e,o.prizes))}).catch(()=>{}),e},code(){let e=f("div","k");return e.setAttribute("aria-live","polite"),i.codes.push(e),e},button:at,decline(e){let t=f("button","btn no",e.label??"No thanks");return t.type="button",t.addEventListener("click",()=>j("decline")),t},countdown:st,progress:lt,image:Oe,video:Oe,spacer(e){let t=f("div","sp");return t.style.height=`${Ge[e.size]??Ge.md}px`,t.setAttribute("aria-hidden","true"),t},divider:()=>f("hr")};function ct(e,t){W.replaceChildren();let r=e.media,s=!r||t&&e.template!=="image-only";if(i.size=Je(k,e),E.className=`w t-${e.template} z-${i.size}${e.side==="right"?" right":""}${k.text_align==="center"?" ta-center":""}${s?" nm":""}`,E.style.setProperty("--sc",String(e.scrim)),A.className=`f pd-${e.padding}`,W.hidden=s,W.hidden)return;let c=Ce(r,"",i.step===a.steps[0],e.template==="split"?.46:1);if(!c)return;if(c.width?E.style.setProperty("--ar",`${c.width} / ${c.height}`):E.style.removeProperty("--ar"),e.template!=="image-only"){c.tagName==="IMG"&&e.template==="overlay"&&(c.alt=""),W.append(c);return}let p=B(r.href),d=p?se(f("a","io"),p):f("button","io");p||(d.type="button",d.addEventListener("click",()=>te({action:"next"}))),d.append(c),W.append(d)}function dt(e){let t=e.blocks.find(c=>c?.t==="heading"),r=e.blocks.find(c=>c?.t==="button"&&c.action==="next"),s=f("button","tb");return s.textContent=t?f("b","",t.text).textContent:r?.label??"Open",s.type="button",s.addEventListener("click",()=>te(r??{action:"next"})),s}function R(e,t=new Set){let r=a.steps.find(z=>z.id===e);if(!r)return j("done");if(r.kind==="question"&&!h&&!u.analytics()&&!t.has(r.id))return R(V(r,i.answers),t.add(r.id));let s=i.step!==null,c=s&&(L.activeElement||document.activeElement===document.body),p=s&&!h?{mode:i.mode,w:E.offsetWidth,h:E.offsetHeight,top:E.offsetTop}:null;i.step=r,i.touched=!1;for(let z of i.timers.splice(0))clearInterval(z);i.codes=[],i.videos=[],i.wheel=null;let d=S==="teaser"&&r===a.steps[0];if(i.mode=S==="teaser"&&!d?"popup":S,S==="teaser"&&v.tab?.(o.slot,d),Y&&Y.setAttribute("aria-label",d?$.closeOffer:"Close"),_e(),y.removeAttribute("aria-labelledby"),p){let z=Ee();A.replaceWith(z),A=z}let b=Ze(r);ct(b,d);let g=p?.mode==="popup"&&i.mode==="popup";if(ae(),p?.mode===i.mode&&p.h>0&&!g&&(E.style.setProperty("--lw",`${p.w}px`),E.style.setProperty("--lh",`${p.h}px`),E.classList.add("lk")),A.replaceChildren(),d&&b.template!=="image-only")A.append(dt(r));else for(let[z,O]of r.blocks.entries()){let q=P(O)&&Object.hasOwn(Ie,O.t)?Ie[O.t](O):null;q&&(h&&(q.dataset.hpBlock=String(z)),A.append(q))}let w=A.querySelector("[data-submit]"),_=A.querySelector("[name=hp_consent]")?.parentNode;w&&_&&w.before(_),A.hidden=A.childElementCount===0,N(null),re(),y.hasAttribute("aria-labelledby")?y.removeAttribute("aria-label"):y.setAttribute("aria-label",b.media?.alt?.trim?.()||"Sign up"),g&&p.h>0&&pt(p),s&&Pe(),oe(),h||ut(r),c&&A.querySelector("h2")?.focus()}function ae(){E.classList.remove("lk","an"),E.style.removeProperty("--tp")}function pt(e){let t=E.offsetHeight;E.classList.add("an");let r=E.offsetTop,s=Math.max(0,e.top-r);if(s+t>y.clientHeight-2*r)return ae();E.style.setProperty("--tp",`${s}px`)}function ut(e){let t=e.next??{};for(let c of[t.default,...Object.values(P(t.answers)?t.answers:{}),...e.blocks.map(p=>p?.next),...Mt])typeof c=="string"&&i.toWarm.add(c);if(i.warming)return;i.warming=!0;let r=()=>{if(i.warming=!1,!i.closed&&C.isConnected)for(let c of i.toWarm)Ae(o,a,c,i.warmed);i.toWarm.clear()},s=()=>globalThis.requestIdleCallback?requestIdleCallback(r,{timeout:2e3}):setTimeout(r,200);document.readyState==="complete"?s():window.addEventListener("load",s,{once:!0})}function Pe(){if(!U)return;let e=He.includes(i.mode);if(!(y.open&&i.modal===e)){try{y.open&&y.close()}catch{}if(i.modal=e,y.setAttribute("aria-modal",String(e)),e&&typeof y.showModal=="function")y.showModal();else if(typeof y.show=="function"){let t=document.activeElement;y.show(),t&&t!==document.body?t.focus?.({preventScroll:!0}):C.blur()}else y.setAttribute("open","");!e||typeof y.showModal!="function"?y.setAttribute("role","dialog"):y.removeAttribute("role")}}function ft(){for(let e of A.querySelectorAll("fieldset[data-question]")){let t=e.dataset.question,r=$e.get(e)?.()??{};if(r.error)return r;let s=[r.value,...[...e.querySelectorAll("input:checked,select:not([name])")].map(c=>c.value)].filter(Boolean);s.length?i.answers[t]=s:delete i.answers[t]}}function mt(){let e=[];for(let[r,s]of Object.entries(i.answers)){let c=`${r}=${s.join(",")}`;i.sent.has(c)||(i.sent.add(c),e.push({question:r,values:s}))}let t=i.step.id;if(i.signedUp&&e.length)return void m.answers({anonymousId:u.anonymousId(),sessionId:u.sessionId()??void 0,slot:l.slot,arm:l.arm??void 0,decision_id:l.decision_id??void 0,step:t,...i.signedUp,answers:e,context:Ne()});if(u.analytics())for(let r of e)x.preferencesStated({...r,source:"onsite",slot:l.slot,arm:l.arm??null,decision_id:l.decision_id??null,step:t})}let Ne=()=>({page:{url:u.pageUrl()},locale:u.locale()??void 0,consent:u.consent()}),ee=e=>{i.receipt&&!h&&mt(),R(V(i.step,i.answers,e))};async function ht(e){let t=i.step,r=A.querySelector("input[name=email],input[name=phone]");if(!r)return ee(e.next);let s=r.name==="email"?"email":"sms",c=A.querySelector("select[name=hp_cc]")?.value,p=s==="email"?Ye(r.value.trim())?r.value.trim():null:Fe(r.value,c);if(!p)return N(s==="email"?$.email:c?$.phone:$.intl,r);let d=A.querySelector("input[name=hp_consent]");if(d&&!d.checked)return N($.consent,d);N(null),H(!0);let b=await Te();i.pow=null;let g=Object.entries(i.answers).map(([D,de])=>({question:D,values:de})),w=s==="sms"?o.sms_consent_text:o.consent_text,_=await m.capture({anonymousId:u.anonymousId(),sessionId:u.sessionId()??void 0,slot:l.slot,arm:l.arm??void 0,decision_id:l.decision_id??void 0,step:t.id,channel:s,[s==="email"?"email":"phone"]:p,...s==="sms"&&i.signedUp,consent_text:w?{id:w.id,version:w.version}:void 0,...g.length?{answers:g}:{},...b?{altcha:b}:{},context:Ne()}),z=_.body?.status,O=(_.status===202||_.status===200)&&_t.includes(z);if(i.closed&&!O)return;O||H(!1);let q=_.status===400?_.body?.field:null;if(q==="email"||q==="phone")return N($[q],r);if(!O)return N(_.status===429?$.busy:$.failed,r);r.value="";for(let[D,de]of Object.entries(i.answers))i.sent.add(`${D}=${de.join(",")}`);i.receipt=typeof _.body.receipt=="string"?_.body.receipt:null,s==="email"&&(i.signedUp={email:p,email_receipt:i.receipt}),i.submitted=!0,v.submitted(o.slot),Q("submit",t.id),i.owed=s==="email"&&o.prizes.length>0;let ne=i.owed?ce([]):null;ne||n.emit?.("submitted",{coupon:null});let yt=ne&&await Promise.race([ne,new Promise(D=>setTimeout(D,It))]);if(i.closed)return;if(H(!1),yt?.held)return R(le("held"));let vt=z!=="ok"&&a.steps.some(D=>D.id===z);R(vt?z:V(t,i.answers,e.next));let wt=i.step;ne?.then(D=>{D?.held&&!i.closed&&i.step===wt&&!i.touched&&R(le("held"))})}function le(e){if(a.steps.some(r=>r.id===e))return e;let t=e==="fallback"||i.claim?.code?[{t:"heading",text:e==="fallback"?$.thanks:$.yours},{t:"code"}]:[{t:"heading",text:$.held},{t:"text",text:o.code_relayed===!0?$.resent:$.inbox},{t:"button",label:$.shop,action:"close"}];return a.steps.push({id:e,blocks:t}),e}function je(e){i.owed&&(i.owed=!1,n.emit?.("submitted",{coupon:e?{code:e.code??null,prize:e.prize?.key}:null}))}function ce(e=Ot){let t=i.receipt;return i.claiming??(i.claiming=(async()=>{let r;for(let d of[0,...e])if(d&&await new Promise(b=>setTimeout(b,d)),r=await m.claim({slot:l.slot,decision_id:l.decision_id??void 0,receipt:t}),r.status!==503)break;let s=r.body,c=s?.fallback===!0&&!s.code;if(r.status!==200||!(P(s?.prize)||c))return i.claiming=null,!e.length&&r.status===503?ce():(je(null),null);let p=s.held&&!s.code?v.code?.():null;return p&&(s={...s,code:p}),i.claim=s,typeof s.code=="string"&&v.code?.(s.code),je(s),i.closed||re(),s})()),i.claiming}async function bt(e){if(!i.receipt)return N($.spin);H(!0);let t=await ce();if(i.closed)return;if(!t)return H(!1),N($.failed);let r=t.fallback===!0&&!t.code,s=t.held?-1:o.prizes.findIndex(d=>d.key===t.prize?.key&&d.label===t.prize?.label);if(i.wheel&&s>=0&&!r){if(await i.wheel.spinTo(s),i.closed)return;M.className="e won",M.textContent=`${$.won} ${t.prize.label}`,await new Promise(d=>setTimeout(d,Pt))}if(i.closed)return;H(!1);let c=r?"fallback":s<0?"held":null,p=c?le(c):V(i.step,i.answers,e.next);p?R(p):re()}function te(e){if(e.action==="close")return j("close");let t=ft();return h?ee(e.next):t?N(t.error,t.input):e.action==="apply"?(n.emit?.("apply",{coupon:i.claim?.code?{code:i.claim.code}:null}),Q("click"),V(i.step,i.answers,e.next)?ee(e.next):j("done")):e.action==="submit"?ht(e):e.action==="spin"?bt(e):ee(e.next)}function gt(e){if(e.preventDefault(),A.getAttribute("aria-busy")==="true")return;let t=i.step.blocks.find(r=>r?.t==="button"&&r.action==="submit");te(t??{action:"next"})}function qe(){i.closed=!0;for(let t of i.timers.splice(0))clearInterval(t);document.removeEventListener("visibilitychange",oe),X?.removeEventListener?.("change",ze);let e=()=>{try{y.open&&y.close()}catch{}C.remove()};if(!U||xe())return e();y.classList.add("out"),setTimeout(e,Lt)}function j(e){i.closed||h||(!i.submitted&&e!=="done"&&Q("dismiss"),v.closed(o.slot),v.tab?.(o.slot,!1),qe(),n.onClose?.(e))}for(let e of["pointerdown","keydown","input"])y.addEventListener(e,()=>{i.touched=!0});y.addEventListener("cancel",e=>{e.preventDefault(),j("esc")}),y.addEventListener("keydown",e=>{e.key==="Escape"&&!i.modal&&S!=="inline"&&j("esc"),e.key==="Tab"&&i.modal&&xt(e)});function xt(e){let t=[...L.querySelectorAll("input[type=radio]:checked")],r=new Set,s=[...L.querySelectorAll("a[href],button,input,select,textarea")].filter(g=>{if(g.disabled||!g.getClientRects().length)return!1;if(g.type!=="radio")return!0;let w=t.find(_=>_.name===g.name);return(w?g!==w:r.has(g.name))?!1:(r.add(g.name),!0)});if(!s.length)return;let c=e.shiftKey,p=L.activeElement;p?.type==="radio"&&(p=s.find(g=>g.type==="radio"&&g.name===p.name)??p);let d=s.indexOf(p),b=null;if(d<0){let g=w=>p&&p.compareDocumentPosition(w)&4;b=c?s.findLast(w=>!g(w))??s.at(-1):s.find(g)??s[0]}else(c?d===0:d===s.length-1)&&(b=s.at(c?-1:0));b&&(e.preventDefault(),b.focus())}return ie.addEventListener("pointerdown",e=>{if(!i.mobile||i.mode!=="sheet")return;let t=e.clientY,r=0,s=p=>{r=Math.max(0,p.clientY-t),E.style.transform=`translateY(${r}px)`},c=()=>{document.removeEventListener("pointermove",s),document.removeEventListener("pointerup",c),E.style.transform="",r>80&&j("close")};document.addEventListener("pointermove",s),document.addEventListener("pointerup",c)}),R(h?.step&&a.steps.some(e=>e.id===h.step)?h.step:a.steps[0].id),(n.container??document.body).append(C),Pe(),{close:j,root:L,host:C,show:R,step:()=>i.step?.id??null,modal:()=>!i.closed&&i.modal,destroy:()=>!i.closed&&qe()}}window[Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.render")]={mountForm:it,warm:Ae};})();
|
|
182
|
+
`;var _t=/^#(?:[0-9a-f]{3}){1,2}$/i,He=["popup","fullscreen"],$t=["ok","verify","already"],zt=["stack","split","overlay","image-only"],Be={bar:["bottom","top"],sheet:["bottom-right","bottom-left","bottom"],teaser:["bottom-left","bottom-right","left","right"]},Tt=["xs","sm","md","lg","xl","display"],Ge={xs:4,sm:8,md:16,lg:28,xl:44},Ve={sm:380,md:480,lg:880},Ct=["left","center","right"],Xe={secondary:" b2",link:" no"},Lt=220,Mt=["verify","already","held","fallback","success"],Ot=[500,1500],It=600,Pt=1800,A={email:"Enter a valid email address.",phone:"Enter a valid mobile number.",intl:"Enter a valid mobile number, starting with + and its country code.",consent:"Tick the box to sign up.",failed:"Something went wrong. Please try again.",busy:"Too many tries. Please wait a moment and try again.",spin:"Sign up first to spin.",yours:"Your code",held:"You\u2019re already on the list",resent:"We\u2019ve sent your code to your inbox again.",inbox:"Your code is in the email we sent when you first signed up.",shop:"Continue shopping",closeOffer:"Close offer",code:"We\u2019ll email your code to you.",claimed:"This email address has already claimed its code.",copied:"Copied",thanks:"Thanks, you\u2019re signed up",retry:"You\u2019re signed up, but we couldn\u2019t load your prize. Please try again.",unclaimed:"We couldn\u2019t load your prize right now.",unavailable:"Not available right now.",won:"You won"},P=i=>i!==null&&typeof i=="object"&&!Array.isArray(i),F=(i,o,a)=>i.includes(o)?o:a,I=(i,o,a)=>Number.isFinite(i)?Math.min(Math.max(i,o),a):null,ge=i=>i.type==="date"||i.type==="number",xe=()=>globalThis.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches===!0;function Ze(i){let o=P(i.layout)?i.layout:{},a=P(o.media)?o.media:null,l=F(zt,o.template,"stack"),f=a?.side==="right"?"right":"left";return!a&&P(o.image)&&o.image.side!=="none"&&(a=o.image,o.image.side!=="top"&&(l="split",f=o.image.side==="right"?"right":"left")),(!a||!G(a.asset))&&(a=null,l="stack"),{template:l,media:a,side:f,scrim:I(o.scrim,0,.9)??.45,padding:F(["none","sm","md","lg"],o.padding,"md")}}var Je=(i,o)=>F(["sm","md","lg","full"],i.size,null)??(o.template==="split"||o.template==="overlay"?"lg":"md"),Qe=(i,o,a=1)=>{if(i==="fullscreen"||o==="full")return"100vw";let l=i==="teaser"?220:i==="sheet"?400:Ve[o]??Ve.md;return`(max-width: 639px) 100vw, ${Math.round(l*a)}px`},et=(i,o,a)=>{if(!a||!Array.isArray(i.widths)||!o.endsWith(".webp"))return null;let l=i.widths.filter(f=>Number.isInteger(f)&&f>0&&f<a).sort((f,x)=>f-x);return l.length===0?null:[...l.map(f=>`${o.slice(0,-5)}-${f}w.webp ${f}w`),`${o} ${a}w`].join(",")},tt=(i,o)=>i.format==="inline"?"inline":o.format&&o.format!=="inline"?o.format:i.format,ve=i=>i.kind==="video"||i.t==="video",we={webm:"video/webm",mp4:"video/mp4",m4v:"video/mp4",mov:"video/quicktime",ogv:"video/ogg"},ke=i=>we[i.split(/[?#]/)[0].split(".").pop().toLowerCase()],Nt=(i,o)=>[...new Set([o,...Array.isArray(i.sources)?i.sources.map(G):[]])].filter(Boolean).sort((a,l)=>(ke(l)===we.webm)-(ke(a)===we.webm));function Ae(i,o,a,l=new Set){let f=o.steps.find(k=>k.id===a);if(!f)return Promise.resolve();let x=tt(i,o),m=Ze(f),v=x==="teaser"&&f===o.steps[0],h=x==="teaser"&&!v?"popup":x,T=Je(o.theme,m),E=v?[]:f.blocks.filter(k=>P(k)&&(k.t==="image"||k.t==="video")).map(k=>[k,1]);return m.media&&!(v&&m.template!=="image-only")&&E.push([m.media,m.template==="split"?.46:1]),Promise.all(E.map(([k,Z])=>{let C=G(ve(k)?k.poster:k.asset);if(!C)return null;let L=document.createElement("img"),U=!ve(k)&&et(k,C,I(k.width,1,8192));U&&(L.sizes=Qe(h,T,Z),L.srcset=U);let y=`${L.sizes} ${U||C}`;return l.has(y)?null:(l.add(y),L.src=C,L.decode?.().catch(()=>{}))}))}var jt=0,ye=i=>`hp-${i}-${++jt}`;function u(i,o,a){let l=document.createElement(i);return o&&(l.className=o),a!==void 0&&l.append(We(a)),l}var nt=i=>{let o=i.length===4?[...i.slice(1)].map(a=>a+a).join(""):i.slice(1);return[0,2,4].map(a=>Number.parseInt(o.slice(a,a+2),16))},K=i=>{let[o,a,l]=nt(i).map(f=>{let x=f/255;return x<=.04045?x/12.92:((x+.055)/1.055)**2.4});return .2126*o+.7152*a+.0722*l},Ke=(i,o=4.5)=>{let a=K(i)+.05,l=1.05/a;return l>=o?"#fff":a/(K("#111")+.05)>l?"#111":"#fff"};function qt(i,o){let a=(E,k)=>k&&i.style.setProperty(E,k),l=E=>_t.test(E??"")?E:null;a("--hp-accent",l(o.accent)),a("--hp-bg",l(o.background)),a("--hp-text",l(o.text));let f=I(o.radius,0,48);f!==null&&a("--hp-radius",`${f}px`),a("--hp-font",o.font==="site"?i.ownerDocument.defaultView.getComputedStyle(i.parentElement??i.ownerDocument.body).fontFamily:Object.hasOwn(he,o.font)?he[o.font]:null),l(o.accent)&&(a("--hp-on-accent",Ke(o.accent,3)),a("--hp-on-accent-text",Ke(o.accent))),l(o.background)&&K(o.background)<.18&&(a("--hp-field","#fff"),a("--hp-field-text","#111"));let x=P(o.overlay)?o.overlay:{},m=[l(o.text),l(o.background)].filter(Boolean).sort((E,k)=>K(E)-K(k))[0],[v,h,T]=nt(l(x.colour)??m??"#0b0b0f");a("--hp-ov",`rgb(${v} ${h} ${T} / ${I(x.opacity,0,1)??.6})`)}function Dt(i){try{let o=new CSSStyleSheet;o.replaceSync(be),i.adoptedStyleSheets=[o]}catch{let o=document.createElement("style");o.textContent=be,i.append(o)}}function it(i){let{form:o,creative:a,decision:l,access:f,client:x,api:m,counters:v}=i,h=i.preview??null,T=e=>{let t=e.blocks.find(r=>r?.t==="email"||r?.t==="phone");return t?.t==="phone"||!t&&!o.consent_text?o.sms_consent_text:o.consent_text},E=tt(o,a),k=a.theme,Z=F(Be[E]??[],k.position,Be[E]?.[0]),C=document.createElement("hp-onsite");h&&C.setAttribute("preview",""),qt(C,k);let L=C.attachShadow({mode:"open"});Dt(L);let U=E!=="inline"&&!h,y=u(U?"dialog":E==="inline"?"section":"div"),Se=u("div","ov"),_=u("div","w"),W=u("div","m");h&&(W.dataset.hpMedia="");let Ee=()=>{let e=u("form","f");return e.noValidate=!0,e.addEventListener("submit",gt),e.addEventListener("input",()=>{M.textContent&&S.getAttribute("aria-busy")!=="true"&&N(null)}),e},S=Ee(),M=u("p","e");M.id=ye("err"),M.setAttribute("aria-live","assertive");let re=u("div","hd");re.setAttribute("aria-hidden","true"),_.append(re,W,S),y.append(Se,_);let Y=E!=="inline"&&u("button","x");Y&&(Y.type="button",Y.addEventListener("click",()=>j("close")),_.append(Y),Se.addEventListener("click",()=>j("close"))),L.append(y);let n={step:null,mode:E,size:"md",modal:!1,mobile:h?.device==="mobile",answers:{},sent:new Set,receipt:null,claim:i.coupon?{code:i.coupon}:h?{code:"WELCOME15"}:null,claiming:null,touched:!1,toWarm:new Set,warmed:i.warmed??new Set,warming:!1,owed:!1,unclaimed:!1,misses:0,submitted:!1,pow:null,wheel:null,timers:[],codes:[],videos:[],closed:!1},J=o.locale||document.documentElement.lang,ot=o.questions.some(ge)&&i.loadAnswers().catch(()=>null),_e=new WeakMap,$e=()=>{let e=P(k.button)?k.button:{};y.className=`s ${n.mode}${Z?` p-${Z}`:""} b-${F(["pill","rounded","square"],e.shape,"rounded")}${n.mobile?" mob":""}${h?" pv":""}${e.style==="outline"?" bo":""}${k.caps===!0?" uc":""}`},X=h?null:globalThis.matchMedia?.("(max-width: 639px)"),ze=()=>{n.mobile=X?.matches===!0,le(),$e()};X&&(n.mobile=X.matches===!0,X.addEventListener?.("change",ze));let Q=(e,t)=>{h||!l.decision_id||!f.analytics()||x.slotEngaged({decision_id:l.decision_id,slot:l.slot,arm:l.arm??null,engagement:e,step:t??n.step?.id??null})},N=(e,t)=>{M.className="e",M.textContent=e??"";for(let s of S.querySelectorAll("[aria-invalid]"))s.removeAttribute("aria-invalid");let r=t?.closest("label,fieldset");r&&S.contains(r)?r.after(M):S.append(M),t&&(t.setAttribute("aria-invalid","true"),t.setAttribute("aria-describedby",M.id),t.focus(),t.scrollIntoView?.({block:"nearest"}))},H=e=>{S.setAttribute("aria-busy",String(e));for(let t of S.querySelectorAll("button"))t.disabled=e},Te=()=>h?Promise.resolve(null):(n.pow??(n.pow=m.challenge().then(e=>e.status===200?Ue(e.body):null).catch(()=>null)),n.pow),se=()=>{for(let e of n.videos)try{document.hidden||xe()?e.pause():e.play()?.catch?.(()=>{})}catch{}};document.addEventListener("visibilitychange",se);function Ce(e,t,r,s){let c=G(e.asset);if(!c)return null;let p=typeof e.alt=="string"?e.alt:"",d;if(ve(e)){d=u("video",t),d.muted=d.defaultMuted=d.loop=d.playsInline=!0;let w=G(e.poster);w&&(d.poster=w),p?d.setAttribute("aria-label",p):d.setAttribute("aria-hidden","true"),d.autoplay=!xe()&&!h,d.preload=d.autoplay?"auto":w?"none":"metadata";let $=Nt(e,c);if($.length>1)for(let z of $){let O=u("source");O.src=z;let q=ke(z);q&&(O.type=q),d.append(O)}else d.src=c;n.videos.push(d)}else{d=u("img",t),d.alt=p,d.decoding="async",d.loading=r?"eager":"lazy",r&&(d.fetchPriority="high");let w=et(e,c,I(e.width,1,8192));w&&(d.sizes=Qe(n.mode,n.size,s),d.srcset=w),d.src=c}let b=I(e.width,1,8192),g=I(e.height,1,8192);return b&&g&&(d.width=b,d.height=g),P(e.focal)&&(d.style.objectPosition=`${I(e.focal.x,0,100)??50}% ${I(e.focal.y,0,100)??50}%`),d}function Le(e,t){let r=u("label","l");r.append(u("span","lt",e.label??(t==="email"?"Email":"Mobile number")));let s=u("input","i");if(s.type=t,s.name=t==="email"?"email":"phone",s.autocomplete=s.inputMode=t,s.required=!0,typeof e.placeholder=="string"&&(s.placeholder=e.placeholder),s.setAttribute("aria-describedby",M.id),s.addEventListener("focus",Te,{once:!0}),t!=="tel")return r.append(s),r;s.id=`${M.id}-p`,r.htmlFor=s.id;let c=fe(u,"Country code","+",Object.entries(ue).map(([d,b])=>[d,`${d} +${b}`]),(e.country||/-(\w\w)$/.exec(J)?.[1]||"").toUpperCase());c.classList.add("cc"),c.name="hp_cc";let p=u("span","ph");return p.append(c,s),r.append(p),r}function rt(e){let t=o.questions.find(b=>b.key===e.question);if(!t||!ge(t)&&!Array.isArray(t.options))return null;let r=u("fieldset");r.dataset.question=t.key,r.append(u("legend","",t.label??""));let s=n.answers[t.key]??[];if(ge(t))return ot.then(b=>{if(!b)return r.append(u("p","sm",A.unavailable));b.answerFields(r,t,u,s[0],J),_e.set(r,()=>b.answerOf(r,t,void 0,J))}),r;let c=t.type==="many",p=F(["list","dropdown"],e.display,"buttons"),d=t.options.filter(b=>P(b)&&typeof b.value=="string");if(p==="dropdown"&&!c)return r.append(fe(u,t.label??t.key,"\u2014",d.map(b=>[b.value,b.label??b.value]),s[0])),r;p!=="buttons"&&(r.className="ls");for(let b of d){let g=u("input");g.value=b.value,g.checked=s.includes(b.value),g.type=c?"checkbox":"radio",g.name=`q_${t.key}`;let w=u("label",p==="buttons"?"ch":"c");w.append(g,u("span","",b.label??b.value)),r.append(w)}return r}function ee(){for(let e of n.codes){if(e.replaceChildren(),!n.claim){n.unclaimed&&e.append(u("p","",A.unclaimed));continue}if(!n.claim.code){let s=n.claim.held?A.claimed:n.claim.fallback===!0?A.code:n.claim.prize?.label;s&&e.append(u("p","",s));continue}n.claim.prize?.label&&e.append(u("p","",n.claim.prize.label));let t=u("output");t.textContent=n.claim.code;let r=u("button","btn cp");r.type="button",r.textContent="Copy code",r.addEventListener("click",()=>{navigator.clipboard?.writeText(n.claim.code).then(()=>{r.textContent=A.copied}).catch(()=>{})}),e.append(t,r)}}function st(e){if(n.claim&&!n.claim.code)return null;let t=u("p","tm");t.setAttribute("role","timer");let r=Date.now()+Math.max(0,Number(e.minutes)||0)*6e4,s=()=>{let p=Math.max(0,Math.round((r-Date.now())/1e3));t.textContent=`${String(Math.floor(p/60)).padStart(2,"0")}:${String(p%60).padStart(2,"0")}`,p===0&&clearInterval(c)},c=setInterval(s,1e3);return n.timers.push(c),s(),t}function ae(e,t){return e.href=t,e.rel="noopener",e.addEventListener("click",r=>{h?r.preventDefault():Q("click")}),e}function at(e){let t=e.label??"Continue";if(e.action==="link"){let s=B(e.href);return s?ae(u("a",`btn${Xe[e.variant]??""}`,t),s):null}let r=u("button",`btn${Xe[e.variant]??""}`,t);return r.type=e.action==="submit"?"submit":"button",r.type==="button"?r.addEventListener("click",()=>ie(e)):r.dataset.submit="",r}let Me=(e,t,r)=>(e.classList.add(`x-${F(Tt,t.size,r)}`),Ct.includes(t.align)&&e.classList.add(`a-${t.align}`),e);function Oe(e){let t=Ce(e,"bi",n.step===a.steps[0]);if(!t)return null;t.style.objectFit=e.fit==="contain"?"contain":"cover";let r=I(e.radius,0,48);r!==null&&(t.style.borderRadius=`${r}px`);let s=I(e.max_height,40,800);if(s!==null&&(t.style.maxHeight=`${s}px`),!t.width&&e.fit!=="contain"&&(t.style.aspectRatio="16 / 9"),e.t==="video"||!B(e.href))return t;let c=ae(u("a","bl"),B(e.href));return c.append(t),c}function lt(e){let t=I(e.threshold,.01,1e7);if(!t)return null;let r=u("div","pg"),s=u("p"),c=u("div","tr"),p=u("i");c.append(p),c.setAttribute("role","progressbar"),c.setAttribute("aria-valuemin","0"),c.setAttribute("aria-valuemax",String(t)),r.append(s,c);let d=g=>g.toFixed(2);try{let g=new Intl.NumberFormat(J||void 0,{style:"currency",currency:e.currency});d=w=>g.format(w)}catch{}let b=()=>{let g=Math.max(0,Number(i.basket?.())||0),w=t-g;s.textContent=w>0?String(e.text??"").replace("{amount}",d(w)):e.done??"",p.style.transform=`scaleX(${Math.min(1,g/t)})`,c.setAttribute("aria-valuenow",String(Math.min(g,t))),s.id||(s.id=ye("pg")),c.setAttribute("aria-labelledby",s.id)};return b(),n.timers.push(setInterval(b,2e3)),r}let Ie={heading(e){let t=Me(u("h2","",e.text),e,"xl");return t.id=ye("h"),t.tabIndex=-1,y.hasAttribute("aria-labelledby")||(y.setAttribute("aria-labelledby",t.id),n.step===a.steps[0]&&He.includes(n.mode)&&!h&&(t.autofocus=!0)),t},text:e=>Me(u("p","",e.text),e,"md"),email:e=>Le(e,"email"),phone:e=>Le(e,"tel"),consent(e){let t=T(n.step)?.text;if(typeof t!="string")return null;if(e.required!==!0)return u("p","sm",t);let r=u("label","c"),s=u("input");return s.type="checkbox",s.name="hp_consent",s.required=!0,s.setAttribute("aria-describedby",M.id),r.append(s,u("span","",t)),r},question:rt,wheel(){let e=u("div","wh");return i.loadWheel().then(t=>{n.closed||(n.wheel=t.mountWheel(e,o.prizes))}).catch(()=>{}),e},code(){let e=u("div","k");return e.setAttribute("aria-live","polite"),n.codes.push(e),e},button:at,decline(e){let t=u("button","btn no",e.label??"No thanks");return t.type="button",t.addEventListener("click",()=>j("decline")),t},countdown:st,progress:lt,image:Oe,video:Oe,spacer(e){let t=u("div","sp");return t.style.height=`${Ge[e.size]??Ge.md}px`,t.setAttribute("aria-hidden","true"),t},divider:()=>u("hr")};function ct(e,t){W.replaceChildren();let r=e.media,s=!r||t&&e.template!=="image-only";if(n.size=Je(k,e),_.className=`w t-${e.template} z-${n.size}${e.side==="right"?" right":""}${k.text_align==="center"?" ta-center":""}${s?" nm":""}`,_.style.setProperty("--sc",String(e.scrim)),S.className=`f pd-${e.padding}`,W.hidden=s,W.hidden)return;let c=Ce(r,"",n.step===a.steps[0],e.template==="split"?.46:1);if(!c)return;if(c.width?_.style.setProperty("--ar",`${c.width} / ${c.height}`):_.style.removeProperty("--ar"),e.template!=="image-only"){c.tagName==="IMG"&&e.template==="overlay"&&(c.alt=""),W.append(c);return}let p=B(r.href),d=p?ae(u("a","io"),p):u("button","io");p||(d.type="button",d.addEventListener("click",()=>ie({action:"next"}))),d.append(c),W.append(d)}function dt(e){let t=e.blocks.find(c=>c?.t==="heading"),r=e.blocks.find(c=>c?.t==="button"&&c.action==="next"),s=u("button","tb");return s.textContent=t?u("b","",t.text).textContent:r?.label??"Open",s.type="button",s.addEventListener("click",()=>ie(r??{action:"next"})),s}function D(e,t=new Set){let r=a.steps.find(z=>z.id===e);if(!r)return j("done");if(r.kind==="question"&&!h&&!f.analytics()&&!t.has(r.id))return D(V(r,n.answers),t.add(r.id));let s=n.step!==null,c=s&&(L.activeElement||document.activeElement===document.body),p=s&&!h?{mode:n.mode,w:_.offsetWidth,h:_.offsetHeight,top:_.offsetTop}:null;n.step=r,n.touched=!1;for(let z of n.timers.splice(0))clearInterval(z);n.codes=[],n.videos=[],n.wheel=null;let d=E==="teaser"&&r===a.steps[0];if(n.mode=E==="teaser"&&!d?"popup":E,E==="teaser"&&v.tab?.(o.slot,d),Y&&Y.setAttribute("aria-label",d?A.closeOffer:"Close"),$e(),y.removeAttribute("aria-labelledby"),p){let z=Ee();S.replaceWith(z),S=z}let b=Ze(r);ct(b,d);let g=p?.mode==="popup"&&n.mode==="popup";if(le(),p?.mode===n.mode&&p.h>0&&!g&&(_.style.setProperty("--lw",`${p.w}px`),_.style.setProperty("--lh",`${p.h}px`),_.classList.add("lk")),S.replaceChildren(),d&&b.template!=="image-only")S.append(dt(r));else for(let[z,O]of r.blocks.entries()){let q=P(O)&&Object.hasOwn(Ie,O.t)?Ie[O.t](O):null;q&&(h&&(q.dataset.hpBlock=String(z)),S.append(q))}let w=S.querySelector("[data-submit]"),$=S.querySelector("[name=hp_consent]")?.parentNode;w&&$&&w.before($),S.hidden=S.childElementCount===0,N(null),ee(),y.hasAttribute("aria-labelledby")?y.removeAttribute("aria-label"):y.setAttribute("aria-label",b.media?.alt?.trim?.()||"Sign up"),g&&p.h>0&&pt(p),s&&Pe(),se(),h||ut(r),c&&S.querySelector("h2")?.focus()}function le(){_.classList.remove("lk","an"),_.style.removeProperty("--tp")}function pt(e){let t=_.offsetHeight;_.classList.add("an");let r=_.offsetTop,s=Math.max(0,e.top-r);if(s+t>y.clientHeight-2*r)return le();_.style.setProperty("--tp",`${s}px`)}function ut(e){let t=e.next??{};for(let c of[t.default,...Object.values(P(t.answers)?t.answers:{}),...e.blocks.map(p=>p?.next),...Mt])typeof c=="string"&&n.toWarm.add(c);if(n.warming)return;n.warming=!0;let r=()=>{if(n.warming=!1,!n.closed&&C.isConnected)for(let c of n.toWarm)Ae(o,a,c,n.warmed);n.toWarm.clear()},s=()=>globalThis.requestIdleCallback?requestIdleCallback(r,{timeout:2e3}):setTimeout(r,200);document.readyState==="complete"?s():window.addEventListener("load",s,{once:!0})}function Pe(){if(!U)return;let e=He.includes(n.mode);if(!(y.open&&n.modal===e)){try{y.open&&y.close()}catch{}if(n.modal=e,y.setAttribute("aria-modal",String(e)),e&&typeof y.showModal=="function")y.showModal();else if(typeof y.show=="function"){let t=document.activeElement;y.show(),t&&t!==document.body?t.focus?.({preventScroll:!0}):C.blur()}else y.setAttribute("open","");!e||typeof y.showModal!="function"?y.setAttribute("role","dialog"):y.removeAttribute("role")}}function ft(){for(let e of S.querySelectorAll("fieldset[data-question]")){let t=e.dataset.question,r=_e.get(e)?.()??{};if(r.error)return r;let s=[r.value,...[...e.querySelectorAll("input:checked,select:not([name])")].map(c=>c.value)].filter(Boolean);s.length?n.answers[t]=s:delete n.answers[t]}}function mt(){let e=[];for(let[r,s]of Object.entries(n.answers)){let c=`${r}=${s.join(",")}`;n.sent.has(c)||(n.sent.add(c),e.push({question:r,values:s}))}let t=n.step.id;if(n.signedUp&&e.length)return void m.answers({anonymousId:f.anonymousId(),sessionId:f.sessionId()??void 0,slot:l.slot,arm:l.arm??void 0,decision_id:l.decision_id??void 0,step:t,...n.signedUp,answers:e,context:Ne()});if(f.analytics())for(let r of e)x.preferencesStated({...r,source:"onsite",slot:l.slot,arm:l.arm??null,decision_id:l.decision_id??null,step:t})}let Ne=()=>({page:{url:f.pageUrl()},locale:f.locale()??void 0,consent:f.consent()}),te=e=>{n.receipt&&!h&&mt(),D(V(n.step,n.answers,e))};async function ht(e){let t=n.step,r=S.querySelector("input[name=email],input[name=phone]");if(!r)return te(e.next);let s=r.name==="email"?"email":"sms",c=S.querySelector("select[name=hp_cc]")?.value,p=s==="email"?Ye(r.value.trim())?r.value.trim():null:Fe(r.value,c);if(!p)return N(s==="email"?A.email:c?A.phone:A.intl,r);let d=S.querySelector("input[name=hp_consent]");if(d&&!d.checked)return N(A.consent,d);N(null),H(!0);let b=await Te();n.pow=null;let g=Object.entries(n.answers).map(([R,de])=>({question:R,values:de})),w=s==="sms"?o.sms_consent_text:o.consent_text,$=await m.capture({anonymousId:f.anonymousId(),sessionId:f.sessionId()??void 0,slot:l.slot,arm:l.arm??void 0,decision_id:l.decision_id??void 0,step:t.id,channel:s,[s==="email"?"email":"phone"]:p,...s==="sms"&&n.signedUp,consent_text:w?{id:w.id,version:w.version}:void 0,...g.length?{answers:g}:{},...b?{altcha:b}:{},context:Ne()}),z=$.body?.status,O=($.status===202||$.status===200)&&$t.includes(z);if(n.closed&&!O)return;O||H(!1);let q=$.status===400?$.body?.field:null;if(q==="email"||q==="phone")return N(A[q],r);if(!O)return N($.status===429?A.busy:A.failed,r);r.value="";for(let[R,de]of Object.entries(n.answers))n.sent.add(`${R}=${de.join(",")}`);n.receipt=typeof $.body.receipt=="string"?$.body.receipt:null,s==="email"&&(n.signedUp={email:p,email_receipt:n.receipt}),n.submitted=!0,v.submitted(o.slot),Q("submit",t.id),n.owed=s==="email"&&o.prizes.length>0;let oe=n.owed?ce([]):null;oe||i.emit?.("submitted",{coupon:null});let yt=oe&&await Promise.race([oe,new Promise(R=>setTimeout(R,It))]);if(n.closed)return;if(H(!1),yt?.held)return D(ne("held"));let vt=z!=="ok"&&a.steps.some(R=>R.id===z);D(vt?z:V(t,n.answers,e.next));let wt=n.step;oe?.then(R=>{R?.held&&!n.closed&&n.step===wt&&!n.touched&&D(ne("held"))})}function ne(e){if(a.steps.some(s=>s.id===e))return e;let t={t:"button",label:A.shop,action:"close"},r=e==="unclaimed"?[{t:"heading",text:A.thanks},{t:"text",text:A.unclaimed},t]:e==="fallback"||n.claim?.code?[{t:"heading",text:e==="fallback"?A.thanks:A.yours},{t:"code"}]:[{t:"heading",text:A.held},{t:"text",text:o.code_relayed===!0?A.resent:A.inbox},t];return a.steps.push({id:e,blocks:r}),e}function je(e){n.owed&&(n.owed=!1,i.emit?.("submitted",{coupon:e?{code:e.code??null,prize:e.prize?.key}:null}))}function ce(e=Ot){let t=n.receipt;return n.claiming??(n.claiming=(async()=>{n.unclaimed=!1;let r;for(let d of[0,...e])if(d&&await new Promise(b=>setTimeout(b,d)),r=await m.claim({slot:l.slot,decision_id:l.decision_id??void 0,receipt:t}),r.status!==503)break;let s=r.body,c=s?.fallback===!0&&!s.code;if(r.status!==200||!(P(s?.prize)||c))return n.claiming=null,!e.length&&r.status===503?ce():(je(null),n.unclaimed=!0,n.closed||ee(),null);let p=s.held&&!s.code?v.code?.():null;return p&&(s={...s,code:p}),n.claim=s,typeof s.code=="string"&&v.code?.(s.code),je(s),n.closed||ee(),s})()),n.claiming}async function bt(e){if(!n.receipt)return N(A.spin);H(!0);let t=await ce();if(n.closed)return;if(!t)return H(!1),n.misses+=1,n.misses<2?N(A.retry):D(ne("unclaimed"));let r=t.fallback===!0&&!t.code,s=t.held?-1:o.prizes.findIndex(d=>d.key===t.prize?.key&&d.label===t.prize?.label);if(n.wheel&&s>=0&&!r){if(await n.wheel.spinTo(s),n.closed)return;M.className="e won",M.textContent=`${A.won} ${t.prize.label}`,await new Promise(d=>setTimeout(d,Pt))}if(n.closed)return;H(!1);let c=r?"fallback":s<0?"held":null,p=c?ne(c):V(n.step,n.answers,e.next);p?D(p):ee()}function ie(e){if(e.action==="close")return j("close");let t=ft();return h?te(e.next):t?N(t.error,t.input):e.action==="apply"?(i.emit?.("apply",{coupon:n.claim?.code?{code:n.claim.code}:null}),Q("click"),V(n.step,n.answers,e.next)?te(e.next):j("done")):e.action==="submit"?ht(e):e.action==="spin"?bt(e):te(e.next)}function gt(e){if(e.preventDefault(),S.getAttribute("aria-busy")==="true")return;let t=n.step.blocks.find(r=>r?.t==="button"&&r.action==="submit");ie(t??{action:"next"})}function qe(){n.closed=!0;for(let t of n.timers.splice(0))clearInterval(t);document.removeEventListener("visibilitychange",se),X?.removeEventListener?.("change",ze);let e=()=>{try{y.open&&y.close()}catch{}C.remove()};if(!U||xe())return e();y.classList.add("out"),setTimeout(e,Lt)}function j(e){n.closed||h||(!n.submitted&&e!=="done"&&Q("dismiss"),v.closed(o.slot),v.tab?.(o.slot,!1),qe(),i.onClose?.(e))}for(let e of["pointerdown","keydown","input"])y.addEventListener(e,()=>{n.touched=!0});y.addEventListener("cancel",e=>{e.preventDefault(),j("esc")}),y.addEventListener("keydown",e=>{e.key==="Escape"&&!n.modal&&E!=="inline"&&j("esc"),e.key==="Tab"&&n.modal&&xt(e)});function xt(e){let t=[...L.querySelectorAll("input[type=radio]:checked")],r=new Set,s=[...L.querySelectorAll("a[href],button,input,select,textarea")].filter(g=>{if(g.disabled||!g.getClientRects().length)return!1;if(g.type!=="radio")return!0;let w=t.find($=>$.name===g.name);return(w?g!==w:r.has(g.name))?!1:(r.add(g.name),!0)});if(!s.length)return;let c=e.shiftKey,p=L.activeElement;p?.type==="radio"&&(p=s.find(g=>g.type==="radio"&&g.name===p.name)??p);let d=s.indexOf(p),b=null;if(d<0){let g=w=>p&&p.compareDocumentPosition(w)&4;b=c?s.findLast(w=>!g(w))??s.at(-1):s.find(g)??s[0]}else(c?d===0:d===s.length-1)&&(b=s.at(c?-1:0));b&&(e.preventDefault(),b.focus())}return re.addEventListener("pointerdown",e=>{if(!n.mobile||n.mode!=="sheet")return;let t=e.clientY,r=0,s=p=>{r=Math.max(0,p.clientY-t),_.style.transform=`translateY(${r}px)`},c=()=>{document.removeEventListener("pointermove",s),document.removeEventListener("pointerup",c),_.style.transform="",r>80&&j("close")};document.addEventListener("pointermove",s),document.addEventListener("pointerup",c)}),D(h?.step&&a.steps.some(e=>e.id===h.step)?h.step:a.steps[0].id),(i.container??document.body).append(C),Pe(),{close:j,root:L,host:C,show:D,step:()=>n.step?.id??null,modal:()=>!n.closed&&n.modal,destroy:()=>!n.closed&&qe()}}window[Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.render")]={mountForm:it,warm:Ae};})();
|
package/dist/onsite-wheel.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{function
|
|
1
|
+
(()=>{function L(e,i,s=5){let n=360/i;return s*360+(360-(e+.5)*n)}var P="http://www.w3.org/2000/svg",z=4500,S=.22,F=.95,k=.105,v=.082,A=.062,I=.62,O=1.15,N="var(--hp-accent,#111)",R="var(--hp-on-accent,#fff)",g="var(--hp-bg,#fff)",T="var(--hp-text,#111)";function c(e,i={}){let s=document.createElementNS(P,e);for(let[n,o]of Object.entries(i))s.setAttribute(n,String(o));return s}function f(e,i,s,n=1){return e.style.fill=i,n<1&&(e.style.fillOpacity=String(n)),s&&(e.style.stroke=s),e}var j=()=>globalThis.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches===!0,$=(e,i=1)=>{let s=(e-90)*Math.PI/180;return`${(i*Math.cos(s)).toFixed(4)} ${(i*Math.sin(s)).toFixed(4)}`};function B(e,i){let s=F-S,n=2*.35*Math.sin(Math.min(Math.PI/i,Math.PI/2)),o=t=>Math.max(v,Math.min(k,n/(t*O))),a=t=>s/(Math.max(1,t.length)*I),r=[...e.matchAll(/ /g)].map(t=>t.index),u=e.length/2,p=r.length?r.reduce((t,l)=>Math.abs(l-u)<Math.abs(t-u)?l:t):-1,h=p<0?null:[e.slice(0,p),e.slice(p+1)];for(let t of[v,A]){let l=a(e);if(l>=t)return{lines:[e],size:Math.min(o(1),l)};if(h){let m=Math.min(o(2),...h.map(a));if(m>=t)return{lines:h,size:m}}}let d=Math.floor(s/(A*I));return{lines:[`${e.slice(0,d-1).trimEnd()}\u2026`],size:A}}function w(e){let i=F-S,s=n=>n.getComputedTextLength?.()??0;for(let n of e){let o=[...n.querySelectorAll("tspan")],a=Math.max(0,...o.map(s));if(!(a>i))continue;let r=Math.max(A,Number(n.getAttribute("font-size"))*i/a);n.setAttribute("font-size",r.toFixed(4)),o.forEach((u,p)=>{u.setAttribute("y",((p-(o.length-1)/2)*r*O).toFixed(4)),s(u)>i&&(u.setAttribute("textLength",i.toFixed(4)),u.setAttribute("lengthAdjust","spacingAndGlyphs"))})}}function C(e,i){let s=(Array.isArray(i)?i:[]).map(d=>String(d?.label??d?.key??"")),n=s.length;if(n<2)return null;let o=360/n,a=c("svg",{viewBox:"-1.2 -1.2 2.4 2.4",role:"img","aria-label":`Prize wheel: ${s.join(", ")}`}),r=c("g");r.style.transformBox="fill-box",r.style.transformOrigin="50% 50%";let u=[[1,R],[.14,T],[.72,R]];r.append(f(c("circle",{r:1}),g));let p=[],h=[];return s.forEach((d,t)=>{let[l,m]=u[n%2===1&&t===n-1?2:t%2];r.append(f(c("path",{d:`M0 0L${$(t*o)}A1 1 0 ${o>180?1:0} 1 ${$((t+1)*o)}Z`,"stroke-width":.012}),N,g,l));let b=(t+.5)*o-90,x=B(d,n),_=f(c("text",{transform:`rotate(${b}) translate(${(S+F)/2} 0)${b>90&&b<270?" rotate(180)":""}`,"text-anchor":"middle","dominant-baseline":"central","font-size":x.size.toFixed(4),"font-weight":700,"aria-hidden":"true"}),m);x.lines.forEach((E,M)=>{let y=c("tspan",{x:0,y:((M-(x.lines.length-1)/2)*x.size*O).toFixed(4)});y.textContent=E,_.append(y)}),r.append(_),h.push(_);for(let E of[t*o,(t+.5)*o]){let[M,y]=$(E,1.075).split(" ");p.push(f(c("circle",{cx:M,cy:y,r:.024}),g))}}),a.append(f(c("circle",{r:1.13}),T),...p,r,f(c("circle",{r:.17,"stroke-width":.035}),g,T),f(c("circle",{r:.065}),N),f(c("path",{d:"M-0.12 -1.2L0.12 -1.2L0 -0.86Z","stroke-width":.03,"stroke-linejoin":"round"}),N,g)),e.append(a),w(h),globalThis.requestAnimationFrame?.(()=>w(h)),{spinTo(d){let t=j();return r.style.transition=t?"none":`transform ${z}ms cubic-bezier(.15,.7,.1,1)`,r.style.transform=`rotate(${L(d,s.length,t?0:5)}deg)`,t?Promise.resolve():new Promise(l=>{let m=setTimeout(l,z+200);r.addEventListener("transitionend",()=>{clearTimeout(m),l()},{once:!0})})},remove:()=>a.remove()}}window[Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.wheel")]={mountWheel:C};})();
|
package/dist/onsite.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{function P(e){let s=typeof document>"u"?null:document.currentScript?.src;return s?new URL(e,s).href:null}var Y=new Map;function O(e,s){return window[s]?Promise.resolve(window[s]):e?(Y.has(s)||Y.set(s,new Promise((n,r)=>{let d=document.createElement("script");d.src=e,d.async=!0,d.onload=()=>window[s]?n(window[s]):r(new Error(`${e} did not register`)),d.onerror=()=>r(new Error(`${e} did not load`)),document.head.append(d)}).finally(()=>Y.delete(s))),Y.get(s)):Promise.reject(new Error("the CDN script\u2019s own URL is unknown"))}var Ne=P("onsite-answers.min.js"),se=()=>O(Ne,Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.answers"));var ke="/v1/capture/challenge",De=e=>typeof e=="string"&&/^\/v1\/[\w/-]+$/.test(e)?e:ke;function oe(e,s={}){let n=s.fetch??globalThis.fetch?.bind(globalThis),r=s.servingHost??e.servingHost,d=s.gatewayHost??e.gatewayHost;async function o(l,{method:p="GET",body:i,consent:c=!1,timeoutMs:u}={}){if(!n)return{status:0,body:null};try{let w=await n(l,{method:p,credentials:"omit",signal:u&&AbortSignal.timeout?.(u),headers:{accept:"application/json",authorization:`Bearer ${e.writeKey}`,...i?{"content-type":"application/json"}:{},...c?e.headers():{}},...i?{body:JSON.stringify(i)}:{}}),b=null;try{b=await w.json()}catch{}return{status:w.status,body:b}}catch{return{status:0,body:null}}}return{decide:(l,p)=>o(`${r}/v1/decisions`,{method:"POST",consent:!0,body:{slots:[p?{key:l,pin:{experiment:p[0],arm:p[1],split:p[2]}}:l],identifier_value:e.anonymousId()??void 0,session_id:e.sessionId()??void 0,...e.preview?{preview:e.preview}:{}}}),challenge:()=>o(`${d}${De(s.challenge)}`),capture:l=>o(`${d}/v1/capture`,{method:"POST",body:l}),answers:l=>o(`${d}/v1/capture/answers`,{method:"POST",body:l}),claim:l=>o(`${r}/v1/offers/claim`,{method:"POST",consent:!0,body:l,timeoutMs:8e3})}}function ie(e,s){let n={},r=()=>{try{let o=JSON.parse(e().getItem(s));o&&typeof o=="object"&&(n=o)}catch{}return n};return{read:r,write:o=>{let l=r();o(l),n=l;try{e().setItem(s,JSON.stringify(l))}catch{}}}}function re(e,s=()=>!0){let n=ie(()=>sessionStorage,e),r=ie(()=>localStorage,e),d=()=>s()?r:n,o=()=>{let i=r.read();(i.sub!==void 0||i.k!==void 0)&&r.write(c=>{delete c.sub,delete c.k})},l=()=>s()?[n.read(),r.read()]:(o(),[n.read()]),p=(i,c)=>{var u;return i.f??(i.f={}),(u=i.f)[c]??(u[c]={}),i.f[c]};return{seen(i){let c=r.read().f?.[i]??{};return{session:n.read().f?.[i]?.n??0,lifetime:c.n??0,closedAt:c.c??null,submitted:c.s===1}},subscribed:()=>l().some(i=>i.sub===1),shown(i){n.write(c=>{let u=p(c,i);u.n=(u.n??0)+1}),r.write(c=>{let u=p(c,i);u.n=(u.n??0)+1})},closed(i,c=Date.now()){r.write(u=>{p(u,i).c=c})},tab(i,c){n.write(u=>{let w=p(u,i);c?w.t=1:delete w.t})},tabOut:i=>n.read().f?.[i]?.t===1,pin(i,c){if(c===void 0)return n.read().f?.[i]?.p??null;n.write(u=>{p(u,i).p=c})},coveredAt:()=>n.read().o??null,covered(i=Date.now()){n.write(c=>{c.o=i})},code(i,c=Date.now()){if(i===void 0){let[u]=l().flatMap(w=>w.k?[w.k]:[]).sort((w,b)=>b.at-w.at);return u&&c-u.at<14*864e5?u.c:null}return d().write(u=>{u.k={c:i,at:c}}),i},submitted(i){r.write(c=>{p(c,i).s=1}),d().write(c=>{c.sub=1})}}}var Le=P("onsite-render.min.js"),le=()=>O(Le,Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.render"));var de=["popup","sheet","bar","teaser","inline","fullscreen"],Fe=["entry","exit","scroll","time","idle","inline","manual"],q=e=>e!=="inline",pe=e=>e==="popup"||e==="sheet"||e==="fullscreen",S=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),D=e=>Array.isArray(e)?e.filter(s=>typeof s=="string"):[],ce=e=>Number.isInteger(e)&&e>=0?e:null;function fe(e){if(!S(e)||typeof e.slot!="string"||e.slot==="")return null;let s=S(e.trigger)?e.trigger:{};if(!de.includes(e.format)||!Fe.includes(s.type))return null;let n=S(e.caps)?e.caps:{};return{...e,trigger:s,priority:Number.isFinite(e.priority)?e.priority:0,pages:S(e.pages)?e.pages:{},devices:D(e.devices),aliases:D(e.aliases),audiences:D(e.audiences),caps:{per_session:ce(n.per_session),lifetime:ce(n.lifetime),after_close_days:Number.isFinite(n.after_close_days)?n.after_close_days:null},window:S(e.window)?e.window:{},questions:Array.isArray(e.questions)?e.questions.filter(S):[],prizes:Array.isArray(e.prizes)?e.prizes.filter(S):[]}}var We=/^[a-z][a-z0-9_-]{0,31}$/;function he(e){if(!S(e)||e.v!==1||!Array.isArray(e.steps))return null;let s=e.steps.filter(n=>S(n)&&We.test(n.id)&&Array.isArray(n.blocks));return s.length===0?null:{format:de.includes(e.format)?e.format:null,theme:S(e.theme)?e.theme:{},steps:s}}function ae(e,s){let n=String(e).split("*").map(r=>r.replace(/[.+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(`^${n}$`,"i").test(s)}function je(e,s){let n=D(e?.include);return(!n.length||n.some(r=>ae(r,s)))&&!D(e?.exclude).some(r=>ae(r,s))}function we(e){return e.matchMedia?.("(hover: none) and (pointer: coarse)")?.matches===!0||e.innerWidth<768?"mobile":"desktop"}var ue=e=>typeof e=="string"?Date.parse(e):Number.NaN,L=e=>e.trigger.type==="manual";function J(e,s){let n=[];je(e.pages,s.path)||n.push("page not included"),e.devices.length&&!e.devices.includes(s.device)&&n.push(`not on ${s.device}`);let r=ue(e.window.starts_at),d=ue(e.window.ends_at);if((s.now<r||s.now>=d)&&n.push("outside its dates"),e.audiences.length&&(s.personalisation?s.audiences&&!e.audiences.some(p=>s.audiences.includes(p))&&n.push("not in its audiences"):n.push("audiences need personalisation consent")),s.preview||s.asked||L(e))return n;let{seen:o}=s,{caps:l}=e;return o.submitted&&n.push("already submitted"),e.exclude_subscribed===!0&&s.subscribed&&n.push("already subscribed"),l.per_session!==null&&o.session>=l.per_session&&n.push("per_session cap"),l.lifetime!==null&&o.lifetime>=l.lifetime&&n.push("lifetime cap"),l.after_close_days!==null&&o.closedAt&&s.now-o.closedAt<l.after_close_days*864e5&&n.push("closed recently"),n}var ge=(e,s)=>s.priority-e.priority;var Ue=["mousemove","pointerdown","keydown","scroll","touchstart","wheel"];function Ke(){let e=[];return{on:(r,d,o,l={passive:!0})=>{r.addEventListener(d,o,l),e.push(()=>r.removeEventListener(d,o,l))},later:(r,d)=>{let o=setTimeout(r,d);e.push(()=>clearTimeout(o))},stop:()=>{for(let r of e.splice(0))r()}}}var ye=(e,s)=>{let n=s.documentElement.scrollHeight||0;return n>0?(e.scrollY+e.innerHeight)/n:0},Ye=e=>{let s=e.activeElement;return!!(s&&(/^(INPUT|TEXTAREA|SELECT)$/.test(s.tagName)||s.isContentEditable))};function He({on:e,later:s},n,r,d,o){let l=!1,p=!1,i=null,c=0;s(()=>{l=!0},r.delay_ms??5e3),e(o,"mousemove",u=>{u.clientY>d.innerHeight*.1&&(p=!0),i!==null&&(c=u.clientY-i),i=u.clientY}),e(o,"mouseout",u=>{l&&p&&!u.relatedTarget&&u.clientY<=0&&c<0&&!Ye(o)&&n("exit")})}function ze({on:e,later:s},n,r,d,o){let l=!1,p=0,i=d.scrollY,c=Date.now(),u=null,w=null;s(()=>{l=!0},r.delay_ms??5e3),e(d,"scroll",()=>{let b=d.scrollY,A=Date.now();if(p=Math.max(p,ye(d,o)),b<i){(!u||A-u.t>300)&&(u={y:i,t:c});let _=u.y-b;l&&p>=.3&&_>=120&&_/Math.max(1,A-u.t)>=.5&&n("exit_scroll")}else u=null;i=b,c=A}),e(o,"visibilitychange",()=>{o.visibilityState==="hidden"?w=Date.now():w!==null&&Date.now()-w>=1e4&&n("return"),o.visibilityState!=="hidden"&&(w=null)})}function me(e,s,{device:n,win:r=window,doc:d=document}){let o=Ke(),l=(e.seconds??30)*1e3;if(e.type==="entry"&&o.later(()=>s("entry"),e.delay_ms??0),e.type==="time"&&o.later(()=>s("time"),l),e.type==="exit"&&(n==="mobile"?ze:He)(o,s,e,r,d),e.type==="scroll"){let p=e.depth??.5;o.on(r,"scroll",()=>ye(r,d)>=p&&s("scroll"))}if(e.type==="idle"){let p=null,i=()=>{clearTimeout(p),p=setTimeout(()=>s("idle"),l)};i();for(let c of Ue)o.on(c==="scroll"?r:d,c,i);return o.on(d,"visibilitychange",i),()=>{clearTimeout(p),o.stop()}}return o.stop}var Xe=P("onsite-wheel.min.js"),be=()=>O(Xe,Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.wheel"));var _e=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite-access"),F=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite"),Be=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.pending"),Ge=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.listeners"),Se=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.suppressed"),Ae={seen:()=>({session:0,lifetime:0,closedAt:null,submitted:!1}),subscribed:()=>!1,shown(){},closed(){},submitted(){},tab(){},tabOut:()=>!1,pin:()=>null,coveredAt:()=>null,covered(){},code:()=>null},Ve=50,qe=300,Je=()=>{try{return new URLSearchParams(location.search).get("hp_debug")==="onsite"}catch{return!1}};function Ee(e,s={}){if(typeof window>"u"||typeof document>"u")return null;if(window[F])return window[F];let n=typeof e?.[_e]=="function"?e[_e]():null;if(!n)return console.warn("[hp] onsite: startOnsite needs the client bootstrap or createAnalytics made"),null;let r=s.settings??n.settings??{},d=oe(n,{...s,challenge:r.capture?.challenge}),o=n.preview?Ae:re(n.storageKey,n.personalisation),l=new Map,p=()=>window[Se]?.size>0,i=(t,a)=>{for(let f of window[Ge]?.[t]??[])try{f(a)}catch(h){console.warn(`[hp] onsite ${t} listener failed:`,h)}},c=we(window),u=n.debug||s.debug===!0||Je(),w=new Map,b=new Map,A=[];for(let t of Array.isArray(r.forms)?r.forms:[]){let a=fe(t);a?A.push(a):w.set(String(t?.slot??`form ${w.size+1}`),{slot:String(t?.slot??""),state:"invalid",reasons:["not a form this SDK can show"]})}let _=new Map,W=new Map,x=null,Z=new Set,ve=t=>{for(let a of _.keys()){let f=l.get(a);if(!t||!f||f.modal?.())return a}return null},j=[],H=Number.isInteger(r.overlay_gap_minutes)?r.overlay_gap_minutes:0,xe=()=>{let t=o.coveredAt();if(!(H>0)||typeof t!="number")return null;let a=Math.max(0,Math.floor((Date.now()-t)/6e4));return a<H?`a popup showed ${a} min ago; ${H} min between them`:null},C=new Set,U=new Map,g=(t,a,f=[])=>(w.set(t.slot,{slot:t.slot,format:t.format,trigger:t.trigger.type,priority:t.priority,state:a,reasons:f}),u&&console.info(`[hp] onsite ${t.slot}: ${a}${f.length?` (${f.join("; ")})`:""}`),a),Te=()=>{try{let t=s.audiences?.();return Array.isArray(t)?t:null}catch{return null}},Q=t=>({path:location.pathname,device:c,now:Date.now(),seen:o.seen(t.slot),subscribed:o.subscribed()||s.subscribed?.()===!0,personalisation:n.personalisation(),preview:!!n.preview,audiences:Te()}),z=t=>{b.get(t.slot)?.(),b.delete(t.slot)};function R(t,a,f=!1,h=L(t)){return l.has(t.slot)?Promise.resolve("shown"):(W.has(t.slot)||W.set(t.slot,Ie(t,a,f,h).finally(()=>W.delete(t.slot))),W.get(t.slot))}async function Ie(t,a,f,h){if(p())return g(t,"not shown",[`suppressed: ${[...window[Se]].join(", ")}`]);let T=J(t,{...Q(t),...f?{seen:Ae.seen()}:{},asked:h});if(T.length)return g(t,"not eligible",T);let E=pe(t.format)&&!f,K=E&&!h&&xe();if(K)return g(t,"not shown",[K]);if(E&&!n.preview&&n.consentAnswered?.()===!1)return U.set(t,h),g(t,"not shown",["waiting for the consent banner"]);let v=q(t.format),k=v&&ve(h);if(k)return g(t,"not shown",[`${k} is showing`]);if(v&&x&&!h)return g(t,"not shown",[`${x} already showed on this page view`]);let I=null;if(!v){try{I=document.querySelector(t.trigger.selector??"")}catch{I=null}if(!I)return g(t,"not shown",["no element matches its selector"])}v&&_.set(t.slot,t),z(t),g(t,"asking for a decision",[a]);let Pe=le().catch(m=>(console.warn("[hp] onsite renderer not loaded:",m?.message??m),null)),$=await d.decide(t.slot,o.pin(t.slot)),y=$.status===200?$.body?.decisions?.find(m=>m?.slot===t.slot):null;y?.experiment&&y.arm&&y.split&&o.pin(t.slot,[y.experiment,y.arm,y.split]);let M=y&&y.holdout!==!0?he(y.payload):null,te=M?.steps[0].blocks??[],ne=y?.offer?.code??(n.preview?"PREVIEW":o.code()),Oe=n.basketValue?.()??null,B=te.some(m=>m?.t==="code")&&!ne&&"no code to remind of"||te.some(m=>m?.t==="progress")&&!(Oe>0)&&"the basket is empty";if(B)return _.delete(t.slot),B==="the basket is empty"&&C.add(t),g(t,"not shown",[B]);if(C.delete(t),!M)return y?.holdout===!0&&n.analytics()&&e.recordExposure($.body),_.delete(t.slot),v&&y?.holdout===!0&&(x??(x=t.slot)),y?.holdout===!0&&!h&&i("held",{slot:t.slot,arm:y.arm??null,format:t.format}),g(t,y?.holdout===!0?"held out":"no decision",[$.status===200?"no creative for this visitor":`decisions answered ${$.status}`]);let G=await Pe;if(!G)return _.delete(t.slot),g(t,"not shown",["the renderer did not load"]);if(await Promise.race([G.warm?.(t,M,M.steps[0].id,Z),new Promise(m=>setTimeout(m,qe))]),p())return _.delete(t.slot),g(t,"not shown",["suppressed"]);f||o.shown(t.slot),E&&o.covered(),v&&(x??(x=t.slot));let V=(m,Re={})=>i(m,{slot:t.slot,arm:y.arm??null,format:M.format??t.format,...Re}),Ce=G.mountForm({form:t,creative:M,decision:y,access:n,client:e,api:d,counters:o,loadWheel:be,loadAnswers:se,container:I,warmed:Z,coupon:ne,basket:()=>n.basketValue?.()??null,emit:V,onClose(m){l.delete(t.slot),_.delete(t.slot),g(t,"closed",[m]),V("closed",{reason:m})}});return l.set(t.slot,Ce),n.analytics()&&e.recordExposure($.body),g(t,"shown",[a]),V("shown"),"shown"}function X(t,a,f=L(t)){if(!q(t.format))return R(t,a,!1,f);j.push([t,a,f]),!(j.length>1)&&setTimeout(async()=>{let h=j.sort(([T],[E])=>ge(T,E));j=[];for(let[T,[E,K,v]]of h.entries()){let k=await R(E,K,!1,v);if(k==="shown"||k==="held out"){for(let[I]of h.slice(T+1))g(I,"not shown",[`${E.slot} has a higher priority`]);return}}},Ve)}for(let t of A){if(t.format==="teaser"&&o.tabOut(t.slot)){R(t,"tab kept from an earlier page",!0);continue}let a=J(t,Q(t));if(a.length){g(t,"not eligible",a);continue}let f=t.trigger.type;f==="manual"?g(t,"waiting for hp.onsite.open"):f==="inline"||t.format==="inline"?R(t,"inline"):ee(t)}function ee(t){z(t),b.set(t.slot,me(t.trigger,a=>{z(t),X(t,a)},{device:c})),g(t,"armed",[`${t.trigger.type} on ${c}`])}let $e=n.onConsentAnswered?.(()=>{let t=[...U];U.clear();for(let[a,f]of t)l.has(a.slot)||(f?X(a,"manual",f):ee(a))}),Me=n.onBasket?.(()=>{if(!((n.basketValue?.()??0)>0))return;let t=[...C];C.clear();for(let a of t)l.has(a.slot)||X(a,"the basket changed")}),N=Object.freeze({open(t){let a=A.find(h=>h.slot===t),f=a??A.find(h=>h.aliases.includes(t));return f?l.has(f.slot)?Promise.resolve(!0):R(f,"manual",!1,L(f)||!a).then(h=>h==="shown"):Promise.resolve(!1)},suppress(){for(let[t,a]of l){a.destroy(),l.delete(t),_.delete(t);let f=A.find(h=>h.slot===t);f&&g(f,"closed",["suppressed"])}},debug:()=>[...w.values()].map(t=>({...t,reasons:[...t.reasons]})),stop(){for(let t of b.values())t();b.clear(),Me?.(),$e?.(),C.clear(),U.clear(),window[F]===N&&delete window[F]}});window[F]=N;for(let{slot:t,resolve:a}of window[Be]?.splice(0)??[])a(N.open(t));return u&&console.table?.(N.debug()),N}window[Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.module")]={startOnsite:Ee};})();
|
|
1
|
+
(()=>{function T(e){let s=typeof document>"u"?null:document.currentScript?.src;return s?new URL(e,s).href:null}var H=new Map;function I(e,s){return window[s]?Promise.resolve(window[s]):e?(H.has(s)||H.set(s,new Promise((n,r)=>{let d=document.createElement("script");d.src=e,d.async=!0,d.onload=()=>window[s]?n(window[s]):r(new Error(`${e} did not register`)),d.onerror=()=>r(new Error(`${e} did not load`)),document.head.append(d)}).finally(()=>H.delete(s))),H.get(s)):Promise.reject(new Error("the CDN script\u2019s own URL is unknown"))}var Ke=T("onsite-answers.min.js"),ae=()=>I(Ke,Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.answers"));var Ye="/v1/capture/challenge",He=e=>typeof e=="string"&&/^\/v1\/[\w/-]+$/.test(e)?e:Ye;function ce(e,s={}){let n=s.fetch??globalThis.fetch?.bind(globalThis),r=s.servingHost??e.servingHost,d=s.gatewayHost??e.gatewayHost;async function o(a,{method:p="GET",body:i,consent:l=!1,timeoutMs:c}={}){if(!n)return{status:0,body:null};try{let w=await n(a,{method:p,credentials:"omit",signal:c&&AbortSignal.timeout?.(c),headers:{accept:"application/json",authorization:`Bearer ${e.writeKey}`,...i?{"content-type":"application/json"}:{},...l?e.headers():{}},...i?{body:JSON.stringify(i)}:{}}),b=null;try{b=await w.json()}catch{}return{status:w.status,body:b}}catch{return{status:0,body:null}}}return{decide:(a,p,{visitAudiences:i,visitCampaign:l}={})=>o(`${r}/v1/decisions`,{method:"POST",consent:!0,body:{slots:[p?{key:a,pin:{experiment:p[0],arm:p[1],split:p[2]}}:a],identifier_value:e.anonymousId()??void 0,session_id:e.sessionId()??void 0,identity_token:e.identityToken?.()??void 0,...e.preview?{preview:e.preview}:{},...i?{visit_audiences:i}:{},...l?{visit_campaign:l}:{}}}),datafile:()=>o(`${r}/v1/audiences/datafile`,{timeoutMs:1500}),challenge:()=>o(`${d}${He(s.challenge)}`),capture:a=>o(`${d}/v1/capture`,{method:"POST",body:a}),answers:a=>o(`${d}/v1/capture/answers`,{method:"POST",body:a}),claim:a=>o(`${r}/v1/offers/claim`,{method:"POST",consent:!0,body:a,timeoutMs:8e3})}}var Xe=T("onsite-audiences.min.js"),ue=()=>I(Xe,Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.audiences"));function de(e,s){let n={},r=()=>{try{let o=JSON.parse(e().getItem(s));o&&typeof o=="object"&&(n=o)}catch{}return n};return{read:r,write:o=>{let a=r();o(a),n=a;try{e().setItem(s,JSON.stringify(a))}catch{}}}}function pe(e,s=()=>!0){let n=de(()=>sessionStorage,e),r=de(()=>localStorage,e),d=()=>s()?r:n,o=()=>{let i=r.read();(i.sub!==void 0||i.k!==void 0)&&r.write(l=>{delete l.sub,delete l.k})},a=()=>s()?[n.read(),r.read()]:(o(),[n.read()]),p=(i,l)=>{var c;return i.f??(i.f={}),(c=i.f)[l]??(c[l]={}),i.f[l]};return{seen(i){let l=r.read().f?.[i]??{};return{session:n.read().f?.[i]?.n??0,lifetime:l.n??0,closedAt:l.c??null,submitted:l.s===1}},subscribed:()=>a().some(i=>i.sub===1),shown(i){n.write(l=>{let c=p(l,i);c.n=(c.n??0)+1}),r.write(l=>{let c=p(l,i);c.n=(c.n??0)+1})},closed(i,l=Date.now()){r.write(c=>{p(c,i).c=l})},tab(i,l){n.write(c=>{let w=p(c,i);l?w.t=1:delete w.t})},tabOut:i=>n.read().f?.[i]?.t===1,pin(i,l){if(l===void 0)return n.read().f?.[i]?.p??null;n.write(c=>{p(c,i).p=l})},coveredAt:()=>n.read().o??null,covered(i=Date.now()){n.write(l=>{l.o=i})},code(i,l=Date.now()){if(i===void 0){let[c]=a().flatMap(w=>w.k?[w.k]:[]).sort((w,b)=>b.at-w.at);return c&&l-c.at<14*864e5?c.c:null}return d().write(c=>{c.k={c:i,at:l}}),i},submitted(i){r.write(l=>{p(l,i).s=1}),d().write(l=>{l.sub=1})}}}var ze=T("onsite-render.min.js"),fe=()=>I(ze,Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.render"));var Ve=["source","medium","campaign","content","term"];var he=e=>String(e).trim().toLowerCase();function ge(e,s){return(Array.isArray(e)?e:[]).every(n=>{let r=s?.[n?.field];if(!Ve.includes(n?.field)||typeof r!="string")return!1;let d=he(r);return[].concat(n.values??[]).some(o=>{let a=he(o);return n.match==="is"?d===a:n.match==="contains"&&a!==""&&d.includes(a)})})}var be=["popup","sheet","bar","teaser","inline","fullscreen"],Ge=["entry","exit","scroll","time","idle","inline","manual"],Z=e=>e!=="inline",Se=e=>e==="popup"||e==="sheet"||e==="fullscreen",S=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),k=e=>Array.isArray(e)?e.filter(s=>typeof s=="string"):[],me=e=>Number.isInteger(e)&&e>=0?e:null,Be={field:null,match:null,values:[]},qe=e=>e==null?[]:Array.isArray(e)&&e.every(S)?e:[Be];function _e(e){if(!S(e)||typeof e.slot!="string"||e.slot==="")return null;let s=S(e.trigger)?e.trigger:{};if(!be.includes(e.format)||!Ge.includes(s.type))return null;let n=S(e.caps)?e.caps:{};return{...e,trigger:s,priority:Number.isFinite(e.priority)?e.priority:0,pages:S(e.pages)?e.pages:{},devices:k(e.devices),aliases:k(e.aliases),audiences:k(e.audiences),arrived_from:qe(e.arrived_from),caps:{per_session:me(n.per_session),lifetime:me(n.lifetime),after_close_days:Number.isFinite(n.after_close_days)?n.after_close_days:null},window:S(e.window)?e.window:{},questions:Array.isArray(e.questions)?e.questions.filter(S):[],prizes:Array.isArray(e.prizes)?e.prizes.filter(S):[]}}var Je=/^[a-z][a-z0-9_-]{0,31}$/;function Ae(e){if(!S(e)||e.v!==1||!Array.isArray(e.steps))return null;let s=e.steps.filter(n=>S(n)&&Je.test(n.id)&&Array.isArray(n.blocks));return s.length===0?null:{format:be.includes(e.format)?e.format:null,theme:S(e.theme)?e.theme:{},steps:s}}function we(e,s){let n=String(e).split("*").map(r=>r.replace(/[.+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(`^${n}$`,"i").test(s)}function Ze(e,s){let n=k(e?.include);return(!n.length||n.some(r=>we(r,s)))&&!k(e?.exclude).some(r=>we(r,s))}function ve(e){return e.matchMedia?.("(hover: none) and (pointer: coarse)")?.matches===!0||e.innerWidth<768?"mobile":"desktop"}var ye=e=>typeof e=="string"?Date.parse(e):Number.NaN,F=e=>e.trigger.type==="manual";function Q(e,s){let n=[];Ze(e.pages,s.path)||n.push("page not included"),e.devices.length&&!e.devices.includes(s.device)&&n.push(`not on ${s.device}`),ge(e.arrived_from,s.campaign)||n.push("not from its campaign");let r=ye(e.window.starts_at),d=ye(e.window.ends_at);if((s.now<r||s.now>=d)&&n.push("outside its dates"),e.audiences.length&&!s.preview&&(s.personalisation?s.audiences&&!e.audiences.some(p=>s.audiences.includes(p))&&n.push("not in its audiences"):n.push("audiences need personalisation consent")),s.preview||s.asked||F(e))return n;let{seen:o}=s,{caps:a}=e;return o.submitted&&n.push("already submitted"),e.exclude_subscribed===!0&&s.subscribed&&n.push("already subscribed"),a.per_session!==null&&o.session>=a.per_session&&n.push("per_session cap"),a.lifetime!==null&&o.lifetime>=a.lifetime&&n.push("lifetime cap"),a.after_close_days!==null&&o.closedAt&&s.now-o.closedAt<a.after_close_days*864e5&&n.push("closed recently"),n}var Ee=(e,s)=>s.priority-e.priority;var Qe=["mousemove","pointerdown","keydown","scroll","touchstart","wheel"];function et(){let e=[];return{on:(r,d,o,a={passive:!0})=>{r.addEventListener(d,o,a),e.push(()=>r.removeEventListener(d,o,a))},later:(r,d)=>{let o=setTimeout(r,d);e.push(()=>clearTimeout(o))},stop:()=>{for(let r of e.splice(0))r()}}}var xe=(e,s)=>{let n=s.documentElement.scrollHeight||0;return n>0?(e.scrollY+e.innerHeight)/n:0},tt=e=>{let s=e.activeElement;return!!(s&&(/^(INPUT|TEXTAREA|SELECT)$/.test(s.tagName)||s.isContentEditable))};function nt({on:e,later:s},n,r,d,o){let a=!1,p=!1,i=null,l=0;s(()=>{a=!0},r.delay_ms??5e3),e(o,"mousemove",c=>{c.clientY>d.innerHeight*.1&&(p=!0),i!==null&&(l=c.clientY-i),i=c.clientY}),e(o,"mouseout",c=>{a&&p&&!c.relatedTarget&&c.clientY<=0&&l<0&&!tt(o)&&n("exit")})}function st({on:e,later:s},n,r,d,o){let a=!1,p=0,i=d.scrollY,l=Date.now(),c=null,w=null;s(()=>{a=!0},r.delay_ms??5e3),e(d,"scroll",()=>{let b=d.scrollY,_=Date.now();if(p=Math.max(p,xe(d,o)),b<i){(!c||_-c.t>300)&&(c={y:i,t:l});let A=c.y-b;a&&p>=.3&&A>=120&&A/Math.max(1,_-c.t)>=.5&&n("exit_scroll")}else c=null;i=b,l=_}),e(o,"visibilitychange",()=>{o.visibilityState==="hidden"?w=Date.now():w!==null&&Date.now()-w>=1e4&&n("return"),o.visibilityState!=="hidden"&&(w=null)})}function Te(e,s,{device:n,win:r=window,doc:d=document}){let o=et(),a=(e.seconds??30)*1e3;if(e.type==="entry"&&o.later(()=>s("entry"),e.delay_ms??0),e.type==="time"&&o.later(()=>s("time"),a),e.type==="exit"&&(n==="mobile"?st:nt)(o,s,e,r,d),e.type==="scroll"){let p=e.depth??.5;o.on(r,"scroll",()=>xe(r,d)>=p&&s("scroll"))}if(e.type==="idle"){let p=null,i=()=>{clearTimeout(p),p=setTimeout(()=>s("idle"),a)};i();for(let l of Qe)o.on(l==="scroll"?r:d,l,i);return o.on(d,"visibilitychange",i),()=>{clearTimeout(p),o.stop()}}return o.stop}var ot=T("onsite-wheel.min.js"),Ie=()=>I(ot,Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.wheel"));var Me=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite-access"),j=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite"),it=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.pending"),rt=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.listeners"),Oe=Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.suppressed"),Ce={seen:()=>({session:0,lifetime:0,closedAt:null,submitted:!1}),subscribed:()=>!1,shown(){},closed(){},submitted(){},tab(){},tabOut:()=>!1,pin:()=>null,coveredAt:()=>null,covered(){},code:()=>null},lt=1500,at=50,ct=300,ut=()=>{try{return new URLSearchParams(location.search).get("hp_debug")==="onsite"}catch{return!1}};function Pe(e,s={}){if(typeof window>"u"||typeof document>"u")return null;if(window[j])return window[j];let n=typeof e?.[Me]=="function"?e[Me]():null;if(!n)return console.warn("[hp] onsite: startOnsite needs the client bootstrap or createAnalytics made"),null;let r=s.settings??n.settings??{},d=ce(n,{...s,challenge:r.capture?.challenge}),o=n.preview?Ce:pe(n.storageKey,n.personalisation),a=()=>n.campaign?.()??null,p=new Map,i=()=>window[Oe]?.size>0,l=(t,u)=>{for(let f of window[rt]?.[t]??[])try{f(u)}catch(g){console.warn(`[hp] onsite ${t} listener failed:`,g)}},c=ve(window),w=n.debug||s.debug===!0||ut(),b=new Map,_=new Map,A=[];for(let t of Array.isArray(r.forms)?r.forms:[]){let u=_e(t);u?A.push(u):b.set(String(t?.slot??`form ${b.size+1}`),{slot:String(t?.slot??""),state:"invalid",reasons:["not a form this SDK can show"]})}let ee=()=>ue().catch(t=>(console.warn("[hp] onsite audiences not loaded:",t?.message??t),null));n.personalisation()&&A.some(t=>t.audiences.length>0)&&ee().then(t=>t?.prefetch(d));let v=new Map,U=new Map,M=null,te=new Set,$e=t=>{for(let u of v.keys()){let f=p.get(u);if(!t||!f||f.modal?.())return u}return null},W=[],X=Number.isInteger(r.overlay_gap_minutes)?r.overlay_gap_minutes:0,Ne=()=>{let t=o.coveredAt();if(!(X>0)||typeof t!="number")return null;let u=Math.max(0,Math.floor((Date.now()-t)/6e4));return u<X?`a popup showed ${u} min ago; ${X} min between them`:null},N=new Set,K=new Map,m=(t,u,f=[])=>(b.set(t.slot,{slot:t.slot,format:t.format,trigger:t.trigger.type,priority:t.priority,state:u,reasons:f}),w&&console.info(`[hp] onsite ${t.slot}: ${u}${f.length?` (${f.join("; ")})`:""}`),u),ne=()=>{try{let t=s.audiences?.();return Array.isArray(t)?t:null}catch{return null}},se=t=>({path:location.pathname,device:c,campaign:a(),now:Date.now(),seen:o.seen(t.slot),subscribed:o.subscribed()||s.subscribed?.()===!0,personalisation:n.personalisation(),preview:!!n.preview,audiences:ne()}),z=t=>{_.get(t.slot)?.(),_.delete(t.slot)};function R(t,u,f=!1,g=F(t)){return p.has(t.slot)?Promise.resolve("shown"):(U.has(t.slot)||U.set(t.slot,Re(t,u,f,g).finally(()=>U.delete(t.slot))),U.get(t.slot))}async function Re(t,u,f,g){if(i())return m(t,"not shown",[`suppressed: ${[...window[Oe]].join(", ")}`]);let O=Q(t,{...se(t),...f?{seen:Ce.seen()}:{},asked:g});if(O.length)return m(t,"not eligible",O);let E=Se(t.format)&&!f,Y=E&&!g&&Ne();if(Y)return m(t,"not shown",[Y]);if(E&&!n.preview&&n.consentAnswered?.()===!1)return K.set(t,g),m(t,"not shown",["waiting for the consent banner"]);let x=Z(t.format),L=x&&$e(g);if(L)return m(t,"not shown",[`${L} is showing`]);if(x&&M&&!g)return m(t,"not shown",[`${M} already showed on this page view`]);let C=null;if(!x){try{C=document.querySelector(t.trigger.selector??"")}catch{C=null}if(!C)return m(t,"not shown",["no element matches its selector"])}x&&v.set(t.slot,t),z(t),m(t,"asking for a decision",[u]);let ke=fe().catch(h=>(console.warn("[hp] onsite renderer not loaded:",h?.message??h),null)),G=ne()??[],ie=[...new Set([...t.audiences,...G])].filter(h=>G.includes(h)).slice(0,100),Fe=t.audiences.length>0&&!n.preview&&n.personalisation()?await Promise.race([ee().then(h=>h?h.visitAudiences(d,n.sessionSignals,t.audiences,G):ie),new Promise(h=>setTimeout(h,lt,ie))]):null,P=await d.decide(t.slot,o.pin(t.slot),{visitAudiences:Fe,visitCampaign:t.arrived_from.length?a():null}),y=P.status===200?P.body?.decisions?.find(h=>h?.slot===t.slot):null;y?.experiment&&y.arm&&y.split&&o.pin(t.slot,[y.experiment,y.arm,y.split]);let $=y&&y.holdout!==!0?Ae(y.payload):null,re=$?.steps[0].blocks??[],le=y?.offer?.code??(n.preview?"PREVIEW":o.code()),je=n.basketValue?.()??null,B=re.some(h=>h?.t==="code")&&!le&&"no code to remind of"||re.some(h=>h?.t==="progress")&&!(je>0)&&"the basket is empty";if(B)return v.delete(t.slot),B==="the basket is empty"&&N.add(t),m(t,"not shown",[B]);if(N.delete(t),!$)return y?.holdout===!0&&n.analytics()&&e.recordExposure(P.body),v.delete(t.slot),x&&y?.holdout===!0&&(M??(M=t.slot)),y?.holdout===!0&&!g&&l("held",{slot:t.slot,arm:y.arm??null,format:t.format}),m(t,y?.holdout===!0?"held out":"no decision",[P.status===200?"no creative for this visitor":`decisions answered ${P.status}`]);let q=await ke;if(!q)return v.delete(t.slot),m(t,"not shown",["the renderer did not load"]);if(await Promise.race([q.warm?.(t,$,$.steps[0].id,te),new Promise(h=>setTimeout(h,ct))]),i())return v.delete(t.slot),m(t,"not shown",["suppressed"]);f||o.shown(t.slot),E&&o.covered(),x&&(M??(M=t.slot));let J=(h,We={})=>l(h,{slot:t.slot,arm:y.arm??null,format:$.format??t.format,...We}),Ue=q.mountForm({form:t,creative:$,decision:y,access:n,client:e,api:d,counters:o,loadWheel:Ie,loadAnswers:ae,container:C,warmed:te,coupon:le,basket:()=>n.basketValue?.()??null,emit:J,onClose(h){p.delete(t.slot),v.delete(t.slot),m(t,"closed",[h]),J("closed",{reason:h})}});return p.set(t.slot,Ue),n.analytics()&&e.recordExposure(P.body),m(t,"shown",[u]),J("shown"),"shown"}function V(t,u,f=F(t)){if(!Z(t.format))return R(t,u,!1,f);W.push([t,u,f]),!(W.length>1)&&setTimeout(async()=>{let g=W.sort(([O],[E])=>Ee(O,E));W=[];for(let[O,[E,Y,x]]of g.entries()){let L=await R(E,Y,!1,x);if(L==="shown"||L==="held out"){for(let[C]of g.slice(O+1))m(C,"not shown",[`${E.slot} has a higher priority`]);return}}},at)}for(let t of A){if(t.format==="teaser"&&o.tabOut(t.slot)){R(t,"tab kept from an earlier page",!0);continue}let u=Q(t,se(t));if(u.length){m(t,"not eligible",u);continue}let f=t.trigger.type;f==="manual"?m(t,"waiting for hp.onsite.open"):f==="inline"||t.format==="inline"?R(t,"inline"):oe(t)}function oe(t){z(t),_.set(t.slot,Te(t.trigger,u=>{z(t),V(t,u)},{device:c})),m(t,"armed",[`${t.trigger.type} on ${c}`])}let De=n.onConsentAnswered?.(()=>{let t=[...K];K.clear();for(let[u,f]of t)p.has(u.slot)||(f?V(u,"manual",f):oe(u))}),Le=n.onBasket?.(()=>{if(!((n.basketValue?.()??0)>0))return;let t=[...N];N.clear();for(let u of t)p.has(u.slot)||V(u,"the basket changed")}),D=Object.freeze({open(t){let u=A.find(g=>g.slot===t),f=u??A.find(g=>g.aliases.includes(t));return f?p.has(f.slot)?Promise.resolve(!0):R(f,"manual",!1,F(f)||!u).then(g=>g==="shown"):Promise.resolve(!1)},suppress(){for(let[t,u]of p){u.destroy(),p.delete(t),v.delete(t);let f=A.find(g=>g.slot===t);f&&m(f,"closed",["suppressed"])}},debug:()=>[...b.values()].map(t=>({...t,reasons:[...t.reasons]})),stop(){for(let t of _.values())t();_.clear(),Le?.(),De?.(),N.clear(),K.clear(),window[j]===D&&delete window[j]}});window[j]=D;for(let{slot:t,resolve:u}of window[it]?.splice(0)??[])u(D.open(t));return w&&console.table?.(D.debug()),D}window[Symbol.for("@thg-agentic/hyper-personalisation-web.onsite.module")]={startOnsite:Pe};})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thg-agentic/hyper-personalisation-web",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.26.0",
|
|
4
4
|
"description": "Browser SDK for the THG Hyper-Personalisation event specification: event tracking from a write key and remote settings, strict CMP-agnostic consent, a GA4 dataLayer bridge and in-session audience evaluation. Dependency-free ESM on the Segment wire protocol, a CDN script (dist/hp.min.js), and an optional @segment/analytics-next plugin path.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
"dist/onsite.min.js",
|
|
61
61
|
"dist/onsite-render.min.js",
|
|
62
62
|
"dist/onsite-wheel.min.js",
|
|
63
|
-
"dist/onsite-answers.min.js"
|
|
63
|
+
"dist/onsite-answers.min.js",
|
|
64
|
+
"dist/onsite-audiences.min.js"
|
|
64
65
|
],
|
|
65
66
|
"sideEffects": false,
|
|
66
67
|
"license": "MIT",
|
|
@@ -90,7 +91,7 @@
|
|
|
90
91
|
"esbuild": "^0.28.2",
|
|
91
92
|
"jsdom": "^30.1.1",
|
|
92
93
|
"typescript": "^7.0.2",
|
|
93
|
-
"@thg-agentic/hyper-personalisation-spec": "1.
|
|
94
|
+
"@thg-agentic/hyper-personalisation-spec": "1.20.0"
|
|
94
95
|
},
|
|
95
96
|
"scripts": {
|
|
96
97
|
"test": "node --test tests/*.test.mjs",
|
package/src/bootstrap.js
CHANGED
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
* cookie, the device cookie's), the per-request currency / ship-to / locale getters, the preset,
|
|
17
17
|
* the page type, debug, and which property this page is for a key scoped to several.
|
|
18
18
|
*
|
|
19
|
-
* Resolves to the client, or null when the key is disabled or unknown, the
|
|
20
|
-
* fetched on a first visit
|
|
21
|
-
* idempotent per page. createAnalytics() and connectDataLayer()
|
|
19
|
+
* Resolves to the client, or null when the key is disabled or unknown, or the options are invalid.
|
|
20
|
+
* Settings that cannot be fetched on a first visit start it on built-in, collect-only defaults.
|
|
21
|
+
* Never throws into the page, and is idempotent per page. createAnalytics() and connectDataLayer()
|
|
22
|
+
* remain the lower-level API.
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
import { assignmentDecisionId, assignmentSession } from './assignment.js';
|
|
@@ -47,16 +48,16 @@ import { createAnalytics, normaliseSha256, PAGE_TYPES } from './index.js';
|
|
|
47
48
|
import { dropPendingOpens, onsite } from './onsite-facade.js';
|
|
48
49
|
import { loadOnsite } from './onsite-loader.js';
|
|
49
50
|
import {
|
|
51
|
+
BUILT_IN,
|
|
50
52
|
cachedSettings,
|
|
51
53
|
cacheSettings,
|
|
52
|
-
DISABLED,
|
|
53
54
|
eventsHost,
|
|
54
55
|
fetchSettings,
|
|
55
56
|
forgetSettings,
|
|
56
57
|
readSettings,
|
|
57
58
|
resolveApiHost,
|
|
58
59
|
} from './settings.js';
|
|
59
|
-
import { realId, uuid } from './shape.js';
|
|
60
|
+
import { realId, tokenOf, uuid } from './shape.js';
|
|
60
61
|
import {
|
|
61
62
|
configureStorageNames,
|
|
62
63
|
migrateLegacyStorage,
|
|
@@ -68,6 +69,9 @@ const INSTANCE = Symbol.for('@thg-agentic/hyper-personalisation-web.bootstrap');
|
|
|
68
69
|
const DRAIN = Symbol.for('@thg-agentic/hyper-personalisation-web.drain');
|
|
69
70
|
const CARRY = Symbol.for('@thg-agentic/hyper-personalisation-web.carry');
|
|
70
71
|
const REPLAY = Symbol.for('@thg-agentic/hyper-personalisation-web.replay');
|
|
72
|
+
const UNREACHABLE = Symbol.for(
|
|
73
|
+
'@thg-agentic/hyper-personalisation-web.unreachable',
|
|
74
|
+
);
|
|
71
75
|
/** Where bootstrap leaves `hp.assignmentDecisionId` its reader of the page's identity cookies. */
|
|
72
76
|
const ASSIGNMENT = Symbol.for(
|
|
73
77
|
'@thg-agentic/hyper-personalisation-web.assignment',
|
|
@@ -191,8 +195,35 @@ function consentAdapter(consent, settings, preset) {
|
|
|
191
195
|
return reportingPurposes(factory(options), settings.consentPurposes);
|
|
192
196
|
}
|
|
193
197
|
|
|
194
|
-
/**
|
|
195
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Who an identify named, as the session remembers it: `userId|email_sha256`, then `|v` once one for
|
|
200
|
+
* them carried an identity token (ADR 0067).
|
|
201
|
+
*/
|
|
202
|
+
const identifyMarker = (userId, email, verified) =>
|
|
203
|
+
`${userId ?? ''}|${email ?? ''}${verified ? '|v' : ''}`;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Whether the session already sent this identify: for the same person, and verified, or not and
|
|
207
|
+
* this one has no token either. So a token is sent once, when it first appears for them, and a page
|
|
208
|
+
* without one (served from a shared cache) never sends them again.
|
|
209
|
+
*/
|
|
210
|
+
const identifiedAlready = (stored, userId, email, verified) =>
|
|
211
|
+
stored === identifyMarker(userId, email, true) ||
|
|
212
|
+
(!verified && stored === identifyMarker(userId, email, false));
|
|
213
|
+
|
|
214
|
+
const withoutToken = (event) => {
|
|
215
|
+
if (event.context?.identity_token === undefined) return event;
|
|
216
|
+
const { identity_token: _, ...context } = event.context;
|
|
217
|
+
return { ...event, context };
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const readIdentified = (key) => {
|
|
221
|
+
try {
|
|
222
|
+
return sessionStorage.getItem(key);
|
|
223
|
+
} catch {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
196
227
|
|
|
197
228
|
/** The traits to identify with on this page, or null when this session already identified them. */
|
|
198
229
|
function pendingIdentify(propertyId, identity) {
|
|
@@ -200,13 +231,19 @@ function pendingIdentify(propertyId, identity) {
|
|
|
200
231
|
const email = normaliseSha256(identity?.emailSha256);
|
|
201
232
|
if (!userId && !email) return null;
|
|
202
233
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
234
|
+
// No session storage: identify on every page rather than never.
|
|
235
|
+
const stored = readIdentified(
|
|
236
|
+
storageKey(storageName('identified'), propertyId),
|
|
237
|
+
);
|
|
238
|
+
if (
|
|
239
|
+
identifiedAlready(
|
|
240
|
+
stored,
|
|
241
|
+
userId,
|
|
242
|
+
email,
|
|
243
|
+
userId !== null && tokenOf(identity?.token) !== null,
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
return null;
|
|
210
247
|
return {
|
|
211
248
|
...(userId ? { userId } : {}),
|
|
212
249
|
...(email ? { email_sha256: email } : {}),
|
|
@@ -218,21 +255,18 @@ function pendingIdentify(propertyId, identity) {
|
|
|
218
255
|
*
|
|
219
256
|
* Inline settings (development, tests) are used as given and never fetched or cached. Otherwise a
|
|
220
257
|
* cached answer starts the page at once while the API is asked again for the next page; with no
|
|
221
|
-
* cache the page waits for the API, and
|
|
258
|
+
* cache the page waits for the API, and starts on BUILT_IN when it cannot be asked: an outage of
|
|
259
|
+
* the API never turns collection off, and the events it cannot take wait in the retry buffer.
|
|
222
260
|
*/
|
|
223
261
|
async function resolveSettings({ writeKey, apiHost }, options) {
|
|
224
262
|
if (options.settings !== undefined)
|
|
225
263
|
return { settings: readSettings(options.settings), revalidated: null };
|
|
226
264
|
const fetched = fetchSettings(apiHost, writeKey);
|
|
227
265
|
const cached = cachedSettings(writeKey);
|
|
228
|
-
if (cached) return { settings: cached, revalidated: fetched };
|
|
266
|
+
if (cached) return { settings: cached, revalidated: fetched, fetched };
|
|
229
267
|
const answer = await fetched;
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
settings,
|
|
233
|
-
revalidated: Promise.resolve(settings),
|
|
234
|
-
unreachable: answer === null,
|
|
235
|
-
};
|
|
268
|
+
if (!answer) return { settings: BUILT_IN, revalidated: null, fetched };
|
|
269
|
+
return { settings: answer, revalidated: fetched, fetched };
|
|
236
270
|
}
|
|
237
271
|
|
|
238
272
|
/**
|
|
@@ -320,14 +354,44 @@ function startAutocapture(client, autocapture) {
|
|
|
320
354
|
};
|
|
321
355
|
}
|
|
322
356
|
|
|
357
|
+
/**
|
|
358
|
+
* How long a page that started from cached settings lets its forms wait for the fresh answer: the
|
|
359
|
+
* settings request already in flight, which the edge answers in well under this. Only the forms
|
|
360
|
+
* wait, never the page or its events; a slower answer leaves this page's forms on the cache.
|
|
361
|
+
*/
|
|
362
|
+
const ONSITE_FRESH_MS = 1000;
|
|
363
|
+
|
|
323
364
|
/**
|
|
324
365
|
* Onsite forms, only when the page opted in and the settings have a live one: the runtime is a
|
|
325
|
-
* separate chunk, fetched here and never part of the core.
|
|
366
|
+
* separate chunk, fetched here and never part of the core. A page that started from cached settings
|
|
367
|
+
* judges its forms by the answer it revalidates with, when that comes within ONSITE_FRESH_MS, so a
|
|
368
|
+
* form set live, changed or stopped in the console shows on the next page, not the one after.
|
|
326
369
|
*/
|
|
327
|
-
function startOnsiteForms(client, opted, settings) {
|
|
328
|
-
if (!opted
|
|
329
|
-
|
|
330
|
-
|
|
370
|
+
function startOnsiteForms(client, opted, settings, revalidated) {
|
|
371
|
+
if (!opted) return dropPendingOpens();
|
|
372
|
+
// The chunk is fetched alongside the fresh answer when the cache has a form, so neither waits.
|
|
373
|
+
const early = settings.onsite ? loadOnsite() : null;
|
|
374
|
+
early?.catch(() => {});
|
|
375
|
+
const fresh = revalidated
|
|
376
|
+
? Promise.race([
|
|
377
|
+
revalidated.then((next) => next ?? settings),
|
|
378
|
+
new Promise((resolve) =>
|
|
379
|
+
setTimeout(() => resolve(settings), ONSITE_FRESH_MS),
|
|
380
|
+
),
|
|
381
|
+
])
|
|
382
|
+
: Promise.resolve(settings);
|
|
383
|
+
fresh
|
|
384
|
+
.then((current) => {
|
|
385
|
+
const forms = current.enabled ? current.onsite : null;
|
|
386
|
+
if (!forms) return dropPendingOpens();
|
|
387
|
+
const options = opted === true ? {} : opted;
|
|
388
|
+
return (early ?? loadOnsite()).then((module) =>
|
|
389
|
+
module.startOnsite(client, {
|
|
390
|
+
...options,
|
|
391
|
+
settings: options.settings ?? forms,
|
|
392
|
+
}),
|
|
393
|
+
);
|
|
394
|
+
})
|
|
331
395
|
.catch((err) => {
|
|
332
396
|
dropPendingOpens();
|
|
333
397
|
console.warn('[hp] onsite not started:', err?.message ?? err);
|
|
@@ -356,11 +420,10 @@ async function start(config, options, carry) {
|
|
|
356
420
|
const { writeKey } = config;
|
|
357
421
|
// The visitor's identity may still be looking itself up; it is waited for alongside the
|
|
358
422
|
// settings, never before them. A lookup that fails leaves the visitor anonymous.
|
|
359
|
-
const [{ settings, revalidated,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
]);
|
|
423
|
+
const [{ settings, revalidated, fetched }, identity] = await Promise.all([
|
|
424
|
+
resolveSettings(config, options),
|
|
425
|
+
Promise.resolve(options.identity).catch(() => null),
|
|
426
|
+
]);
|
|
364
427
|
|
|
365
428
|
// What to cache is the newest answer; when is the first grant. Either may come first.
|
|
366
429
|
let latest = revalidated ? settings : null;
|
|
@@ -373,11 +436,6 @@ async function start(config, options, carry) {
|
|
|
373
436
|
else forgetSettings(writeKey);
|
|
374
437
|
});
|
|
375
438
|
|
|
376
|
-
// Unreachable, or left before the answer: what `hp` queued goes to the next page.
|
|
377
|
-
if (unreachable) {
|
|
378
|
-
dropPendingOpens();
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
439
|
if (!settings.enabled) {
|
|
382
440
|
if (options.debug)
|
|
383
441
|
console.info(
|
|
@@ -386,8 +444,9 @@ async function start(config, options, carry) {
|
|
|
386
444
|
return off(carry);
|
|
387
445
|
}
|
|
388
446
|
// The page's own property, else the source's default; a source with several and no default needs the page's.
|
|
389
|
-
|
|
390
|
-
|
|
447
|
+
// On BUILT_IN the ingest stamps the key's, and quarantines the events of a key with several.
|
|
448
|
+
const propertyId = options.propertyId ?? settings.propertyId ?? null;
|
|
449
|
+
if (!propertyId && settings !== BUILT_IN) {
|
|
391
450
|
console.warn(
|
|
392
451
|
'[hp] not started: this write key covers several properties, so propertyId is required',
|
|
393
452
|
);
|
|
@@ -439,6 +498,7 @@ async function start(config, options, carry) {
|
|
|
439
498
|
: undefined,
|
|
440
499
|
// Every page carries the signed-in user; only the identify itself is once per session.
|
|
441
500
|
getUserId: () => userId,
|
|
501
|
+
identityToken: identity?.token,
|
|
442
502
|
beforeSend: options.beforeSend,
|
|
443
503
|
...options.extend,
|
|
444
504
|
onConsentStatus(status) {
|
|
@@ -463,25 +523,30 @@ async function start(config, options, carry) {
|
|
|
463
523
|
// arrives later in the session is still identified. Any identify counts, including one the
|
|
464
524
|
// page sends itself at sign-in, so the next page of the session does not repeat it.
|
|
465
525
|
if (event.type === 'identify') {
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
526
|
+
const key = storageKey(storageName('identified'), propertyId);
|
|
527
|
+
const user = realId(event.userId);
|
|
528
|
+
const email = normaliseSha256(event.traits?.email_sha256);
|
|
529
|
+
// One without a token never unsays an earlier verified one for the same person.
|
|
530
|
+
const verified =
|
|
531
|
+
Boolean(event.context?.identity_token) ||
|
|
532
|
+
readIdentified(key) === identifyMarker(user, email, true);
|
|
470
533
|
try {
|
|
471
|
-
sessionStorage.setItem(
|
|
472
|
-
storageKey(storageName('identified'), propertyId),
|
|
473
|
-
marker,
|
|
474
|
-
);
|
|
534
|
+
sessionStorage.setItem(key, identifyMarker(user, email, verified));
|
|
475
535
|
} catch {
|
|
476
536
|
/* identify again next page */
|
|
477
537
|
}
|
|
478
538
|
}
|
|
479
|
-
|
|
539
|
+
// The observer passed to bootstrap never sees the token: it vouches for the shopper until
|
|
540
|
+
// it expires. A host's own beforeSend sees the event as sent.
|
|
541
|
+
options.extend?.onEvent?.(withoutToken(event));
|
|
480
542
|
},
|
|
481
543
|
});
|
|
482
544
|
|
|
483
545
|
if (typeof window !== 'undefined')
|
|
484
546
|
window[RECOMMENDATION_CONTEXT] = () => client.recommendationContext();
|
|
547
|
+
// The settings and the ingest share a host: one failing says the other likely is too, so what
|
|
548
|
+
// leaves at unload is also kept for the next page.
|
|
549
|
+
fetched?.then((answer) => answer === null && client[UNREACHABLE]());
|
|
485
550
|
|
|
486
551
|
// Before this page's own events: they were made first. Consent here decides, as for any call.
|
|
487
552
|
for (const [method, args, made] of carry.take()) {
|
|
@@ -535,7 +600,7 @@ async function start(config, options, carry) {
|
|
|
535
600
|
|
|
536
601
|
if (identified) client.identify(identified, undefined, IDENTITY_OPTION);
|
|
537
602
|
client.onsite = onsite;
|
|
538
|
-
startOnsiteForms(client, options.onsite, settings);
|
|
603
|
+
startOnsiteForms(client, options.onsite, settings, revalidated);
|
|
539
604
|
return client;
|
|
540
605
|
} catch (err) {
|
|
541
606
|
console.warn('[hp] not started:', err?.message ?? err);
|