!function (global) { const SmartLoad = { assets: [], functions: [], loaded: new Set(), isLibraryAvailable: n => typeof global[n] !== "undefined" }; const EVENTS = ["keydown", "mousemove", "wheel", "touchmove", "touchstart", "touchend"]; const MAX_RETRIES = 3; let hasStarted = false, timeoutHandle; const sleep = ms => new Promise(r => setTimeout(r, ms)); const validURL = /^(https?:)?\/\/[^\s]+$/; async function loadAsset({ type, src, location = "body", callback }) { if (SmartLoad.loaded.has(src)) return; for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) { try { await new Promise((resolve, reject) => { let el; switch (type) { case "script": el = document.createElement("script"); el.src = src; el.async = false; // maintain order el.onload = resolve; el.onerror = reject; (location === "head" ? document.head : document.body).appendChild(el); break; case "style": el = document.createElement("link"); el.rel = "stylesheet"; el.href = src; el.onload = resolve; el.onerror = reject; document.head.appendChild(el); break; } }); SmartLoad.loaded.add(src); callback?.(); return; } catch { const delay = Math.min(2 ** attempt * 200, 4000); console.warn(`[SmartLoad] Retry ${attempt}/${MAX_RETRIES} for ${src} in ${delay}ms`); await sleep(delay); } } console.error(`[SmartLoad] Failed to load after ${MAX_RETRIES} attempts: ${src}`); } async function loadSequential(assets, onComplete) { for (const asset of assets) await loadAsset(asset); onComplete?.(); } function processSmartLoadTags() { const els = document.querySelectorAll("[smartload]"); const assets = []; els.forEach(el => { const src = el.getAttribute("smartload"); if (!validURL.test(src)) return console.error(`[SmartLoad] Invalid URL: ${src}`); const tag = el.tagName; if (tag === "SCRIPT") assets.push({ type: "script", src }); else if (tag === "LINK") assets.push({ type: "style", src }); else if (tag === "IFRAME") observeIframe(el, src); el.removeAttribute("smartload"); }); if (assets.length) loadSequential(assets); } function observeIframe(el, src) { const obs = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.src = src; obs.unobserve(entry.target); } }); }); obs.observe(el); } function setupSmartVideos() { const videoDivs = document.querySelectorAll("[smartload-video]"); const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); videoDivs.forEach(div => { const videoUrl = div.getAttribute("smartload-video"); if (!validURL.test(videoUrl)) { console.error(`[SmartLoad] Invalid video URL: ${videoUrl}`); return; } // accessibility + cursor div.setAttribute("role", "button"); div.setAttribute("tabindex", "0"); div.setAttribute("aria-label", "Play video"); div.style.cursor = "pointer"; const loadVideo = () => { let videoFinalUrl = videoUrl; // Add enablejsapi=1 only for Safari if (isSafari && !/[?&]enablejsapi=/.test(videoFinalUrl)) { videoFinalUrl += (videoFinalUrl.includes("?") ? "&" : "?") + "enablejsapi=1"; } const iframe = document.createElement("iframe"); iframe.src = videoFinalUrl; iframe.allow = "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"; iframe.allowFullscreen = true; iframe.frameBorder = "0"; iframe.width = "100%"; iframe.height = "100%"; iframe.setAttribute("class", "iframe-youtube"); iframe.style.display = "block"; // ensure visibility div.innerHTML = ""; // Clear placeholder content div.appendChild(iframe); iframe.offsetHeight; // Force layout requestAnimationFrame(() => { iframe.style.transform = "translateZ(0)"; iframe.style.willChange = "transform"; }); // Safari autoplay helper (use JS API) if (isSafari) { iframe.addEventListener("load", () => { try { iframe.contentWindow?.postMessage( JSON.stringify({ event: "command", func: "playVideo", args: [] }), "*" ); } catch (err) { console.warn("[SmartLoad] Safari playVideo postMessage failed:", err); } }); } div.removeAttribute("role", "button"); div.removeAttribute("tabindex", "0"); div.removeAttribute("aria-label", "Play video"); div.removeAttribute("smartload-video"); div.style.cursor = "default"; }; // The `{ once: true }` ensures this only runs the first time div.addEventListener("click", loadVideo, { once: true }); }); } function triggerLoad() { if (hasStarted) return; hasStarted = true; clearTimeout(timeoutHandle); loadSequential(SmartLoad.assets, () => { SmartLoad.functions.forEach(fn => fn instanceof Function && fn()); processSmartLoadTags(); setupSmartVideos(); }); } document.addEventListener("DOMContentLoaded", () => { processSmartLoadTags(); setupSmartVideos(); EVENTS.forEach(e => document.addEventListener(e, triggerLoad, { once: true })); timeoutHandle = setTimeout(triggerLoad, 10000); }); global.smartload = SmartLoad; }(this);