const win=window,doc=win.document,consoleLogPrefix="[Optimization Detective]",storageLockTimeSessionKey="odStorageLockTime";function isStorageLocked(e,t){if(0===t)return!1;try{const n=parseInt(sessionStorage.getItem("odStorageLockTime"));return!isNaN(n)&&en.minimumViewportWidth&&(null===n.maximumViewportWidth||e<=n.maximumViewportWidth))return n;throw new Error(`${consoleLogPrefix} Unexpectedly unable to locate group for the current viewport width.`)}async function getAlreadySubmittedSessionStorageKey(e,t,n,{warn:o,error:r}){if(!window.crypto||!window.crypto.subtle)return o("Unable to generate sessionStorage key for already-submitted URL since crypto is not available, likely due to to the page not being served via HTTPS."),null;try{const o=[e,t,n.minimumViewportWidth,n.maximumViewportWidth||""].join("-"),r=(new TextEncoder).encode(o),i=await crypto.subtle.digest("SHA-1",r);return`odSubmitted-${Array.from(new Uint8Array(i)).map((e=>e.toString(16).padStart(2,"0"))).join("")}`}catch(e){return r("Unable to generate sessionStorage key for already-submitted URL due to error:",e),null}}function getCurrentTime(){return Date.now()}function recursiveFreeze(e){for(const t of Object.getOwnPropertyNames(e)){const n=e[t];null!==n&&"object"==typeof n&&recursiveFreeze(n)}Object.freeze(e)}let urlMetric;const reservedRootPropertyKeys=new Set(["url","viewport","elements"]);function getRootData(){const e=structuredClone(urlMetric);return recursiveFreeze(e),e}function extendRootData(e){for(const t of Object.getOwnPropertyNames(e))if(reservedRootPropertyKeys.has(t))throw new Error(`Disallowed setting of key '${t}' on root.`);Object.assign(urlMetric,e)}const elementsByXPath=new Map,reservedElementPropertyKeys=new Set(["isLCP","isLCPCandidate","xpath","intersectionRatio","intersectionRect","boundingClientRect"]);function getElementData(e){const t=elementsByXPath.get(e);if(t){const e=structuredClone(t);return recursiveFreeze(e),e}return null}function extendElementData(e,t){if(!elementsByXPath.has(e))throw new Error(`Unknown element with XPath: ${e}`);for(const e of Object.getOwnPropertyNames(t))if(reservedElementPropertyKeys.has(e))throw new Error(`Disallowed setting of key '${e}' on element.`);const n=elementsByXPath.get(e);Object.assign(n,t)}export default async function detect({minViewportAspectRatio:e,maxViewportAspectRatio:t,isDebug:n,extensionModuleUrls:o,restApiEndpoint:r,restApiNonce:i,currentETag:s,currentUrl:a,urlMetricSlug:c,cachePurgePostId:l,urlMetricHMAC:d,urlMetricGroupStatuses:u,storageLockTTL:g,freshnessTTL:m,webVitalsLibrarySrc:f,urlMetricGroupCollection:p}){const w=createLogger(n,consoleLogPrefix),{log:h,warn:y,error:L}=w;if(n){const e=[];for(const t of p.groups)for(const n of t.url_metrics)n.creationDate=new Date(1e3*n.timestamp),e.push(n);h("Stored URL Metric Group Collection:",p),e.sort(((e,t)=>t.timestamp-e.timestamp)),h("Stored URL Metrics in reverse chronological order:",e)}if(0===win.innerWidth||0===win.innerHeight)return void h("Window must have non-zero dimensions for URL Metric collection.");const b=getGroupForViewportWidth(win.innerWidth,u);if(b.complete)return void h("No need for URL Metrics from the current viewport.");const S=await getAlreadySubmittedSessionStorageKey(s,a,b,w);if(null!==S&&S in sessionStorage){const e=parseInt(sessionStorage.getItem(S),10);if(!isNaN(e)&&(getCurrentTime()-e)/1e3t)return void y(`Viewport aspect ratio (${P}) is not in the accepted range of ${e} to ${t}.`);if(await new Promise((e=>{"loading"!==doc.readyState?e():doc.addEventListener("DOMContentLoaded",e,{once:!0})})),await new Promise((e=>{"complete"===doc.readyState?e():win.addEventListener("load",e,{once:!0})})),"function"==typeof requestIdleCallback&&await new Promise((e=>{requestIdleCallback(e)})),isStorageLocked(getCurrentTime(),g))return void y("Aborted detection due to storage being locked.");let v=!1;window.addEventListener("resize",(()=>{v=!0}),{once:!0});const{onTTFB:R,onFCP:M,onLCP:C,onINP:x,onCLS:E}=await import(f);if(doc.documentElement.scrollTop>0)return void y("Aborted detection since initial scroll position of page is not at the top.");h("Proceeding with detection");const z=new Map,D=[],U=[];for(const e of o)try{const t=await import(e);z.set(e,t);const o=createLogger(n,`[Optimization Detective: ${t.name||"Unnamed Extension"}]`);if(t.initialize instanceof Function){const r=t.initialize({isDebug:n,...o,onTTFB:R,onFCP:M,onLCP:C,onINP:x,onCLS:E});r instanceof Promise&&(D.push(r),U.push(e))}}catch(t){L(`Failed to start initializing extension '${e}':`,t)}const T=await Promise.allSettled(D);for(const[e,t]of T.entries())"rejected"===t.status&&L(`Failed to initialize extension '${U[e]}':`,t.reason);const k=doc.body.querySelectorAll("[data-od-xpath]"),$=new Map([...k].map((e=>[e,e.dataset.odXpath]))),O=[];let A;function F(){A instanceof IntersectionObserver&&(A.disconnect(),win.removeEventListener("scroll",F))}$.size>0&&(await new Promise((e=>{A=new IntersectionObserver((t=>{for(const e of t)O.push(e);e()}),{root:null,threshold:0});for(const e of $.keys())A.observe(e)})),win.addEventListener("scroll",F,{once:!0,passive:!0}));const I=[];await new Promise((e=>{C((t=>{I.push(t),e()}),{reportAllChanges:!0})})),F(),h("Detection is stopping."),urlMetric={url:a,viewport:{width:win.innerWidth,height:win.innerHeight},elements:[]};const j=I.at(-1);for(const e of O){const t=$.get(e.target);if(!t){y("Unable to look up XPath for element");continue}const n=j?.entries[0]?.element,o={isLCP:e.target===n,isLCPCandidate:!!I.find((t=>{const n=t.entries[0]?.element;return n===e.target})),xpath:t,intersectionRatio:e.intersectionRatio,intersectionRect:e.intersectionRect,boundingClientRect:e.boundingClientRect};urlMetric.elements.push(o),elementsByXPath.set(o.xpath,o)}if(h("Current URL Metric:",urlMetric),await new Promise((e=>{win.addEventListener("pagehide",e,{once:!0}),win.addEventListener("pageswap",e,{once:!0}),doc.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState&&e()}),{once:!0})})),v)return void h("Aborting URL Metric collection due to viewport size change.");if(z.size>0){const e=[],t=[];for(const[o,r]of z.entries())if(r.finalize instanceof Function){const i=createLogger(n,`[Optimization Detective: ${r.name||"Unnamed Extension"}]`);try{const s=r.finalize({isDebug:n,...i,getRootData,getElementData,extendElementData,extendRootData});s instanceof Promise&&(e.push(s),t.push(o))}catch(e){L(`Unable to start finalizing extension '${o}':`,e)}}const o=await Promise.allSettled(e);for(const[e,n]of o.entries())"rejected"===n.status&&L(`Failed to finalize extension '${t[e]}':`,n.reason)}const N=JSON.stringify(urlMetric),W=new Blob([N],{type:"application/json"}),B=W.size/64e3*100;if(W.size>65536)return void L(`Unable to send URL Metric because it is ${W.size.toLocaleString()} bytes, ${Math.round(B)}% of 64 KiB limit:`,urlMetric);setStorageLock(getCurrentTime()),null!==S&&sessionStorage.setItem(S,String(getCurrentTime()));const V=`Sending URL Metric (${W.size.toLocaleString()} bytes, ${Math.round(B)}% of 64 KiB limit):`;B<50?h(V,urlMetric):y(V,urlMetric);const K=new URL(r);"string"==typeof i&&K.searchParams.set("_wpnonce",i),K.searchParams.set("slug",c),K.searchParams.set("current_etag",s),"number"==typeof l&&K.searchParams.set("cache_purge_post_id",l.toString()),K.searchParams.set("hmac",d),navigator.sendBeacon(K,W),$.clear()}