PageSourceSearch

https://fathead.com/cdn/shop/t/342/assets/fh-discovery-analytics.js?v=55393042665626544401790031334

js fathead.com collected 2026-09-24 17:50:28 UTC 18,470 bytes, 2 lines download raw bytes

1(()=>{if(window.FHDiscoveryAnalytics)return;const safeTerm=value=>String(value||"").replace(/[\w.+-]+@[\w.-]+\.[a-z]{2,}/gi,"[email]").replace(/(?:\+?\d[\d\s().-]{6,}\d)/g,"[number]").replace(/\b(?:order|confirmation)\s*#?\s*[a-z0-9-]{5,}/gi,"[order]").replace(/[\x00-\x1f\x7f]/g," ").trim().toLowerCase().slice(0,150);let privacy=window.Shopify?.customerPrivacy;const allowed=()=>(privacy=window.Shopify?.customerPrivacy||privacy,privacy?.analyticsProcessingAllowed?.()===!0),uuid=()=>crypto.randomUUID();let impressionId=null,queue=[],timer,session,lastActivity=0,queryId,query="",selected=!1,openedAt=0,inflight=null;const seen=new Set,renderedContexts=new Map,productContexts=new WeakMap;let consentEpoch=0;const resultContextFor=container=>{const value=container&&productContexts.get(container);return value?.epoch===consentEpoch?value.context:null};function ids(){if(lastActivity&&Date.now()-lastActivity>=18e5&&(session=null,queryId=null),lastActivity=Date.now(),!session){try{const s=JSON.parse(sessionStorage.getItem("fh-discovery-session-v2")||"null");s&&Date.now()-s.at<18e5&&(session=s.id)}catch{}session||=uuid()}try{sessionStorage.setItem("fh-discovery-session-v2",JSON.stringify({id:session,at:Date.now()}))}catch{}return session}function context(){try{const c=JSON.parse(sessionStorage.getItem("fh-discovery-context-v2")||"null");return c&&Date.now()-c.at<6e5?c:null}catch{return null}}function beginQuery(value){const next=safeTerm(value);if(next!==query||!queryId){const prior=allowed()?context():null;queryId=!queryId&&prior?.destination===location.pathname&&prior.query===next?prior.queryId:uuid(),query=next,selected=!1,impressionId=null}return queryId}function track(type,data={}){if(!allowed())return!1;const q=safeTerm(data.query??query),prior=context();q&&data.queryId===void 0&&beginQuery(q);const filters={};Object.entries(data.filters||{}).slice(0,25).forEach(([k,v])=>{filters[k.slice(0,100)]=v.slice(0,30).map(v2=>safeTerm(v2).slice(0,120))});const changedFilters={};Object.entries(data.changedFilters||{}).slice(0,25).forEach(([k,v])=>{changedFilters[k.slice(0,100)]=v.slice(0,30).map(v2=>safeTerm(v2).slice(0,120))});const removedFilters={};Object.entries(data.removedFilters||{}).slice(0,25).forEach(([k,v])=>{removedFilters[k.slice(0,100)]=v.slice(0,30).map(x=>safeTerm(x).slice(0,120))});const destination=data.destination?new URL(data.destination,location.origin).pathname:null,inherited=prior?.destination===location.pathname&&(!q||q===prior.query)?prior:null;(type==="search_results"||type==="collection_results")&&!data.impressionId&&(impressionId=uuid());const event={eventId:uuid(),schemaVersion:type==="add_to_cart"?4:3,...type==="add_to_cart"?{operationId:data.operationId||null}:{},occurredAt:new Date().toISOString(),impressionId:data.impressionId!==void 0?data.impressionId:impressionId||inherited?.impressionId||null,removedFilters,ruleId:data.ruleId||inherited?.ruleId||null,strategy:(data.strategy||inherited?.strategy)==="learned"?"learned":"baseline",products:(data.products||[]).slice(0,48).map(p=>({id:String(p.id).slice(0,100),position:p.position})),droppedEvents:data.droppedEvents||0,sessionId:ids(),queryId:data.queryId!==void 0?data.queryId:(q?queryId:inherited?.queryId)||null,type,surface:data.surface||"navigation",query:q||inherited?.query||"",collection:data.collection||null,productId:data.productId?String(data.productId):null,position:data.position||null,resultsCount:data.resultsCount??null,collectionCount:data.collectionCount??null,filters,changedFilters,sort:data.sort||null,action:data.action||null,outcome:data.outcome||null,provider:data.provider||null,latencyMs:data.latencyMs??null,path:location.pathname,destination,preview:window.Shopify?.theme?.role!=="main",release:"discovery-v2"};if((type==="search_results"||type==="collection_results")&&(renderedContexts.set(event.impressionId,{impressionId:event.impressionId,queryId:event.queryId,query:event.query,ruleId:event.ruleId,strategy:event.strategy,surface:event.surface}),renderedContexts.size>32&&renderedContexts.delete(renderedContexts.keys().next().value)),type==="product_view"){const key="product-view:"+location.pathname+":"+event.productId;if(seen.has(key))return!0;seen.add(key)}return hydrate(),queue.push(event),save(event),prune(),clearTimeout(timer),timer=setTimeout(flush,900),event.preview||(window.dataLayer=window.dataLayer||[],window.dataLayer.push({event:"fh_"+type,search_term:event.query,collection_handle:event.collection,result_count:event.resultsCount,provider:event.provider,discovery_preview:event.preview,discovery_surface:event.surface})),!0}const OUTBOX="fh-discovery-outbox-v3:",TTL=864e5,MAX_EVENTS=500,MAX_BYTES=1048576,BATCH_BYTES=2e4;let retries=0,retryNotBefore=0,hydrated=!1,deliveryGeneration=0,reportedLoss=0;const delivery={dropped:0,expired:0,rejected:0,storageErrors:0},bytes=value=>new TextEncoder().encode(JSON.stringify(value)).length;function removeStored(event){try{localStorage.removeItem(OUTBOX+event.eventId)}catch{delivery.storageErrors++}}function save(event){try{localStorage.setItem(OUTBOX+event.eventId,JSON.stringify(event))}catch{delivery.storageErrors++}}function hydrate(){if(!(hydrated||!allowed())){hydrated=!0;try{const existing=new Set(queue.map(e=>e.eventId));for(let i=0;i<localStorage.length;i++){const key=localStorage.key(i);if(key?.startsWith(OUTBOX))try{const e=JSON.parse(localStorage.getItem(key));e?.eventId&&!existing.has(e.eventId)&&(queue.push(e),existing.add(e.eventId))}catch{}}}catch{delivery.storageErrors++}prune()}}function prune(){queue=queue.filter(e=>!e.occurredAt||Date.now()-Date.parse(e.occurredAt)>TTL?(removeStored(e),delivery.expired++,!1):!0);let size=queue.reduce((n,e)=>n+bytes(e),0);for(;queue.length>MAX_EVENTS||size>MAX_BYTES;){const e=queue.shift();size-=bytes(e),removeStored(e),delivery.dropped++}}function erase(){deliveryGeneration++,consentEpoch++,inflight?.abort(),retries=0,retryNotBefore=0,renderedContexts.clear(),queue=[],hydrated=!1,clearTimeout(timer);try{const keys=[];for(let i=0;i<localStorage.length;i++){const k=localStorage.key(i);k?.startsWith(OUTBOX)&&keys.push(k)}keys.forEach(k=>localStorage.removeItem(k))}catch{}}function withdrawConsent(){erase(),seen.clear(),cartOperations.clear(),session=null,queryId=null,impressionId=null;try{sessionStorage.removeItem("fh-discovery-session-v2"),sessionStorage.removeItem("fh-discovery-context-v2")}catch{}}async function sendBatch(batch,signal){const started=performance.now(),timeout=7e3,maxBytes=65536,controller=new AbortController,abort=()=>controller.abort(),aborted=()=>Object.assign(new Error("Analytics delivery aborted"),{name:"AbortError"}),cancel=body=>{try{body?.cancel?.().catch(()=>{})}catch{}};let rejectStopped;const stopped=new Promise((_,reject)=>{rejectStopped=reject}),stop=()=>rejectStopped(aborted());controller.signal.addEventListener("abort",stop,{once:!0}),signal.addEventListener("abort",abort,{once:!0});const timer2=setTimeout(abort,timeout),check=()=>
1{if(signal.aborted||controller.signal.aborted||performance.now()-started>=timeout)throw abort(),aborted()};let response,reader,complete=!1;try{if(signal.aborted)throw aborted();const request=fetch((window.Shopify?.routes?.root||"/")+"apps/search/events",{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({consent:!0,events:batch}),keepalive:!0,signal:controller.signal}).then(value=>{if(controller.signal.aborted)throw cancel(value.body),aborted();return value});if(response=await Promise.race([request,stopped]),check(),!response.ok)return{ok:!1,retryAfter:response.headers.get("Retry-After")};const length=response.headers.get("content-length");if(length&&/^\d+$/.test(length)&&Number(length)>maxBytes)throw Error("Analytics response too large");if(!response.body?.getReader)throw Error("Analytics response body unavailable");reader=response.body.getReader();const decoder=new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0});let size=0,chunks=0,empty=0,text="";for(;;){check(),++chunks%64===0&&await Promise.race([new Promise(resolve=>setTimeout(resolve,0)),stopped]);const{value,done}=await Promise.race([reader.read(),stopped]);if(check(),done){complete=!0;break}if(!value.byteLength){if(++empty>32)throw Error("Invalid analytics stream");continue}if(empty=0,size+=value.byteLength,size>maxBytes)throw Error("Analytics response too large");text+=decoder.decode(value,{stream:!0})}text+=decoder.decode(),check();const result=JSON.parse(text),object=value=>value!==null&&typeof value=="object"&&!Array.isArray(value),rejected=result?.rejected===void 0?[]:result.rejected;if(!object(result)||result.ok!==!0||!Array.isArray(result.acknowledged)||!Array.isArray(rejected)||result.acknowledged.length+rejected.length>batch.length)throw Error("Invalid analytics acknowledgement");const sent=new Set(batch.map(e=>e.eventId)),ack=new Set,bad=new Set;for(const id of result.acknowledged){if(typeof id!="string"||!sent.has(id)||ack.has(id))throw Error("Invalid analytics acknowledgement");ack.add(id)}for(const entry of rejected){if(!object(entry)||typeof entry.eventId!="string"||!sent.has(entry.eventId)||bad.has(entry.eventId)||ack.has(entry.eventId)||entry.reason!=="invalid_event"||!Number.isInteger(entry.index)||batch[entry.index]?.eventId!==entry.eventId)throw Error("Invalid analytics rejection");bad.add(entry.eventId)}if(result.accepted!==void 0&&result.accepted!==ack.size||result.inserted!==void 0&&(!Number.isInteger(result.inserted)||result.inserted<0||result.inserted>ack.size))throw Error("Invalid analytics acknowledgement");return check(),{ok:!0,ack,bad}}finally{if(clearTimeout(timer2),signal.removeEventListener("abort",abort),controller.signal.removeEventListener("abort",stop),complete||(controller.abort(),cancel(reader||response?.body)),reader)try{reader.releaseLock()}catch{}}}function retryDeadline(value){if(typeof value!="string"||value.length>128)return 0;const text=value.trim(),now=Date.now(),at=/^\d+$/.test(text)?now+Number(text)*1e3:Date.parse(text);return Number.isSafeInteger(at)&&at>now?at:0}async function flush(){if(!allowed()){withdrawConsent();return}if(hydrate(),prune(),inflight||!queue.length)return;if(Date.now()<retryNotBefore){clearTimeout(timer),timer=setTimeout(flush,Math.min(3e5,retryNotBefore-Date.now()));return}const loss=delivery.dropped+delivery.expired+delivery.rejected;if(loss>reportedLoss){const delta=loss-reportedLoss;reportedLoss=loss,track("delivery_health",{surface:"navigation",query:"",droppedEvents:delta})}const batch=[];for(const e of queue){if(batch.length===20)break;if(bytes({consent:!0,events:[...batch,e]})>BATCH_BYTES){if(!batch.length){queue=queue.filter(x=>x!==e),removeStored(e),delivery.dropped++;continue}break}batch.push(e)}if(!batch.length)return;const controller=new AbortController;inflight=controller;const generation=deliveryGeneration,epoch=consentEpoch,current=()=>generation!==deliveryGeneration||epoch!==consentEpoch?!1:allowed()?!controller.signal.aborted:(withdrawConsent(),!1);let delay=1e3;try{const result=await sendBatch(batch,controller.signal);if(!current())return;if(result.ok){const{ack,bad}=result;queue=queue.filter(e=>ack.has(e.eventId)||bad.has(e.eventId)?(removeStored(e),!1):!0),delivery.rejected+=bad.size,retries=ack.size||bad.size?0:retries+1,document.documentElement.dataset.fhAnalytics=ack.size?"persisted":"awaiting-ack"}
1else retries++,retryNotBefore=retryDeadline(result.retryAfter),document.documentElement.dataset.fhAnalytics="retrying"}catch{current()&&(retries++,document.documentElement.dataset.fhAnalytics="offline")}finally{generation===deliveryGeneration&&epoch===consentEpoch&&!allowed()&&withdrawConsent(),inflight===controller&&(inflight=null),allowed()&&queue.length&&(delay=Math.max(delay,Math.min(6e4,1e3*2**Math.min(retries,6))*(1+Math.random()*.25),retryNotBefore-Date.now()),clearTimeout(timer),timer=setTimeout(flush,Math.min(3e5,delay)))}}function select(type,a,data={}){const prior=context(),matching=prior?.destination===location.pathname&&(!data.query||safeTerm(data.query)===prior.query)?prior:null;if(data.query=safeTerm(data.query||matching?.query||""),data.query&&data.queryId===void 0&&beginQuery(data.query),selected=!0,track(type,{...data,destination:a.href,action:"select"}),allowed())try{sessionStorage.setItem("fh-discovery-context-v2",JSON.stringify({query:data.query,queryId:data.queryId!==void 0?data.queryId:data.query?queryId:null,impressionId:data.impressionId!==void 0?data.impressionId:impressionId||matching?.impressionId||null,ruleId:data.ruleId||matching?.ruleId||null,strategy:data.strategy||matching?.strategy||"baseline",productId:data.productId?String(data.productId):null,destination:new URL(a.href).pathname,at:Date.now()}))}catch{}flush()}document.addEventListener("click",e=>{const a=e.target.closest("a");if(!a)return;if(a.dataset.recoveryRule){select("recovery_select",a,{surface:a.dataset.recoverySurface||"suggest",query:a.dataset.recoveryQuery,ruleId:a.dataset.recoveryRule,position:Number(a.dataset.recoveryPosition)||null,impressionId:a.dataset.recoveryImpression||null});return}if(a.closest("[data-catalog-product],.fh-search-product")){const grid=a.closest("fh-catalog"),results=a.closest("[data-search-results]"),rendered=resultContextFor(grid||results);select("product_select",a,{...rendered,impressionId:rendered?.impressionId??null,surface:rendered?.surface||grid?.dataset.surface||"suggest",query:rendered?.query??(grid?.dataset.query||a.closest("fh-discovery")?.querySelector("[data-query]")?.value||""),collection:grid?.dataset.collection||null,strategy:rendered?.strategy||grid?.dataset.strategy||results?.dataset.strategy||"baseline",productId:a.dataset.productId||a.href.split("/products/")[1]?.split("?")[0],position:Number(a.dataset.position)||1})}else if(a.pathname?.startsWith("/collections/")&&a.closest("fh-discovery,fh-catalog")){const rendered=resultContextFor(a.closest("[data-search-results]"));select("collection_select",a,{...rendered,impressionId:rendered?.impressionId??null,surface:rendered?.surface||(a.closest("[data-search]")?"suggest":"navigation"),query:rendered?.query??"",collection:a.pathname.split("/collections/")[1]})}else a.pathname==="/pages/customs"&&a.closest("fh-discovery,fh-catalog")&&select("customs_select",a,{surface:"navigation",impressionId:null,query:""})},!0);const cartOperations=new Set;document.addEventListener("fathead:cart:operation",e=>{const detail=e.detail,prior=allowed()?context():null;if(!prior||prior.destination!==location.pathname||!prior.productId||!prior.query||detail?.event_name!=="add_succeeded"||detail.outcome!=="confirmed"||!detail.operation_id)return;const canonical=value=>String(value).replace(/^gid:\/\/shopify\/Product\//,"");!Array.isArray(detail.product_ids)||!detail.product_ids.some(id=>canonical(id)===canonical(prior.productId))||cartOperations.has(detail.operation_id)||(cartOperations.size>=200&&cartOperations.delete(cartOperations.values().next().value),cartOperations.add(detail.operation_id),track("add_to_cart",{operationId:detail.operation_id,surface:"product",query:prior.query,productId:canonical(prior.productId),impressionId:prior.impressionId,ruleId:prior.ruleId,strategy:prior.strategy,action:"apply",outcome:"selected"}),flush())}),document.addEventListener("submit",e=>{if(e.target.matches("[data-search-form],[data-full-search]")){const q=new FormData(e.target).get("q");if(beginQuery(q),allowed())try{sessionStorage.setItem("fh-discovery-context-v2",JSON.stringify({query:safeTerm(q),queryId,destination:new URL(e.target.action).pathname,at:Date.now()}))}catch{}track("search_submit",{surface:e.target.matches("[data-search-form]")?"suggest":"search",query:q,action:"submit"}),flush()}},!0),document.addEventListener("click",e=>{const a=e.target.closest(".fh-search-all");if(a){const q=new URL(a.href).searchParams.get("q");
1if(beginQuery(q),allowed())try{sessionStorage.setItem("fh-discovery-context-v2",JSON.stringify({query:safeTerm(q),queryId,destination:new URL(a.href).pathname,at:Date.now()}))}catch{}track("search_submit",{surface:"suggest",query,action:"submit"}),flush()}},!0),document.addEventListener("visibilitychange",()=>{document.hidden&&flush()}),window.addEventListener("pagehide",flush),window.addEventListener("online",()=>{retries=0,flush()});const ready=()=>{if(!allowed()){withdrawConsent();return}hydrate(),flush(),document.dispatchEvent(new CustomEvent("fh:analytics:ready"));const product=document.querySelector("[data-fh-product-id]");product&&track("product_view",{surface:"product",query:context()?.destination===location.pathname?context().query:"",productId:product.dataset.fhProductId})};document.addEventListener("visitorConsentCollected",ready);const productObservers=new WeakMap;function observeProducts(container,data={}){if(!container||!allowed())return;productObservers.get(container)?.disconnect();const currentImpression=data.impressionId||impressionId,context2={...data,...renderedContexts.get(currentImpression),impressionId:currentImpression},binding={context:context2,epoch:consentEpoch};if(productContexts.set(container,binding),!window.IntersectionObserver)return;const observer=new IntersectionObserver(entries=>{if(productContexts.get(container)!==binding||binding.epoch!==consentEpoch)return;const products=[];for(const entry of entries)if(entry.isIntersecting&&entry.intersectionRatio>=.25){const a=entry.target;products.push({id:a.dataset.productId,position:Number(a.dataset.position)||1}),observer.unobserve(a)}products.length&&track("product_impressions",{...context2,products})},{threshold:.25});container.querySelectorAll("[data-product-id][data-position]").forEach(a=>observer.observe(a)),productObservers.set(container,observer)}window.FHDiscoveryAnalytics={track,beginQuery,flush,observeProducts,open(){openedAt=Date.now(),selected=!1,impressionId=null,queryId=null,track("search_open",{surface:"suggest",query:""})},close(){openedAt&&(track("search_exit",{surface:"suggest",outcome:selected?"selected":"abandoned"}),openedAt=0,flush())},results(data){return beginQuery(data.query),impressionId=uuid(),track("search_results",{surface:"suggest",...data,impressionId}),impressionId},health(){return{...delivery,queued:queue.length}},safeTerm},window.Shopify?.customerPrivacy?ready():window.Shopify?.loadFeatures?.([{name:"consent-tracking-api",version:"0.1"}],error=>{error||ready()})})();
2//# sourceMappingURL=/cdn/shop/t/342/assets/fh-discovery-analytics.js.map?v=55393042665626544401790031334

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.