import "./chunk-LK32TJAX.js"; // node_modules/@zumer/snapdom/dist/snapdom.mjs var cache = { image: /* @__PURE__ */ new Map(), background: /* @__PURE__ */ new Map(), resource: /* @__PURE__ */ new Map(), defaultStyle: /* @__PURE__ */ new Map(), baseStyle: /* @__PURE__ */ new Map(), computedStyle: /* @__PURE__ */ new WeakMap(), font: /* @__PURE__ */ new Set(), session: { styleMap: /* @__PURE__ */ new Map(), styleCache: /* @__PURE__ */ new WeakMap(), nodeMap: /* @__PURE__ */ new Map() } }; function applyCachePolicy(policy = "soft") { switch (policy) { case "auto": { cache.session.styleMap = /* @__PURE__ */ new Map(); cache.session.nodeMap = /* @__PURE__ */ new Map(); return; } case "soft": { cache.session.styleMap = /* @__PURE__ */ new Map(); cache.session.nodeMap = /* @__PURE__ */ new Map(); cache.session.styleCache = /* @__PURE__ */ new WeakMap(); return; } case "full": { return; } case "disabled": { cache.session.styleMap = /* @__PURE__ */ new Map(); cache.session.nodeMap = /* @__PURE__ */ new Map(); cache.session.styleCache = /* @__PURE__ */ new WeakMap(); cache.computedStyle = /* @__PURE__ */ new WeakMap(); cache.baseStyle = /* @__PURE__ */ new Map(); cache.defaultStyle = /* @__PURE__ */ new Map(); cache.image = /* @__PURE__ */ new Map(); cache.background = /* @__PURE__ */ new Map(); cache.resource = /* @__PURE__ */ new Map(); cache.font = /* @__PURE__ */ new Set(); return; } default: { cache.session.styleMap = /* @__PURE__ */ new Map(); cache.session.nodeMap = /* @__PURE__ */ new Map(); cache.session.styleCache = /* @__PURE__ */ new WeakMap(); return; } } } function extractURL(value) { const match = value.match(/url\((['"]?)(.*?)(\1)\)/); if (!match) return null; const url = match[2].trim(); if (url.startsWith("#")) return null; return url; } function stripTranslate(transform) { if (!transform || transform === "none") return ""; let cleaned = transform.replace(/translate[XY]?\([^)]*\)/g, ""); cleaned = cleaned.replace(/matrix\(([^)]+)\)/g, (_, values) => { const parts = values.split(",").map((s) => s.trim()); if (parts.length !== 6) return `matrix(${values})`; parts[4] = "0"; parts[5] = "0"; return `matrix(${parts.join(", ")})`; }); cleaned = cleaned.replace(/matrix3d\(([^)]+)\)/g, (_, values) => { const parts = values.split(",").map((s) => s.trim()); if (parts.length !== 16) return `matrix3d(${values})`; parts[12] = "0"; parts[13] = "0"; return `matrix3d(${parts.join(", ")})`; }); return cleaned.trim().replace(/\s{2,}/g, " "); } function safeEncodeURI(uri) { if (/%[0-9A-Fa-f]{2}/.test(uri)) return uri; try { return encodeURI(uri); } catch { return uri; } } function createSnapLogger(prefix = "[snapDOM]", { ttlMs = 5 * 6e4, maxEntries = 12 } = {}) { const seen = /* @__PURE__ */ new Map(); let emitted = 0; function log(level, key, msg) { if (emitted >= maxEntries) return; const now = Date.now(); const until = seen.get(key) || 0; if (until > now) return; seen.set(key, now + ttlMs); emitted++; if (level === "warn" && console && console.warn) console.warn(`${prefix} ${msg}`); else if (console && console.error) console.error(`${prefix} ${msg}`); } return { warnOnce(key, msg) { log("warn", key, msg); }, errorOnce(key, msg) { log("error", key, msg); }, reset() { seen.clear(); emitted = 0; } }; } var snapLogger = createSnapLogger("[snapDOM]", { ttlMs: 3 * 6e4, maxEntries: 10 }); var _inflight = /* @__PURE__ */ new Map(); var _errorCache = /* @__PURE__ */ new Map(); function isSpecialURL(url) { return /^data:|^blob:|^about:blank$/i.test(url); } function isAlreadyProxied(url, useProxy) { try { const baseHref = typeof location !== "undefined" && location.href ? location.href : "http://localhost/"; const proxyBaseRaw = useProxy.includes("{url}") ? useProxy.split("{url}")[0] : useProxy; const proxyBase = new URL(proxyBaseRaw || ".", baseHref); const u = new URL(url, baseHref); if (u.origin === proxyBase.origin) return true; const sp = u.searchParams; if (sp && (sp.has("url") || sp.has("target"))) return true; } catch { } return false; } function shouldProxy(url, useProxy) { if (!useProxy) return false; if (isSpecialURL(url)) return false; if (isAlreadyProxied(url, useProxy)) return false; try { const base = typeof location !== "undefined" && location.href ? location.href : "http://localhost/"; const u = new URL(url, base); return typeof location !== "undefined" ? u.origin !== location.origin : true; } catch { return !!useProxy; } } function applyProxy(url, useProxy) { if (!useProxy) return url; if (useProxy.includes("{url}")) { return useProxy.replace("{urlRaw}", safeEncodeURI(url)).replace("{url}", encodeURIComponent(url)); } if (/[?&]url=?$/.test(useProxy)) { return `${useProxy}${encodeURIComponent(url)}`; } if (useProxy.endsWith("?")) { return `${useProxy}url=${encodeURIComponent(url)}`; } if (useProxy.endsWith("/")) { return `${useProxy}${safeEncodeURI(url)}`; } const sep = useProxy.includes("?") ? "&" : "?"; return `${useProxy}${sep}url=${encodeURIComponent(url)}`; } function blobToDataURL(blob) { return new Promise((res, rej) => { const fr = new FileReader(); fr.onload = () => res(String(fr.result || "")); fr.onerror = () => rej(new Error("read_failed")); fr.readAsDataURL(blob); }); } function makeKey(url, o) { return [ o.as || "blob", o.timeout ?? 3e3, o.useProxy || "", o.errorTTL ?? 8e3, url ].join("|"); } async function snapFetch(url, options = {}) { const as = options.as ?? "blob"; const timeout = options.timeout ?? 3e3; const useProxy = options.useProxy || ""; const errorTTL = options.errorTTL ?? 8e3; const headers = options.headers || {}; const silent = !!options.silent; if (/^data:/i.test(url)) { try { if (as === "text") { return { ok: true, data: String(url), status: 200, url, fromCache: false }; } if (as === "dataURL") { return { ok: true, data: String(url), status: 200, url, fromCache: false, mime: String(url).slice(5).split(";")[0] || "" }; } const [, meta = "", data = ""] = String(url).match(/^data:([^,]*),(.*)$/) || []; const isBase64 = /;base64/i.test(meta); const bin = isBase64 ? atob(data) : decodeURIComponent(data); const bytes = new Uint8Array([...bin].map((c) => c.charCodeAt(0))); const b = new Blob([bytes], { type: (meta || "").split(";")[0] || "" }); return { ok: true, data: b, status: 200, url, fromCache: false, mime: b.type || "" }; } catch { return { ok: false, data: null, status: 0, url, fromCache: false, reason: "special_url_error" }; } } if (/^blob:/i.test(url)) { try { const resp = await fetch(url); if (!resp.ok) { return { ok: false, data: null, status: resp.status, url, fromCache: false, reason: "http_error" }; } const blob = await resp.blob(); const mime = blob.type || resp.headers.get("content-type") || ""; if (as === "dataURL") { const dataURL = await blobToDataURL(blob); return { ok: true, data: dataURL, status: resp.status, url, fromCache: false, mime }; } if (as === "text") { const text = await blob.text(); return { ok: true, data: text, status: resp.status, url, fromCache: false, mime }; } return { ok: true, data: blob, status: resp.status, url, fromCache: false, mime }; } catch { return { ok: false, data: null, status: 0, url, fromCache: false, reason: "network" }; } } if (/^about:blank$/i.test(url)) { if (as === "dataURL") { return { ok: true, data: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==", status: 200, url, fromCache: false, mime: "image/png" }; } return { ok: true, data: as === "text" ? "" : new Blob([]), status: 200, url, fromCache: false }; } const key = makeKey(url, { as, timeout, useProxy, errorTTL }); const e = _errorCache.get(key); if (e && e.until > Date.now()) { return { ...e.result, fromCache: true }; } else if (e) { _errorCache.delete(key); } const inflight = _inflight.get(key); if (inflight) return inflight; const finalURL = shouldProxy(url, useProxy) ? applyProxy(url, useProxy) : url; let cred = options.credentials; if (!cred) { try { const base = typeof location !== "undefined" && location.href ? location.href : "http://localhost/"; const u = new URL(url, base); const sameOrigin = typeof location !== "undefined" && u.origin === location.origin; cred = sameOrigin ? "include" : "omit"; } catch { cred = "omit"; } } const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort("timeout"), timeout); const p = (async () => { try { const resp = await fetch(finalURL, { signal: ctrl.signal, credentials: cred, headers }); if (!resp.ok) { const result = { ok: false, data: null, status: resp.status, url: finalURL, fromCache: false, reason: "http_error" }; if (errorTTL > 0) _errorCache.set(key, { until: Date.now() + errorTTL, result }); if (!silent) { const short = `${resp.status} ${resp.statusText || ""}`.trim(); snapLogger.warnOnce( `http:${resp.status}:${as}:${new URL(url, (location == null ? void 0 : location.href) ?? "http://localhost/").origin}`, `HTTP error ${short} while fetching ${as} ${url}` ); } options.onError && options.onError(result); return result; } if (as === "text") { const text = await resp.text(); return { ok: true, data: text, status: resp.status, url: finalURL, fromCache: false }; } const blob = await resp.blob(); const mime = blob.type || resp.headers.get("content-type") || ""; if (as === "dataURL") { const dataURL = await blobToDataURL(blob); return { ok: true, data: dataURL, status: resp.status, url: finalURL, fromCache: false, mime }; } return { ok: true, data: blob, status: resp.status, url: finalURL, fromCache: false, mime }; } catch (err) { const reason = err && typeof err === "object" && "name" in err && err.name === "AbortError" ? String(err.message || "").includes("timeout") ? "timeout" : "abort" : "network"; const result = { ok: false, data: null, status: 0, url: finalURL, fromCache: false, reason }; if (!/^blob:/i.test(url) && errorTTL > 0) { _errorCache.set(key, { until: Date.now() + errorTTL, result }); } if (!silent) { const k = `${reason}:${as}:${new URL(url, (location == null ? void 0 : location.href) ?? "http://localhost/").origin}`; const tips = reason === "timeout" ? `Timeout after ${timeout}ms. Consider increasing timeout or using a proxy for ${url}` : reason === "abort" ? `Request aborted while fetching ${as} ${url}` : `Network/CORS issue while fetching ${as} ${url}. A proxy may be required`; snapLogger.errorOnce(k, tips); } options.onError && options.onError(result); return result; } finally { clearTimeout(timer); _inflight.delete(key); } })(); _inflight.set(key, p); return p; } function createBackground(baseCanvas, backgroundColor) { if (!backgroundColor || !baseCanvas.width || !baseCanvas.height) { return baseCanvas; } const temp = document.createElement("canvas"); temp.width = baseCanvas.width; temp.height = baseCanvas.height; const ctx = temp.getContext("2d"); ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, temp.width, temp.height); ctx.drawImage(baseCanvas, 0, 0); return temp; } async function inlineSingleBackgroundEntry(entry, options = {}) { const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry); if (isGradient || entry.trim() === "none") { return entry; } const rawUrl = extractURL(entry); if (!rawUrl) { return entry; } const encodedUrl = safeEncodeURI(rawUrl); if (cache.background.has(encodedUrl)) { const dataUrl = cache.background.get(encodedUrl); return dataUrl ? `url("${dataUrl}")` : "none"; } try { const dataUrl = await snapFetch(encodedUrl, { as: "dataURL", useProxy: options.useProxy }); if (dataUrl.ok) { cache.background.set(encodedUrl, dataUrl.data); return `url("${dataUrl.data}")`; } cache.background.set(encodedUrl, null); return "none"; } catch { cache.background.set(encodedUrl, null); return "none"; } } var NO_CAPTURE_TAGS = /* @__PURE__ */ new Set([ "meta", "script", "noscript", "title", "link", "template" ]); var NO_DEFAULTS_TAGS = /* @__PURE__ */ new Set([ // non-painting / head stuff "meta", "link", "style", "title", "noscript", "script", "template", // SVG whole namespace (safe for LeaderLine/presentation attrs) "g", "defs", "use", "marker", "mask", "clipPath", "pattern", "path", "polygon", "polyline", "line", "circle", "ellipse", "rect", "filter", "lineargradient", "radialgradient", "stop" ]); var commonTags = [ "div", "span", "p", "a", "img", "ul", "li", "button", "input", "select", "textarea", "label", "section", "article", "header", "footer", "nav", "main", "aside", "h1", "h2", "h3", "h4", "h5", "h6", "table", "thead", "tbody", "tr", "td", "th" ]; function precacheCommonTags() { for (let tag of commonTags) { const t = String(tag).toLowerCase(); if (NO_CAPTURE_TAGS.has(t)) continue; if (NO_DEFAULTS_TAGS.has(t)) continue; getDefaultStyleForTag(t); } } function getDefaultStyleForTag(tagName) { tagName = String(tagName).toLowerCase(); if (NO_DEFAULTS_TAGS.has(tagName)) { const empty = {}; cache.defaultStyle.set(tagName, empty); return empty; } if (cache.defaultStyle.has(tagName)) { return cache.defaultStyle.get(tagName); } let sandbox = document.getElementById("snapdom-sandbox"); if (!sandbox) { sandbox = document.createElement("div"); sandbox.id = "snapdom-sandbox"; sandbox.setAttribute("data-snapdom-sandbox", "true"); sandbox.setAttribute("aria-hidden", "true"); sandbox.style.position = "absolute"; sandbox.style.left = "-9999px"; sandbox.style.top = "-9999px"; sandbox.style.width = "0px"; sandbox.style.height = "0px"; sandbox.style.overflow = "hidden"; document.body.appendChild(sandbox); } const el = document.createElement(tagName); el.style.all = "initial"; sandbox.appendChild(el); const styles = getComputedStyle(el); const defaults = {}; for (let prop of styles) { if (shouldIgnoreProp(prop)) continue; const value = styles.getPropertyValue(prop); defaults[prop] = value; } sandbox.removeChild(el); cache.defaultStyle.set(tagName, defaults); return defaults; } var NO_PAINT_TOKEN = /(?:^|-)(animation|transition)(?:-|$)/i; var NO_PAINT_PREFIX = /^(--|view-timeline|scroll-timeline|animation-trigger|offset-|position-try|app-region|interactivity|overlay|view-transition|-webkit-locale|-webkit-user-(?:drag|modify)|-webkit-tap-highlight-color|-webkit-text-security)$/i; var NO_PAINT_EXACT = /* @__PURE__ */ new Set([ // Interaction hints "cursor", "pointer-events", "touch-action", "user-select", // Printing/speech/reading-mode hints "print-color-adjust", "speak", "reading-flow", "reading-order", // Anchoring/container/timeline scopes (metadata for layout queries) "anchor-name", "anchor-scope", "container-name", "container-type", "timeline-scope" ]); function shouldIgnoreProp(prop) { const p = String(prop).toLowerCase(); if (NO_PAINT_EXACT.has(p)) return true; if (NO_PAINT_PREFIX.test(p)) return true; if (NO_PAINT_TOKEN.test(p)) return true; return false; } function getStyleKey(snapshot, tagName) { tagName = String(tagName || "").toLowerCase(); if (NO_DEFAULTS_TAGS.has(tagName)) { return ""; } const entries = []; const defaults = getDefaultStyleForTag(tagName); for (let [prop, value] of Object.entries(snapshot)) { if (shouldIgnoreProp(prop)) continue; const def = defaults[prop]; if (value && value !== def) entries.push(`${prop}:${value}`); } entries.sort(); return entries.join(";"); } function collectUsedTagNames(root) { const tagSet = /* @__PURE__ */ new Set(); if (root.nodeType !== Node.ELEMENT_NODE && root.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) { return []; } if (root.tagName) { tagSet.add(root.tagName.toLowerCase()); } if (typeof root.querySelectorAll === "function") { root.querySelectorAll("*").forEach((el) => tagSet.add(el.tagName.toLowerCase())); } return Array.from(tagSet); } function generateDedupedBaseCSS(usedTagNames) { const groups = /* @__PURE__ */ new Map(); for (let tagName of usedTagNames) { const styles = cache.defaultStyle.get(tagName); if (!styles) continue; const key = Object.entries(styles).map(([k, v]) => `${k}:${v};`).sort().join(""); if (!key) continue; if (!groups.has(key)) { groups.set(key, []); } groups.get(key).push(tagName); } let css = ""; for (let [styleBlock, tagList] of groups.entries()) { css += `${tagList.join(",")} { ${styleBlock} } `; } return css; } function generateCSSClasses(styleMap) { const keys = Array.from(new Set(styleMap.values())).filter(Boolean).sort(); const classMap = /* @__PURE__ */ new Map(); let i = 1; for (const k of keys) classMap.set(k, `c${i++}`); return classMap; } function getStyle(el, pseudo = null) { if (!(el instanceof Element)) { return window.getComputedStyle(el, pseudo); } let map = cache.computedStyle.get(el); if (!map) { map = /* @__PURE__ */ new Map(); cache.computedStyle.set(el, map); } if (!map.has(pseudo)) { const st = window.getComputedStyle(el, pseudo); map.set(pseudo, st); } return map.get(pseudo); } function snapshotComputedStyle(style) { const snap = {}; for (let prop of style) { snap[prop] = style.getPropertyValue(prop); } return snap; } function splitBackgroundImage(bg) { const parts = []; let depth = 0; let lastIndex = 0; for (let i = 0; i < bg.length; i++) { const char = bg[i]; if (char === "(") depth++; if (char === ")") depth--; if (char === "," && depth === 0) { parts.push(bg.slice(lastIndex, i).trim()); lastIndex = i + 1; } } parts.push(bg.slice(lastIndex).trim()); return parts; } function idle(fn, { fast = false } = {}) { if (fast) return fn(); if ("requestIdleCallback" in window) { requestIdleCallback(fn, { timeout: 50 }); } else { setTimeout(fn, 1); } } function isSafari() { const ua = typeof navigator !== "undefined" && navigator.userAgent ? navigator.userAgent : ""; const isSafariUA = /^((?!chrome|android).)*safari/i.test(ua); const isUIWebView = /AppleWebKit/i.test(ua) && /Mobile/i.test(ua) && !/Safari/i.test(ua); const isWeChatUA = /(MicroMessenger|wxwork|WeCom|WindowsWechat|MacWechat)/i.test(ua); return isSafariUA || isUIWebView || isWeChatUA; } var snapshotCache = /* @__PURE__ */ new WeakMap(); var snapshotKeyCache = /* @__PURE__ */ new Map(); var __epoch = 0; function bumpEpoch() { __epoch++; } var __wired = false; function setupInvalidationOnce(root = document.documentElement) { var _a, _b; if (__wired) return; __wired = true; try { const domObs = new MutationObserver(() => bumpEpoch()); domObs.observe(root, { subtree: true, childList: true, characterData: true, attributes: true }); } catch { } try { const headObs = new MutationObserver(() => bumpEpoch()); headObs.observe(document.head, { subtree: true, childList: true, characterData: true, attributes: true }); } catch { } try { const f = document.fonts; if (f) { (_a = f.addEventListener) == null ? void 0 : _a.call(f, "loadingdone", bumpEpoch); (_b = f.ready) == null ? void 0 : _b.then(() => bumpEpoch()).catch(() => { }); } } catch { } } function snapshotComputedStyleFull(style, options = {}) { const out = {}; const vis = style.getPropertyValue("visibility"); for (let i = 0; i < style.length; i++) { const prop = style[i]; let val = style.getPropertyValue(prop); if ((prop === "background-image" || prop === "content") && val.includes("url(") && !val.includes("data:")) { val = "none"; } out[prop] = val; } if (options.embedFonts) { const EXTRA_FONT_PROPS = [ "font-feature-settings", "font-variation-settings", "font-kerning", "font-variant", "font-variant-ligatures", "font-optical-sizing" ]; for (const prop of EXTRA_FONT_PROPS) { if (out[prop]) continue; try { const v = style.getPropertyValue(prop); if (v) out[prop] = v; } catch { } } } if (vis === "hidden") out.opacity = "0"; return out; } var __snapshotSig = /* @__PURE__ */ new WeakMap(); function styleSignature(snap) { let sig = __snapshotSig.get(snap); if (sig) return sig; const entries = Object.entries(snap).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0); sig = entries.map(([k, v]) => `${k}:${v}`).join(";"); __snapshotSig.set(snap, sig); return sig; } function getSnapshot(el, preStyle = null, options = {}) { const rec = snapshotCache.get(el); if (rec && rec.epoch === __epoch) return rec.snapshot; const style = preStyle || getComputedStyle(el); const snap = snapshotComputedStyleFull(style, options); stripHeightForWrappers(el, style, snap); snapshotCache.set(el, { epoch: __epoch, snapshot: snap }); return snap; } function _resolveCtx(sessionOrCtx, opts) { if (sessionOrCtx && sessionOrCtx.session && sessionOrCtx.persist) return sessionOrCtx; if (sessionOrCtx && (sessionOrCtx.styleMap || sessionOrCtx.styleCache || sessionOrCtx.nodeMap)) { return { session: sessionOrCtx, persist: { snapshotKeyCache, defaultStyle: cache.defaultStyle, baseStyle: cache.baseStyle, image: cache.image, resource: cache.resource, background: cache.background, font: cache.font }, options: opts || {} }; } return { session: cache.session, persist: { snapshotKeyCache, defaultStyle: cache.defaultStyle, baseStyle: cache.baseStyle, image: cache.image, resource: cache.resource, background: cache.background, font: cache.font }, options: sessionOrCtx || opts || {} }; } async function inlineAllStyles(source, clone, sessionOrCtx, opts) { var _a, _b, _c; if (source.tagName === "STYLE") return; const ctx = _resolveCtx(sessionOrCtx, opts); const resetMode = ctx.options && ctx.options.cache || "auto"; if (resetMode !== "disabled") setupInvalidationOnce(document.documentElement); if (resetMode === "disabled" && !ctx.session.__bumpedForDisabled) { bumpEpoch(); snapshotKeyCache.clear(); ctx.session.__bumpedForDisabled = true; } if (NO_DEFAULTS_TAGS.has((_a = source.tagName) == null ? void 0 : _a.toLowerCase())) { const author = (_b = source.getAttribute) == null ? void 0 : _b.call(source, "style"); if (author) clone.setAttribute("style", author); } const { session, persist } = ctx; if (!session.styleCache.has(source)) { session.styleCache.set(source, getComputedStyle(source)); } const pre = session.styleCache.get(source); const snap = getSnapshot(source, pre, ctx.options); const sig = styleSignature(snap); let key = persist.snapshotKeyCache.get(sig); if (!key) { const tag = ((_c = source.tagName) == null ? void 0 : _c.toLowerCase()) || "div"; key = getStyleKey(snap, tag); persist.snapshotKeyCache.set(sig, key); } session.styleMap.set(clone, key); } function isReplaced(el) { return el instanceof HTMLImageElement || el instanceof HTMLCanvasElement || el instanceof HTMLVideoElement || el instanceof HTMLIFrameElement || el instanceof SVGElement || el instanceof HTMLObjectElement || el instanceof HTMLEmbedElement; } function hasBox(cs) { if (cs.backgroundImage && cs.backgroundImage !== "none") return true; if (cs.backgroundColor && cs.backgroundColor !== "rgba(0, 0, 0, 0)" && cs.backgroundColor !== "transparent") return true; if ((parseFloat(cs.borderTopWidth) || 0) > 0) return true; if ((parseFloat(cs.borderBottomWidth) || 0) > 0) return true; if ((parseFloat(cs.paddingTop) || 0) > 0) return true; if ((parseFloat(cs.paddingBottom) || 0) > 0) return true; const ob = cs.overflowBlock || cs.overflowY || "visible"; return ob !== "visible"; } function isFlexOrGridItem(el) { const p = el.parentElement; if (!p) return false; const pd = getComputedStyle(p).display || ""; return pd.includes("flex") || pd.includes("grid"); } function hasFlowFast(el, cs) { if (el.textContent && /\S/.test(el.textContent)) return true; const f = el.firstElementChild, l = el.lastElementChild; if (f && f.tagName === "BR" || l && l.tagName === "BR") return true; const sh = el.scrollHeight; if (sh === 0) return false; const pt = parseFloat(cs.paddingTop) || 0; const pb = parseFloat(cs.paddingBottom) || 0; return sh > pt + pb; } function stripHeightForWrappers(el, cs, snap) { if (el instanceof HTMLElement && el.style && el.style.height) return; const disp = cs.display || ""; if (disp.includes("flex") || disp.includes("grid")) return; if (isReplaced(el)) return; const pos = cs.position; if (pos === "absolute" || pos === "fixed" || pos === "sticky") return; if (cs.transform !== "none") return; if (hasBox(cs)) return; if (isFlexOrGridItem(el)) return; if (!hasFlowFast(el, cs)) return; delete snap.height; delete snap["block-size"]; } function resolveCSSVars(sourceEl, cloneEl) { var _a, _b, _c; if (!(sourceEl instanceof Element) || !(cloneEl instanceof Element)) return; const styleAttr = (_a = sourceEl.getAttribute) == null ? void 0 : _a.call(sourceEl, "style"); let hasVar = !!(styleAttr && styleAttr.includes("var(")); if (!hasVar && ((_b = sourceEl.attributes) == null ? void 0 : _b.length)) { const attrs = sourceEl.attributes; for (let i = 0; i < attrs.length; i++) { const a = attrs[i]; if (a && typeof a.value === "string" && a.value.includes("var(")) { hasVar = true; break; } } } if (!hasVar) return; let cs; try { cs = getComputedStyle(sourceEl); } catch { return; } const author = sourceEl.style; if (author && author.length) { for (let i = 0; i < author.length; i++) { const prop = author[i]; const val = author.getPropertyValue(prop); if (!val || !val.includes("var(")) continue; const resolved = cs.getPropertyValue(prop); if (resolved) { try { cloneEl.style.setProperty(prop, resolved.trim(), author.getPropertyPriority(prop)); } catch { } } } } if ((_c = sourceEl.attributes) == null ? void 0 : _c.length) { const attrs = sourceEl.attributes; for (let i = 0; i < attrs.length; i++) { const a = attrs[i]; if (!a || typeof a.value !== "string" || !a.value.includes("var(")) continue; const propName = a.name; let resolved = ""; try { resolved = cs.getPropertyValue(propName); } catch { } if (resolved) { try { cloneEl.style.setProperty(propName, resolved.trim()); } catch { } } } } } function idleCallback(childList, callback, fast) { return Promise.all(childList.map((child) => { return new Promise((resolve) => { function deal() { idle((deadline) => { const hasIdleBudget = deadline && typeof deadline.timeRemaining === "function" ? deadline.timeRemaining() > 0 : true; if (hasIdleBudget) { callback(child, resolve); } else { deal(); } }, { fast }); } deal(); }); })); } function addNotSlottedRightmost(sel) { sel = sel.trim(); if (!sel) return sel; if (/:not\(\s*\[data-sd-slotted\]\s*\)\s*$/.test(sel)) return sel; return `${sel}:not([data-sd-slotted])`; } function wrapWithScope(selectorList, scopeSelector, excludeSlotted = true) { return selectorList.split(",").map((s) => s.trim()).filter(Boolean).map((s) => { if (s.startsWith(":where(")) return s; if (s.startsWith("@")) return s; const body = excludeSlotted ? addNotSlottedRightmost(s) : s; return `:where(${scopeSelector} ${body})`; }).join(", "); } function rewriteShadowCSS(cssText, scopeSelector) { if (!cssText) return ""; cssText = cssText.replace(/:host\(([^)]+)\)/g, (_, sel) => { return `:where(${scopeSelector}:is(${sel.trim()}))`; }); cssText = cssText.replace(/:host\b/g, `:where(${scopeSelector})`); cssText = cssText.replace(/:host-context\(([^)]+)\)/g, (_, sel) => { return `:where(:where(${sel.trim()}) ${scopeSelector})`; }); cssText = cssText.replace(/::slotted\(([^)]+)\)/g, (_, sel) => { return `:where(${scopeSelector} ${sel.trim()})`; }); cssText = cssText.replace(/(^|})(\s*)([^@}{]+){/g, (_, brace, ws, selectorList) => { const wrapped = wrapWithScope( selectorList, scopeSelector, /*excludeSlotted*/ true ); return `${brace}${ws}${wrapped}{`; }); return cssText; } function nextShadowScopeId(sessionCache) { sessionCache.shadowScopeSeq = (sessionCache.shadowScopeSeq || 0) + 1; return `s${sessionCache.shadowScopeSeq}`; } function extractShadowCSS(sr) { let css = ""; try { sr.querySelectorAll("style").forEach((s) => { css += (s.textContent || "") + "\n"; }); const sheets = sr.adoptedStyleSheets || []; for (const sh of sheets) { try { if (sh && sh.cssRules) { for (const rule of sh.cssRules) css += rule.cssText + "\n"; } } catch { } } } catch { } return css; } function injectScopedStyle(hostClone, cssText, scopeId) { if (!cssText) return; const style = document.createElement("style"); style.setAttribute("data-sd", scopeId); style.textContent = cssText; hostClone.insertBefore(style, hostClone.firstChild || null); } function freezeImgSrcset(original, cloned) { try { const chosen = original.currentSrc || original.src || ""; if (!chosen) return; cloned.setAttribute("src", chosen); cloned.removeAttribute("srcset"); cloned.removeAttribute("sizes"); cloned.loading = "eager"; cloned.decoding = "sync"; } catch { } } function collectCustomPropsFromCSS(cssText) { const out = /* @__PURE__ */ new Set(); if (!cssText) return out; const re = /var\(\s*(--[A-Za-z0-9_-]+)\b/g; let m; while (m = re.exec(cssText)) out.add(m[1]); return out; } function resolveCustomProp(el, name) { try { const cs = getComputedStyle(el); let v = cs.getPropertyValue(name).trim(); if (v) return v; } catch { } try { const rootCS = getComputedStyle(document.documentElement); let v = rootCS.getPropertyValue(name).trim(); if (v) return v; } catch { } return ""; } function buildSeedCustomPropsRule(hostEl, names, scopeSelector) { const decls = []; for (const name of names) { const val = resolveCustomProp(hostEl, name); if (val) decls.push(`${name}: ${val};`); } if (!decls.length) return ""; return `${scopeSelector}{${decls.join("")}} `; } function markSlottedSubtree(root) { if (!root) return; if (root.nodeType === Node.ELEMENT_NODE) { root.setAttribute("data-sd-slotted", ""); } if (root.querySelectorAll) { root.querySelectorAll("*").forEach((el) => el.setAttribute("data-sd-slotted", "")); } } async function getAccessibleIframeDocument(iframe, attempts = 3) { const probe = () => { var _a; try { return iframe.contentDocument || ((_a = iframe.contentWindow) == null ? void 0 : _a.document) || null; } catch { return null; } }; let doc = probe(); let i = 0; while (i < attempts && (!doc || !doc.body && !doc.documentElement)) { await new Promise((r) => setTimeout(r, 0)); doc = probe(); i++; } return doc && (doc.body || doc.documentElement) ? doc : null; } function measureContentBox(el) { const rect = el.getBoundingClientRect(); let bl = 0, br = 0, bt = 0, bb = 0; try { const cs = getComputedStyle(el); bl = parseFloat(cs.borderLeftWidth) || 0; br = parseFloat(cs.borderRightWidth) || 0; bt = parseFloat(cs.borderTopWidth) || 0; bb = parseFloat(cs.borderBottomWidth) || 0; } catch { } const contentWidth = Math.max(0, Math.round(rect.width - (bl + br))); const contentHeight = Math.max(0, Math.round(rect.height - (bt + bb))); return { contentWidth, contentHeight, rect }; } function pinIframeViewport(doc, w, h) { const style = doc.createElement("style"); style.setAttribute("data-sd-iframe-pin", ""); style.textContent = `html, body {margin: 0 !important;padding: 0 !important;width: ${w}px !important;height: ${h}px !important;min-width: ${w}px !important;min-height: ${h}px !important;box-sizing: border-box !important;overflow: hidden !important;background-clip: border-box !important;}`; (doc.head || doc.documentElement).appendChild(style); return () => { try { style.remove(); } catch { } }; } async function rasterizeIframe(iframe, sessionCache, options) { const doc = await getAccessibleIframeDocument(iframe, 3); if (!doc) throw new Error("iframe document not accessible/ready"); const { contentWidth, contentHeight, rect } = measureContentBox(iframe); const snap = options == null ? void 0 : options.snap; if (!snap || typeof snap.toPng !== "function") { throw new Error("snapdom.toPng not available in iframe or window"); } const nested = { ...options, scale: 1 }; const unpin = pinIframeViewport(doc, contentWidth, contentHeight); let imgEl; try { imgEl = await snap.toPng(doc.documentElement, nested); } finally { unpin(); } imgEl.style.display = "block"; imgEl.style.width = `${contentWidth}px`; imgEl.style.height = `${contentHeight}px`; const wrapper = document.createElement("div"); sessionCache.nodeMap.set(wrapper, iframe); inlineAllStyles(iframe, wrapper, sessionCache, options); wrapper.style.overflow = "hidden"; wrapper.style.display = "block"; if (!wrapper.style.width) wrapper.style.width = `${Math.round(rect.width)}px`; if (!wrapper.style.height) wrapper.style.height = `${Math.round(rect.height)}px`; wrapper.appendChild(imgEl); return wrapper; } async function deepClone(node, sessionCache, options) { var _a, _b, _c, _d, _e, _f; if (!node) throw new Error("Invalid node"); const clonedAssignedNodes = /* @__PURE__ */ new Set(); let pendingSelectValue = null; let pendingTextAreaValue = null; if (node.nodeType === Node.ELEMENT_NODE) { const tag = (node.localName || node.tagName || "").toLowerCase(); if (node.id === "snapdom-sandbox" || node.hasAttribute("data-snapdom-sandbox")) { return null; } if (NO_CAPTURE_TAGS.has(tag)) { return null; } } if (node.nodeType === Node.TEXT_NODE) { return node.cloneNode(true); } if (node.nodeType !== Node.ELEMENT_NODE) { return node.cloneNode(true); } if (node.getAttribute("data-capture") === "exclude") { if (options.excludeMode === "hide") { const spacer = document.createElement("div"); const rect = node.getBoundingClientRect(); spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; return spacer; } else if (options.excludeMode === "remove") { return null; } } if (options.exclude && Array.isArray(options.exclude)) { for (const selector of options.exclude) { try { if ((_a = node.matches) == null ? void 0 : _a.call(node, selector)) { if (options.excludeMode === "hide") { const spacer = document.createElement("div"); const rect = node.getBoundingClientRect(); spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; return spacer; } else if (options.excludeMode === "remove") { return null; } } } catch (err) { console.warn(`Invalid selector in exclude option: ${selector}`, err); } } } if (typeof options.filter === "function") { try { if (!options.filter(node)) { if (options.filterMode === "hide") { const spacer = document.createElement("div"); const rect = node.getBoundingClientRect(); spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; return spacer; } else if (options.filterMode === "remove") { return null; } } } catch (err) { console.warn("Error in filter function:", err); } } if (node.tagName === "IFRAME") { let sameOrigin = false; try { sameOrigin = !!(node.contentDocument || ((_b = node.contentWindow) == null ? void 0 : _b.document)); } catch { sameOrigin = false; } if (sameOrigin) { try { const wrapper = await rasterizeIframe(node, sessionCache, options); return wrapper; } catch (err) { console.warn("[SnapDOM] iframe rasterization failed, fallback:", err); } } if (options.placeholders) { const fallback = document.createElement("div"); fallback.style.cssText = `width:${node.offsetWidth}px;height:${node.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`; inlineAllStyles(node, fallback, sessionCache, options); return fallback; } else { const rect = node.getBoundingClientRect(); const spacer = document.createElement("div"); spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; inlineAllStyles(node, spacer, sessionCache, options); return spacer; } } if (node.getAttribute("data-capture") === "placeholder") { const clone2 = node.cloneNode(false); sessionCache.nodeMap.set(clone2, node); inlineAllStyles(node, clone2, sessionCache, options); const placeholder = document.createElement("div"); placeholder.textContent = node.getAttribute("data-placeholder-text") || ""; placeholder.style.cssText = "color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;"; clone2.appendChild(placeholder); return clone2; } if (node.tagName === "CANVAS") { const dataURL = node.toDataURL(); const img = document.createElement("img"); img.src = dataURL; img.width = node.width; img.height = node.height; sessionCache.nodeMap.set(img, node); inlineAllStyles(node, img, sessionCache, options); return img; } let clone; try { clone = node.cloneNode(false); resolveCSSVars(node, clone); sessionCache.nodeMap.set(clone, node); if (node.tagName === "IMG") { freezeImgSrcset(node, clone); try { const rect = node.getBoundingClientRect(); let w = Math.round(rect.width || 0); let h = Math.round(rect.height || 0); if (!w || !h) { const computed = window.getComputedStyle(node); const cssW = parseFloat(computed.width) || 0; const cssH = parseFloat(computed.height) || 0; const attrW = parseInt(node.getAttribute("width") || "", 10) || 0; const attrH = parseInt(node.getAttribute("height") || "", 10) || 0; const propW = node.width || node.naturalWidth || 0; const propH = node.height || node.naturalHeight || 0; w = Math.round(w || cssW || attrW || propW || 0); h = Math.round(h || cssH || attrH || propH || 0); } if (w) clone.dataset.snapdomWidth = String(w); if (h) clone.dataset.snapdomHeight = String(h); } catch { } } } catch (err) { console.error("[Snapdom] Failed to clone node:", node, err); throw err; } if (node instanceof HTMLTextAreaElement) { const rect = node.getBoundingClientRect(); clone.style.width = `${rect.width}px`; clone.style.height = `${rect.height}px`; } if (node instanceof HTMLInputElement) { clone.value = node.value; clone.setAttribute("value", node.value); if (node.checked !== void 0) { clone.checked = node.checked; if (node.checked) clone.setAttribute("checked", ""); if (node.indeterminate) clone.indeterminate = node.indeterminate; } } if (node instanceof HTMLSelectElement) { pendingSelectValue = node.value; } if (node instanceof HTMLTextAreaElement) { pendingTextAreaValue = node.value; } inlineAllStyles(node, clone, sessionCache, options); if (node.shadowRoot) { let callback2 = function(child, resolve) { if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") { return resolve(null); } else { deepClone(child, sessionCache, options).then((clonedChild) => { resolve(clonedChild || null); }).catch(() => { resolve(null); }); } }; try { const slots = node.shadowRoot.querySelectorAll("slot"); for (const s of slots) { let assigned = []; try { assigned = ((_c = s.assignedNodes) == null ? void 0 : _c.call(s, { flatten: true })) || ((_d = s.assignedNodes) == null ? void 0 : _d.call(s)) || []; } catch { assigned = ((_e = s.assignedNodes) == null ? void 0 : _e.call(s)) || []; } for (const an of assigned) clonedAssignedNodes.add(an); } } catch { } const scopeId = nextShadowScopeId(sessionCache); const scopeSelector = `[data-sd="${scopeId}"]`; try { clone.setAttribute("data-sd", scopeId); } catch { } const rawCSS = extractShadowCSS(node.shadowRoot); const rewritten = rewriteShadowCSS(rawCSS, scopeSelector); const neededVars = collectCustomPropsFromCSS(rawCSS); const seed = buildSeedCustomPropsRule(node, neededVars, scopeSelector); injectScopedStyle(clone, seed + rewritten, scopeId); const shadowFrag = document.createDocumentFragment(); const cloneList2 = await idleCallback(Array.from(node.shadowRoot.childNodes), callback2, options.fast); shadowFrag.append(...cloneList2.filter((clonedChild) => !!clonedChild)); clone.appendChild(shadowFrag); } if (node.tagName === "SLOT") { let callback2 = function(child, resolve) { deepClone(child, sessionCache, options).then((clonedChild) => { if (clonedChild) { markSlottedSubtree(clonedChild); } resolve(clonedChild || null); }).catch(() => { resolve(null); }); }; const assigned = ((_f = node.assignedNodes) == null ? void 0 : _f.call(node, { flatten: true })) || []; const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes); const fragment = document.createDocumentFragment(); const cloneList2 = await idleCallback(Array.from(nodesToClone), callback2, options.fast); fragment.append(...cloneList2.filter((clonedChild) => !!clonedChild)); return fragment; } function callback(child, resolve) { if (clonedAssignedNodes.has(child)) return resolve(null); deepClone(child, sessionCache, options).then((clonedChild) => { resolve(clonedChild || null); }).catch(() => { resolve(null); }); } const cloneList = await idleCallback(Array.from(node.childNodes), callback, options.fast); clone.append(...cloneList.filter((clonedChild) => !!clonedChild)); if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) { clone.value = pendingSelectValue; for (const opt of clone.options) { if (opt.value === pendingSelectValue) { opt.setAttribute("selected", ""); } else { opt.removeAttribute("selected"); } } } if (pendingTextAreaValue !== null && clone instanceof HTMLTextAreaElement) { clone.textContent = pendingTextAreaValue; } return clone; } var defaultIconFonts = [ // /uicons/i, /font\s*awesome/i, /material\s*icons/i, /ionicons/i, /glyphicons/i, /feather/i, /bootstrap\s*icons/i, /remix\s*icons/i, /heroicons/i, /layui/i, /lucide/i ]; var userIconFonts = []; function extendIconFonts(fonts) { const list = Array.isArray(fonts) ? fonts : [fonts]; for (const f of list) { if (f instanceof RegExp) { userIconFonts.push(f); } else if (typeof f === "string") { userIconFonts.push(new RegExp(f, "i")); } else { console.warn("[snapdom] Ignored invalid iconFont value:", f); } } } function isIconFont2(input) { const text = typeof input === "string" ? input : ""; const candidates = [...defaultIconFonts, ...userIconFonts]; for (const rx of candidates) { if (rx instanceof RegExp && rx.test(text)) return true; } if (/icon/i.test(text) || /glyph/i.test(text) || /symbols/i.test(text) || /feather/i.test(text) || /fontawesome/i.test(text)) return true; return false; } async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") { fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, ""); const dpr = window.devicePixelRatio || 1; try { await document.fonts.ready; } catch { } const span = document.createElement("span"); span.textContent = unicodeChar; span.style.position = "absolute"; span.style.visibility = "hidden"; span.style.fontFamily = `"${fontFamily}"`; span.style.fontWeight = fontWeight || "normal"; span.style.fontSize = `${fontSize}px`; span.style.lineHeight = "1"; span.style.whiteSpace = "nowrap"; span.style.padding = "0"; span.style.margin = "0"; document.body.appendChild(span); const rect = span.getBoundingClientRect(); const width = Math.ceil(rect.width); const height = Math.ceil(rect.height); document.body.removeChild(span); const canvas = document.createElement("canvas"); canvas.width = Math.max(1, width * dpr); canvas.height = Math.max(1, height * dpr); const ctx = canvas.getContext("2d"); ctx.scale(dpr, dpr); ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`; ctx.textAlign = "left"; ctx.textBaseline = "top"; ctx.fillStyle = color; ctx.fillText(unicodeChar, 0, 0); return { dataUrl: canvas.toDataURL(), width, height }; } var GENERIC_FAMILIES = /* @__PURE__ */ new Set([ "serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui", "emoji", "math", "fangsong", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded" ]); function pickPrimaryFamily(familyList) { if (!familyList) return ""; for (let raw of familyList.split(",")) { let f = raw.trim().replace(/^['"]+|['"]+$/g, ""); if (!f) continue; if (!GENERIC_FAMILIES.has(f.toLowerCase())) return f; } return ""; } function normWeight(w) { const t = String(w ?? "400").trim().toLowerCase(); if (t === "normal") return 400; if (t === "bold") return 700; const n = parseInt(t, 10); return Number.isFinite(n) ? Math.min(900, Math.max(100, n)) : 400; } function normStyle(s) { const t = String(s ?? "normal").trim().toLowerCase(); if (t.startsWith("italic")) return "italic"; if (t.startsWith("oblique")) return "oblique"; return "normal"; } function normStretchPct(st) { const m = String(st ?? "100%").match(/(\d+(?:\.\d+)?)\s*%/); return m ? Math.max(50, Math.min(200, parseFloat(m[1]))) : 100; } function parseWeightSpec(spec) { const s = String(spec || "400").trim(); const m = s.match(/^(\d{2,3})\s+(\d{2,3})$/); if (m) { const a = normWeight(m[1]), b = normWeight(m[2]); return { min: Math.min(a, b), max: Math.max(a, b) }; } const v = normWeight(s); return { min: v, max: v }; } function parseStyleSpec(spec) { const t = String(spec || "normal").trim().toLowerCase(); if (t === "italic") return { kind: "italic" }; if (t.startsWith("oblique")) return { kind: "oblique" }; return { kind: "normal" }; } function parseStretchSpec(spec) { const s = String(spec || "100%").trim(); const mm = s.match(/(\d+(?:\.\d+)?)\s*%\s+(\d+(?:\.\d+)?)\s*%/); if (mm) { const a = parseFloat(mm[1]), b = parseFloat(mm[2]); return { min: Math.min(a, b), max: Math.max(a, b) }; } const m = s.match(/(\d+(?:\.\d+)?)\s*%/); const v = m ? parseFloat(m[1]) : 100; return { min: v, max: v }; } function isLikelyFontStylesheet(href, requiredFamilies) { if (!href) return false; try { const u = new URL(href, location.href); const sameOrigin = u.origin === location.origin; if (sameOrigin) return true; const host = u.host.toLowerCase(); const FONT_HOSTS = [ "fonts.googleapis.com", "fonts.gstatic.com", "use.typekit.net", "p.typekit.net", "kit.fontawesome.com", "use.fontawesome.com" ]; if (FONT_HOSTS.some((h) => host.endsWith(h))) return true; const path = (u.pathname + u.search).toLowerCase(); if (/\bfont(s)?\b/.test(path) || /\.woff2?(\b|$)/.test(path)) return true; for (const fam of requiredFamilies) { const tokenA = fam.toLowerCase().replace(/\s+/g, "+"); const tokenB = fam.toLowerCase().replace(/\s+/g, "-"); if (path.includes(tokenA) || path.includes(tokenB)) return true; } return false; } catch { return false; } } function familiesFromRequired(required) { var _a; const out = /* @__PURE__ */ new Set(); for (const k of required || []) { const fam = (_a = String(k).split("__")[0]) == null ? void 0 : _a.trim(); if (fam) out.add(fam); } return out; } function rewriteRelativeUrls(cssText, baseHref) { if (!cssText) return cssText; return cssText.replace( /url\(\s*(['"]?)([^)'"]+)\1\s*\)/g, (m, q, u) => { const src = (u || "").trim(); if (!src || /^data:|^blob:|^https?:|^file:|^about:/i.test(src)) return m; let abs = src; try { abs = new URL(src, baseHref || location.href).href; } catch { } return `url("${abs}")`; } ); } var IMPORT_ANY_RE = /@import\s+(?:url\(\s*(['"]?)([^)"']+)\1\s*\)|(['"])([^"']+)\3)([^;]*);/g; var MAX_IMPORT_DEPTH = 4; async function inlineImportsAndRewrite(cssText, ownerHref, useProxy) { if (!cssText) return cssText; const visited = /* @__PURE__ */ new Set(); function normalizeUrl(u, base) { try { return new URL(u, base || location.href).href; } catch { return u; } } async function resolveOnce(text, baseHref, depth = 0) { if (depth > MAX_IMPORT_DEPTH) { console.warn(`[snapDOM] @import depth exceeded (${MAX_IMPORT_DEPTH}) at ${baseHref}`); return text; } let out = ""; let last = 0; let m; while (m = IMPORT_ANY_RE.exec(text)) { out += text.slice(last, m.index); last = IMPORT_ANY_RE.lastIndex; const rawUrl = (m[2] || m[4] || "").trim(); const absUrl = normalizeUrl(rawUrl, baseHref); if (visited.has(absUrl)) { console.warn(`[snapDOM] Skipping circular @import: ${absUrl}`); continue; } visited.add(absUrl); let imported = ""; try { const r = await snapFetch(absUrl, { as: "text", useProxy, silent: true }); if (r.ok && typeof r.data === "string") imported = r.data; } catch { } if (imported) { imported = rewriteRelativeUrls(imported, absUrl); imported = await resolveOnce(imported, absUrl, depth + 1); out += ` /* inlined: ${absUrl} */ ${imported} `; } else { out += m[0]; } } out += text.slice(last); return out; } let rewritten = rewriteRelativeUrls(cssText, ownerHref || location.href); rewritten = await resolveOnce(rewritten, ownerHref || location.href, 0); return rewritten; } var URL_RE = /url\((["']?)([^"')]+)\1\)/g; var FACE_RE = /@font-face[^{}]*\{[^}]*\}/g; function parseUnicodeRange(ur) { if (!ur) return []; const ranges = []; const parts = ur.split(",").map((s) => s.trim()).filter(Boolean); for (const p of parts) { const m = p.match(/^U\+([0-9A-Fa-f?]+)(?:-([0-9A-Fa-f?]+))?$/); if (!m) continue; const a = m[1], b = m[2]; const expand = (hex) => { if (!hex.includes("?")) return parseInt(hex, 16); const min = parseInt(hex.replace(/\?/g, "0"), 16); const max = parseInt(hex.replace(/\?/g, "F"), 16); return [min, max]; }; if (b) { const A = expand(a), B = expand(b); const min = Array.isArray(A) ? A[0] : A; const max = Array.isArray(B) ? B[1] : B; ranges.push([Math.min(min, max), Math.max(min, max)]); } else { const X = expand(a); if (Array.isArray(X)) ranges.push([X[0], X[1]]); else ranges.push([X, X]); } } return ranges; } function unicodeIntersects(used, ranges) { if (!ranges.length) return true; if (!used || used.size === 0) return true; for (const cp of used) { for (const [a, b] of ranges) if (cp >= a && cp <= b) return true; } return false; } function extractSrcUrls(srcValue, baseHref) { const urls = []; if (!srcValue) return urls; for (const m of srcValue.matchAll(URL_RE)) { let u = (m[2] || "").trim(); if (!u || u.startsWith("data:")) continue; if (!/^https?:/i.test(u)) { try { u = new URL(u, baseHref || location.href).href; } catch { } } urls.push(u); } return urls; } async function inlineUrlsInCssBlock(cssBlock, baseHref, useProxy = "") { var _a, _b, _c, _d, _e; let out = cssBlock; for (const m of cssBlock.matchAll(URL_RE)) { const raw = extractURL(m[0]); if (!raw) continue; let abs = raw; if (!abs.startsWith("http") && !abs.startsWith("data:")) { try { abs = new URL(abs, baseHref || location.href).href; } catch { } } if (isIconFont2(abs)) continue; if ((_a = cache.resource) == null ? void 0 : _a.has(abs)) { (_b = cache.font) == null ? void 0 : _b.add(abs); out = out.replace(m[0], `url(${cache.resource.get(abs)})`); continue; } if ((_c = cache.font) == null ? void 0 : _c.has(abs)) continue; try { const r = await snapFetch(abs, { as: "dataURL", useProxy, silent: true }); if (r.ok && typeof r.data === "string") { const b64 = r.data; (_d = cache.resource) == null ? void 0 : _d.set(abs, b64); (_e = cache.font) == null ? void 0 : _e.add(abs); out = out.replace(m[0], `url(${b64})`); } } catch { console.warn("[snapDOM] Failed to fetch font resource:", abs); } } return out; } function subsetFromRanges(ranges) { if (!ranges.length) return null; const hit = (a, b) => ranges.some(([x, y]) => !(y < a || x > b)); const latin = hit(0, 255) || hit(305, 305); const latinExt = hit(256, 591) || hit(7680, 7935); const greek = hit(880, 1023); const cyr = hit(1024, 1279); const viet = hit(7840, 7929) || hit(258, 259) || hit(416, 417) || hit(431, 432); if (viet) return "vietnamese"; if (cyr) return "cyrillic"; if (greek) return "greek"; if (latinExt) return "latin-ext"; if (latin) return "latin"; return null; } function buildSimpleExcluder(ex = {}) { const famSet = new Set((ex.families || []).map((s) => String(s).toLowerCase())); const domSet = new Set((ex.domains || []).map((s) => String(s).toLowerCase())); const subSet = new Set((ex.subsets || []).map((s) => String(s).toLowerCase())); return (meta, parsedRanges) => { if (famSet.size && famSet.has(meta.family.toLowerCase())) return true; if (domSet.size) { for (const u of meta.srcUrls) { try { if (domSet.has(new URL(u).host.toLowerCase())) return true; } catch { } } } if (subSet.size) { const label = subsetFromRanges(parsedRanges); if (label && subSet.has(label)) return true; } return false; }; } function dedupeFontFaces(cssText) { var _a, _b, _c, _d, _e, _f; if (!cssText) return cssText; const FACE_RE_G = /@font-face[^{}]*\{[^}]*\}/gi; const seen = /* @__PURE__ */ new Set(); const out = []; for (const block of cssText.match(FACE_RE_G) || []) { const familyRaw = ((_a = block.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _a[1]) || ""; const family = pickPrimaryFamily(familyRaw); const weightSpec = (((_b = block.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _b[1]) || "400").trim(); const styleSpec = (((_c = block.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _c[1]) || "normal").trim(); const stretchSpec = (((_d = block.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _d[1]) || "100%").trim(); const urange = (((_e = block.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _e[1]) || "").trim(); const srcRaw = (((_f = block.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _f[1]) || "").trim(); const urls = extractSrcUrls(srcRaw, location.href); const srcPart = urls.length ? urls.map((u) => String(u).toLowerCase()).sort().join("|") : srcRaw.toLowerCase(); const key = [ String(family || "").toLowerCase(), weightSpec, styleSpec, stretchSpec, urange.toLowerCase(), srcPart ].join("|"); if (!seen.has(key)) { seen.add(key); out.push(block); } } if (out.length === 0) return cssText; let i = 0; return cssText.replace(FACE_RE_G, () => out[i++] || ""); } function buildFontsCacheKey(required, exclude, localFonts, useProxy) { const req = Array.from(required || []).sort().join("|"); const ex = exclude ? JSON.stringify({ families: (exclude.families || []).map((s) => String(s).toLowerCase()).sort(), domains: (exclude.domains || []).map((s) => String(s).toLowerCase()).sort(), subsets: (exclude.subsets || []).map((s) => String(s).toLowerCase()).sort() }) : ""; const lf = (localFonts || []).map((f) => `${(f.family || "").toLowerCase()}::${f.weight || "normal"}::${f.style || "normal"}::${f.src || ""}`).sort().join("|"); const px = useProxy || ""; return `fonts-embed-css::req=${req}::ex=${ex}::lf=${lf}::px=${px}`; } async function collectFacesFromSheet(sheet, baseHref, emitFace, ctx) { let rules; try { rules = sheet.cssRules || []; } catch { return; } const normalizeUrl = (u, base) => { try { return new URL(u, base || location.href).href; } catch { return u; } }; for (const rule of rules) { if (rule.type === CSSRule.IMPORT_RULE && rule.styleSheet) { const childHref = rule.href ? normalizeUrl(rule.href, baseHref) : baseHref; if (ctx.depth >= MAX_IMPORT_DEPTH) { console.warn(`[snapDOM] CSSOM import depth exceeded (${MAX_IMPORT_DEPTH}) at ${childHref}`); continue; } if (childHref && ctx.visitedSheets.has(childHref)) { console.warn(`[snapDOM] Skipping circular CSSOM import: ${childHref}`); continue; } if (childHref) ctx.visitedSheets.add(childHref); const nextCtx = { ...ctx, depth: (ctx.depth || 0) + 1 }; await collectFacesFromSheet(rule.styleSheet, childHref, emitFace, nextCtx); continue; } if (rule.type === CSSRule.FONT_FACE_RULE) { const famRaw = (rule.style.getPropertyValue("font-family") || "").trim(); const family = pickPrimaryFamily(famRaw); if (!family || isIconFont2(family)) continue; const weightSpec = (rule.style.getPropertyValue("font-weight") || "400").trim(); const styleSpec = (rule.style.getPropertyValue("font-style") || "normal").trim(); const stretchSpec = (rule.style.getPropertyValue("font-stretch") || "100%").trim(); const srcRaw = (rule.style.getPropertyValue("src") || "").trim(); const urange = (rule.style.getPropertyValue("unicode-range") || "").trim(); if (!ctx.faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) continue; const ranges = parseUnicodeRange(urange); if (!unicodeIntersects(ctx.usedCodepoints, ranges)) continue; const meta = { family, weightSpec, styleSpec, stretchSpec, unicodeRange: urange, srcRaw, srcUrls: extractSrcUrls(srcRaw, baseHref || location.href), href: baseHref || location.href }; if (ctx.simpleExcluder && ctx.simpleExcluder(meta, ranges)) continue; if (/url\(/i.test(srcRaw)) { const inlinedSrc = await inlineUrlsInCssBlock(srcRaw, baseHref || location.href, ctx.useProxy); await emitFace(`@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`); } else { await emitFace(`@font-face{font-family:${family};src:${srcRaw};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`); } } } } async function embedCustomFonts({ required, usedCodepoints, exclude = void 0, localFonts = [], useProxy = "" } = {}) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r; if (!(required instanceof Set)) required = /* @__PURE__ */ new Set(); if (!(usedCodepoints instanceof Set)) usedCodepoints = /* @__PURE__ */ new Set(); const requiredIndex = /* @__PURE__ */ new Map(); for (const key of required) { const [fam, w, s, st] = String(key).split("__"); if (!fam) continue; const arr = requiredIndex.get(fam) || []; arr.push({ w: parseInt(w, 10), s, st: parseInt(st, 10) }); requiredIndex.set(fam, arr); } function faceMatchesRequired(fam, styleSpec, weightSpec, stretchSpec) { if (!requiredIndex.has(fam)) return false; const need = requiredIndex.get(fam); const ws = parseWeightSpec(weightSpec); const ss = parseStyleSpec(styleSpec); const ts = parseStretchSpec(stretchSpec); const faceIsRange = ws.min !== ws.max; const faceSingleW = ws.min; const styleOK = (reqKind) => ss.kind === "normal" && reqKind === "normal" || ss.kind !== "normal" && (reqKind === "italic" || reqKind === "oblique"); let exactMatched = false; for (const r of need) { const wOk = faceIsRange ? r.w >= ws.min && r.w <= ws.max : r.w === faceSingleW; const sOk = styleOK(normStyle(r.s)); const tOk = r.st >= ts.min && r.st <= ts.max; if (wOk && sOk && tOk) { exactMatched = true; break; } } if (exactMatched) return true; if (!faceIsRange) { for (const r of need) { const sOk = styleOK(normStyle(r.s)); const tOk = r.st >= ts.min && r.st <= ts.max; const nearWeight = Math.abs(faceSingleW - r.w) <= 300; if (nearWeight && sOk && tOk) return true; } } return false; } const simpleExcluder = buildSimpleExcluder(exclude); const cacheKey = buildFontsCacheKey(required, exclude, localFonts, useProxy); if ((_a = cache.resource) == null ? void 0 : _a.has(cacheKey)) { return cache.resource.get(cacheKey); } const requiredFamilies = familiesFromRequired(required); const importUrls = []; const IMPORT_ANY_RE_LOCAL = IMPORT_ANY_RE; for (const styleTag of document.querySelectorAll("style")) { const cssText = styleTag.textContent || ""; for (const m of cssText.matchAll(IMPORT_ANY_RE_LOCAL)) { const u = (m[2] || m[4] || "").trim(); if (!u || isIconFont2(u)) continue; const hasLink = !!document.querySelector(`link[rel="stylesheet"][href="${u}"]`); if (!hasLink) importUrls.push(u); } } if (importUrls.length) { await Promise.all(importUrls.map((u) => new Promise((resolve) => { if (document.querySelector(`link[rel="stylesheet"][href="${u}"]`)) return resolve(null); const link = document.createElement("link"); link.rel = "stylesheet"; link.href = u; link.setAttribute("data-snapdom", "injected-import"); link.onload = () => resolve(link); link.onerror = () => resolve(null); document.head.appendChild(link); }))); } let finalCSS = ""; const linkNodes = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((l) => !!l.href); for (const link of linkNodes) { try { if (isIconFont2(link.href)) continue; let cssText = ""; let sameOrigin = false; try { sameOrigin = new URL(link.href, location.href).origin === location.origin; } catch { } if (!sameOrigin) { if (!isLikelyFontStylesheet(link.href, requiredFamilies)) continue; } if (sameOrigin) { const sheet = Array.from(document.styleSheets).find((s) => s.href === link.href); if (sheet) { try { const rules = sheet.cssRules || []; cssText = Array.from(rules).map((r) => r.cssText).join(""); } catch { } } } if (!cssText) { const res = await snapFetch(link.href, { as: "text", useProxy }); cssText = res.data; if (isIconFont2(link.href)) continue; } cssText = await inlineImportsAndRewrite(cssText, link.href, useProxy); let facesOut = ""; for (const face of cssText.match(FACE_RE) || []) { const famRaw = (((_b = face.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _b[1]) || "").trim(); const family = pickPrimaryFamily(famRaw); if (!family || isIconFont2(family)) continue; const weightSpec = (((_c = face.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _c[1]) || "400").trim(); const styleSpec = (((_d = face.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _d[1]) || "normal").trim(); const stretchSpec = (((_e = face.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _e[1]) || "100%").trim(); const urange = (((_f = face.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _f[1]) || "").trim(); const srcRaw = (((_g = face.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _g[1]) || "").trim(); const srcUrls = extractSrcUrls(srcRaw, link.href); if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) continue; const ranges = parseUnicodeRange(urange); if (!unicodeIntersects(usedCodepoints, ranges)) continue; const meta = { family, weightSpec, styleSpec, stretchSpec, unicodeRange: urange, srcRaw, srcUrls, href: link.href }; if (exclude && simpleExcluder(meta, ranges)) continue; const newFace = /url\(/i.test(srcRaw) ? await inlineUrlsInCssBlock(face, link.href, useProxy) : face; facesOut += newFace; } if (facesOut.trim()) finalCSS += facesOut; } catch { console.warn("[snapDOM] Failed to process stylesheet:", link.href); } } const ctx = { requiredIndex, usedCodepoints, faceMatchesRequired, simpleExcluder: exclude ? buildSimpleExcluder(exclude) : null, useProxy, visitedSheets: /* @__PURE__ */ new Set(), depth: 0 }; for (const sheet of document.styleSheets) { if (sheet.href && linkNodes.some((l) => l.href === sheet.href)) continue; try { const rootHref = sheet.href || location.href; if (rootHref) ctx.visitedSheets.add(rootHref); await collectFacesFromSheet( sheet, rootHref, async (faceCss) => { finalCSS += faceCss; }, ctx ); } catch { } } try { for (const f of document.fonts || []) { if (!f || !f.family || f.status !== "loaded" || !f._snapdomSrc) continue; const fam = String(f.family).replace(/^['"]+|['"]+$/g, ""); if (isIconFont2(fam)) continue; if (!requiredIndex.has(fam)) continue; if ((exclude == null ? void 0 : exclude.families) && exclude.families.some((n) => String(n).toLowerCase() === fam.toLowerCase())) { continue; } let b64 = f._snapdomSrc; if (!String(b64).startsWith("data:")) { if ((_h = cache.resource) == null ? void 0 : _h.has(f._snapdomSrc)) { b64 = cache.resource.get(f._snapdomSrc); (_i = cache.font) == null ? void 0 : _i.add(f._snapdomSrc); } else if (!((_j = cache.font) == null ? void 0 : _j.has(f._snapdomSrc))) { try { const r = await snapFetch(f._snapdomSrc, { as: "dataURL", useProxy, silent: true }); if (r.ok && typeof r.data === "string") { b64 = r.data; (_k = cache.resource) == null ? void 0 : _k.set(f._snapdomSrc, b64); (_l = cache.font) == null ? void 0 : _l.add(f._snapdomSrc); } else { continue; } } catch { console.warn("[snapDOM] Failed to fetch dynamic font src:", f._snapdomSrc); continue; } } } finalCSS += `@font-face{font-family:'${fam}';src:url(${b64});font-style:${f.style || "normal"};font-weight:${f.weight || "normal"};}`; } } catch { } for (const font of localFonts) { if (!font || typeof font !== "object") continue; const family = String(font.family || "").replace(/^['"]+|['"]+$/g, ""); if (!family || isIconFont2(family)) continue; if (!requiredIndex.has(family)) continue; if ((exclude == null ? void 0 : exclude.families) && exclude.families.some((n) => String(n).toLowerCase() === family.toLowerCase())) continue; const weight = font.weight != null ? String(font.weight) : "normal"; const style = font.style != null ? String(font.style) : "normal"; const stretch = font.stretchPct != null ? `${font.stretchPct}%` : "100%"; const src = String(font.src || ""); let b64 = src; if (!b64.startsWith("data:")) { if ((_m = cache.resource) == null ? void 0 : _m.has(src)) { b64 = cache.resource.get(src); (_n = cache.font) == null ? void 0 : _n.add(src); } else if (!((_o = cache.font) == null ? void 0 : _o.has(src))) { try { const r = await snapFetch(src, { as: "dataURL", useProxy, silent: true }); if (r.ok && typeof r.data === "string") { b64 = r.data; (_p = cache.resource) == null ? void 0 : _p.set(src, b64); (_q = cache.font) == null ? void 0 : _q.add(src); } else { continue; } } catch { console.warn("[snapDOM] Failed to fetch localFonts src:", src); continue; } } } finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};font-stretch:${stretch};}`; } if (finalCSS) { finalCSS = dedupeFontFaces(finalCSS); (_r = cache.resource) == null ? void 0 : _r.set(cacheKey, finalCSS); } return finalCSS; } function collectUsedFontVariants(root) { const req = /* @__PURE__ */ new Set(); if (!root) return req; const tw = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null); const addFromStyle = (cs) => { const family = pickPrimaryFamily(cs.fontFamily); if (!family) return; const key = (w, s, st) => `${family}__${normWeight(w)}__${normStyle(s)}__${normStretchPct(st)}`; req.add(key(cs.fontWeight, cs.fontStyle, cs.fontStretch)); }; addFromStyle(getComputedStyle(root)); const csBeforeRoot = getComputedStyle(root, "::before"); if (csBeforeRoot && csBeforeRoot.content && csBeforeRoot.content !== "none") addFromStyle(csBeforeRoot); const csAfterRoot = getComputedStyle(root, "::after"); if (csAfterRoot && csAfterRoot.content && csAfterRoot.content !== "none") addFromStyle(csAfterRoot); while (tw.nextNode()) { const el = ( /** @type {Element} */ tw.currentNode ); const cs = getComputedStyle(el); addFromStyle(cs); const b = getComputedStyle(el, "::before"); if (b && b.content && b.content !== "none") addFromStyle(b); const a = getComputedStyle(el, "::after"); if (a && a.content && a.content !== "none") addFromStyle(a); } return req; } function collectUsedCodepoints(root) { const used = /* @__PURE__ */ new Set(); const pushText = (txt) => { if (!txt) return; for (const ch of txt) used.add(ch.codePointAt(0)); }; const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null); while (walker.nextNode()) { const n = walker.currentNode; if (n.nodeType === Node.TEXT_NODE) { pushText(n.nodeValue || ""); } else if (n.nodeType === Node.ELEMENT_NODE) { const el = ( /** @type {Element} */ n ); for (const pseudo of ["::before", "::after"]) { const cs = getComputedStyle(el, pseudo); const c = cs == null ? void 0 : cs.getPropertyValue("content"); if (!c || c === "none") continue; if (/^"/.test(c) || /^'/.test(c)) { pushText(c.slice(1, -1)); } else { const matches = c.match(/\\[0-9A-Fa-f]{1,6}/g); if (matches) { for (const m of matches) { try { used.add(parseInt(m.slice(1), 16)); } catch { } } } } } } } return used; } async function ensureFontsReady(families, warmupRepetitions = 2) { try { await document.fonts.ready; } catch { } const fams = Array.from(families || []).filter(Boolean); if (fams.length === 0) return; const warmupOnce = () => { const container = document.createElement("div"); container.style.cssText = "position:absolute!important;left:-9999px!important;top:0!important;opacity:0!important;pointer-events:none!important;contain:layout size style;"; for (const fam of fams) { const span = document.createElement("span"); span.textContent = "AaBbGg1234ÁÉÍÓÚçñ—∞"; span.style.fontFamily = `"${fam}"`; span.style.fontWeight = "700"; span.style.fontStyle = "italic"; span.style.fontSize = "32px"; span.style.lineHeight = "1"; span.style.whiteSpace = "nowrap"; span.style.margin = "0"; span.style.padding = "0"; container.appendChild(span); } document.body.appendChild(container); container.offsetWidth; document.body.removeChild(container); }; for (let i = 0; i < Math.max(1, warmupRepetitions); i++) { warmupOnce(); await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r))); } } function hasCounters(input) { return /\bcounter\s*\(|\bcounters\s*\(/.test(input || ""); } function unquoteDoubleStrings(s) { return (s || "").replace(/"([^"]*)"/g, "$1"); } function alpha(n, upper = false) { let s = "", x = Math.max(1, n); while (x > 0) { x--; s = String.fromCharCode(97 + x % 26) + s; x = Math.floor(x / 26); } return upper ? s.toUpperCase() : s; } function roman(n, upper = true) { const map = [[1e3, "M"], [900, "CM"], [500, "D"], [400, "CD"], [100, "C"], [90, "XC"], [50, "L"], [40, "XL"], [10, "X"], [9, "IX"], [5, "V"], [4, "IV"], [1, "I"]]; let num = Math.max(1, Math.min(3999, n)), out = ""; for (const [v, sym] of map) while (num >= v) { out += sym; num -= v; } return upper ? out : out.toLowerCase(); } function formatCounter(value, style) { switch ((style || "decimal").toLowerCase()) { case "decimal": return String(Math.max(0, value)); case "decimal-leading-zero": return (value < 10 ? "0" : "") + String(Math.max(0, value)); case "lower-alpha": return alpha(value, false); case "upper-alpha": return alpha(value, true); case "lower-roman": return roman(value, false); case "upper-roman": return roman(value, true); default: return String(Math.max(0, value)); } } function buildCounterContext(root) { const nodeCounters = /* @__PURE__ */ new WeakMap(); const rootEl = root instanceof Document ? root.documentElement : root; const isLi = (el) => el && el.tagName === "LI"; const countPrevLi = (li) => { let c = 0, p = li == null ? void 0 : li.parentElement; if (!p) return 0; for (const sib of p.children) { if (sib === li) break; if (sib.tagName === "LI") c++; } return c; }; const cloneMap = (m) => { const out = /* @__PURE__ */ new Map(); for (const [k, arr] of m) out.set(k, arr.slice()); return out; }; const applyTo = (baseMap, parentMap, el) => { var _a, _b; const map = cloneMap(baseMap); let reset; try { reset = ((_a = el.style) == null ? void 0 : _a.counterReset) || getComputedStyle(el).counterReset; } catch { } if (reset && reset !== "none") { for (const part of reset.split(",")) { const toks = part.trim().split(/\s+/); const name = toks[0]; const val = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : 0; if (!name) continue; const parentStack = parentMap.get(name); if (parentStack && parentStack.length) { const s = parentStack.slice(); s.push(val); map.set(name, s); } else { map.set(name, [val]); } } } let inc; try { inc = ((_b = el.style) == null ? void 0 : _b.counterIncrement) || getComputedStyle(el).counterIncrement; } catch { } if (inc && inc !== "none") { for (const part of inc.split(",")) { const toks = part.trim().split(/\s+/); const name = toks[0]; const by = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : 1; if (!name) continue; const stack = map.get(name) || []; if (stack.length === 0) stack.push(0); stack[stack.length - 1] += by; map.set(name, stack); } } try { const cs = getComputedStyle(el); if (cs.display === "list-item" && isLi(el)) { const p = el.parentElement; let idx = 1; if (p && p.tagName === "OL") { const startAttr = p.getAttribute("start"); const start = Number.isFinite(Number(startAttr)) ? Number(startAttr) : 1; const prev = countPrevLi(el); const ownAttr = el.getAttribute("value"); idx = Number.isFinite(Number(ownAttr)) ? Number(ownAttr) : start + prev; } else { idx = 1 + countPrevLi(el); } const s = map.get("list-item") || []; if (s.length === 0) s.push(0); s[s.length - 1] = idx; map.set("list-item", s); } } catch { } return map; }; const build = (el, parentMap, carryMap) => { const curr = applyTo(carryMap, parentMap, el); nodeCounters.set(el, curr); let nextCarry = curr; for (const child of el.children) { const childCarry = build(child, curr, nextCarry); nextCarry = childCarry; } return curr; }; const empty = /* @__PURE__ */ new Map(); build(rootEl, empty, empty); return { /** * Get top value for counter name at given node. * @param {Element} node * @param {string} name */ get(node, name) { var _a; const s = (_a = nodeCounters.get(node)) == null ? void 0 : _a.get(name); return s && s.length ? s[s.length - 1] : 0; }, /** * Get full stack for counter name at given node. * @param {Element} node * @param {string} name */ getStack(node, name) { var _a; const s = (_a = nodeCounters.get(node)) == null ? void 0 : _a.get(name); return s ? s.slice() : []; } }; } function resolveCountersInContent(raw, node, ctx) { if (!raw || raw === "none") return raw; try { const RX = /\b(counter|counters)\s*\(([^)]+)\)/g; let out = raw.replace(RX, (_, fn, args) => { var _a, _b, _c; const parts = String(args).split(",").map((s) => s.trim()); if (fn === "counter") { const name = (_a = parts[0]) == null ? void 0 : _a.replace(/^["']|["']$/g, ""); const style = (parts[1] || "decimal").toLowerCase(); const v = ctx.get(node, name); return formatCounter(v, style); } else { const name = (_b = parts[0]) == null ? void 0 : _b.replace(/^["']|["']$/g, ""); const sep = ((_c = parts[1]) == null ? void 0 : _c.replace(/^["']|["']$/g, "")) ?? ""; const style = (parts[2] || "decimal").toLowerCase(); const stack = ctx.getStack(node, name); if (!stack.length) return ""; const pieces = stack.map((v) => formatCounter(v, style)); return pieces.join(sep); } }); return unquoteDoubleStrings(out); } catch { return "- "; } } var counterCtx = null; var __siblingCounters = /* @__PURE__ */ new WeakMap(); function unquoteDoubleStrings2(s) { return (s || "").replace(/"([^"]*)"/g, "$1"); } function collapseCssContent(raw) { if (!raw) return ""; const tokens = []; const rx = /"([^"]*)"/g; let m; while (m = rx.exec(raw)) tokens.push(m[1]); if (tokens.length) return tokens.join(""); return unquoteDoubleStrings2(raw); } function withSiblingOverrides(node, base) { const parent = node.parentElement; const map = parent ? __siblingCounters.get(parent) : null; if (!map) return base; return { get(n, name) { const v = base.get(n, name); const ov = map.get(name); return typeof ov === "number" ? Math.max(v, ov) : v; }, getStack(n, name) { const s = base.getStack(n, name); if (!s.length) return s; const ov = map.get(name); if (typeof ov === "number") { const out = s.slice(); out[out.length - 1] = Math.max(out[out.length - 1], ov); return out; } return s; } }; } function deriveCounterCtxForPseudo(node, pseudoStyle, baseCtx) { const modStacks = /* @__PURE__ */ new Map(); function parseListDecl(value) { const out = []; if (!value || value === "none") return out; for (const part of String(value).split(",")) { const toks = part.trim().split(/\s+/); const name = toks[0]; const num = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : void 0; if (name) out.push({ name, num }); } return out; } const resets = parseListDecl(pseudoStyle == null ? void 0 : pseudoStyle.counterReset); const incs = parseListDecl(pseudoStyle == null ? void 0 : pseudoStyle.counterIncrement); function getStackDerived(name) { if (modStacks.has(name)) return modStacks.get(name).slice(); let stack = baseCtx.getStack(node, name); stack = stack.length ? stack.slice() : []; const r = resets.find((x) => x.name === name); if (r) { const val = Number.isFinite(r.num) ? r.num : 0; stack = stack.length ? [...stack, val] : [val]; } const inc = incs.find((x) => x.name === name); if (inc) { const by = Number.isFinite(inc.num) ? inc.num : 1; if (stack.length === 0) stack = [0]; stack[stack.length - 1] += by; } modStacks.set(name, stack.slice()); return stack; } return { get(_node, name) { const s = getStackDerived(name); return s.length ? s[s.length - 1] : 0; }, getStack(_node, name) { return getStackDerived(name); }, /** expone increments del pseudo para que el caller pueda propagar a hermanos */ __incs: incs }; } function resolvePseudoContentAndIncs(node, pseudo, baseCtx) { let ps; try { ps = getComputedStyle(node, pseudo); } catch { } const raw = ps == null ? void 0 : ps.content; if (!raw || raw === "none" || raw === "normal") return { text: "", incs: [] }; const baseWithSiblings = withSiblingOverrides(node, baseCtx); const derived = deriveCounterCtxForPseudo(node, ps, baseWithSiblings); let resolved = hasCounters(raw) ? resolveCountersInContent(raw, node, derived) : raw; const text = collapseCssContent(resolved); return { text, incs: derived.__incs || [] }; } async function inlinePseudoElements(source, clone, sessionCache, options) { var _a; if (!(source instanceof Element) || !(clone instanceof Element)) return; if (!counterCtx) { try { counterCtx = buildCounterContext(source.ownerDocument || document); } catch { } } for (const pseudo of ["::before", "::after", "::first-letter"]) { try { const style = getStyle(source, pseudo); if (!style || typeof style[Symbol.iterator] !== "function") continue; const isEmptyPseudo = style.content === "none" && style.backgroundImage === "none" && style.backgroundColor === "transparent" && (style.borderStyle === "none" || parseFloat(style.borderWidth) === 0) && (!style.transform || style.transform === "none") && style.display === "inline"; if (isEmptyPseudo) continue; if (pseudo === "::first-letter") { const normal = getComputedStyle(source); const isMeaningful = style.color !== normal.color || style.fontSize !== normal.fontSize || style.fontWeight !== normal.fontWeight; if (!isMeaningful) continue; const textNode = Array.from(clone.childNodes).find( (n) => { var _a2; return n.nodeType === Node.TEXT_NODE && ((_a2 = n.textContent) == null ? void 0 : _a2.trim().length) > 0; } ); if (!textNode) continue; const text = textNode.textContent; const match = text.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u); const first = match == null ? void 0 : match[0]; const rest = text.slice((first == null ? void 0 : first.length) || 0); if (!first || /[\uD800-\uDFFF]/.test(first)) continue; const span = document.createElement("span"); span.textContent = first; span.dataset.snapdomPseudo = "::first-letter"; const snapshot2 = snapshotComputedStyle(style); const key2 = getStyleKey(snapshot2, "span"); sessionCache.styleMap.set(span, key2); const restNode = document.createTextNode(rest); clone.replaceChild(restNode, textNode); clone.insertBefore(span, restNode); continue; } const rawContent = style.content; const { text: cleanContent, incs } = resolvePseudoContentAndIncs(source, pseudo, counterCtx); const bg = style.backgroundImage; const bgColor = style.backgroundColor; const fontFamily = style.fontFamily; const fontSize = parseInt(style.fontSize) || 32; const fontWeight = parseInt(style.fontWeight) || false; const color = style.color || "#000"; const borderStyle = style.borderStyle; const borderWidth = parseFloat(style.borderWidth); const transform = style.transform; const isIconFont22 = isIconFont2(fontFamily); const hasExplicitContent = rawContent !== "none" && cleanContent !== ""; const hasBg = bg && bg !== "none"; const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)"; const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0; const hasTransform = transform && transform !== "none"; const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBorder || hasTransform; if (!shouldRender) { if (incs && incs.length && source.parentElement) { const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map(); for (const { name } of incs) { if (!name) continue; const baseWithSibs = withSiblingOverrides(source, counterCtx); const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs); const finalVal = derived.get(source, name); map.set(name, finalVal); } __siblingCounters.set(source.parentElement, map); } continue; } const pseudoEl = document.createElement("span"); pseudoEl.dataset.snapdomPseudo = pseudo; pseudoEl.style.verticalAlign = "middle"; pseudoEl.style.pointerEvents = "none"; const snapshot = snapshotComputedStyle(style); const key = getStyleKey(snapshot, "span"); sessionCache.styleMap.set(pseudoEl, key); if (isIconFont22 && cleanContent && cleanContent.length === 1) { const { dataUrl, width: w, height: h } = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color); const imgEl = document.createElement("img"); imgEl.src = dataUrl; imgEl.style = `height:${fontSize}px;width:${w / h * fontSize}px;object-fit:contain;`; pseudoEl.appendChild(imgEl); clone.dataset.snapdomHasIcon = "true"; } else if (cleanContent && cleanContent.startsWith("url(")) { const rawUrl = extractURL(cleanContent); if (rawUrl == null ? void 0 : rawUrl.trim()) { try { const imgEl = document.createElement("img"); const dataUrl = await snapFetch(safeEncodeURI(rawUrl), { as: "dataURL", useProxy: options.useProxy }); imgEl.src = dataUrl.data; imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`; pseudoEl.appendChild(imgEl); } catch (e) { console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e); } } } else if (!isIconFont22 && hasExplicitContent) { pseudoEl.textContent = cleanContent; } pseudoEl.style.background = "none"; if ("mask" in pseudoEl.style) { pseudoEl.style.mask = "none"; } if (hasBg) { try { const bgSplits = splitBackgroundImage(bg); const newBgParts = await Promise.all(bgSplits.map(inlineSingleBackgroundEntry)); pseudoEl.style.backgroundImage = newBgParts.join(", "); } catch (e) { console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e); } } if (hasBgColor) pseudoEl.style.backgroundColor = bgColor; const hasContent2 = pseudoEl.childNodes.length > 0 || ((_a = pseudoEl.textContent) == null ? void 0 : _a.trim()) !== ""; const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBorder || hasTransform; if (incs && incs.length && source.parentElement) { const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map(); const baseWithSibs = withSiblingOverrides(source, counterCtx); const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs); for (const { name } of incs) { if (!name) continue; const finalVal = derived.get(source, name); map.set(name, finalVal); } __siblingCounters.set(source.parentElement, map); } if (!hasVisibleBox) continue; if (pseudo === "::before") { clone.insertBefore(pseudoEl, clone.firstChild); } else { clone.appendChild(pseudoEl); } } catch (e) { console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e); } } const sChildren = Array.from(source.children); const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo); for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) { await inlinePseudoElements(sChildren[i], cChildren[i], sessionCache, options); } } function inlineExternalDefsAndSymbols(rootElement) { if (!rootElement) return; const usedIds = /* @__PURE__ */ new Set(); rootElement.querySelectorAll("use").forEach((use) => { const href = use.getAttribute("xlink:href") || use.getAttribute("href"); if (href && href.startsWith("#")) { usedIds.add(href.slice(1)); } }); if (!usedIds.size) return; const allGlobal = Array.from(document.querySelectorAll("svg > symbol, svg > defs")); const globalSymbols = allGlobal.filter((el) => el.tagName.toLowerCase() === "symbol"); const globalDefs = allGlobal.filter((el) => el.tagName.toLowerCase() === "defs"); let container = rootElement.querySelector("svg.inline-defs-container"); if (!container) { container = document.createElementNS("http://www.w3.org/2000/svg", "svg"); container.setAttribute("aria-hidden", "true"); container.setAttribute("style", "position: absolute; width: 0; height: 0; overflow: hidden;"); container.classList.add("inline-defs-container"); rootElement.insertBefore(container, rootElement.firstChild); } const existingIds = /* @__PURE__ */ new Set(); rootElement.querySelectorAll("symbol[id], defs > *[id]").forEach((el) => { existingIds.add(el.id); }); usedIds.forEach((id) => { if (existingIds.has(id)) return; const symbol = globalSymbols.find((sym) => sym.id === id); if (symbol) { container.appendChild(symbol.cloneNode(true)); existingIds.add(id); return; } for (const defs of globalDefs) { const defEl = defs.querySelector(`#${CSS.escape(id)}`); if (defEl) { let defsContainer = container.querySelector("defs"); if (!defsContainer) { defsContainer = document.createElementNS("http://www.w3.org/2000/svg", "defs"); container.appendChild(defsContainer); } defsContainer.appendChild(defEl.cloneNode(true)); existingIds.add(id); break; } } }); } function freezeSticky(originalRoot, cloneRoot) { var _a; if (!originalRoot || !cloneRoot) return; const scrollTop = originalRoot.scrollTop || 0; if (!scrollTop) return; if (getComputedStyle(cloneRoot).position === "static") { cloneRoot.style.position = "relative"; } const rootRect = originalRoot.getBoundingClientRect(); const viewportH = originalRoot.clientHeight; const PH_ATTR = "data-snap-ph"; const walker = document.createTreeWalker(originalRoot, NodeFilter.SHOW_ELEMENT); while (walker.nextNode()) { const el = ( /** @type {HTMLElement} */ walker.currentNode ); const cs = getComputedStyle(el); const pos = cs.position; if (pos !== "sticky" && pos !== "-webkit-sticky") continue; const topInit = _toPx(cs.top); const bottomInit = _toPx(cs.bottom); if (topInit == null && bottomInit == null) continue; const path = _pathOf(el, originalRoot); const cloneEl = _findByPathIgnoringPlaceholders(cloneRoot, path, PH_ATTR); if (!cloneEl) continue; const elRect = el.getBoundingClientRect(); const widthPx = elRect.width; const heightPx = elRect.height; const leftPx = elRect.left - rootRect.left; if (!(widthPx > 0 && heightPx > 0)) continue; if (!Number.isFinite(leftPx)) continue; const topAbsPx = topInit != null ? topInit + scrollTop : scrollTop + (viewportH - heightPx - /** bottomInit non-null */ bottomInit); if (!Number.isFinite(topAbsPx)) continue; const zParsed = Number.parseInt(cs.zIndex, 10); const hasZ = Number.isFinite(zParsed); const overlayZ = hasZ ? Math.max(zParsed, 1) + 1 : 2; const placeholderZ = hasZ ? zParsed - 1 : 0; const ph = cloneEl.cloneNode(false); ph.setAttribute(PH_ATTR, "1"); ph.style.position = "sticky"; ph.style.left = `${leftPx}px`; ph.style.top = `${topAbsPx}px`; ph.style.width = `${widthPx}px`; ph.style.height = `${heightPx}px`; ph.style.visibility = "hidden"; ph.style.zIndex = String(placeholderZ); ph.style.overflow = "hidden"; ph.style.background = "transparent"; ph.style.boxShadow = "none"; ph.style.filter = "none"; (_a = cloneEl.parentElement) == null ? void 0 : _a.insertBefore(ph, cloneEl); cloneEl.style.position = "absolute"; cloneEl.style.left = `${leftPx}px`; cloneEl.style.top = `${topAbsPx}px`; cloneEl.style.bottom = "auto"; cloneEl.style.zIndex = String(overlayZ); cloneEl.style.pointerEvents = "none"; } } function _toPx(v) { if (!v || v === "auto") return null; const n = Number.parseFloat(v); return Number.isFinite(n) ? n : null; } function _pathOf(el, root) { const path = []; for (let cur = el; cur && cur !== root; ) { const p = cur.parentElement; if (!p) break; path.push(Array.prototype.indexOf.call(p.children, cur)); cur = p; } return path.reverse(); } function _findByPathIgnoringPlaceholders(root, path, phAttr) { let cur = root; for (let i = 0; i < path.length; i++) { const kids = _childrenWithoutPlaceholders(cur, phAttr); cur = /** @type {HTMLElement|undefined} */ kids[path[i]]; if (!cur) return null; } return cur instanceof HTMLElement ? cur : null; } function _childrenWithoutPlaceholders(el, phAttr) { const out = []; const ch = el.children; for (let i = 0; i < ch.length; i++) { const c = ch[i]; if (!c.hasAttribute(phAttr)) out.push(c); } return out; } async function prepareClone(element, options = {}) { var _a, _b; const sessionCache = { styleMap: cache.session.styleMap, styleCache: cache.session.styleCache, nodeMap: cache.session.nodeMap }; let clone; let classCSS = ""; let shadowScopedCSS = ""; stabilizeLayout(element); try { inlineExternalDefsAndSymbols(element); } catch (e) { console.warn("inlineExternal defs or symbol failed:", e); } try { clone = await deepClone(element, sessionCache, options, element); } catch (e) { console.warn("deepClone failed:", e); throw e; } try { await inlinePseudoElements(element, clone, sessionCache, options); } catch (e) { console.warn("inlinePseudoElements failed:", e); } await resolveBlobUrlsInTree(clone); try { const styleNodes = clone.querySelectorAll("style[data-sd]"); for (const s of styleNodes) { shadowScopedCSS += s.textContent || ""; s.remove(); } } catch { } const keyToClass = generateCSSClasses(sessionCache.styleMap); classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join(""); classCSS = shadowScopedCSS + classCSS; for (const [node, key] of sessionCache.styleMap.entries()) { if (node.tagName === "STYLE") continue; if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) { node.setAttribute("style", key.replace(/;/g, "; ")); continue; } const className = keyToClass.get(key); if (className) node.classList.add(className); const bgImage = (_a = node.style) == null ? void 0 : _a.backgroundImage; const hasIcon = (_b = node.dataset) == null ? void 0 : _b.snapdomHasIcon; if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage; if (hasIcon) { node.style.verticalAlign = "middle"; node.style.display = "inline"; } } for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) { const scrollX = originalNode.scrollLeft; const scrollY = originalNode.scrollTop; const hasScroll = scrollX || scrollY; if (hasScroll && cloneNode instanceof HTMLElement) { cloneNode.style.overflow = "hidden"; cloneNode.style.scrollbarWidth = "none"; cloneNode.style.msOverflowStyle = "none"; const inner = document.createElement("div"); inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`; inner.style.willChange = "transform"; inner.style.display = "inline-block"; inner.style.width = "100%"; while (cloneNode.firstChild) { inner.appendChild(cloneNode.firstChild); } cloneNode.appendChild(inner); } } const contentRoot = clone instanceof HTMLElement && clone.firstElementChild instanceof HTMLElement ? clone.firstElementChild : clone; freezeSticky(element, contentRoot); if (element === sessionCache.nodeMap.get(clone)) { const computed = sessionCache.styleCache.get(element) || window.getComputedStyle(element); sessionCache.styleCache.set(element, computed); const transform = stripTranslate(computed.transform); clone.style.margin = "0"; clone.style.top = "auto"; clone.style.left = "auto"; clone.style.right = "auto"; clone.style.bottom = "auto"; clone.style.animation = "none"; clone.style.transition = "none"; clone.style.willChange = "auto"; clone.style.float = "none"; clone.style.clear = "none"; clone.style.transform = transform || ""; } for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) { if (originalNode.tagName === "PRE") { cloneNode.style.marginTop = "0"; cloneNode.style.marginBlockStart = "0"; } } return { clone, classCSS, styleCache: sessionCache.styleCache }; } function stabilizeLayout(element) { const style = getComputedStyle(element); const outlineStyle = style.outlineStyle; const outlineWidth = style.outlineWidth; const borderStyle = style.borderStyle; const borderWidth = style.borderWidth; const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0; const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0; if (outlineVisible && borderAbsent) { element.style.border = `${outlineWidth} solid transparent`; } } var _blobToDataUrlCache = /* @__PURE__ */ new Map(); async function blobUrlToDataUrl(blobUrl) { var _a; if ((_a = cache.resource) == null ? void 0 : _a.has(blobUrl)) return cache.resource.get(blobUrl); if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl); const p = (async () => { var _a2; const r = await snapFetch(blobUrl, { as: "dataURL", silent: true }); if (!r.ok || typeof r.data !== "string") { throw new Error(`[snapDOM] Failed to read blob URL: ${blobUrl}`); } (_a2 = cache.resource) == null ? void 0 : _a2.set(blobUrl, r.data); return r.data; })(); _blobToDataUrlCache.set(blobUrl, p); try { const data = await p; _blobToDataUrlCache.set(blobUrl, data); return data; } catch (e) { _blobToDataUrlCache.delete(blobUrl); throw e; } } var BLOB_URL_RE = /\bblob:[^)"'\s]+/g; async function replaceBlobUrlsInCssText(cssText) { if (!cssText || cssText.indexOf("blob:") === -1) return cssText; const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || [])); if (uniques.length === 0) return cssText; let out = cssText; for (const u of uniques) { try { const d = await blobUrlToDataUrl(u); out = out.split(u).join(d); } catch { } } return out; } function isBlobUrl(u) { return typeof u === "string" && u.startsWith("blob:"); } function parseSrcset(srcset) { return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => { const m = item.match(/^(\S+)(\s+.+)?$/); return m ? { url: m[1], desc: m[2] || "" } : null; }).filter(Boolean); } function stringifySrcset(parts) { return parts.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", "); } async function resolveBlobUrlsInTree(root) { var _a, _b; if (!root) return; const imgs = root.querySelectorAll ? root.querySelectorAll("img") : []; for (const img of imgs) { try { const srcAttr = img.getAttribute("src"); const effective = srcAttr || img.currentSrc || ""; if (isBlobUrl(effective)) { const data = await blobUrlToDataUrl(effective); img.setAttribute("src", data); } const srcset = img.getAttribute("srcset"); if (srcset && srcset.includes("blob:")) { const parts = parseSrcset(srcset); let changed = false; for (const p of parts) { if (isBlobUrl(p.url)) { try { p.url = await blobUrlToDataUrl(p.url); changed = true; } catch { } } } if (changed) img.setAttribute("srcset", stringifySrcset(parts)); } } catch { } } const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : []; for (const node of svgImages) { try { const XLINK_NS = "http://www.w3.org/1999/xlink"; const href = node.getAttribute("href") || ((_a = node.getAttributeNS) == null ? void 0 : _a.call(node, XLINK_NS, "href")); if (isBlobUrl(href)) { const d = await blobUrlToDataUrl(href); node.setAttribute("href", d); (_b = node.removeAttributeNS) == null ? void 0 : _b.call(node, XLINK_NS, "href"); } } catch { } } const styled = root.querySelectorAll ? root.querySelectorAll("[style*='blob:']") : []; for (const el of styled) { try { const styleText = el.getAttribute("style"); if (styleText && styleText.includes("blob:")) { const replaced = await replaceBlobUrlsInCssText(styleText); el.setAttribute("style", replaced); } } catch { } } const styleTags = root.querySelectorAll ? root.querySelectorAll("style") : []; for (const s of styleTags) { try { const css = s.textContent || ""; if (css.includes("blob:")) { s.textContent = await replaceBlobUrlsInCssText(css); } } catch { } } const urlAttrs = ["poster"]; for (const attr of urlAttrs) { const nodes = root.querySelectorAll ? root.querySelectorAll(`[${attr}^='blob:']`) : []; for (const n of nodes) { try { const u = n.getAttribute(attr); if (isBlobUrl(u)) { n.setAttribute(attr, await blobUrlToDataUrl(u)); } } catch { } } } } async function inlineImages(clone, options = {}) { const imgs = Array.from(clone.querySelectorAll("img")); const processImg = async (img) => { var _a, _b, _c, _d; if (!img.getAttribute("src")) { const eff = img.currentSrc || img.src || ""; if (eff) img.setAttribute("src", eff); } img.removeAttribute("srcset"); img.removeAttribute("sizes"); const src = img.src || ""; if (!src) return; const r = await snapFetch(src, { as: "dataURL", useProxy: options.useProxy }); if (r.ok && typeof r.data === "string" && r.data.startsWith("data:")) { img.src = r.data; if (!img.width) img.width = img.naturalWidth || 100; if (!img.height) img.height = img.naturalHeight || 100; return; } const { fallbackURL } = options || {}; if (fallbackURL) { try { const dsW = parseInt(((_a = img.dataset) == null ? void 0 : _a.snapdomWidth) || "", 10) || 0; const dsH = parseInt(((_b = img.dataset) == null ? void 0 : _b.snapdomHeight) || "", 10) || 0; const attrW = parseInt(img.getAttribute("width") || "", 10) || 0; const attrH = parseInt(img.getAttribute("height") || "", 10) || 0; const styleW = parseFloat(((_c = img.style) == null ? void 0 : _c.width) || "") || 0; const styleH = parseFloat(((_d = img.style) == null ? void 0 : _d.height) || "") || 0; const fbW = dsW || styleW || attrW || img.width || void 0; const fbH = dsH || styleH || attrH || img.height || void 0; const fallbackUrl = typeof fallbackURL === "function" ? await fallbackURL({ width: fbW, height: fbH, src, element: img }) : fallbackURL; if (fallbackUrl) { const fallbackData = await snapFetch(fallbackUrl, { as: "dataURL", useProxy: options.useProxy }); img.src = fallbackData.data; if (!img.width && fbW) img.width = fbW; if (!img.height && fbH) img.height = fbH; if (!img.width) img.width = img.naturalWidth || 100; if (!img.height) img.height = img.naturalHeight || 100; return; } } catch { } } const w = img.width || img.naturalWidth || 100; const h = img.height || img.naturalHeight || 100; if (options.placeholders !== false) { const fallback = document.createElement("div"); fallback.style.cssText = [ `width:${w}px`, `height:${h}px`, "background:#ccc", "display:inline-block", "text-align:center", `line-height:${h}px`, "color:#666", "font-size:12px", "overflow:hidden" ].join(";"); fallback.textContent = "img"; img.replaceWith(fallback); } else { const spacer = document.createElement("div"); spacer.style.cssText = `display:inline-block;width:${w}px;height:${h}px;visibility:hidden;`; img.replaceWith(spacer); } }; for (let i = 0; i < imgs.length; i += 4) { const group = imgs.slice(i, i + 4).map(processImg); await Promise.allSettled(group); } } async function inlineBackgroundImages(source, clone, styleCache, options = {}) { const queue = [[source, clone]]; const URL_PROPS = [ "background-image", // Mask shorthands & images (both standard and WebKit) "mask", "mask-image", "-webkit-mask", "-webkit-mask-image", // Mask sources (rare, but keep) "mask-source", "mask-box-image-source", "mask-border-source", "-webkit-mask-box-image-source", // Border image "border-image", "border-image-source" ]; const MASK_LAYOUT_PROPS = [ "mask-position", "mask-size", "mask-repeat", // WebKit variants "-webkit-mask-position", "-webkit-mask-size", "-webkit-mask-repeat", // Extra (optional but helpful across engines) "mask-origin", "mask-clip", "-webkit-mask-origin", "-webkit-mask-clip", // Some engines expose X/Y position separately: "-webkit-mask-position-x", "-webkit-mask-position-y" ]; const BORDER_AUX_PROPS = [ "border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat" ]; while (queue.length) { const [srcNode, cloneNode] = queue.shift(); const style = styleCache.get(srcNode) || getStyle(srcNode); if (!styleCache.has(srcNode)) styleCache.set(srcNode, style); const hasBorderImage = (() => { const bi = style.getPropertyValue("border-image"); const bis = style.getPropertyValue("border-image-source"); return bi && bi !== "none" || bis && bis !== "none"; })(); for (const prop of URL_PROPS) { const val = style.getPropertyValue(prop); if (!val || val === "none") continue; const splits = splitBackgroundImage(val); const inlined = await Promise.all( splits.map((entry) => inlineSingleBackgroundEntry(entry, options)) ); if (inlined.some((p) => p && p !== "none" && !/^url\(undefined/.test(p))) { cloneNode.style.setProperty(prop, inlined.join(", ")); } } for (const prop of MASK_LAYOUT_PROPS) { const val = style.getPropertyValue(prop); if (!val || val === "initial") continue; cloneNode.style.setProperty(prop, val); } if (hasBorderImage) { for (const prop of BORDER_AUX_PROPS) { const val = style.getPropertyValue(prop); if (!val || val === "initial") continue; cloneNode.style.setProperty(prop, val); } } const sChildren = Array.from(srcNode.children); const cChildren = Array.from(cloneNode.children); for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) { queue.push([sChildren[i], cChildren[i]]); } } } function lineClamp(el) { if (!el) return () => { }; const lines = getClamp(el); if (lines <= 0) return () => { }; if (!isPlainTextContainer(el)) return () => { }; const cs = getComputedStyle(el); const targetH = Math.round(usedLineHeightPx(cs) * lines + vpad(cs)); const original = el.textContent ?? ""; const prevText = original; if (el.scrollHeight <= targetH + 0.5) { return () => { }; } let lo = 0, hi = original.length, best = -1; while (lo <= hi) { const mid = lo + hi >> 1; el.textContent = original.slice(0, mid) + "…"; if (el.scrollHeight <= targetH + 0.5) { best = mid; lo = mid + 1; } else { hi = mid - 1; } } el.textContent = (best >= 0 ? original.slice(0, best) : "") + "…"; return () => { el.textContent = prevText; }; } function getClamp(el) { const cs = getComputedStyle(el); let v = cs.getPropertyValue("-webkit-line-clamp") || cs.getPropertyValue("line-clamp"); v = (v || "").trim(); const n = parseInt(v, 10); return Number.isFinite(n) && n > 0 ? n : 0; } function usedLineHeightPx(cs) { const lh = (cs.lineHeight || "").trim(); const fs = parseFloat(cs.fontSize) || 16; if (!lh || lh === "normal") return Math.round(fs * 1.2); if (lh.endsWith("px")) return parseFloat(lh); if (/^\d+(\.\d+)?$/.test(lh)) return Math.round(parseFloat(lh) * fs); if (lh.endsWith("%")) return Math.round(parseFloat(lh) / 100 * fs); return Math.round(fs * 1.2); } function vpad(cs) { return (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0); } function isPlainTextContainer(el) { if (el.childElementCount > 0) return false; return Array.from(el.childNodes).some((n) => n.nodeType === Node.TEXT_NODE); } function stripRootShadows(originalEl, cloneRoot) { if (!originalEl || !cloneRoot || !cloneRoot.style) return; const cs = getComputedStyle(originalEl); try { cloneRoot.style.boxShadow = "none"; } catch { } try { cloneRoot.style.textShadow = "none"; } catch { } try { cloneRoot.style.outline = "none"; } catch { } const f = cs.filter || ""; const cleaned = f.replace(/\bblur\([^()]*\)\s*/gi, "").replace(/\bdrop-shadow\([^()]*\)\s*/gi, "").trim().replace(/\s+/g, " "); try { cloneRoot.style.filter = cleaned.length ? cleaned : "none"; } catch { } } async function captureDOM(element, options) { if (!element) throw new Error("Element cannot be null or undefined"); applyCachePolicy(options.cache); const fast = options.fast; const straighten = !!options.straighten; const noShadows = !!options.noShadows; let clone, classCSS, styleCache; let fontsCSS = ""; let baseCSS = ""; let dataURL; let svgString; let rootTransform2D = null; const undoClamp = lineClamp(element); try { ({ clone, classCSS, styleCache } = await prepareClone(element, options)); if (straighten && clone) { rootTransform2D = normalizeRootTransforms(element, clone); } if (noShadows && clone) { stripRootShadows(element, clone); } } finally { undoClamp(); } await new Promise((resolve) => { idle(async () => { await inlineImages(clone, options); resolve(); }, { fast }); }); await new Promise((resolve) => { idle(async () => { await inlineBackgroundImages(element, clone, styleCache, options); resolve(); }, { fast }); }); if (options.embedFonts) { await new Promise((resolve) => { idle(async () => { const required = collectUsedFontVariants(element); const usedCodepoints = collectUsedCodepoints(element); if (isSafari()) { const families = new Set( Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean) ); await ensureFontsReady(families, 1); } fontsCSS = await embedCustomFonts({ required, usedCodepoints, preCached: false, exclude: options.excludeFonts, useProxy: options.useProxy }); resolve(); }, { fast }); }); } const usedTags = collectUsedTagNames(clone).sort(); const tagKey = usedTags.join(","); if (cache.baseStyle.has(tagKey)) { baseCSS = cache.baseStyle.get(tagKey); } else { await new Promise((resolve) => { idle(() => { baseCSS = generateDedupedBaseCSS(usedTags); cache.baseStyle.set(tagKey, baseCSS); resolve(); }, { fast }); }); } await new Promise((resolve) => { idle(() => { const csEl = getComputedStyle(element); function parseFilterDropShadows(cs) { var _a; const raw = `${cs.filter || ""} ${cs.webkitFilter || ""}`.trim(); if (!raw || raw === "none") { return { bleed: { top: 0, right: 0, bottom: 0, left: 0 }, has: false }; } const tokens = raw.match(/drop-shadow\((?:[^()]|\([^()]*\))*\)/gi) || []; let t = 0, r = 0, b = 0, l = 0; let found = false; for (const tok of tokens) { found = true; const nums = ((_a = tok.match(/-?\d+(?:\.\d+)?px/gi)) == null ? void 0 : _a.map((v) => parseFloat(v))) || []; const [ox = 0, oy = 0, blur = 0] = nums; const extX = Math.abs(ox) + blur; const extY = Math.abs(oy) + blur; r = Math.max(r, extX + Math.max(ox, 0)); l = Math.max(l, extX + Math.max(-ox, 0)); b = Math.max(b, extY + Math.max(oy, 0)); t = Math.max(t, extY + Math.max(-oy, 0)); } return { bleed: { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b), left: Math.ceil(l) }, has: found }; } const rect = element.getBoundingClientRect(); const w0 = Math.max(1, Math.ceil(element.offsetWidth || parseFloat(csEl.width) || rect.width || 1)); const h0 = Math.max(1, Math.ceil(element.offsetHeight || parseFloat(csEl.height) || rect.height || 1)); const coerceNum = (v, def = NaN) => { const n = typeof v === "string" ? parseFloat(v) : v; return Number.isFinite(n) ? n : def; }; const optW = coerceNum(options.width); const optH = coerceNum(options.height); let w = w0, h = h0; const hasW = Number.isFinite(optW); const hasH = Number.isFinite(optH); const aspect0 = h0 > 0 ? w0 / h0 : 1; if (hasW && hasH) { w = Math.max(1, Math.ceil(optW)); h = Math.max(1, Math.ceil(optH)); } else if (hasW) { w = Math.max(1, Math.ceil(optW)); h = Math.max(1, Math.ceil(w / (aspect0 || 1))); } else if (hasH) { h = Math.max(1, Math.ceil(optH)); w = Math.max(1, Math.ceil(h * (aspect0 || 1))); } else { w = w0; h = h0; } let minX = 0, minY = 0, maxX = w0, maxY = h0; if (straighten && rootTransform2D && Number.isFinite(rootTransform2D.a)) { const M2 = { a: rootTransform2D.a, b: rootTransform2D.b || 0, c: rootTransform2D.c || 0, d: rootTransform2D.d || 1, e: 0, f: 0 }; const bb2 = bboxWithOriginFull(w0, h0, M2, 0, 0); minX = bb2.minX; minY = bb2.minY; maxX = bb2.maxX; maxY = bb2.maxY; } else { const useTFBBox = !straighten && hasTFBBox(element); if (useTFBBox) { const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : ""; const ind2 = readIndividualTransforms(element); const TOTAL = readTotalTransformMatrix({ baseTransform: baseTransform2, rotate: ind2.rotate || "0deg", scale: ind2.scale, translate: ind2.translate }); const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0); const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString()); const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2); minX = bb.minX; minY = bb.minY; maxX = bb.maxX; maxY = bb.maxY; } } const bleedShadow = parseBoxShadow(csEl); const bleedBlur = parseFilterBlur(csEl); const bleedOutline = parseOutline(csEl); const drop = parseFilterDropShadows(csEl); const bleed = noShadows ? { top: 0, right: 0, bottom: 0, left: 0 } : { top: bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top, right: bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right, bottom: bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom, left: bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left }; minX -= bleed.left; minY -= bleed.top; maxX += bleed.right; maxY += bleed.bottom; const vbW0 = Math.max(1, Math.ceil(maxX - minX)); const vbH0 = Math.max(1, Math.ceil(maxY - minY)); const outW = Math.max(1, Math.round(vbW0 * (hasW || hasH ? w / w0 : 1))); const outH = Math.max(1, Math.round(vbH0 * (hasH || hasW ? h / h0 : 1))); const svgNS = "http://www.w3.org/2000/svg"; const basePad = isSafari() ? 1 : 0; const extraPad = straighten ? 1 : 0; const pad = basePad + extraPad; const fo = document.createElementNS(svgNS, "foreignObject"); const vbMinX = Math.floor(minX); const vbMinY = Math.floor(minY); fo.setAttribute("x", String(-(vbMinX - pad))); fo.setAttribute("y", String(-(vbMinY - pad))); fo.setAttribute("width", String(Math.ceil(w0 + pad * 2))); fo.setAttribute("height", String(Math.ceil(h0 + pad * 2))); fo.style.overflow = "visible"; const styleTag = document.createElement("style"); styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + classCSS; fo.appendChild(styleTag); const container = document.createElement("div"); container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); container.style.width = `${w0}px`; container.style.height = `${h0}px`; container.style.overflow = "visible"; clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); container.appendChild(clone); fo.appendChild(container); const serializer = new XMLSerializer(); const foString = serializer.serializeToString(fo); const vbW = vbW0 + pad * 2; const vbH = vbH0 + pad * 2; const wantsSize = hasW || hasH; options.meta = { w0, h0, vbW, vbH, targetW: w, targetH: h }; const svgOutW = isSafari() && wantsSize ? vbW : outW + pad * 2; const svgOutH = isSafari() && wantsSize ? vbH : outH + pad * 2; const svgHeader = ``; const svgFooter = ""; svgString = svgHeader + foString + svgFooter; dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`; resolve(); }, { fast }); }); const sandbox = document.getElementById("snapdom-sandbox"); if (sandbox && sandbox.style.position === "absolute") sandbox.remove(); return dataURL; } function normalizeRootTransforms(originalEl, cloneRoot) { if (!originalEl || !cloneRoot || !cloneRoot.style) return null; const cs = getComputedStyle(originalEl); try { cloneRoot.style.transformOrigin = "0 0"; } catch { } try { if ("translate" in cloneRoot.style) cloneRoot.style.translate = "none"; if ("rotate" in cloneRoot.style) cloneRoot.style.rotate = "none"; } catch { } const tr = cs.transform || "none"; if (!tr || tr === "none") { try { const M = matrixFromComputed(originalEl); if (M.a === 1 && M.b === 0 && M.c === 0 && M.d === 1) { cloneRoot.style.transform = "none"; return { a: 1, b: 0, c: 0, d: 1 }; } } catch { } } const m2d = tr.match(/^matrix\(\s*([^)]+)\)$/i); if (m2d) { const nums = m2d[1].split(",").map((v) => parseFloat(v.trim())); if (nums.length === 6 && nums.every(Number.isFinite)) { const [a, b, c, d] = nums; const scaleX = Math.sqrt(a * a + b * b) || 0; let a1 = 0, b1 = 0, shear = 0, c2 = 0, d2 = 0, scaleY = 0; if (scaleX > 0) { a1 = a / scaleX; b1 = b / scaleX; shear = a1 * c + b1 * d; c2 = c - a1 * shear; d2 = d - b1 * shear; scaleY = Math.sqrt(c2 * c2 + d2 * d2) || 0; if (scaleY > 0) shear = shear / scaleY; else shear = 0; } const aP = scaleX; const bP = 0; const cP = shear * scaleY; const dP = scaleY; try { cloneRoot.style.transform = `matrix(${aP}, ${bP}, ${cP}, ${dP}, 0, 0)`; } catch { } return { a: aP, b: bP, c: cP, d: dP }; } } try { const legacy = String(tr).trim(); cloneRoot.style.transform = legacy + " translate(0px, 0px) rotate(0deg)"; return null; } catch { return null; } } function parseBoxShadow(cs) { var _a; const v = cs.boxShadow || ""; if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 }; const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim()); let t = 0, r = 0, b2 = 0, l = 0; for (const part of parts) { const nums = ((_a = part.match(/-?\d+(\.\d+)?px/g)) == null ? void 0 : _a.map((n) => parseFloat(n))) || []; if (nums.length < 2) continue; const [ox2, oy2, blur = 0, spread = 0] = nums; const extX = Math.abs(ox2) + blur + spread; const extY = Math.abs(oy2) + blur + spread; r = Math.max(r, extX + Math.max(ox2, 0)); l = Math.max(l, extX + Math.max(-ox2, 0)); b2 = Math.max(b2, extY + Math.max(oy2, 0)); t = Math.max(t, extY + Math.max(-oy2, 0)); } return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) }; } function parseFilterBlur(cs) { const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/); const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0; return { top: b2, right: b2, bottom: b2, left: b2 }; } function parseOutline(cs) { if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 }; const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0); return { top: w2, right: w2, bottom: w2, left: w2 }; } function bboxWithOriginFull(w2, h2, M, ox2, oy2) { const a2 = M.a, b2 = M.b, c2 = M.c, d2 = M.d, e2 = M.e || 0, f2 = M.f || 0; function pt(x, y) { let X = x - ox2, Y = y - oy2; let X2 = a2 * X + c2 * Y, Y2 = b2 * X + d2 * Y; X2 += ox2 + e2; Y2 += oy2 + f2; return [X2, Y2]; } const P = [pt(0, 0), pt(w2, 0), pt(0, h2), pt(w2, h2)]; let minX2 = Infinity, minY2 = Infinity, maxX2 = -Infinity, maxY2 = -Infinity; for (const [X, Y] of P) { if (X < minX2) minX2 = X; if (Y < minY2) minY2 = Y; if (X > maxX2) maxX2 = X; if (Y > maxY2) maxY2 = Y; } return { minX: minX2, minY: minY2, maxX: maxX2, maxY: maxY2, width: maxX2 - minX2, height: maxY2 - minY2 }; } function hasTFBBox(el) { return hasBBoxAffectingTransform(el); } function matrixFromComputed(el) { const tr = getComputedStyle(el).transform; if (!tr || tr === "none") return new DOMMatrix(); try { return new DOMMatrix(tr); } catch { return new WebKitCSSMatrix(tr); } } function readIndividualTransforms(el) { var _a, _b, _c, _d, _e, _f, _g, _h; const out = { rotate: "0deg", scale: null, translate: null }; const map = typeof el.computedStyleMap === "function" ? el.computedStyleMap() : null; if (map) { const safeGet = (prop) => { try { if (typeof map.has === "function" && !map.has(prop)) return null; if (typeof map.get !== "function") return null; return map.get(prop); } catch { return null; } }; const rot = safeGet("rotate"); if (rot) { if (rot.angle) { const ang = rot.angle; out.rotate = ang.unit === "rad" ? ang.value * 180 / Math.PI + "deg" : ang.value + ang.unit; } else if (rot.unit) { out.rotate = rot.unit === "rad" ? rot.value * 180 / Math.PI + "deg" : rot.value + rot.unit; } else { out.rotate = String(rot); } } else { const cs2 = getComputedStyle(el); out.rotate = cs2.rotate && cs2.rotate !== "none" ? cs2.rotate : "0deg"; } const sc = safeGet("scale"); if (sc) { const sx = "x" in sc && ((_a = sc.x) == null ? void 0 : _a.value) != null ? sc.x.value : Array.isArray(sc) ? (_b = sc[0]) == null ? void 0 : _b.value : Number(sc) || 1; const sy = "y" in sc && ((_c = sc.y) == null ? void 0 : _c.value) != null ? sc.y.value : Array.isArray(sc) ? (_d = sc[1]) == null ? void 0 : _d.value : sx; out.scale = `${sx} ${sy}`; } else { const cs2 = getComputedStyle(el); out.scale = cs2.scale && cs2.scale !== "none" ? cs2.scale : null; } const tr = safeGet("translate"); if (tr) { const tx = "x" in tr && "value" in tr.x ? tr.x.value : Array.isArray(tr) ? (_e = tr[0]) == null ? void 0 : _e.value : 0; const ty = "y" in tr && "value" in tr.y ? tr.y.value : Array.isArray(tr) ? (_f = tr[1]) == null ? void 0 : _f.value : 0; const ux = "x" in tr && ((_g = tr.x) == null ? void 0 : _g.unit) ? tr.x.unit : "px"; const uy = "y" in tr && ((_h = tr.y) == null ? void 0 : _h.unit) ? tr.y.unit : "px"; out.translate = `${tx}${ux} ${ty}${uy}`; } else { const cs2 = getComputedStyle(el); out.translate = cs2.translate && cs2.translate !== "none" ? cs2.translate : null; } return out; } const cs = getComputedStyle(el); out.rotate = cs.rotate && cs.rotate !== "none" ? cs.rotate : "0deg"; out.scale = cs.scale && cs.scale !== "none" ? cs.scale : null; out.translate = cs.translate && cs.translate !== "none" ? cs.translate : null; return out; } function hasBBoxAffectingTransform(el) { const cs = getComputedStyle(el); const t = cs.transform || "none"; const hasMatrix = t !== "none" && !/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,\s*0\s*,\s*0\s*\)$/i.test(t); if (hasMatrix) return true; const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg"; const s = cs.scale && cs.scale !== "none" && cs.scale !== "1"; const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px"; return Boolean(r || s || tr); } function parseTransformOriginPx(cs, w, h) { const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/); const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"]; const toPx = (token, size) => { const t = token.toLowerCase(); if (t === "left" || t === "top") return 0; if (t === "center") return size / 2; if (t === "right") return size; if (t === "bottom") return size; if (t.endsWith("px")) return parseFloat(t) || 0; if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100; if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0; return 0; }; return { ox: toPx(oxRaw, w), oy: toPx(oyRaw, h) }; } var __measureHost = null; function getMeasureHost() { if (__measureHost) return __measureHost; const n = document.createElement("div"); n.id = "snapdom-measure-slot"; n.setAttribute("aria-hidden", "true"); Object.assign(n.style, { position: "absolute", left: "-99999px", top: "0px", width: "0px", height: "0px", overflow: "hidden", opacity: "0", pointerEvents: "none", contain: "size layout style" }); document.documentElement.appendChild(n); __measureHost = n; return n; } function readTotalTransformMatrix(t) { const host = getMeasureHost(); const tmp = document.createElement("div"); tmp.style.transformOrigin = "0 0"; if (t.baseTransform) tmp.style.transform = t.baseTransform; if (t.rotate) tmp.style.rotate = t.rotate; if (t.scale) tmp.style.scale = t.scale; if (t.translate) tmp.style.translate = t.translate; host.appendChild(tmp); const M = matrixFromComputed(tmp); host.removeChild(tmp); return M; } function normalizeCachePolicy(v) { if (typeof v === "string") { const s = v.toLowerCase().trim(); if (s === "disabled" || s === "full" || s === "auto" || s === "soft") return ( /** @type {CachePolicy} */ s ); } return "soft"; } function createContext(options = {}) { const resolvedFormat = options.format ?? "png"; const cachePolicy = normalizeCachePolicy(options.cache); return { // Debug & perf debug: options.debug ?? false, fast: options.fast ?? true, scale: options.scale ?? 1, // DOM filters exclude: options.exclude ?? [], excludeMode: options.excludeMode ?? "hide", filter: options.filter ?? null, filterMode: options.filterMode ?? "hide", // Placeholders placeholders: options.placeholders !== false, // default true // Fonts embedFonts: options.embedFonts ?? false, iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [], localFonts: Array.isArray(options.localFonts) ? options.localFonts : [], excludeFonts: options.excludeFonts ?? void 0, fallbackURL: options.fallbackURL ?? void 0, /** @type {CachePolicy} */ cache: cachePolicy, // Network useProxy: typeof options.useProxy === "string" ? options.useProxy : "", // Output width: options.width ?? null, height: options.height ?? null, format: resolvedFormat, type: options.type ?? "svg", quality: options.quality ?? 0.92, dpr: options.dpr ?? (window.devicePixelRatio || 1), backgroundColor: options.backgroundColor ?? (["jpg", "jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null), filename: options.filename ?? "snapDOM", // NEW flags (user-friendly) straighten: options.straighten ?? false, noShadows: options.noShadows ?? false // Plugins (reservado) // plugins: normalizePlugins(...), }; } function isSvgDataURL(u) { return typeof u === "string" && /^data:image\/svg\+xml/i.test(u); } function decodeSvgFromDataURL(u) { const i = u.indexOf(","); return i >= 0 ? decodeURIComponent(u.slice(i + 1)) : ""; } function encodeSvgToDataURL(svgText) { return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`; } function splitDecls(s) { let parts = [], buf = "", depth = 0; for (let i = 0; i < s.length; i++) { const ch = s[i]; if (ch === "(") depth++; if (ch === ")") depth = Math.max(0, depth - 1); if (ch === ";" && depth === 0) { parts.push(buf); buf = ""; } else buf += ch; } if (buf.trim()) parts.push(buf); return parts.map((x) => x.trim()).filter(Boolean); } function boxShadowToDropShadow(value) { const layers = []; let buf = "", depth = 0; for (let i = 0; i < value.length; i++) { const ch = value[i]; if (ch === "(") depth++; if (ch === ")") depth = Math.max(0, depth - 1); if (ch === "," && depth === 0) { layers.push(buf.trim()); buf = ""; } else buf += ch; } if (buf.trim()) layers.push(buf.trim()); const fns = []; for (const layer of layers) { if (/\binset\b/i.test(layer)) continue; const nums = layer.match(/-?\d+(?:\.\d+)?px/gi) || []; const [ox = "0px", oy = "0px", blur = "0px"] = nums; let color = layer.replace(/-?\d+(?:\.\d+)?px/gi, "").replace(/\binset\b/ig, "").trim().replace(/\s{2,}/g, " "); const hasColor = !!color && color !== ","; fns.push(`drop-shadow(${ox} ${oy} ${blur}${hasColor ? ` ${color}` : ""})`); } return fns.join(" "); } function rewriteDeclList(list) { const decls = splitDecls(list); let filter = null, wfilter = null, box = null; const rest = []; for (const d of decls) { const idx = d.indexOf(":"); if (idx < 0) continue; const prop = d.slice(0, idx).trim().toLowerCase(); const val = d.slice(idx + 1).trim(); if (prop === "box-shadow") box = val; else if (prop === "filter") filter = val; else if (prop === "-webkit-filter") wfilter = val; else rest.push([prop, val]); } if (box) { const ds = boxShadowToDropShadow(box); if (ds) { filter = filter ? `${filter} ${ds}` : ds; wfilter = wfilter ? `${wfilter} ${ds}` : ds; } } const out = [...rest]; if (filter) out.push(["filter", filter]); if (wfilter) out.push(["-webkit-filter", wfilter]); return out.map(([k, v]) => `${k}:${v}`).join(";"); } function rewriteCssBlock(css) { return css.replace(/([^{}]+)\{([^}]*)\}/g, (_m, sel, body) => `${sel}{${rewriteDeclList(body)}}`); } function rewriteSvgBoxShadowToDropShadow(svgText) { svgText = svgText.replace( /]*>([\s\S]*?)<\/style>/gi, (m, css) => m.replace(css, rewriteCssBlock(css)) ); svgText = svgText.replace( /style=(['"])([\s\S]*?)\1/gi, (m, q, body) => `style=${q}${rewriteDeclList(body)}${q}` ); return svgText; } function maybeConvertBoxShadowForSafari(url) { if (!isSafari() || !isSvgDataURL(url)) return url; try { const svg = decodeSvgFromDataURL(url); const fixed = rewriteSvgBoxShadowToDropShadow(svg); return encodeSvgToDataURL(fixed); } catch { return url; } } async function toCanvas(url, options) { let { width: optW, height: optH, scale = 1, dpr = 1, meta = {} } = options; url = maybeConvertBoxShadowForSafari(url); const img = new Image(); img.loading = "eager"; img.decoding = "sync"; img.crossOrigin = "anonymous"; img.src = url; await img.decode(); const natW = img.naturalWidth; const natH = img.naturalHeight; const refW = Number.isFinite(meta.w0) ? meta.w0 : natW; const refH = Number.isFinite(meta.h0) ? meta.h0 : natH; let outW, outH; const hasW = Number.isFinite(optW); const hasH = Number.isFinite(optH); if (hasW && hasH) { outW = Math.max(1, optW); outH = Math.max(1, optH); } else if (hasW) { const k = optW / Math.max(1, refW); outW = optW; outH = Math.round(refH * k); } else if (hasH) { const k = optH / Math.max(1, refH); outH = optH; outW = Math.round(refW * k); } else { outW = natW; outH = natH; } outW = Math.round(outW * scale); outH = Math.round(outH * scale); const canvas = document.createElement("canvas"); canvas.width = Math.ceil(outW * dpr); canvas.height = Math.ceil(outH * dpr); canvas.style.width = `${outW}px`; canvas.style.height = `${outH}px`; const ctx = canvas.getContext("2d"); if (dpr !== 1) ctx.scale(dpr, dpr); ctx.drawImage(img, 0, 0, outW, outH); return canvas; } async function rasterize(url, options) { const canvas = await toCanvas(url, options); const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas; const img = new Image(); img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality); await img.decode(); img.style.width = `${finalCanvas.width / options.dpr}px`; img.style.height = `${finalCanvas.height / options.dpr}px`; return img; } async function toImg(url, options) { const { scale = 1, width, height, meta = {} } = options; const hasW = Number.isFinite(width); const hasH = Number.isFinite(height); const wantsScale = Number.isFinite(scale) && scale !== 1 || hasW || hasH; if (isSafari() && wantsScale) { const pngUrl = await rasterize(url, { ...options, format: "png", quality: 1, meta }); return pngUrl; } const img = new Image(); img.decoding = "sync"; img.loading = "eager"; img.src = url; await img.decode(); if (hasW && hasH) { img.style.width = `${width}px`; img.style.height = `${height}px`; } else if (hasW) { const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth; const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight; const k = width / Math.max(1, refW); img.style.width = `${width}px`; img.style.height = `${Math.round(refH * k)}px`; } else if (hasH) { const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth; const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight; const k = height / Math.max(1, refH); img.style.height = `${height}px`; img.style.width = `${Math.round(refW * k)}px`; } else { const cssW = Math.round(img.naturalWidth * scale); const cssH = Math.round(img.naturalHeight * scale); img.style.width = `${cssW}px`; img.style.height = `${cssH}px`; if (typeof url === "string" && url.startsWith("data:image/svg+xml")) { try { const decoded = decodeURIComponent(url.split(",")[1]); const patched = decoded.replace(/width="[^"]*"/, `width="${cssW}"`).replace(/height="[^"]*"/, `height="${cssH}"`); url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(patched)}`; img.src = url; } catch { } } } return img; } async function toBlob(url, options) { const type = options.type; if (type === "svg") { const svgText = decodeURIComponent(url.split(",")[1]); return new Blob([svgText], { type: "image/svg+xml" }); } const canvas = await toCanvas(url, options); const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas; return new Promise( (resolve) => finalCanvas.toBlob( (blob) => resolve(blob), `image/${type}`, options.quality ) ); } async function download(url, options) { options.dpr = 1; if (options.format === "svg") { const blob = await toBlob(url, { ...options, type: "svg" }); const objectURL = URL.createObjectURL(blob); const a2 = document.createElement("a"); a2.href = objectURL; a2.download = options.filename; a2.click(); URL.revokeObjectURL(objectURL); return; } const canvas = await toCanvas(url, options); const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas; const a = document.createElement("a"); a.href = finalCanvas.toDataURL(`image/${options.format}`, options.quality); a.download = options.filename; a.click(); } var INTERNAL_TOKEN = Symbol("snapdom.internal"); var _safariWarmup = false; async function snapdom(element, userOptions) { if (!element) throw new Error("Element cannot be null or undefined"); const context = createContext(userOptions); if (isSafari() && (context.embedFonts === true || hasBackgroundOrMask(element))) { for (let i = 0; i < 3; i++) { try { await safariWarmup(element, userOptions); console.log("Iteración número:", i); _safariWarmup = false; } catch { } } } if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts); if (!context.snap) { context.snap = { toPng: (el, opts) => snapdom.toPng(el, opts), toSvg: (el, opts) => snapdom.toSvg(el, opts) }; } return snapdom.capture(element, context, INTERNAL_TOKEN); } snapdom.capture = async (el, context, _token) => { if (_token !== INTERNAL_TOKEN) throw new Error("[snapdom.capture] is internal. Use snapdom(...) instead."); const url = await captureDOM(el, context); const ensureContext = (opts) => ({ ...context, ...opts || {} }); const withFormat = (format) => (opts) => { const next = ensureContext({ ...opts || {}, format }); const wantsJpeg = format === "jpeg" || format === "jpg"; const noBg = next.backgroundColor == null || next.backgroundColor === "transparent"; if (wantsJpeg && noBg) { next.backgroundColor = "#ffffff"; } return rasterize(url, next); }; return { url, toRaw: () => url, toImg: (opts) => toImg(url, ensureContext(opts)), toSvg: (opts) => toImg(url, ensureContext(opts)), toCanvas: (opts) => toCanvas(url, ensureContext(opts)), toBlob: (opts) => toBlob(url, ensureContext(opts)), toPng: withFormat("png"), toJpg: withFormat("jpeg"), toWebp: withFormat("webp"), download: (opts) => download(url, ensureContext(opts)) }; }; snapdom.toRaw = (el, options) => snapdom(el, options).then((result) => result.toRaw()); snapdom.toImg = (el, options) => snapdom(el, options).then((result) => result.toImg()); snapdom.toSvg = (el, options) => snapdom(el, options).then((result) => result.toSvg()); snapdom.toCanvas = (el, options) => snapdom(el, options).then((result) => result.toCanvas()); snapdom.toBlob = (el, options) => snapdom(el, options).then((result) => result.toBlob()); snapdom.toPng = (el, options) => snapdom(el, { ...options, format: "png" }).then((result) => result.toPng()); snapdom.toJpg = (el, options) => snapdom(el, { ...options, format: "jpeg" }).then((result) => result.toJpg()); snapdom.toWebp = (el, options) => snapdom(el, { ...options, format: "webp" }).then((result) => result.toWebp()); snapdom.download = (el, options) => snapdom(el, options).then((result) => result.download()); async function safariWarmup(element, baseOptions) { if (_safariWarmup) return; const preflight = { ...baseOptions, fast: true, embedFonts: true, scale: 0.2 }; let url; try { url = await captureDOM(element, preflight); } catch { return; } await new Promise((resolve) => { const img = new Image(); img.decoding = "sync"; img.loading = "eager"; img.style.position = "fixed"; img.style.left = 0; img.style.top = 0; img.style.width = "10px"; img.style.height = "10px"; img.style.opacity = "0.01"; img.style.transform = "translateZ(10px)"; img.style.willChange = "transform,opacity;"; img.src = url; const cleanup = async () => { await new Promise((r) => setTimeout(r, 100)); if (img && img.parentNode) img.parentNode.removeChild(img); _safariWarmup = true; resolve(); }; document.body.appendChild(img); cleanup(); }); } function hasBackgroundOrMask(el) { const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT); while (walker.nextNode()) { const node = ( /** @type {Element} */ walker.currentNode ); const cs = getComputedStyle(node); const bg = cs.backgroundImage && cs.backgroundImage !== "none"; const mask = cs.maskImage && cs.maskImage !== "none" || cs.webkitMaskImage && cs.webkitMaskImage !== "none"; if (bg || mask) return true; } return false; } async function preCache(root = document, options = {}) { var _a; const { embedFonts = true, useProxy = "" } = options; const cacheMode = options.cache ?? options.cacheOpt ?? "full"; applyCachePolicy(cacheMode); try { await ((_a = document.fonts) == null ? void 0 : _a.ready); } catch { } try { precacheCommonTags(); } catch { } cache.session = cache.session || {}; if (!cache.session.styleCache) { cache.session.styleCache = /* @__PURE__ */ new WeakMap(); } cache.image = cache.image || /* @__PURE__ */ new Map(); try { await inlineBackgroundImages( root, /* mirror */ void 0, cache.session.styleCache, { useProxy } ); } catch { } let imgEls = [], allEls = []; try { if (root == null ? void 0 : root.querySelectorAll) { imgEls = Array.from(root.querySelectorAll("img[src]")); allEls = Array.from(root.querySelectorAll("*")); } } catch { } const promises = []; for (const img of imgEls) { const src = (img == null ? void 0 : img.currentSrc) || (img == null ? void 0 : img.src); if (!src) continue; if (!cache.image.has(src)) { const p = Promise.resolve().then(async () => { const res = await snapFetch(src, { as: "dataURL", useProxy }); if ((res == null ? void 0 : res.ok) && typeof res.data === "string") { cache.image.set(src, res.data); } }).catch(() => { }); promises.push(p); } } for (const el of allEls) { let bg = ""; try { bg = getStyle(el).backgroundImage; } catch { } if (bg && bg !== "none") { const parts = splitBackgroundImage(bg); for (const entry of parts) { if (entry.startsWith("url(")) { const p = Promise.resolve().then(() => inlineSingleBackgroundEntry(entry, { ...options, useProxy })).catch(() => { }); promises.push(p); } } } } if (embedFonts) { try { const required = collectUsedFontVariants(root); const usedCodepoints = collectUsedCodepoints(root); const safari = typeof isSafari === "function" ? isSafari() : !!isSafari; if (safari) { const families = new Set( Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean) ); await ensureFontsReady(families, 3); } await embedCustomFonts({ required, usedCodepoints, exclude: options.excludeFonts, localFonts: options.localFonts, useProxy: options.useProxy ?? useProxy }); } catch { } } await Promise.allSettled(promises); } export { preCache, snapdom }; //# sourceMappingURL=@zumer_snapdom.js.map