File: /var/www/html/wp-content/plugins/optimization-detective/detect.min.js
const win=window,doc=win.document,consoleLogPrefix="[Optimization Detective]",storageLockTimeSessionKey="odStorageLockTime",compressionDebounceWaitDuration=1e3;function isStorageLocked(e,t){if(0===t)return!1;try{const n=parseInt(sessionStorage.getItem("odStorageLockTime"));return!isNaN(n)&&e<n+1e3*t}catch(e){return!1}}function setStorageLock(e){try{sessionStorage.setItem("odStorageLockTime",String(e))}catch(e){}}function createLogger(e=!1,t=null,n=null){const o=n?`\nSource: ${n}`:null,i=(e,n=!1)=>[t,...e,n?o:null].filter((e=>null!==e));return{log(...t){e&&console.log(...i(t,!1))},info(...t){e&&console.info(...i(t,!1))},warn(...t){e&&console.warn(...i(t,!0))},error(...e){console.error(...i(e,!0))}}}function getExtensionNameFromScriptModuleUrl(e){try{const t=new URL(e,win.location.href),n=t.pathname.match(/\/(?:themes|plugins)\/([^\/]+)\//);return n?n[1]:t.pathname}catch(t){return e}}function getGroupForViewportWidth(e,t){for(const n of t)if(e>n.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:i}){if(!win.crypto||!win.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("-"),i=(new TextEncoder).encode(o),r=await crypto.subtle.digest("SHA-1",i);return`odSubmitted-${Array.from(new Uint8Array(r)).map((e=>e.toString(16).padStart(2,"0"))).join("")}`}catch(e){return i("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),debounceCompressUrlMetric()}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),debounceCompressUrlMetric()}async function compress(e){const t=(new TextEncoder).encode(e),n=new Blob([t]).stream().pipeThrough(new CompressionStream("gzip")),o=await new Response(n).arrayBuffer();return new Blob([o],{type:"application/gzip"})}let compressedPayload=null,recompressionTimeout=null,idleCallbackHandle=null,compressionEnabled=!0;function debounceCompressUrlMetric(){compressionEnabled&&(null!==recompressionTimeout&&(clearTimeout(recompressionTimeout),recompressionTimeout=null),null!==idleCallbackHandle&&"function"==typeof cancelIdleCallback&&(cancelIdleCallback(idleCallbackHandle),idleCallbackHandle=null),recompressionTimeout=setTimeout((async()=>{"function"==typeof requestIdleCallback&&(await new Promise((e=>{idleCallbackHandle=requestIdleCallback(e)})),idleCallbackHandle=null);try{compressedPayload=await compress(JSON.stringify(urlMetric))}catch(e){const{error:t}=createLogger(!1,consoleLogPrefix);t("Failed to compress URL Metric falling back to sending uncompressed data:",e),compressionEnabled=!1}recompressionTimeout=null}),1e3))}export default async function detect({minViewportAspectRatio:e,maxViewportAspectRatio:t,isDebug:n,extensionModuleUrls:o,restApiEndpoint:i,restApiNonce:r,gzdecodeAvailable:s,maxUrlMetricSize:a,currentETag:c,currentUrl:l,urlMetricSlug:d,cachePurgePostId:u,urlMetricHMAC:m,urlMetricGroupStatuses:p,storageLockTTL:g,freshnessTTL:f,webVitalsLibrarySrc:h,urlMetricGroupCollection:w}){const b=createLogger(n,consoleLogPrefix),{log:y,warn:L,error:S}=b;if(compressionEnabled=s,n&&Array.isArray(w?.groups)){const e=[];for(const t of w.groups)for(const n of t.url_metrics)n.creationDate=new Date(1e3*n.timestamp),e.push(n);y("Stored URL Metric Group Collection:",w),e.sort(((e,t)=>t.timestamp-e.timestamp)),y("Stored URL Metrics in reverse chronological order:",e)}if(0===win.innerWidth||0===win.innerHeight)return void y("Window must have non-zero dimensions for URL Metric collection.");if("hidden"===doc.visibilityState&&!doc.prerendering)return void y("Page opened in background tab so URL Metric is not collected.");const P=getGroupForViewportWidth(win.innerWidth,p);if(P.complete)return void y("No need for URL Metrics from the current viewport.");const v=await getAlreadySubmittedSessionStorageKey(c,l,P,b);if(null!==v&&v in sessionStorage){const e=parseInt(sessionStorage.getItem(v),10);if(!isNaN(e)&&(f<0||(getCurrentTime()-e)/1e3<f))return void y("The current client session already submitted a fresh URL Metric for this URL so a new one will not be collected now.")}const M=win.innerWidth/win.innerHeight;if(M<e||M>t)return void L(`Viewport aspect ratio (${M}) is not in the accepted range of ${e} to ${t}.`);if(isStorageLocked(getCurrentTime(),g))return void L("Aborted detection due to storage being locked.");let C=!1;win.addEventListener("resize",(()=>{C=!0}),{once:!0});const{onTTFB:R,onFCP:E,onLCP:x,onINP:z,onCLS:U}=await import(h);if(doc.documentElement.scrollTop>0)return void L("Aborted detection since initial scroll position of page is not at the top.");y("Proceeding with detection");const T=doc.body.querySelectorAll("[data-od-xpath]"),D=new Map([...T].map((e=>[e,e.getAttribute("data-od-xpath")]))),k=[];let $;function F(){$ instanceof IntersectionObserver&&($.disconnect(),win.removeEventListener("scroll",F))}D.size>0&&(await new Promise((e=>{$=new IntersectionObserver((t=>{for(const e of t)k.push(e);e()}),{root:null,threshold:0});for(const e of D.keys())$.observe(e)})),win.addEventListener("scroll",F,{once:!0,passive:!0}));const A=[];await new Promise((e=>{x((t=>{A.push(t),e()}),{reportAllChanges:!0})})),F(),urlMetric={url:l,viewport:{width:win.innerWidth,height:win.innerHeight},elements:[]};const O=A[A.length-1];for(const e of k){const t=D.get(e.target);if(!t){L("Unable to look up XPath for element");continue}const n=O?.entries[0]?.element,o={isLCP:e.target===n,isLCPCandidate:!!A.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)}D.clear();const N=new Map;let I=!1;const j=[],B=[];await Promise.all(o.map((async e=>{const t=await import(e);N.set(e,t)})));for(const[e,t]of N.entries())try{const o=createLogger(n,`[Optimization Detective: ${t.name||getExtensionNameFromScriptModuleUrl(e)}]`,e);if(t.initialize instanceof Function){const i=t.initialize({isDebug:n,...o,onTTFB:R,onFCP:E,onLCP:x,onINP:z,onCLS:U,getRootData,extendRootData,getElementData,extendElementData});i instanceof Promise&&(j.push(i),B.push(e))}t.finalize instanceof Function&&(o.warn("Use of the finalize function in extensions is deprecated. Please refactor your extension to use the initialize function instead, and update the URL Metric data as soon as a change is detected rather than waiting until finalization."),I=!0)}catch(t){S(`Failed to start initializing extension '${e}':`,t)}const W=await Promise.allSettled(j);for(const[e,t]of W.entries())"rejected"===t.status&&S(`Failed to initialize extension '${B[e]}':`,t.reason);if(compressionEnabled&&I&&(compressionEnabled=!1,L("URL Metric compression is disabled because one or more extensions use the deprecated finalize function.")),y("Current URL Metric:",urlMetric),debounceCompressUrlMetric(),await new Promise((e=>{win.addEventListener("pagehide",e,{once:!0}),win.addEventListener("pageswap",e,{once:!0}),doc.addEventListener("visibilitychange",(()=>{"hidden"===doc.visibilityState&&e()}),{once:!0})})),C)return void y("Aborting URL Metric collection due to viewport size change.");if(N.size>0){const e=[],t=[];for(const[o,i]of N.entries())if(i.finalize instanceof Function){const r=createLogger(n,`[Optimization Detective: ${i.name||getExtensionNameFromScriptModuleUrl(o)}]`,o);try{const s=i.finalize({isDebug:n,...r,getRootData,getElementData,extendElementData,extendRootData});s instanceof Promise&&(e.push(s),t.push(o))}catch(e){S(`Unable to start finalizing extension '${o}':`,e)}}const o=await Promise.allSettled(e);for(const[e,n]of o.entries())"rejected"===n.status&&S(`Failed to finalize extension '${t[e]}':`,n.reason)}const H=JSON.stringify(urlMetric);if(H.length>a)return void S(`URL Metric is ${H.length.toLocaleString()} bytes, exceeding the maximum size of ${a.toLocaleString()} bytes:`,urlMetric);compressionEnabled=compressionEnabled&&null!==compressedPayload;const V=compressionEnabled?compressedPayload:new Blob([H],{type:"application/json"}),K=V.size/64e3*100;if(V.size>65536)return void S(`Unable to send URL Metric because it is ${V.size.toLocaleString()} bytes, ${Math.round(K)}% of 64 KiB limit:`,urlMetric);setStorageLock(getCurrentTime()),null!==v&&sessionStorage.setItem(v,String(getCurrentTime()));let X="Sending URL Metric (";X+=`${V.size.toLocaleString()} bytes`,X+=`, ${Math.round(K)}% of 64 KiB limit`,X+=compressionEnabled?`, gzip compressed -${Math.round((H.length-V.size)/H.length*100)}%`:", uncompressed",X+="):",K<50?y(X,urlMetric):L(X,urlMetric);const _=new URL(i);"string"==typeof r&&_.searchParams.set("_wpnonce",r),_.searchParams.set("slug",d),_.searchParams.set("current_etag",c),"number"==typeof u&&_.searchParams.set("cache_purge_post_id",u.toString()),_.searchParams.set("hmac",m);const G={"Content-Type":"application/json"};compressionEnabled&&(G["Content-Encoding"]="gzip");const q=new Request(_,{method:"POST",body:V,headers:G,keepalive:!0});await fetch(q)}