@brainfish-ai/web-tracker 0.0.19 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,7 +43,7 @@ const encodeBlobAsBase64 = (data, callback) => {
43
43
  };
44
44
  return fileReader.readAsDataURL(data);
45
45
  };
46
- function toArray$1(data) {
46
+ function toArray(data) {
47
47
  if (data instanceof Uint8Array) {
48
48
  return data;
49
49
  } else if (data instanceof ArrayBuffer) {
@@ -55,9 +55,9 @@ function toArray$1(data) {
55
55
  let TEXT_ENCODER;
56
56
  function encodePacketToBinary(packet, callback) {
57
57
  if (withNativeBlob$1 && packet.data instanceof Blob) {
58
- return packet.data.arrayBuffer().then(toArray$1).then(callback);
58
+ return packet.data.arrayBuffer().then(toArray).then(callback);
59
59
  } else if (withNativeArrayBuffer$2 && (packet.data instanceof ArrayBuffer || isView$1(packet.data))) {
60
- return callback(toArray$1(packet.data));
60
+ return callback(toArray(packet.data));
61
61
  }
62
62
  encodePacket(packet, false, (encoded) => {
63
63
  if (!TEXT_ENCODER) {
@@ -3611,781 +3611,1659 @@ class TrackerSdk {
3611
3611
  this.queue = [];
3612
3612
  }
3613
3613
  }
3614
- function resolveUrl(url2, baseUrl) {
3615
- if (url2.match(/^[a-z]+:\/\//i)) {
3616
- return url2;
3617
- }
3618
- if (url2.match(/^\/\//)) {
3619
- return window.location.protocol + url2;
3620
- }
3621
- if (url2.match(/^[a-z]+:/i)) {
3622
- return url2;
3623
- }
3624
- const doc = document.implementation.createHTMLDocument();
3625
- const base = doc.createElement("base");
3626
- const a = doc.createElement("a");
3627
- doc.head.appendChild(base);
3628
- doc.body.appendChild(a);
3629
- if (baseUrl) {
3630
- base.href = baseUrl;
3631
- }
3632
- a.href = url2;
3633
- return a.href;
3634
- }
3635
- const uuid = /* @__PURE__ */ (() => {
3636
- let counter = 0;
3637
- const random = () => (
3638
- // eslint-disable-next-line no-bitwise
3639
- `0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4)
3640
- );
3641
- return () => {
3642
- counter += 1;
3643
- return `u${random()}${counter}`;
3644
- };
3645
- })();
3646
- function toArray(arrayLike) {
3647
- const arr = [];
3648
- for (let i = 0, l = arrayLike.length; i < l; i++) {
3649
- arr.push(arrayLike[i]);
3650
- }
3651
- return arr;
3652
- }
3653
- let styleProps = null;
3654
- function getStyleProperties(options = {}) {
3655
- if (styleProps) {
3656
- return styleProps;
3657
- }
3658
- if (options.includeStyleProperties) {
3659
- styleProps = options.includeStyleProperties;
3660
- return styleProps;
3661
- }
3662
- styleProps = toArray(window.getComputedStyle(document.documentElement));
3663
- return styleProps;
3664
- }
3665
- function px(node2, styleProperty) {
3666
- const win = node2.ownerDocument.defaultView || window;
3667
- const val = win.getComputedStyle(node2).getPropertyValue(styleProperty);
3668
- return val ? parseFloat(val.replace("px", "")) : 0;
3669
- }
3670
- function getNodeWidth(node2) {
3671
- const leftBorder = px(node2, "border-left-width");
3672
- const rightBorder = px(node2, "border-right-width");
3673
- return node2.clientWidth + leftBorder + rightBorder;
3674
- }
3675
- function getNodeHeight(node2) {
3676
- const topBorder = px(node2, "border-top-width");
3677
- const bottomBorder = px(node2, "border-bottom-width");
3678
- return node2.clientHeight + topBorder + bottomBorder;
3679
- }
3680
- function getImageSize(targetNode, options = {}) {
3681
- const width = options.width || getNodeWidth(targetNode);
3682
- const height = options.height || getNodeHeight(targetNode);
3683
- return { width, height };
3684
- }
3685
- function getPixelRatio() {
3686
- let ratio;
3687
- let FINAL_PROCESS;
3688
- try {
3689
- FINAL_PROCESS = process;
3690
- } catch (e) {
3691
- }
3692
- const val = FINAL_PROCESS && FINAL_PROCESS.env ? FINAL_PROCESS.env.devicePixelRatio : null;
3693
- if (val) {
3694
- ratio = parseInt(val, 10);
3695
- if (Number.isNaN(ratio)) {
3696
- ratio = 1;
3614
+ var cache = {
3615
+ image: /* @__PURE__ */ new Map(),
3616
+ background: /* @__PURE__ */ new Map(),
3617
+ resource: /* @__PURE__ */ new Map(),
3618
+ defaultStyle: /* @__PURE__ */ new Map(),
3619
+ baseStyle: /* @__PURE__ */ new Map(),
3620
+ computedStyle: /* @__PURE__ */ new WeakMap(),
3621
+ font: /* @__PURE__ */ new Set(),
3622
+ snapshot: /* @__PURE__ */ new WeakMap(),
3623
+ snapshotKey: /* @__PURE__ */ new Map(),
3624
+ reset: resetCache
3625
+ };
3626
+ function resetCache() {
3627
+ cache.computedStyle = /* @__PURE__ */ new WeakMap();
3628
+ }
3629
+ function getDefaultStyleForTag(tagName) {
3630
+ if (cache.defaultStyle.has(tagName)) {
3631
+ return cache.defaultStyle.get(tagName);
3632
+ }
3633
+ const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template", "defs", "symbol", "title", "metadata", "desc"]);
3634
+ if (skipTags.has(tagName)) {
3635
+ const empty2 = {};
3636
+ cache.defaultStyle.set(tagName, empty2);
3637
+ return empty2;
3638
+ }
3639
+ let sandbox = document.getElementById("snapdom-sandbox");
3640
+ if (!sandbox) {
3641
+ sandbox = document.createElement("div");
3642
+ sandbox.id = "snapdom-sandbox";
3643
+ sandbox.style.position = "absolute";
3644
+ sandbox.style.left = "-9999px";
3645
+ sandbox.style.top = "-9999px";
3646
+ sandbox.style.width = "0";
3647
+ sandbox.style.height = "0";
3648
+ sandbox.style.overflow = "hidden";
3649
+ document.body.appendChild(sandbox);
3650
+ }
3651
+ const el = document.createElement(tagName);
3652
+ el.style.all = "initial";
3653
+ sandbox.appendChild(el);
3654
+ const styles = getComputedStyle(el);
3655
+ const defaults = {};
3656
+ for (let prop of styles) {
3657
+ defaults[prop] = styles.getPropertyValue(prop);
3658
+ }
3659
+ sandbox.removeChild(el);
3660
+ cache.defaultStyle.set(tagName, defaults);
3661
+ return defaults;
3662
+ }
3663
+ var IGNORED_PROPS = /* @__PURE__ */ new Set([
3664
+ "-webkit-locale"
3665
+ ]);
3666
+ function getStyleKey(snapshot2, tagName, compress = false) {
3667
+ const entries = [];
3668
+ const defaultStyles = getDefaultStyleForTag(tagName);
3669
+ for (let [prop, value2] of Object.entries(snapshot2)) {
3670
+ if (IGNORED_PROPS.has(prop)) continue;
3671
+ if (!compress) {
3672
+ if (value2) {
3673
+ entries.push(`${prop}:${value2}`);
3674
+ }
3675
+ } else {
3676
+ const defaultValue = defaultStyles[prop];
3677
+ if (value2 && value2 !== defaultValue) {
3678
+ entries.push(`${prop}:${value2}`);
3679
+ }
3697
3680
  }
3698
3681
  }
3699
- return ratio || window.devicePixelRatio || 1;
3682
+ return entries.sort().join(";");
3700
3683
  }
3701
- const canvasDimensionLimit = 16384;
3702
- function checkCanvasDimensions(canvas) {
3703
- if (canvas.width > canvasDimensionLimit || canvas.height > canvasDimensionLimit) {
3704
- if (canvas.width > canvasDimensionLimit && canvas.height > canvasDimensionLimit) {
3705
- if (canvas.width > canvas.height) {
3706
- canvas.height *= canvasDimensionLimit / canvas.width;
3707
- canvas.width = canvasDimensionLimit;
3708
- } else {
3709
- canvas.width *= canvasDimensionLimit / canvas.height;
3710
- canvas.height = canvasDimensionLimit;
3711
- }
3712
- } else if (canvas.width > canvasDimensionLimit) {
3713
- canvas.height *= canvasDimensionLimit / canvas.width;
3714
- canvas.width = canvasDimensionLimit;
3684
+ function collectUsedTagNames(root2) {
3685
+ const tagSet = /* @__PURE__ */ new Set();
3686
+ if (root2.nodeType !== Node.ELEMENT_NODE && root2.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
3687
+ return [];
3688
+ }
3689
+ if (root2.tagName) {
3690
+ tagSet.add(root2.tagName.toLowerCase());
3691
+ }
3692
+ if (typeof root2.querySelectorAll === "function") {
3693
+ root2.querySelectorAll("*").forEach((el) => tagSet.add(el.tagName.toLowerCase()));
3694
+ }
3695
+ return Array.from(tagSet);
3696
+ }
3697
+ function generateDedupedBaseCSS(usedTagNames) {
3698
+ const groups = /* @__PURE__ */ new Map();
3699
+ for (let tagName of usedTagNames) {
3700
+ const styles = cache.defaultStyle.get(tagName);
3701
+ if (!styles) continue;
3702
+ const key = Object.entries(styles).map(([k, v]) => `${k}:${v};`).sort().join("");
3703
+ if (!groups.has(key)) {
3704
+ groups.set(key, []);
3705
+ }
3706
+ groups.get(key).push(tagName);
3707
+ }
3708
+ let css = "";
3709
+ for (let [styleBlock, tagList] of groups.entries()) {
3710
+ css += `${tagList.join(",")} { ${styleBlock} }
3711
+ `;
3712
+ }
3713
+ return css;
3714
+ }
3715
+ function generateCSSClasses(styleMap) {
3716
+ const keySet = new Set(styleMap.values());
3717
+ const classMap = /* @__PURE__ */ new Map();
3718
+ let counter = 1;
3719
+ for (const key of keySet) {
3720
+ if (!key.trim()) continue;
3721
+ classMap.set(key, `c${counter++}`);
3722
+ }
3723
+ return classMap;
3724
+ }
3725
+ async function inlineSingleBackgroundEntry(entry, options = {}) {
3726
+ const rawUrl = extractURL(entry);
3727
+ const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry);
3728
+ if (rawUrl) {
3729
+ const encodedUrl = safeEncodeURI(rawUrl);
3730
+ if (cache.background.has(encodedUrl)) {
3731
+ return options.skipInline ? void 0 : `url(${cache.background.get(encodedUrl)})`;
3715
3732
  } else {
3716
- canvas.width *= canvasDimensionLimit / canvas.height;
3717
- canvas.height = canvasDimensionLimit;
3733
+ const dataUrl = await fetchImage(encodedUrl, { useProxy: options.useProxy });
3734
+ cache.background.set(encodedUrl, dataUrl);
3735
+ return options.skipInline ? void 0 : `url("${dataUrl}")`;
3718
3736
  }
3719
3737
  }
3738
+ if (isGradient || entry === "none") {
3739
+ return entry;
3740
+ }
3741
+ return entry;
3720
3742
  }
3721
- function createImage(url2) {
3722
- return new Promise((resolve2, reject) => {
3723
- const img = new Image();
3724
- img.onload = () => {
3725
- img.decode().then(() => {
3726
- requestAnimationFrame(() => resolve2(img));
3727
- });
3728
- };
3729
- img.onerror = reject;
3730
- img.crossOrigin = "anonymous";
3731
- img.decoding = "async";
3732
- img.src = url2;
3733
- });
3743
+ function idle(fn, { fast = false } = {}) {
3744
+ if (fast) return fn();
3745
+ if ("requestIdleCallback" in window) {
3746
+ requestIdleCallback(fn, { timeout: 50 });
3747
+ } else {
3748
+ setTimeout(fn, 1);
3749
+ }
3734
3750
  }
3735
- async function svgToDataURL(svg) {
3736
- return Promise.resolve().then(() => new XMLSerializer().serializeToString(svg)).then(encodeURIComponent).then((html) => `data:image/svg+xml;charset=utf-8,${html}`);
3737
- }
3738
- async function nodeToDataURL(node2, width, height) {
3739
- const xmlns = "http://www.w3.org/2000/svg";
3740
- const svg = document.createElementNS(xmlns, "svg");
3741
- const foreignObject = document.createElementNS(xmlns, "foreignObject");
3742
- svg.setAttribute("width", `${width}`);
3743
- svg.setAttribute("height", `${height}`);
3744
- svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
3745
- foreignObject.setAttribute("width", "100%");
3746
- foreignObject.setAttribute("height", "100%");
3747
- foreignObject.setAttribute("x", "0");
3748
- foreignObject.setAttribute("y", "0");
3749
- foreignObject.setAttribute("externalResourcesRequired", "true");
3750
- svg.appendChild(foreignObject);
3751
- foreignObject.appendChild(node2);
3752
- return svgToDataURL(svg);
3753
- }
3754
- const isInstanceOfElement = (node2, instance) => {
3755
- if (node2 instanceof instance)
3756
- return true;
3757
- const nodePrototype = Object.getPrototypeOf(node2);
3758
- if (nodePrototype === null)
3759
- return false;
3760
- return nodePrototype.constructor.name === instance.name || isInstanceOfElement(nodePrototype, instance);
3761
- };
3762
- function formatCSSText(style) {
3763
- const content = style.getPropertyValue("content");
3764
- return `${style.cssText} content: '${content.replace(/'|"/g, "")}';`;
3765
- }
3766
- function formatCSSProperties(style, options) {
3767
- return getStyleProperties(options).map((name) => {
3768
- const value2 = style.getPropertyValue(name);
3769
- const priority = style.getPropertyPriority(name);
3770
- return `${name}: ${value2}${priority ? " !important" : ""};`;
3771
- }).join(" ");
3772
- }
3773
- function getPseudoElementStyle(className, pseudo, style, options) {
3774
- const selector = `.${className}:${pseudo}`;
3775
- const cssText = style.cssText ? formatCSSText(style) : formatCSSProperties(style, options);
3776
- return document.createTextNode(`${selector}{${cssText}}`);
3777
- }
3778
- function clonePseudoElement(nativeNode, clonedNode, pseudo, options) {
3779
- const style = window.getComputedStyle(nativeNode, pseudo);
3780
- const content = style.getPropertyValue("content");
3781
- if (content === "" || content === "none") {
3782
- return;
3751
+ function getStyle(el, pseudo = null) {
3752
+ if (!(el instanceof Element)) {
3753
+ return window.getComputedStyle(el, pseudo);
3783
3754
  }
3784
- const className = uuid();
3785
- try {
3786
- clonedNode.className = `${clonedNode.className} ${className}`;
3787
- } catch (err) {
3788
- return;
3755
+ let map = cache.computedStyle.get(el);
3756
+ if (!map) {
3757
+ map = /* @__PURE__ */ new Map();
3758
+ cache.computedStyle.set(el, map);
3789
3759
  }
3790
- const styleElement = document.createElement("style");
3791
- styleElement.appendChild(getPseudoElementStyle(className, pseudo, style, options));
3792
- clonedNode.appendChild(styleElement);
3793
- }
3794
- function clonePseudoElements(nativeNode, clonedNode, options) {
3795
- clonePseudoElement(nativeNode, clonedNode, ":before", options);
3796
- clonePseudoElement(nativeNode, clonedNode, ":after", options);
3797
- }
3798
- const WOFF = "application/font-woff";
3799
- const JPEG = "image/jpeg";
3800
- const mimes = {
3801
- woff: WOFF,
3802
- woff2: WOFF,
3803
- ttf: "application/font-truetype",
3804
- eot: "application/vnd.ms-fontobject",
3805
- png: "image/png",
3806
- jpg: JPEG,
3807
- jpeg: JPEG,
3808
- gif: "image/gif",
3809
- tiff: "image/tiff",
3810
- svg: "image/svg+xml",
3811
- webp: "image/webp"
3812
- };
3813
- function getExtension(url2) {
3814
- const match2 = /\.([^./]*?)$/g.exec(url2);
3815
- return match2 ? match2[1] : "";
3816
- }
3817
- function getMimeType(url2) {
3818
- const extension = getExtension(url2).toLowerCase();
3819
- return mimes[extension] || "";
3760
+ if (!map.has(pseudo)) {
3761
+ const st = window.getComputedStyle(el, pseudo);
3762
+ map.set(pseudo, st);
3763
+ }
3764
+ return map.get(pseudo);
3820
3765
  }
3821
- function getContentFromDataUrl(dataURL) {
3822
- return dataURL.split(/,/)[1];
3766
+ function parseContent(content) {
3767
+ let clean = content.replace(/^['"]|['"]$/g, "");
3768
+ if (clean.startsWith("\\")) {
3769
+ try {
3770
+ return String.fromCharCode(parseInt(clean.replace("\\", ""), 16));
3771
+ } catch {
3772
+ return clean;
3773
+ }
3774
+ }
3775
+ return clean;
3823
3776
  }
3824
- function isDataUrl(url2) {
3825
- return url2.search(/^(data:)/) !== -1;
3777
+ function extractURL(value2) {
3778
+ const match2 = value2.match(/url\((['"]?)(.*?)(\1)\)/);
3779
+ if (!match2) return null;
3780
+ const url2 = match2[2].trim();
3781
+ if (url2.startsWith("#")) return null;
3782
+ return url2;
3826
3783
  }
3827
- function makeDataUrl(content, mimeType) {
3828
- return `data:${mimeType};base64,${content}`;
3784
+ async function fetchResource(url2, { useProxy = "" } = {}) {
3785
+ async function doFetch(u) {
3786
+ const res = await fetch(u);
3787
+ if (!res.ok) throw new Error(`[snapdom] Failed to fetch resource: ${u}`);
3788
+ return res;
3789
+ }
3790
+ try {
3791
+ return await doFetch(url2);
3792
+ } catch (e) {
3793
+ if (useProxy && typeof useProxy === "string") {
3794
+ const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url2);
3795
+ return doFetch(proxied);
3796
+ }
3797
+ throw e;
3798
+ }
3829
3799
  }
3830
- async function fetchAsDataURL(url2, init, process2) {
3831
- const res = await fetch(url2, init);
3832
- if (res.status === 404) {
3833
- throw new Error(`Resource "${res.url}" not found`);
3800
+ var _inflight = /* @__PURE__ */ new Map();
3801
+ var _errorCache = /* @__PURE__ */ new Map();
3802
+ function fetchImage(src, { timeout = 3e3, useProxy = "", errorTTL = 8e3 } = {}) {
3803
+ function getCrossOriginMode(url2) {
3804
+ try {
3805
+ const parsed = new URL(url2, window.location.href);
3806
+ return parsed.origin === window.location.origin ? "use-credentials" : "anonymous";
3807
+ } catch {
3808
+ return "anonymous";
3809
+ }
3834
3810
  }
3835
- const blob2 = await res.blob();
3836
- return new Promise((resolve2, reject) => {
3837
- const reader = new FileReader();
3838
- reader.onerror = reject;
3839
- reader.onloadend = () => {
3840
- try {
3841
- resolve2(process2({ res, result: reader.result }));
3842
- } catch (error) {
3843
- reject(error);
3811
+ const ok = (data) => ({ ok: true, data });
3812
+ const fail = (e) => ({ ok: false, error: e instanceof Error ? e : new Error(String(e)) });
3813
+ function fetchBlobAsDataURLSafe(fetchUrl) {
3814
+ try {
3815
+ return fetch(fetchUrl, {
3816
+ mode: "cors",
3817
+ credentials: getCrossOriginMode(fetchUrl) === "use-credentials" ? "include" : "omit"
3818
+ }).then((r) => {
3819
+ if (!r.ok) return fail(new Error("HTTP " + r.status));
3820
+ return r.blob().then((blob2) => new Promise((resolve2) => {
3821
+ const reader = new FileReader();
3822
+ reader.onloadend = () => {
3823
+ const base64 = reader.result;
3824
+ if (typeof base64 !== "string" || !base64.startsWith("data:image/")) {
3825
+ resolve2(fail(new Error("Invalid image data URL")));
3826
+ } else {
3827
+ resolve2(ok(base64));
3828
+ }
3829
+ };
3830
+ reader.onerror = () => resolve2(fail(new Error("FileReader error")));
3831
+ reader.readAsDataURL(blob2);
3832
+ }));
3833
+ }).catch((e) => fail(e));
3834
+ } catch (e) {
3835
+ return Promise.resolve(fail(e));
3836
+ }
3837
+ }
3838
+ function fetchWithFallbackOnceSafe(url2) {
3839
+ return fetchBlobAsDataURLSafe(url2).then((r) => {
3840
+ if (r.ok) return r;
3841
+ if (useProxy && typeof useProxy === "string") {
3842
+ const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url2);
3843
+ return fetchBlobAsDataURLSafe(proxied).then((r2) => {
3844
+ if (r2.ok) return r2;
3845
+ return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"));
3846
+ });
3844
3847
  }
3848
+ return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"));
3849
+ });
3850
+ }
3851
+ const now = Date.now();
3852
+ const until = _errorCache.get(src);
3853
+ if (until && until > now) {
3854
+ const pr = Promise.reject(new Error("[SnapDOM - fetchImage] Recently failed (cooldown)."));
3855
+ pr.catch(() => {
3856
+ });
3857
+ return pr;
3858
+ }
3859
+ if (_inflight.has(src)) return _inflight.get(src);
3860
+ const crossOriginValue = getCrossOriginMode(src);
3861
+ if (cache.image.has(src)) return Promise.resolve(cache.image.get(src));
3862
+ if (src.startsWith("data:image/")) {
3863
+ cache.image.set(src, src);
3864
+ return Promise.resolve(src);
3865
+ }
3866
+ if (/\.svg(\?.*)?$/i.test(src)) {
3867
+ const p2 = (async () => {
3868
+ const direct = await (async () => {
3869
+ try {
3870
+ const res = await fetch(src, {
3871
+ mode: "cors",
3872
+ credentials: crossOriginValue === "use-credentials" ? "include" : "omit"
3873
+ });
3874
+ if (!res.ok) return fail(new Error("HTTP " + res.status));
3875
+ const svgText = await res.text();
3876
+ return ok(`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`);
3877
+ } catch (e) {
3878
+ return fail(e);
3879
+ }
3880
+ })();
3881
+ if (direct.ok) {
3882
+ cache.image.set(src, direct.data);
3883
+ return direct.data;
3884
+ }
3885
+ const via = await fetchWithFallbackOnceSafe(src);
3886
+ if (via.ok) {
3887
+ cache.image.set(src, via.data);
3888
+ return via.data;
3889
+ }
3890
+ _errorCache.set(src, now + errorTTL);
3891
+ return Promise.reject(via.error);
3892
+ })();
3893
+ _inflight.set(src, p2);
3894
+ p2.finally(() => _inflight.delete(src));
3895
+ p2.catch(() => {
3896
+ });
3897
+ return p2;
3898
+ }
3899
+ const p = new Promise((resolve2, reject) => {
3900
+ let finished = false;
3901
+ const img = new Image();
3902
+ const finish = (fn) => (arg) => {
3903
+ if (finished) return;
3904
+ finished = true;
3905
+ clearTimeout(timeoutId);
3906
+ img.onload = img.onerror = null;
3907
+ fn(arg);
3908
+ };
3909
+ const onSuccess = (d) => {
3910
+ cache.image.set(src, d);
3911
+ resolve2(d);
3845
3912
  };
3846
- reader.readAsDataURL(blob2);
3913
+ const onFinalError = (e) => {
3914
+ _errorCache.set(src, Date.now() + errorTTL);
3915
+ reject(e);
3916
+ };
3917
+ const timeoutId = setTimeout(
3918
+ finish(() => {
3919
+ fetchWithFallbackOnceSafe(src).then((r) => {
3920
+ if (r.ok) onSuccess(r.data);
3921
+ else onFinalError(new Error("Image load timed out"));
3922
+ });
3923
+ }),
3924
+ timeout
3925
+ );
3926
+ img.crossOrigin = crossOriginValue;
3927
+ img.onload = finish(() => {
3928
+ Promise.resolve(img.decode()).then(() => {
3929
+ try {
3930
+ const canvas = document.createElement("canvas");
3931
+ canvas.width = img.naturalWidth || img.width;
3932
+ canvas.height = img.naturalHeight || img.height;
3933
+ const ctx = canvas.getContext("2d");
3934
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
3935
+ onSuccess(canvas.toDataURL("image/png"));
3936
+ } catch {
3937
+ fetchWithFallbackOnceSafe(src).then((r) => {
3938
+ if (r.ok) onSuccess(r.data);
3939
+ else onFinalError(r.error);
3940
+ });
3941
+ }
3942
+ }).catch(() => {
3943
+ fetchWithFallbackOnceSafe(src).then((r) => {
3944
+ if (r.ok) onSuccess(r.data);
3945
+ else onFinalError(r.error);
3946
+ });
3947
+ });
3948
+ });
3949
+ img.onerror = finish(() => {
3950
+ fetchWithFallbackOnceSafe(src).then((r) => {
3951
+ if (r.ok) onSuccess(r.data);
3952
+ else onFinalError(r.error);
3953
+ });
3954
+ });
3955
+ img.src = src;
3956
+ });
3957
+ _inflight.set(src, p);
3958
+ p.finally(() => _inflight.delete(src));
3959
+ p.catch(() => {
3960
+ });
3961
+ return p;
3962
+ }
3963
+ function snapshotComputedStyle(style) {
3964
+ const snap = {};
3965
+ for (let prop of style) {
3966
+ snap[prop] = style.getPropertyValue(prop);
3967
+ }
3968
+ return snap;
3969
+ }
3970
+ function isSafari() {
3971
+ return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
3972
+ }
3973
+ function stripTranslate(transform) {
3974
+ if (!transform || transform === "none") return "";
3975
+ let cleaned = transform.replace(/translate[XY]?\([^)]*\)/g, "");
3976
+ cleaned = cleaned.replace(/matrix\(([^)]+)\)/g, (_, values) => {
3977
+ const parts2 = values.split(",").map((s) => s.trim());
3978
+ if (parts2.length !== 6) return `matrix(${values})`;
3979
+ parts2[4] = "0";
3980
+ parts2[5] = "0";
3981
+ return `matrix(${parts2.join(", ")})`;
3847
3982
  });
3983
+ cleaned = cleaned.replace(/matrix3d\(([^)]+)\)/g, (_, values) => {
3984
+ const parts2 = values.split(",").map((s) => s.trim());
3985
+ if (parts2.length !== 16) return `matrix3d(${values})`;
3986
+ parts2[12] = "0";
3987
+ parts2[13] = "0";
3988
+ return `matrix3d(${parts2.join(", ")})`;
3989
+ });
3990
+ return cleaned.trim().replace(/\s{2,}/g, " ");
3848
3991
  }
3849
- const cache = {};
3850
- function getCacheKey(url2, contentType, includeQueryParams) {
3851
- let key = url2.replace(/\?.*/, "");
3852
- if (includeQueryParams) {
3853
- key = url2;
3854
- }
3855
- if (/ttf|otf|eot|woff2?/i.test(key)) {
3856
- key = key.replace(/.*\//, "");
3992
+ function safeEncodeURI(uri) {
3993
+ if (/%[0-9A-Fa-f]{2}/.test(uri)) return uri;
3994
+ try {
3995
+ return encodeURI(uri);
3996
+ } catch {
3997
+ return uri;
3857
3998
  }
3858
- return contentType ? `[${contentType}]${key}` : key;
3859
3999
  }
3860
- async function resourceToDataURL(resourceUrl, contentType, options) {
3861
- const cacheKey = getCacheKey(resourceUrl, contentType, options.includeQueryParams);
3862
- if (cache[cacheKey] != null) {
3863
- return cache[cacheKey];
3864
- }
3865
- if (options.cacheBust) {
3866
- resourceUrl += (/\?/.test(resourceUrl) ? "&" : "?") + (/* @__PURE__ */ new Date()).getTime();
3867
- }
3868
- let dataURL;
3869
- try {
3870
- const content = await fetchAsDataURL(resourceUrl, options.fetchRequestInit, ({ res, result: result2 }) => {
3871
- if (!contentType) {
3872
- contentType = res.headers.get("Content-Type") || "";
3873
- }
3874
- return getContentFromDataUrl(result2);
3875
- });
3876
- dataURL = makeDataUrl(content, contentType);
3877
- } catch (error) {
3878
- dataURL = options.imagePlaceholder || "";
3879
- let msg = `Failed to fetch resource: ${resourceUrl}`;
3880
- if (error) {
3881
- msg = typeof error === "string" ? error : error.message;
3882
- }
3883
- if (msg) {
3884
- console.warn(msg);
4000
+ function splitBackgroundImage(bg) {
4001
+ const parts2 = [];
4002
+ let depth = 0;
4003
+ let lastIndex = 0;
4004
+ for (let i = 0; i < bg.length; i++) {
4005
+ const char = bg[i];
4006
+ if (char === "(") depth++;
4007
+ if (char === ")") depth--;
4008
+ if (char === "," && depth === 0) {
4009
+ parts2.push(bg.slice(lastIndex, i).trim());
4010
+ lastIndex = i + 1;
3885
4011
  }
3886
4012
  }
3887
- cache[cacheKey] = dataURL;
3888
- return dataURL;
4013
+ parts2.push(bg.slice(lastIndex).trim());
4014
+ return parts2;
3889
4015
  }
3890
- async function cloneCanvasElement(canvas) {
3891
- const dataURL = canvas.toDataURL();
3892
- if (dataURL === "data:,") {
3893
- return canvas.cloneNode(false);
3894
- }
3895
- return createImage(dataURL);
3896
- }
3897
- async function cloneVideoElement(video, options) {
3898
- if (video.currentSrc) {
3899
- const canvas = document.createElement("canvas");
3900
- const ctx = canvas.getContext("2d");
3901
- canvas.width = video.clientWidth;
3902
- canvas.height = video.clientHeight;
3903
- ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
3904
- const dataURL2 = canvas.toDataURL();
3905
- return createImage(dataURL2);
3906
- }
3907
- const poster = video.poster;
3908
- const contentType = getMimeType(poster);
3909
- const dataURL = await resourceToDataURL(poster, contentType, options);
3910
- return createImage(dataURL);
3911
- }
3912
- async function cloneIFrameElement(iframe, options) {
3913
- var _a2;
3914
- try {
3915
- if ((_a2 = iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument) === null || _a2 === void 0 ? void 0 : _a2.body) {
3916
- return await cloneNode$3(iframe.contentDocument.body, options, true);
4016
+ var snapshotCache = /* @__PURE__ */ new WeakMap();
4017
+ var snapshotKeyCache = /* @__PURE__ */ new Map();
4018
+ function snapshotComputedStyleFull(style) {
4019
+ const result2 = {};
4020
+ const computedVisibility = style.getPropertyValue("visibility");
4021
+ for (let i = 0; i < style.length; i++) {
4022
+ const prop = style[i];
4023
+ let val = style.getPropertyValue(prop);
4024
+ if ((prop === "background-image" || prop === "content") && val.includes("url(") && !val.includes("data:")) {
4025
+ val = "none";
3917
4026
  }
3918
- } catch (_b) {
4027
+ result2[prop] = val;
4028
+ }
4029
+ if (computedVisibility === "hidden") {
4030
+ result2.opacity = "0";
3919
4031
  }
3920
- return iframe.cloneNode(false);
4032
+ return result2;
3921
4033
  }
3922
- async function cloneSingleNode(node2, options) {
3923
- if (isInstanceOfElement(node2, HTMLCanvasElement)) {
3924
- return cloneCanvasElement(node2);
4034
+ function inlineAllStyles(source, clone, styleMap, cache2, compress) {
4035
+ if (source.tagName === "STYLE") return;
4036
+ if (!cache2.has(source)) {
4037
+ cache2.set(source, getStyle(source));
3925
4038
  }
3926
- if (isInstanceOfElement(node2, HTMLVideoElement)) {
3927
- return cloneVideoElement(node2, options);
4039
+ const style = cache2.get(source);
4040
+ if (!snapshotCache.has(source)) {
4041
+ const snapshot22 = snapshotComputedStyleFull(style);
4042
+ snapshotCache.set(source, snapshot22);
3928
4043
  }
3929
- if (isInstanceOfElement(node2, HTMLIFrameElement)) {
3930
- return cloneIFrameElement(node2, options);
4044
+ const snapshot2 = snapshotCache.get(source);
4045
+ const hash = Object.entries(snapshot2).sort(([a], [b]) => a.localeCompare(b)).map(([prop, val]) => `${prop}:${val}`).join(";");
4046
+ if (snapshotKeyCache.has(hash)) {
4047
+ styleMap.set(clone, snapshotKeyCache.get(hash));
4048
+ return;
3931
4049
  }
3932
- return node2.cloneNode(isSVGElement$1(node2));
4050
+ const tagName = source.tagName?.toLowerCase() || "div";
4051
+ const key = getStyleKey(snapshot2, tagName, compress);
4052
+ snapshotKeyCache.set(hash, key);
4053
+ styleMap.set(clone, key);
3933
4054
  }
3934
- const isSlotElement = (node2) => node2.tagName != null && node2.tagName.toUpperCase() === "SLOT";
3935
- const isSVGElement$1 = (node2) => node2.tagName != null && node2.tagName.toUpperCase() === "SVG";
3936
- async function cloneChildren(nativeNode, clonedNode, options) {
3937
- var _a2, _b;
3938
- if (isSVGElement$1(clonedNode)) {
3939
- return clonedNode;
3940
- }
3941
- let children = [];
3942
- if (isSlotElement(nativeNode) && nativeNode.assignedNodes) {
3943
- children = toArray(nativeNode.assignedNodes());
3944
- } else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && ((_a2 = nativeNode.contentDocument) === null || _a2 === void 0 ? void 0 : _a2.body)) {
3945
- children = toArray(nativeNode.contentDocument.body.childNodes);
3946
- } else {
3947
- children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);
4055
+ function freezeImgSrcset(original, cloned) {
4056
+ try {
4057
+ const chosen = original.currentSrc || original.src || "";
4058
+ if (!chosen) return;
4059
+ cloned.setAttribute("src", chosen);
4060
+ cloned.removeAttribute("srcset");
4061
+ cloned.removeAttribute("sizes");
4062
+ cloned.loading = "eager";
4063
+ cloned.decoding = "sync";
4064
+ } catch {
3948
4065
  }
3949
- if (children.length === 0 || isInstanceOfElement(nativeNode, HTMLVideoElement)) {
3950
- return clonedNode;
4066
+ }
4067
+ function deepClone(node2, styleMap, styleCache, nodeMap, compress, options = {}, originalRoot) {
4068
+ if (!node2) throw new Error("Invalid node");
4069
+ const clonedAssignedNodes = /* @__PURE__ */ new Set();
4070
+ let pendingSelectValue = null;
4071
+ if (node2.nodeType === Node.TEXT_NODE) {
4072
+ return node2.cloneNode(true);
3951
4073
  }
3952
- await children.reduce((deferred, child) => deferred.then(() => cloneNode$3(child, options)).then((clonedChild) => {
3953
- if (clonedChild) {
3954
- clonedNode.appendChild(clonedChild);
4074
+ if (node2.nodeType !== Node.ELEMENT_NODE) {
4075
+ return node2.cloneNode(true);
4076
+ }
4077
+ if (node2.getAttribute("data-capture") === "exclude") {
4078
+ const spacer = document.createElement("div");
4079
+ const rect = node2.getBoundingClientRect();
4080
+ spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
4081
+ return spacer;
4082
+ }
4083
+ if (options.exclude && Array.isArray(options.exclude)) {
4084
+ for (const selector of options.exclude) {
4085
+ try {
4086
+ if (node2.matches?.(selector)) {
4087
+ const spacer = document.createElement("div");
4088
+ const rect = node2.getBoundingClientRect();
4089
+ spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
4090
+ return spacer;
4091
+ }
4092
+ } catch (err) {
4093
+ console.warn(`Invalid selector in exclude option: ${selector}`, err);
4094
+ }
3955
4095
  }
3956
- }), Promise.resolve());
3957
- return clonedNode;
3958
- }
3959
- function cloneCSSStyle(nativeNode, clonedNode, options) {
3960
- const targetStyle = clonedNode.style;
3961
- if (!targetStyle) {
3962
- return;
3963
4096
  }
3964
- const sourceStyle = window.getComputedStyle(nativeNode);
3965
- if (sourceStyle.cssText) {
3966
- targetStyle.cssText = sourceStyle.cssText;
3967
- targetStyle.transformOrigin = sourceStyle.transformOrigin;
3968
- } else {
3969
- getStyleProperties(options).forEach((name) => {
3970
- let value2 = sourceStyle.getPropertyValue(name);
3971
- if (name === "font-size" && value2.endsWith("px")) {
3972
- const reducedFont = Math.floor(parseFloat(value2.substring(0, value2.length - 2))) - 0.1;
3973
- value2 = `${reducedFont}px`;
4097
+ if (typeof options.filter === "function") {
4098
+ try {
4099
+ if (!options.filter(node2, originalRoot || node2)) {
4100
+ const spacer = document.createElement("div");
4101
+ const rect = node2.getBoundingClientRect();
4102
+ spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
4103
+ return spacer;
3974
4104
  }
3975
- if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && name === "display" && value2 === "inline") {
3976
- value2 = "block";
4105
+ } catch (err) {
4106
+ console.warn("Error in filter function:", err);
4107
+ }
4108
+ }
4109
+ if (node2.tagName === "IFRAME") {
4110
+ const fallback = document.createElement("div");
4111
+ fallback.style.cssText = `width:${node2.offsetWidth}px;height:${node2.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;`;
4112
+ return fallback;
4113
+ }
4114
+ if (node2.getAttribute("data-capture") === "placeholder") {
4115
+ const clone2 = node2.cloneNode(false);
4116
+ nodeMap.set(clone2, node2);
4117
+ inlineAllStyles(node2, clone2, styleMap, styleCache, compress);
4118
+ const placeholder = document.createElement("div");
4119
+ placeholder.textContent = node2.getAttribute("data-placeholder-text") || "";
4120
+ placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`;
4121
+ clone2.appendChild(placeholder);
4122
+ return clone2;
4123
+ }
4124
+ if (node2.tagName === "CANVAS") {
4125
+ const dataURL = node2.toDataURL();
4126
+ const img = document.createElement("img");
4127
+ img.src = dataURL;
4128
+ img.width = node2.width;
4129
+ img.height = node2.height;
4130
+ nodeMap.set(img, node2);
4131
+ inlineAllStyles(node2, img, styleMap, styleCache, compress);
4132
+ return img;
4133
+ }
4134
+ let clone;
4135
+ try {
4136
+ clone = node2.cloneNode(false);
4137
+ nodeMap.set(clone, node2);
4138
+ if (node2.tagName === "IMG") {
4139
+ freezeImgSrcset(node2, clone);
4140
+ }
4141
+ } catch (err) {
4142
+ console.error("[Snapdom] Failed to clone node:", node2, err);
4143
+ throw err;
4144
+ }
4145
+ if (node2 instanceof HTMLTextAreaElement) {
4146
+ clone.textContent = node2.value;
4147
+ clone.value = node2.value;
4148
+ const rect = node2.getBoundingClientRect();
4149
+ clone.style.width = `${rect.width}px`;
4150
+ clone.style.height = `${rect.height}px`;
4151
+ return clone;
4152
+ }
4153
+ if (node2 instanceof HTMLInputElement) {
4154
+ clone.value = node2.value;
4155
+ clone.setAttribute("value", node2.value);
4156
+ if (node2.checked !== void 0) {
4157
+ clone.checked = node2.checked;
4158
+ if (node2.checked) clone.setAttribute("checked", "");
4159
+ if (node2.indeterminate) clone.indeterminate = node2.indeterminate;
4160
+ }
4161
+ }
4162
+ if (node2 instanceof HTMLSelectElement) {
4163
+ pendingSelectValue = node2.value;
4164
+ }
4165
+ inlineAllStyles(node2, clone, styleMap, styleCache, compress);
4166
+ if (node2.shadowRoot) {
4167
+ const hasSlot = Array.from(node2.shadowRoot.querySelectorAll("slot")).length > 0;
4168
+ if (hasSlot) {
4169
+ for (const child of node2.shadowRoot.childNodes) {
4170
+ if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
4171
+ const cssText = child.textContent || "";
4172
+ if (cssText.trim() && compress) {
4173
+ styleCache.set(child, cssText);
4174
+ }
4175
+ }
3977
4176
  }
3978
- if (name === "d" && clonedNode.getAttribute("d")) {
3979
- value2 = `path(${clonedNode.getAttribute("d")})`;
4177
+ } else {
4178
+ const shadowFrag = document.createDocumentFragment();
4179
+ for (const child of node2.shadowRoot.childNodes) {
4180
+ if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
4181
+ const cssText = child.textContent || "";
4182
+ if (cssText.trim() && compress) {
4183
+ styleCache.set(child, cssText);
4184
+ }
4185
+ continue;
4186
+ }
4187
+ const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node2);
4188
+ if (clonedChild) shadowFrag.appendChild(clonedChild);
3980
4189
  }
3981
- targetStyle.setProperty(name, value2, sourceStyle.getPropertyPriority(name));
3982
- });
4190
+ clone.appendChild(shadowFrag);
4191
+ }
3983
4192
  }
3984
- }
3985
- function cloneInputValue(nativeNode, clonedNode) {
3986
- if (isInstanceOfElement(nativeNode, HTMLTextAreaElement)) {
3987
- clonedNode.innerHTML = nativeNode.value;
4193
+ if (node2.tagName === "SLOT") {
4194
+ const assigned = node2.assignedNodes?.({ flatten: true }) || [];
4195
+ const nodesToClone = assigned.length > 0 ? assigned : Array.from(node2.childNodes);
4196
+ const fragment = document.createDocumentFragment();
4197
+ for (const child of nodesToClone) {
4198
+ const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node2);
4199
+ if (clonedChild) fragment.appendChild(clonedChild);
4200
+ }
4201
+ return fragment;
3988
4202
  }
3989
- if (isInstanceOfElement(nativeNode, HTMLInputElement)) {
3990
- clonedNode.setAttribute("value", nativeNode.value);
4203
+ for (const child of node2.childNodes) {
4204
+ if (clonedAssignedNodes.has(child)) continue;
4205
+ const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node2);
4206
+ if (clonedChild) clone.appendChild(clonedChild);
3991
4207
  }
3992
- }
3993
- function cloneSelectValue(nativeNode, clonedNode) {
3994
- if (isInstanceOfElement(nativeNode, HTMLSelectElement)) {
3995
- const clonedSelect = clonedNode;
3996
- const selectedOption = Array.from(clonedSelect.children).find((child) => nativeNode.value === child.getAttribute("value"));
3997
- if (selectedOption) {
3998
- selectedOption.setAttribute("selected", "");
4208
+ if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
4209
+ clone.value = pendingSelectValue;
4210
+ for (const opt of clone.options) {
4211
+ if (opt.value === pendingSelectValue) {
4212
+ opt.setAttribute("selected", "");
4213
+ } else {
4214
+ opt.removeAttribute("selected");
4215
+ }
3999
4216
  }
4000
4217
  }
4218
+ return clone;
4001
4219
  }
4002
- function decorate(nativeNode, clonedNode, options) {
4003
- if (isInstanceOfElement(clonedNode, Element)) {
4004
- cloneCSSStyle(nativeNode, clonedNode, options);
4005
- clonePseudoElements(nativeNode, clonedNode, options);
4006
- cloneInputValue(nativeNode, clonedNode);
4007
- cloneSelectValue(nativeNode, clonedNode);
4220
+ var defaultIconFonts = [
4221
+ // /uicons/i,
4222
+ /font\s*awesome/i,
4223
+ /material\s*icons/i,
4224
+ /ionicons/i,
4225
+ /glyphicons/i,
4226
+ /feather/i,
4227
+ /bootstrap\s*icons/i,
4228
+ /remix\s*icons/i,
4229
+ /heroicons/i,
4230
+ /layui/i,
4231
+ /lucide/i
4232
+ ];
4233
+ var userIconFonts = [];
4234
+ function extendIconFonts(fonts) {
4235
+ const list2 = Array.isArray(fonts) ? fonts : [fonts];
4236
+ for (const f of list2) {
4237
+ if (f instanceof RegExp) {
4238
+ userIconFonts.push(f);
4239
+ } else if (typeof f === "string") {
4240
+ userIconFonts.push(new RegExp(f, "i"));
4241
+ } else {
4242
+ console.warn("[snapdom] Ignored invalid iconFont value:", f);
4243
+ }
4008
4244
  }
4009
- return clonedNode;
4010
4245
  }
4011
- async function ensureSVGSymbols(clone, options) {
4012
- const uses = clone.querySelectorAll ? clone.querySelectorAll("use") : [];
4013
- if (uses.length === 0) {
4014
- return clone;
4015
- }
4016
- const processedDefs = {};
4017
- for (let i = 0; i < uses.length; i++) {
4018
- const use = uses[i];
4019
- const id = use.getAttribute("xlink:href");
4020
- if (id) {
4021
- const exist = clone.querySelector(id);
4022
- const definition = document.querySelector(id);
4023
- if (!exist && definition && !processedDefs[id]) {
4024
- processedDefs[id] = await cloneNode$3(definition, options, true);
4025
- }
4026
- }
4027
- }
4028
- const nodes = Object.values(processedDefs);
4029
- if (nodes.length) {
4030
- const ns = "http://www.w3.org/1999/xhtml";
4031
- const svg = document.createElementNS(ns, "svg");
4032
- svg.setAttribute("xmlns", ns);
4033
- svg.style.position = "absolute";
4034
- svg.style.width = "0";
4035
- svg.style.height = "0";
4036
- svg.style.overflow = "hidden";
4037
- svg.style.display = "none";
4038
- const defs = document.createElementNS(ns, "defs");
4039
- svg.appendChild(defs);
4040
- for (let i = 0; i < nodes.length; i++) {
4041
- defs.appendChild(nodes[i]);
4042
- }
4043
- clone.appendChild(svg);
4246
+ function isIconFont(input2) {
4247
+ const text = typeof input2 === "string" ? input2 : "";
4248
+ const candidates = [...defaultIconFonts, ...userIconFonts];
4249
+ for (const rx of candidates) {
4250
+ if (rx instanceof RegExp && rx.test(text)) return true;
4044
4251
  }
4045
- return clone;
4252
+ if (/icon/i.test(text) || /glyph/i.test(text) || /symbols/i.test(text) || /feather/i.test(text) || /fontawesome/i.test(text)) return true;
4253
+ return false;
4046
4254
  }
4047
- async function cloneNode$3(node2, options, isRoot) {
4048
- if (!isRoot && options.filter && !options.filter(node2)) {
4049
- return null;
4050
- }
4051
- return Promise.resolve(node2).then((clonedNode) => cloneSingleNode(clonedNode, options)).then((clonedNode) => cloneChildren(node2, clonedNode, options)).then((clonedNode) => decorate(node2, clonedNode, options)).then((clonedNode) => ensureSVGSymbols(clonedNode, options));
4255
+ async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
4256
+ fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
4257
+ const dpr = window.devicePixelRatio || 1;
4258
+ await document.fonts.ready;
4259
+ const span = document.createElement("span");
4260
+ span.textContent = unicodeChar;
4261
+ span.style.position = "absolute";
4262
+ span.style.visibility = "hidden";
4263
+ span.style.fontFamily = `"${fontFamily}"`;
4264
+ span.style.fontWeight = fontWeight || "normal";
4265
+ span.style.fontSize = `${fontSize}px`;
4266
+ span.style.lineHeight = "1";
4267
+ span.style.whiteSpace = "nowrap";
4268
+ span.style.padding = "0";
4269
+ span.style.margin = "0";
4270
+ document.body.appendChild(span);
4271
+ const rect = span.getBoundingClientRect();
4272
+ const width = Math.ceil(rect.width);
4273
+ const height = Math.ceil(rect.height);
4274
+ document.body.removeChild(span);
4275
+ const canvas = document.createElement("canvas");
4276
+ canvas.width = width * dpr;
4277
+ canvas.height = height * dpr;
4278
+ const ctx = canvas.getContext("2d");
4279
+ ctx.scale(dpr, dpr);
4280
+ ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
4281
+ ctx.textAlign = "left";
4282
+ ctx.textBaseline = "top";
4283
+ ctx.fillStyle = color;
4284
+ ctx.fillText(unicodeChar, 0, 0);
4285
+ return {
4286
+ dataUrl: canvas.toDataURL(),
4287
+ width,
4288
+ height
4289
+ };
4052
4290
  }
4053
- const URL_REGEX = /url\((['"]?)([^'"]+?)\1\)/g;
4054
- const URL_WITH_FORMAT_REGEX = /url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g;
4055
- const FONT_SRC_REGEX = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
4056
- function toRegex(url2) {
4057
- const escaped = url2.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
4058
- return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
4291
+ function isStylesheetLoaded(href) {
4292
+ return Array.from(document.styleSheets).some((sheet) => sheet.href === href);
4059
4293
  }
4060
- function parseURLs(cssText) {
4061
- const urls = [];
4062
- cssText.replace(URL_REGEX, (raw, quotation, url2) => {
4063
- urls.push(url2);
4064
- return raw;
4294
+ function injectLinkIfMissing(href) {
4295
+ return new Promise((resolve2) => {
4296
+ if (isStylesheetLoaded(href)) return resolve2(null);
4297
+ const link = document.createElement("link");
4298
+ link.rel = "stylesheet";
4299
+ link.href = href;
4300
+ link.setAttribute("data-snapdom", "injected-import");
4301
+ link.onload = () => resolve2(link);
4302
+ link.onerror = () => resolve2(null);
4303
+ document.head.appendChild(link);
4065
4304
  });
4066
- return urls.filter((url2) => !isDataUrl(url2));
4067
4305
  }
4068
- async function embed(cssText, resourceURL, baseURL, options, getContentFromUrl) {
4069
- try {
4070
- const resolvedURL = baseURL ? resolveUrl(resourceURL, baseURL) : resourceURL;
4071
- const contentType = getMimeType(resourceURL);
4072
- let dataURL;
4073
- if (getContentFromUrl) ;
4074
- else {
4075
- dataURL = await resourceToDataURL(resolvedURL, contentType, options);
4306
+ async function embedCustomFonts({ preCached = false, localFonts = [], useProxy = "" } = {}) {
4307
+ if (cache.resource.has("fonts-embed-css")) {
4308
+ if (preCached) {
4309
+ const style = document.createElement("style");
4310
+ style.setAttribute("data-snapdom", "embedFonts");
4311
+ style.textContent = cache.resource.get("fonts-embed-css");
4312
+ document.head.appendChild(style);
4076
4313
  }
4077
- return cssText.replace(toRegex(resourceURL), `$1${dataURL}$3`);
4078
- } catch (error) {
4314
+ return cache.resource.get("fonts-embed-css");
4079
4315
  }
4080
- return cssText;
4081
- }
4082
- function filterPreferredFontFormat(str, { preferredFontFormat }) {
4083
- return !preferredFontFormat ? str : str.replace(FONT_SRC_REGEX, (match2) => {
4084
- while (true) {
4085
- const [src, , format] = URL_WITH_FORMAT_REGEX.exec(match2) || [];
4086
- if (!format) {
4087
- return "";
4316
+ const loadedFonts = /* @__PURE__ */ new Set();
4317
+ try {
4318
+ for (const f of document.fonts) {
4319
+ if (f.status === "loaded") {
4320
+ loadedFonts.add(`${f.family}__${f.weight || "normal"}__${f.style || "normal"}`);
4088
4321
  }
4089
- if (format === preferredFontFormat) {
4090
- return `src: ${src};`;
4322
+ }
4323
+ } catch {
4324
+ }
4325
+ const importRegex = /@import\s+url\(["']?([^"')]+)["']?\)/g;
4326
+ const styleImports = [];
4327
+ for (const styleTag of document.querySelectorAll("style")) {
4328
+ const cssText = styleTag.textContent || "";
4329
+ const matches = Array.from(cssText.matchAll(importRegex));
4330
+ for (const match2 of matches) {
4331
+ const importUrl = match2[1];
4332
+ if (isIconFont(importUrl)) continue;
4333
+ if (!isStylesheetLoaded(importUrl)) {
4334
+ styleImports.push(importUrl);
4091
4335
  }
4092
4336
  }
4093
- });
4094
- }
4095
- function shouldEmbed(url2) {
4096
- return url2.search(URL_REGEX) !== -1;
4097
- }
4098
- async function embedResources(cssText, baseUrl, options) {
4099
- if (!shouldEmbed(cssText)) {
4100
- return cssText;
4101
4337
  }
4102
- const filteredCSSText = filterPreferredFontFormat(cssText, options);
4103
- const urls = parseURLs(filteredCSSText);
4104
- return urls.reduce((deferred, url2) => deferred.then((css) => embed(css, url2, baseUrl, options)), Promise.resolve(filteredCSSText));
4105
- }
4106
- async function embedProp(propName, node2, options) {
4107
- var _a2;
4108
- const propValue = (_a2 = node2.style) === null || _a2 === void 0 ? void 0 : _a2.getPropertyValue(propName);
4109
- if (propValue) {
4110
- const cssString = await embedResources(propValue, null, options);
4111
- node2.style.setProperty(propName, cssString, node2.style.getPropertyPriority(propName));
4112
- return true;
4338
+ await Promise.all(styleImports.map(injectLinkIfMissing));
4339
+ const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((link) => link.href);
4340
+ let finalCSS = "";
4341
+ for (const link of links) {
4342
+ try {
4343
+ const res = await fetchResource(link.href, { useProxy });
4344
+ const cssText = await res.text();
4345
+ if (isIconFont(link.href) || isIconFont(cssText)) continue;
4346
+ const faceRegex = /@font-face[^{}]*{[^}]*}/g;
4347
+ let cssFinal = cssText;
4348
+ for (const face of cssText.match(faceRegex) || []) {
4349
+ const famMatch = face.match(/font-family:\s*([^;]+);/i);
4350
+ if (!famMatch) continue;
4351
+ const family = famMatch[1].replace(/['"]/g, "").trim();
4352
+ const weightMatch = face.match(/font-weight:\s*([^;]+);/i);
4353
+ const styleMatch = face.match(/font-style:\s*([^;]+);/i);
4354
+ const weight = weightMatch ? weightMatch[1].trim() : "normal";
4355
+ const style = styleMatch ? styleMatch[1].trim() : "normal";
4356
+ const key = `${family}__${weight}__${style}`;
4357
+ const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
4358
+ const hasURL = /url\(/i.test(face);
4359
+ const hasLocal = /local\(/i.test(face);
4360
+ if (!hasURL && hasLocal) {
4361
+ continue;
4362
+ }
4363
+ if (!loadedFonts.has(key)) {
4364
+ cssFinal = cssFinal.replace(face, "");
4365
+ continue;
4366
+ }
4367
+ let inlined = face;
4368
+ const matches = Array.from(face.matchAll(urlRegex));
4369
+ for (const match2 of matches) {
4370
+ let rawUrl = extractURL(match2[0]);
4371
+ if (!rawUrl) continue;
4372
+ let url2 = rawUrl;
4373
+ if (!url2.startsWith("http") && !url2.startsWith("data:")) {
4374
+ url2 = new URL(url2, link.href).href;
4375
+ }
4376
+ if (isIconFont(url2)) continue;
4377
+ if (cache.resource.has(url2)) {
4378
+ cache.font.add(url2);
4379
+ inlined = inlined.replace(match2[0], `url(${cache.resource.get(url2)})`);
4380
+ continue;
4381
+ }
4382
+ if (cache.font.has(url2)) continue;
4383
+ try {
4384
+ const fontRes = await fetchResource(url2, { useProxy });
4385
+ const blob2 = await fontRes.blob();
4386
+ const b64 = await new Promise((resolve2) => {
4387
+ const reader = new FileReader();
4388
+ reader.onload = () => resolve2(reader.result);
4389
+ reader.readAsDataURL(blob2);
4390
+ });
4391
+ cache.resource.set(url2, b64);
4392
+ cache.font.add(url2);
4393
+ inlined = inlined.replace(match2[0], `url(${b64})`);
4394
+ } catch (e) {
4395
+ console.warn("[snapdom] Failed to fetch font resource:", url2);
4396
+ }
4397
+ }
4398
+ cssFinal = cssFinal.replace(face, inlined);
4399
+ }
4400
+ finalCSS += cssFinal + "\n";
4401
+ } catch (e) {
4402
+ console.warn("[snapdom] Failed to fetch CSS:", link.href);
4403
+ }
4113
4404
  }
4114
- return false;
4115
- }
4116
- async function embedBackground(clonedNode, options) {
4117
- await embedProp("background", clonedNode, options) || await embedProp("background-image", clonedNode, options);
4118
- await embedProp("mask", clonedNode, options) || await embedProp("-webkit-mask", clonedNode, options) || await embedProp("mask-image", clonedNode, options) || await embedProp("-webkit-mask-image", clonedNode, options);
4119
- }
4120
- async function embedImageNode(clonedNode, options) {
4121
- const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement);
4122
- if (!(isImageElement && !isDataUrl(clonedNode.src)) && !(isInstanceOfElement(clonedNode, SVGImageElement) && !isDataUrl(clonedNode.href.baseVal))) {
4123
- return;
4405
+ for (const sheet of document.styleSheets) {
4406
+ try {
4407
+ if (!sheet.href || links.every((link) => link.href !== sheet.href)) {
4408
+ for (const rule2 of sheet.cssRules) {
4409
+ if (rule2.type === CSSRule.FONT_FACE_RULE) {
4410
+ const src = rule2.style.getPropertyValue("src");
4411
+ const family = rule2.style.getPropertyValue("font-family");
4412
+ if (!src || isIconFont(family)) continue;
4413
+ const weightVal = rule2.style.getPropertyValue("font-weight") || "normal";
4414
+ const styleVal = rule2.style.getPropertyValue("font-style") || "normal";
4415
+ const key = `${family}__${weightVal}__${styleVal}`;
4416
+ const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
4417
+ const localRegex = /local\((["']?)[^)]+?\1\)/g;
4418
+ const hasURL = !!src.match(urlRegex);
4419
+ const hasLocal = !!src.match(localRegex);
4420
+ if (!hasURL && hasLocal) {
4421
+ finalCSS += `@font-face{font-family:${family};src:${src};font-style:${styleVal};font-weight:${weightVal};}`;
4422
+ continue;
4423
+ }
4424
+ if (!loadedFonts.has(key)) continue;
4425
+ let inlinedSrc = src;
4426
+ const matches = Array.from(src.matchAll(urlRegex));
4427
+ for (const match2 of matches) {
4428
+ let rawUrl = match2[2].trim();
4429
+ if (!rawUrl) continue;
4430
+ let url2 = rawUrl;
4431
+ if (!url2.startsWith("http") && !url2.startsWith("data:")) {
4432
+ url2 = new URL(url2, sheet.href || location.href).href;
4433
+ }
4434
+ if (isIconFont(url2)) continue;
4435
+ if (cache.resource.has(url2)) {
4436
+ cache.font.add(url2);
4437
+ inlinedSrc = inlinedSrc.replace(match2[0], `url(${cache.resource.get(url2)})`);
4438
+ continue;
4439
+ }
4440
+ if (cache.font.has(url2)) continue;
4441
+ try {
4442
+ const res = await fetchResource(url2, { useProxy });
4443
+ const blob2 = await res.blob();
4444
+ const b64 = await new Promise((resolve2) => {
4445
+ const reader = new FileReader();
4446
+ reader.onload = () => resolve2(reader.result);
4447
+ reader.readAsDataURL(blob2);
4448
+ });
4449
+ cache.resource.set(url2, b64);
4450
+ cache.font.add(url2);
4451
+ inlinedSrc = inlinedSrc.replace(match2[0], `url(${b64})`);
4452
+ } catch (e) {
4453
+ console.warn("[snapdom] Failed to fetch font URL:", url2);
4454
+ }
4455
+ }
4456
+ finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleVal};font-weight:${weightVal};}`;
4457
+ }
4458
+ }
4459
+ }
4460
+ } catch (e) {
4461
+ console.warn("[snapdom] Cannot access stylesheet", sheet.href, e);
4462
+ }
4463
+ }
4464
+ for (const font of document.fonts) {
4465
+ if (font.family && font.status === "loaded" && font._snapdomSrc) {
4466
+ if (isIconFont(font.family)) continue;
4467
+ let b64 = font._snapdomSrc;
4468
+ if (!b64.startsWith("data:")) {
4469
+ if (cache.resource.has(font._snapdomSrc)) {
4470
+ b64 = cache.resource.get(font._snapdomSrc);
4471
+ cache.font.add(font._snapdomSrc);
4472
+ } else if (!cache.font.has(font._snapdomSrc)) {
4473
+ try {
4474
+ const res = await fetchResource(font._snapdomSrc, { useProxy });
4475
+ const blob2 = await res.blob();
4476
+ b64 = await new Promise((resolve2) => {
4477
+ const reader = new FileReader();
4478
+ reader.onload = () => resolve2(reader.result);
4479
+ reader.readAsDataURL(blob2);
4480
+ });
4481
+ cache.resource.set(font._snapdomSrc, b64);
4482
+ cache.font.add(font._snapdomSrc);
4483
+ } catch (e) {
4484
+ console.warn("[snapdom] Failed to fetch dynamic font src:", font._snapdomSrc);
4485
+ continue;
4486
+ }
4487
+ }
4488
+ }
4489
+ finalCSS += `@font-face{font-family:'${font.family}';src:url(${b64});font-style:${font.style || "normal"};font-weight:${font.weight || "normal"};}`;
4490
+ }
4124
4491
  }
4125
- const url2 = isImageElement ? clonedNode.src : clonedNode.href.baseVal;
4126
- const dataURL = await resourceToDataURL(url2, getMimeType(url2), options);
4127
- await new Promise((resolve2, reject) => {
4128
- clonedNode.onload = resolve2;
4129
- clonedNode.onerror = options.onImageErrorHandler ? (...attributes) => {
4492
+ for (const font of localFonts) {
4493
+ if (!font || typeof font !== "object") continue;
4494
+ const { family, src, weight = "normal", style = "normal" } = font;
4495
+ if (!family || !src) continue;
4496
+ let b64 = src;
4497
+ if (!b64.startsWith("data:")) {
4130
4498
  try {
4131
- resolve2(options.onImageErrorHandler(...attributes));
4132
- } catch (error) {
4133
- reject(error);
4499
+ const res = await fetchResource(src, { useProxy });
4500
+ const blob2 = await res.blob();
4501
+ b64 = await new Promise((resolve2) => {
4502
+ const reader = new FileReader();
4503
+ reader.onload = () => resolve2(reader.result);
4504
+ reader.readAsDataURL(blob2);
4505
+ });
4506
+ cache.resource.set(src, b64);
4507
+ cache.font.add(src);
4508
+ } catch (e) {
4509
+ console.warn("[snapdom] Failed to load local font:", src);
4510
+ continue;
4134
4511
  }
4135
- } : reject;
4136
- const image = clonedNode;
4137
- if (image.decode) {
4138
- image.decode = resolve2;
4139
- }
4140
- if (image.loading === "lazy") {
4141
- image.loading = "eager";
4142
- }
4143
- if (isImageElement) {
4144
- clonedNode.srcset = "";
4145
- clonedNode.src = dataURL;
4146
4512
  } else {
4147
- clonedNode.href.baseVal = dataURL;
4513
+ cache.resource.set(src, b64);
4514
+ cache.font.add(src);
4148
4515
  }
4149
- });
4150
- }
4151
- async function embedChildren(clonedNode, options) {
4152
- const children = toArray(clonedNode.childNodes);
4153
- const deferreds = children.map((child) => embedImages(child, options));
4154
- await Promise.all(deferreds).then(() => clonedNode);
4155
- }
4156
- async function embedImages(clonedNode, options) {
4157
- if (isInstanceOfElement(clonedNode, Element)) {
4158
- await embedBackground(clonedNode, options);
4159
- await embedImageNode(clonedNode, options);
4160
- await embedChildren(clonedNode, options);
4516
+ finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};}`;
4161
4517
  }
4162
- }
4163
- function applyStyle(node2, options) {
4164
- const { style } = node2;
4165
- if (options.backgroundColor) {
4166
- style.backgroundColor = options.backgroundColor;
4167
- }
4168
- if (options.width) {
4169
- style.width = `${options.width}px`;
4518
+ if (finalCSS) {
4519
+ cache.resource.set("fonts-embed-css", finalCSS);
4520
+ if (preCached) {
4521
+ const style = document.createElement("style");
4522
+ style.setAttribute("data-snapdom", "embedFonts");
4523
+ style.textContent = finalCSS;
4524
+ document.head.appendChild(style);
4525
+ }
4170
4526
  }
4171
- if (options.height) {
4172
- style.height = `${options.height}px`;
4527
+ return finalCSS;
4528
+ }
4529
+ async function inlinePseudoElements(source, clone, styleMap, styleCache, options) {
4530
+ if (!(source instanceof Element) || !(clone instanceof Element)) return;
4531
+ for (const pseudo of ["::before", "::after", "::first-letter"]) {
4532
+ try {
4533
+ const style = getStyle(source, pseudo);
4534
+ if (!style || typeof style[Symbol.iterator] !== "function") continue;
4535
+ 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";
4536
+ if (isEmptyPseudo) continue;
4537
+ if (pseudo === "::first-letter") {
4538
+ const normal = getComputedStyle(source);
4539
+ const isMeaningful = style.color !== normal.color || style.fontSize !== normal.fontSize || style.fontWeight !== normal.fontWeight;
4540
+ if (!isMeaningful) continue;
4541
+ const textNode = Array.from(clone.childNodes).find(
4542
+ (n2) => n2.nodeType === Node.TEXT_NODE && n2.textContent?.trim().length > 0
4543
+ );
4544
+ if (!textNode) continue;
4545
+ const text = textNode.textContent;
4546
+ const match2 = text.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u);
4547
+ const first = match2?.[0];
4548
+ const rest = text.slice(first?.length || 0);
4549
+ if (!first || /[\uD800-\uDFFF]/.test(first)) continue;
4550
+ const span = document.createElement("span");
4551
+ span.textContent = first;
4552
+ span.dataset.snapdomPseudo = "::first-letter";
4553
+ const snapshot22 = snapshotComputedStyle(style);
4554
+ const key2 = getStyleKey(snapshot22, "span", options);
4555
+ styleMap.set(span, key2);
4556
+ const restNode = document.createTextNode(rest);
4557
+ clone.replaceChild(restNode, textNode);
4558
+ clone.insertBefore(span, restNode);
4559
+ continue;
4560
+ }
4561
+ const content = style.content;
4562
+ const cleanContent = /counter\s*\(|counters\s*\(/.test(content) ? "- " : parseContent(content);
4563
+ const bg = style.backgroundImage;
4564
+ const bgColor = style.backgroundColor;
4565
+ const fontFamily = style.fontFamily;
4566
+ const fontSize = parseInt(style.fontSize) || 32;
4567
+ const fontWeight = parseInt(style.fontWeight) || false;
4568
+ const color = style.color || "#000";
4569
+ const display = style.display;
4570
+ const width = parseFloat(style.width);
4571
+ const height = parseFloat(style.height);
4572
+ const borderStyle = style.borderStyle;
4573
+ const borderWidth = parseFloat(style.borderWidth);
4574
+ const transform = style.transform;
4575
+ const isIconFont2 = isIconFont(fontFamily);
4576
+ const hasExplicitContent = content !== "none" && cleanContent !== "";
4577
+ const hasBg = bg && bg !== "none";
4578
+ const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
4579
+ const hasBox = display !== "inline" && (width > 0 || height > 0);
4580
+ const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
4581
+ const hasTransform = transform && transform !== "none";
4582
+ const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox || hasBorder || hasTransform;
4583
+ if (!shouldRender) continue;
4584
+ const pseudoEl = document.createElement("span");
4585
+ pseudoEl.dataset.snapdomPseudo = pseudo;
4586
+ pseudoEl.style.verticalAlign = "middle";
4587
+ const snapshot2 = snapshotComputedStyle(style);
4588
+ const key = getStyleKey(snapshot2, "span", options);
4589
+ styleMap.set(pseudoEl, key);
4590
+ if (isIconFont2 && cleanContent.length === 1) {
4591
+ const { dataUrl, width: width2, height: height2 } = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
4592
+ const imgEl = document.createElement("img");
4593
+ imgEl.src = dataUrl;
4594
+ imgEl.style = `height:${fontSize}px;width:${width2 / height2 * fontSize}px;object-fit:contain;`;
4595
+ pseudoEl.appendChild(imgEl);
4596
+ clone.dataset.snapdomHasIcon = "true";
4597
+ } else if (cleanContent.startsWith("url(")) {
4598
+ const rawUrl = extractURL(cleanContent);
4599
+ if (rawUrl?.trim()) {
4600
+ try {
4601
+ const imgEl = document.createElement("img");
4602
+ const dataUrl = await fetchImage(safeEncodeURI(rawUrl), options);
4603
+ imgEl.src = dataUrl;
4604
+ imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
4605
+ pseudoEl.appendChild(imgEl);
4606
+ } catch (e) {
4607
+ console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
4608
+ }
4609
+ }
4610
+ } else if (!isIconFont2 && hasExplicitContent) {
4611
+ pseudoEl.textContent = cleanContent;
4612
+ }
4613
+ if (hasBg) {
4614
+ try {
4615
+ const bgSplits = splitBackgroundImage(bg);
4616
+ const newBgParts = await Promise.all(bgSplits.map(inlineSingleBackgroundEntry));
4617
+ pseudoEl.style.backgroundImage = newBgParts.join(", ");
4618
+ } catch (e) {
4619
+ console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
4620
+ }
4621
+ }
4622
+ if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
4623
+ const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent?.trim() !== "";
4624
+ const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox || hasBorder || hasTransform;
4625
+ if (!hasVisibleBox) continue;
4626
+ if (pseudo === "::before") {
4627
+ clone.insertBefore(pseudoEl, clone.firstChild);
4628
+ } else {
4629
+ clone.appendChild(pseudoEl);
4630
+ }
4631
+ } catch (e) {
4632
+ console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e);
4633
+ }
4173
4634
  }
4174
- const manual = options.style;
4175
- if (manual != null) {
4176
- Object.keys(manual).forEach((key) => {
4177
- style[key] = manual[key];
4178
- });
4635
+ const sChildren = Array.from(source.children);
4636
+ const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
4637
+ for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
4638
+ await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, options);
4179
4639
  }
4180
- return node2;
4181
4640
  }
4182
- const cssFetchCache = {};
4183
- async function fetchCSS(url2) {
4184
- let cache2 = cssFetchCache[url2];
4185
- if (cache2 != null) {
4186
- return cache2;
4187
- }
4188
- const res = await fetch(url2);
4189
- const cssText = await res.text();
4190
- cache2 = { url: url2, cssText };
4191
- cssFetchCache[url2] = cache2;
4192
- return cache2;
4193
- }
4194
- async function embedFonts(data, options) {
4195
- let cssText = data.cssText;
4196
- const regexUrl = /url\(["']?([^"')]+)["']?\)/g;
4197
- const fontLocs = cssText.match(/url\([^)]+\)/g) || [];
4198
- const loadFonts = fontLocs.map(async (loc) => {
4199
- let url2 = loc.replace(regexUrl, "$1");
4200
- if (!url2.startsWith("https://")) {
4201
- url2 = new URL(url2, data.url).href;
4202
- }
4203
- return fetchAsDataURL(url2, options.fetchRequestInit, ({ result: result2 }) => {
4204
- cssText = cssText.replace(loc, `url(${result2})`);
4205
- return [loc, result2];
4206
- });
4641
+ function inlineExternalDefsAndSymbols(rootElement) {
4642
+ if (!rootElement) return;
4643
+ const usedIds = /* @__PURE__ */ new Set();
4644
+ rootElement.querySelectorAll("use").forEach((use) => {
4645
+ const href = use.getAttribute("xlink:href") || use.getAttribute("href");
4646
+ if (href && href.startsWith("#")) {
4647
+ usedIds.add(href.slice(1));
4648
+ }
4649
+ });
4650
+ if (!usedIds.size) return;
4651
+ const allGlobal = Array.from(document.querySelectorAll("svg > symbol, svg > defs"));
4652
+ const globalSymbols = allGlobal.filter((el) => el.tagName.toLowerCase() === "symbol");
4653
+ const globalDefs = allGlobal.filter((el) => el.tagName.toLowerCase() === "defs");
4654
+ let container2 = rootElement.querySelector("svg.inline-defs-container");
4655
+ if (!container2) {
4656
+ container2 = document.createElementNS("http://www.w3.org/2000/svg", "svg");
4657
+ container2.setAttribute("aria-hidden", "true");
4658
+ container2.setAttribute("style", "position: absolute; width: 0; height: 0; overflow: hidden;");
4659
+ container2.classList.add("inline-defs-container");
4660
+ rootElement.insertBefore(container2, rootElement.firstChild);
4661
+ }
4662
+ const existingIds = /* @__PURE__ */ new Set();
4663
+ rootElement.querySelectorAll("symbol[id], defs > *[id]").forEach((el) => {
4664
+ existingIds.add(el.id);
4665
+ });
4666
+ usedIds.forEach((id) => {
4667
+ if (existingIds.has(id)) return;
4668
+ const symbol = globalSymbols.find((sym) => sym.id === id);
4669
+ if (symbol) {
4670
+ container2.appendChild(symbol.cloneNode(true));
4671
+ existingIds.add(id);
4672
+ return;
4673
+ }
4674
+ for (const defs of globalDefs) {
4675
+ const defEl = defs.querySelector(`#${CSS.escape(id)}`);
4676
+ if (defEl) {
4677
+ let defsContainer = container2.querySelector("defs");
4678
+ if (!defsContainer) {
4679
+ defsContainer = document.createElementNS("http://www.w3.org/2000/svg", "defs");
4680
+ container2.appendChild(defsContainer);
4681
+ }
4682
+ defsContainer.appendChild(defEl.cloneNode(true));
4683
+ existingIds.add(id);
4684
+ break;
4685
+ }
4686
+ }
4207
4687
  });
4208
- return Promise.all(loadFonts).then(() => cssText);
4209
4688
  }
4210
- function parseCSS(source) {
4211
- if (source == null) {
4212
- return [];
4689
+ async function prepareClone(element, compress = false, embedFonts = false, options = {}) {
4690
+ const styleMap = /* @__PURE__ */ new Map();
4691
+ const styleCache = /* @__PURE__ */ new WeakMap();
4692
+ const nodeMap = /* @__PURE__ */ new Map();
4693
+ let clone;
4694
+ let classCSS = "";
4695
+ stabilizeLayout(element);
4696
+ try {
4697
+ inlineExternalDefsAndSymbols(element);
4698
+ } catch (e) {
4699
+ console.warn("inlineExternal defs or symbol failed:", e);
4213
4700
  }
4214
- const result2 = [];
4215
- const commentsRegex = /(\/\*[\s\S]*?\*\/)/gi;
4216
- let cssText = source.replace(commentsRegex, "");
4217
- const keyframesRegex = new RegExp("((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})", "gi");
4218
- while (true) {
4219
- const matches = keyframesRegex.exec(cssText);
4220
- if (matches === null) {
4221
- break;
4222
- }
4223
- result2.push(matches[0]);
4701
+ try {
4702
+ clone = deepClone(element, styleMap, styleCache, nodeMap, compress, options, element);
4703
+ } catch (e) {
4704
+ console.warn("deepClone failed:", e);
4705
+ throw e;
4224
4706
  }
4225
- cssText = cssText.replace(keyframesRegex, "");
4226
- const importRegex = /@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi;
4227
- const combinedCSSRegex = "((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})";
4228
- const unifiedRegex = new RegExp(combinedCSSRegex, "gi");
4229
- while (true) {
4230
- let matches = importRegex.exec(cssText);
4231
- if (matches === null) {
4232
- matches = unifiedRegex.exec(cssText);
4233
- if (matches === null) {
4234
- break;
4235
- } else {
4236
- importRegex.lastIndex = unifiedRegex.lastIndex;
4707
+ try {
4708
+ await inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts, options.useProxy);
4709
+ } catch (e) {
4710
+ console.warn("inlinePseudoElements failed:", e);
4711
+ }
4712
+ await resolveBlobUrlsInTree(clone);
4713
+ if (compress) {
4714
+ const keyToClass = generateCSSClasses(styleMap);
4715
+ classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
4716
+ for (const [node2, key] of styleMap.entries()) {
4717
+ if (node2.tagName === "STYLE") continue;
4718
+ if (node2.getRootNode && node2.getRootNode() instanceof ShadowRoot) {
4719
+ node2.setAttribute("style", key.replace(/;/g, "; "));
4720
+ continue;
4721
+ }
4722
+ const className = keyToClass.get(key);
4723
+ if (className) node2.classList.add(className);
4724
+ const bgImage = node2.style?.backgroundImage;
4725
+ const hasIcon = node2.dataset?.snapdomHasIcon;
4726
+ if (bgImage && bgImage !== "none") node2.style.backgroundImage = bgImage;
4727
+ if (hasIcon) {
4728
+ node2.style.verticalAlign = "middle";
4729
+ node2.style.display = "inline";
4237
4730
  }
4238
- } else {
4239
- unifiedRegex.lastIndex = importRegex.lastIndex;
4240
4731
  }
4241
- result2.push(matches[0]);
4732
+ } else {
4733
+ for (const [node2, key] of styleMap.entries()) {
4734
+ if (node2.tagName === "STYLE") continue;
4735
+ node2.setAttribute("style", key.replace(/;/g, "; "));
4736
+ }
4737
+ }
4738
+ for (const [cloneNode2, originalNode] of nodeMap.entries()) {
4739
+ const scrollX = originalNode.scrollLeft;
4740
+ const scrollY = originalNode.scrollTop;
4741
+ const hasScroll = scrollX || scrollY;
4742
+ if (hasScroll && cloneNode2 instanceof HTMLElement) {
4743
+ cloneNode2.style.overflow = "hidden";
4744
+ cloneNode2.style.scrollbarWidth = "none";
4745
+ cloneNode2.style.msOverflowStyle = "none";
4746
+ const inner = document.createElement("div");
4747
+ inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`;
4748
+ inner.style.willChange = "transform";
4749
+ inner.style.display = "inline-block";
4750
+ inner.style.width = "100%";
4751
+ while (cloneNode2.firstChild) {
4752
+ inner.appendChild(cloneNode2.firstChild);
4753
+ }
4754
+ cloneNode2.appendChild(inner);
4755
+ }
4756
+ }
4757
+ if (element === nodeMap.get(clone)) {
4758
+ const computed = styleCache.get(element) || window.getComputedStyle(element);
4759
+ styleCache.set(element, computed);
4760
+ const transform = stripTranslate(computed.transform);
4761
+ clone.style.margin = "0";
4762
+ clone.style.position = "static";
4763
+ clone.style.top = "auto";
4764
+ clone.style.left = "auto";
4765
+ clone.style.right = "auto";
4766
+ clone.style.bottom = "auto";
4767
+ clone.style.zIndex = "auto";
4768
+ clone.style.float = "none";
4769
+ clone.style.clear = "none";
4770
+ clone.style.transform = transform || "";
4771
+ }
4772
+ for (const [cloneNode2, originalNode] of nodeMap.entries()) {
4773
+ if (originalNode.tagName === "PRE") {
4774
+ cloneNode2.style.marginTop = "0";
4775
+ cloneNode2.style.marginBlockStart = "0";
4776
+ }
4777
+ }
4778
+ return { clone, classCSS, styleCache };
4779
+ }
4780
+ function stabilizeLayout(element) {
4781
+ const style = getComputedStyle(element);
4782
+ const outlineStyle = style.outlineStyle;
4783
+ const outlineWidth = style.outlineWidth;
4784
+ const borderStyle = style.borderStyle;
4785
+ const borderWidth = style.borderWidth;
4786
+ const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0;
4787
+ const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0;
4788
+ if (outlineVisible && borderAbsent) {
4789
+ element.style.border = `${outlineWidth} solid transparent`;
4790
+ }
4791
+ }
4792
+ var _blobToDataUrlCache = /* @__PURE__ */ new Map();
4793
+ async function blobUrlToDataUrl(blobUrl) {
4794
+ if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl);
4795
+ const res = await fetch(blobUrl);
4796
+ if (!res.ok) throw new Error(`[SnapDOM] HTTP ${res.status} on blob fetch (${blobUrl})`);
4797
+ const blob2 = await res.blob();
4798
+ const dataUrl = await new Promise((resolve2, reject) => {
4799
+ const fr = new FileReader();
4800
+ fr.onloadend = () => {
4801
+ const v = fr.result;
4802
+ if (typeof v === "string" && v.startsWith("data:")) resolve2(v);
4803
+ else reject(new Error("[SnapDOM] Invalid data URL from blob"));
4804
+ };
4805
+ fr.onerror = () => reject(new Error("[SnapDOM] FileReader error"));
4806
+ fr.readAsDataURL(blob2);
4807
+ });
4808
+ _blobToDataUrlCache.set(blobUrl, dataUrl);
4809
+ return dataUrl;
4810
+ }
4811
+ var BLOB_URL_RE = /\bblob:[^)"'\s]+/g;
4812
+ async function replaceBlobUrlsInCssText(cssText) {
4813
+ if (!cssText || cssText.indexOf("blob:") === -1) return cssText;
4814
+ const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || []));
4815
+ if (uniques.length === 0) return cssText;
4816
+ let out = cssText;
4817
+ for (const u of uniques) {
4818
+ try {
4819
+ const d = await blobUrlToDataUrl(u);
4820
+ out = out.split(u).join(d);
4821
+ } catch {
4822
+ }
4242
4823
  }
4243
- return result2;
4824
+ return out;
4244
4825
  }
4245
- async function getCSSRules(styleSheets2, options) {
4246
- const ret = [];
4247
- const deferreds = [];
4248
- styleSheets2.forEach((sheet) => {
4249
- if ("cssRules" in sheet) {
4250
- try {
4251
- toArray(sheet.cssRules || []).forEach((item, index2) => {
4252
- if (item.type === CSSRule.IMPORT_RULE) {
4253
- let importIndex = index2 + 1;
4254
- const url2 = item.href;
4255
- const deferred = fetchCSS(url2).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule2) => {
4256
- try {
4257
- sheet.insertRule(rule2, rule2.startsWith("@import") ? importIndex += 1 : sheet.cssRules.length);
4258
- } catch (error) {
4259
- console.error("Error inserting rule from remote css", {
4260
- rule: rule2,
4261
- error
4262
- });
4263
- }
4264
- })).catch((e) => {
4265
- console.error("Error loading remote css", e.toString());
4266
- });
4267
- deferreds.push(deferred);
4826
+ function isBlobUrl(u) {
4827
+ return typeof u === "string" && u.startsWith("blob:");
4828
+ }
4829
+ function parseSrcset(srcset) {
4830
+ return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => {
4831
+ const m = item.match(/^(\S+)(\s+.+)?$/);
4832
+ return m ? { url: m[1], desc: m[2] || "" } : null;
4833
+ }).filter(Boolean);
4834
+ }
4835
+ function stringifySrcset(parts2) {
4836
+ return parts2.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", ");
4837
+ }
4838
+ async function resolveBlobUrlsInTree(root2) {
4839
+ if (!root2) return;
4840
+ const imgs = root2.querySelectorAll ? root2.querySelectorAll("img") : [];
4841
+ for (const img of imgs) {
4842
+ try {
4843
+ const srcAttr = img.getAttribute("src");
4844
+ const effective = srcAttr || img.currentSrc || "";
4845
+ if (isBlobUrl(effective)) {
4846
+ const data = await blobUrlToDataUrl(effective);
4847
+ img.setAttribute("src", data);
4848
+ }
4849
+ const srcset = img.getAttribute("srcset");
4850
+ if (srcset && srcset.includes("blob:")) {
4851
+ const parts2 = parseSrcset(srcset);
4852
+ let changed = false;
4853
+ for (const p of parts2) {
4854
+ if (isBlobUrl(p.url)) {
4855
+ try {
4856
+ p.url = await blobUrlToDataUrl(p.url);
4857
+ changed = true;
4858
+ } catch {
4859
+ }
4268
4860
  }
4269
- });
4270
- } catch (e) {
4271
- const inline = styleSheets2.find((a) => a.href == null) || document.styleSheets[0];
4272
- if (sheet.href != null) {
4273
- deferreds.push(fetchCSS(sheet.href).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule2) => {
4274
- inline.insertRule(rule2, inline.cssRules.length);
4275
- })).catch((err) => {
4276
- console.error("Error loading remote stylesheet", err);
4277
- }));
4278
4861
  }
4279
- console.error("Error inlining remote css file", e);
4862
+ if (changed) img.setAttribute("srcset", stringifySrcset(parts2));
4280
4863
  }
4864
+ } catch {
4281
4865
  }
4282
- });
4283
- return Promise.all(deferreds).then(() => {
4284
- styleSheets2.forEach((sheet) => {
4285
- if ("cssRules" in sheet) {
4286
- try {
4287
- toArray(sheet.cssRules || []).forEach((item) => {
4288
- ret.push(item);
4289
- });
4290
- } catch (e) {
4291
- console.error(`Error while reading CSS rules from ${sheet.href}`, e);
4866
+ }
4867
+ const svgImages = root2.querySelectorAll ? root2.querySelectorAll("image") : [];
4868
+ for (const node2 of svgImages) {
4869
+ try {
4870
+ const XLINK_NS = "http://www.w3.org/1999/xlink";
4871
+ const href = node2.getAttribute("href") || node2.getAttributeNS?.(XLINK_NS, "href");
4872
+ if (isBlobUrl(href)) {
4873
+ const d = await blobUrlToDataUrl(href);
4874
+ node2.setAttribute("href", d);
4875
+ node2.removeAttributeNS?.(XLINK_NS, "href");
4876
+ }
4877
+ } catch {
4878
+ }
4879
+ }
4880
+ const styled = root2.querySelectorAll ? root2.querySelectorAll("[style*='blob:']") : [];
4881
+ for (const el of styled) {
4882
+ try {
4883
+ const styleText = el.getAttribute("style");
4884
+ if (styleText && styleText.includes("blob:")) {
4885
+ const replaced = await replaceBlobUrlsInCssText(styleText);
4886
+ el.setAttribute("style", replaced);
4887
+ }
4888
+ } catch {
4889
+ }
4890
+ }
4891
+ const styleTags = root2.querySelectorAll ? root2.querySelectorAll("style") : [];
4892
+ for (const s of styleTags) {
4893
+ try {
4894
+ const css = s.textContent || "";
4895
+ if (css.includes("blob:")) {
4896
+ s.textContent = await replaceBlobUrlsInCssText(css);
4897
+ }
4898
+ } catch {
4899
+ }
4900
+ }
4901
+ const urlAttrs = ["poster"];
4902
+ for (const attr of urlAttrs) {
4903
+ const nodes = root2.querySelectorAll ? root2.querySelectorAll(`[${attr}^='blob:']`) : [];
4904
+ for (const n2 of nodes) {
4905
+ try {
4906
+ const u = n2.getAttribute(attr);
4907
+ if (isBlobUrl(u)) {
4908
+ n2.setAttribute(attr, await blobUrlToDataUrl(u));
4292
4909
  }
4910
+ } catch {
4293
4911
  }
4294
- });
4295
- return ret;
4296
- });
4297
- }
4298
- function getWebFontRules(cssRules) {
4299
- return cssRules.filter((rule2) => rule2.type === CSSRule.FONT_FACE_RULE).filter((rule2) => shouldEmbed(rule2.style.getPropertyValue("src")));
4300
- }
4301
- async function parseWebFontRules(node2, options) {
4302
- if (node2.ownerDocument == null) {
4303
- throw new Error("Provided element is not within a Document");
4912
+ }
4304
4913
  }
4305
- const styleSheets2 = toArray(node2.ownerDocument.styleSheets);
4306
- const cssRules = await getCSSRules(styleSheets2, options);
4307
- return getWebFontRules(cssRules);
4308
4914
  }
4309
- function normalizeFontFamily(font) {
4310
- return font.trim().replace(/["']/g, "");
4311
- }
4312
- function getUsedFonts(node2) {
4313
- const fonts = /* @__PURE__ */ new Set();
4314
- function traverse(node3) {
4315
- const fontFamily = node3.style.fontFamily || getComputedStyle(node3).fontFamily;
4316
- fontFamily.split(",").forEach((font) => {
4317
- fonts.add(normalizeFontFamily(font));
4318
- });
4319
- Array.from(node3.children).forEach((child) => {
4320
- if (child instanceof HTMLElement) {
4321
- traverse(child);
4915
+ async function inlineImages(clone, options = {}) {
4916
+ const imgs = Array.from(clone.querySelectorAll("img"));
4917
+ const processImg = async (img) => {
4918
+ if (!img.getAttribute("src")) {
4919
+ const eff = img.currentSrc || img.src || "";
4920
+ if (eff) img.setAttribute("src", eff);
4921
+ }
4922
+ img.removeAttribute("srcset");
4923
+ img.removeAttribute("sizes");
4924
+ const src = img.src;
4925
+ try {
4926
+ const dataUrl = await fetchImage(src, { useProxy: options.useProxy });
4927
+ img.src = dataUrl;
4928
+ if (!img.width) img.width = img.naturalWidth || 100;
4929
+ if (!img.height) img.height = img.naturalHeight || 100;
4930
+ } catch {
4931
+ const fallback = document.createElement("div");
4932
+ fallback.style = `width: ${img.width || 100}px; height: ${img.height || 100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${img.height || 100}px; color: #666; font-size: 12px;`;
4933
+ fallback.innerText = "img";
4934
+ img.replaceWith(fallback);
4935
+ }
4936
+ };
4937
+ for (let i = 0; i < imgs.length; i += 4) {
4938
+ const group = imgs.slice(i, i + 4).map(processImg);
4939
+ await Promise.allSettled(group);
4940
+ }
4941
+ }
4942
+ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
4943
+ const queue = [[source, clone]];
4944
+ const imageProps = [
4945
+ "background-image",
4946
+ "mask",
4947
+ "mask-image",
4948
+ "-webkit-mask-image",
4949
+ "mask-source",
4950
+ "mask-box-image-source",
4951
+ "mask-border-source",
4952
+ "-webkit-mask-box-image-source",
4953
+ "border-image",
4954
+ "border-image-source",
4955
+ "border-image-slice",
4956
+ "border-image-width",
4957
+ "border-image-outset",
4958
+ "border-image-repeat"
4959
+ ];
4960
+ while (queue.length) {
4961
+ const [srcNode, cloneNode2] = queue.shift();
4962
+ const style = styleCache.get(srcNode) || getStyle(srcNode);
4963
+ if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
4964
+ const hasBorderImage = (() => {
4965
+ const bi = style.getPropertyValue("border-image");
4966
+ const bis = style.getPropertyValue("border-image-source");
4967
+ return bi && bi !== "none" || bis && bis !== "none";
4968
+ })();
4969
+ for (const prop of imageProps) {
4970
+ if (["border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat"].includes(prop) && !hasBorderImage) {
4971
+ continue;
4322
4972
  }
4973
+ const val = style.getPropertyValue(prop);
4974
+ if (!val || val === "none") continue;
4975
+ const splits = splitBackgroundImage(val);
4976
+ const inlined = await Promise.all(
4977
+ splits.map((entry) => inlineSingleBackgroundEntry(entry, options))
4978
+ );
4979
+ if (inlined.some((p) => p && p !== "none" && !/^url\(undefined/.test(p))) {
4980
+ cloneNode2.style.setProperty(prop, inlined.join(", "));
4981
+ }
4982
+ }
4983
+ const bgColor = style.getPropertyValue("background-color");
4984
+ if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") {
4985
+ cloneNode2.style.backgroundColor = bgColor;
4986
+ }
4987
+ const sChildren = Array.from(srcNode.children);
4988
+ const cChildren = Array.from(cloneNode2.children);
4989
+ for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
4990
+ queue.push([sChildren[i], cChildren[i]]);
4991
+ }
4992
+ }
4993
+ }
4994
+ async function captureDOM(element, options = {}) {
4995
+ if (!element) throw new Error("Element cannot be null or undefined");
4996
+ cache.reset();
4997
+ const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "", localFonts = [] } = options;
4998
+ let clone, classCSS, styleCache;
4999
+ let fontsCSS = "";
5000
+ let baseCSS = "";
5001
+ let dataURL;
5002
+ let svgString;
5003
+ ({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts, options));
5004
+ await new Promise((resolve2) => {
5005
+ idle(async () => {
5006
+ await inlineImages(clone, options);
5007
+ resolve2();
5008
+ }, { fast });
5009
+ });
5010
+ await new Promise((resolve2) => {
5011
+ idle(async () => {
5012
+ await inlineBackgroundImages(element, clone, styleCache, options);
5013
+ resolve2();
5014
+ }, { fast });
5015
+ });
5016
+ if (embedFonts) {
5017
+ await new Promise((resolve2) => {
5018
+ idle(async () => {
5019
+ fontsCSS = await embedCustomFonts({ localFonts, useProxy });
5020
+ resolve2();
5021
+ }, { fast });
4323
5022
  });
4324
5023
  }
4325
- traverse(node2);
4326
- return fonts;
4327
- }
4328
- async function getWebFontCSS(node2, options) {
4329
- const rules = await parseWebFontRules(node2, options);
4330
- const usedFonts = getUsedFonts(node2);
4331
- const cssTexts = await Promise.all(rules.filter((rule2) => usedFonts.has(normalizeFontFamily(rule2.style.fontFamily))).map((rule2) => {
4332
- const baseUrl = rule2.parentStyleSheet ? rule2.parentStyleSheet.href : null;
4333
- return embedResources(rule2.cssText, baseUrl, options);
4334
- }));
4335
- return cssTexts.join("\n");
4336
- }
4337
- async function embedWebFonts(clonedNode, options) {
4338
- const cssText = options.fontEmbedCSS != null ? options.fontEmbedCSS : options.skipFonts ? null : await getWebFontCSS(clonedNode, options);
4339
- if (cssText) {
4340
- const styleNode = document.createElement("style");
4341
- const sytleContent = document.createTextNode(cssText);
4342
- styleNode.appendChild(sytleContent);
4343
- if (clonedNode.firstChild) {
4344
- clonedNode.insertBefore(styleNode, clonedNode.firstChild);
5024
+ if (compress) {
5025
+ const usedTags = collectUsedTagNames(clone).sort();
5026
+ const tagKey = usedTags.join(",");
5027
+ if (cache.baseStyle.has(tagKey)) {
5028
+ baseCSS = cache.baseStyle.get(tagKey);
4345
5029
  } else {
4346
- clonedNode.appendChild(styleNode);
5030
+ await new Promise((resolve2) => {
5031
+ idle(() => {
5032
+ baseCSS = generateDedupedBaseCSS(usedTags);
5033
+ cache.baseStyle.set(tagKey, baseCSS);
5034
+ resolve2();
5035
+ }, { fast });
5036
+ });
4347
5037
  }
4348
5038
  }
5039
+ await new Promise((resolve2) => {
5040
+ idle(() => {
5041
+ const rect = element.getBoundingClientRect();
5042
+ let w = rect.width;
5043
+ let h = rect.height;
5044
+ const hasW = Number.isFinite(options.width);
5045
+ const hasH = Number.isFinite(options.height);
5046
+ const hasScale = typeof scale === "number" && scale !== 1;
5047
+ if (!hasScale) {
5048
+ const aspect = rect.width / rect.height;
5049
+ if (hasW && hasH) {
5050
+ w = options.width;
5051
+ h = options.height;
5052
+ } else if (hasW) {
5053
+ w = options.width;
5054
+ h = w / aspect;
5055
+ } else if (hasH) {
5056
+ h = options.height;
5057
+ w = h * aspect;
5058
+ }
5059
+ }
5060
+ w = Math.ceil(w);
5061
+ h = Math.ceil(h);
5062
+ clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
5063
+ clone.style.transformOrigin = "top left";
5064
+ if (!hasScale && (hasW || hasH)) {
5065
+ const originalW = rect.width;
5066
+ const originalH = rect.height;
5067
+ const scaleX = w / originalW;
5068
+ const scaleY = h / originalH;
5069
+ const existingTransform = clone.style.transform || "";
5070
+ const scaleTransform = `scale(${scaleX}, ${scaleY})`;
5071
+ clone.style.transform = `${scaleTransform} ${existingTransform}`.trim();
5072
+ }
5073
+ const svgNS = "http://www.w3.org/2000/svg";
5074
+ const fo = document.createElementNS(svgNS, "foreignObject");
5075
+ fo.setAttribute("width", "100%");
5076
+ fo.setAttribute("height", "100%");
5077
+ const styleTag = document.createElement("style");
5078
+ styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;}" + classCSS;
5079
+ fo.appendChild(styleTag);
5080
+ fo.appendChild(clone);
5081
+ const serializer = new XMLSerializer();
5082
+ const foString = serializer.serializeToString(fo);
5083
+ const svgHeader = `<svg xmlns="${svgNS}" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`;
5084
+ const svgFooter = "</svg>";
5085
+ svgString = svgHeader + foString + svgFooter;
5086
+ dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
5087
+ resolve2();
5088
+ }, { fast });
5089
+ });
5090
+ const sandbox = document.getElementById("snapdom-sandbox");
5091
+ if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
5092
+ return dataURL;
4349
5093
  }
4350
- async function toSvg(node2, options = {}) {
4351
- const { width, height } = getImageSize(node2, options);
4352
- const clonedNode = await cloneNode$3(node2, options, true);
4353
- await embedWebFonts(clonedNode, options);
4354
- await embedImages(clonedNode, options);
4355
- applyStyle(clonedNode, options);
4356
- const datauri = await nodeToDataURL(clonedNode, width, height);
4357
- return datauri;
4358
- }
4359
- async function toCanvas(node2, options = {}) {
4360
- const { width, height } = getImageSize(node2, options);
4361
- const svg = await toSvg(node2, options);
4362
- const img = await createImage(svg);
5094
+ async function toImg(url2, { scale = 1 } = {}) {
5095
+ const img = new Image();
5096
+ img.src = url2;
5097
+ await img.decode();
5098
+ if (scale !== 1) {
5099
+ img.style.width = `${img.naturalWidth * scale}px`;
5100
+ img.style.height = `${img.naturalHeight * scale}px`;
5101
+ }
5102
+ return img;
5103
+ }
5104
+ async function toCanvas(url2, { dpr = 1, scale = 1 } = {}) {
5105
+ const img = new Image();
5106
+ img.src = url2;
5107
+ img.crossOrigin = "anonymous";
5108
+ img.loading = "eager";
5109
+ img.decoding = "sync";
5110
+ const isSafariBrowser = isSafari();
5111
+ let appended = false;
5112
+ if (isSafariBrowser) {
5113
+ document.body.appendChild(img);
5114
+ appended = true;
5115
+ }
5116
+ await img.decode();
5117
+ if (isSafariBrowser) {
5118
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
5119
+ }
5120
+ if (img.width === 0 || img.height === 0) {
5121
+ if (appended) img.remove();
5122
+ throw new Error("Image failed to load or has no dimensions");
5123
+ }
5124
+ const width = img.naturalWidth * scale;
5125
+ const height = img.naturalHeight * scale;
4363
5126
  const canvas = document.createElement("canvas");
4364
- const context = canvas.getContext("2d");
4365
- const ratio = options.pixelRatio || getPixelRatio();
4366
- const canvasWidth = options.canvasWidth || width;
4367
- const canvasHeight = options.canvasHeight || height;
4368
- canvas.width = canvasWidth * ratio;
4369
- canvas.height = canvasHeight * ratio;
4370
- if (!options.skipAutoScale) {
4371
- checkCanvasDimensions(canvas);
4372
- }
4373
- canvas.style.width = `${canvasWidth}`;
4374
- canvas.style.height = `${canvasHeight}`;
4375
- if (options.backgroundColor) {
4376
- context.fillStyle = options.backgroundColor;
4377
- context.fillRect(0, 0, canvas.width, canvas.height);
4378
- }
4379
- context.drawImage(img, 0, 0, canvas.width, canvas.height);
5127
+ canvas.width = Math.ceil(width * dpr);
5128
+ canvas.height = Math.ceil(height * dpr);
5129
+ canvas.style.width = `${width}px`;
5130
+ canvas.style.height = `${height}px`;
5131
+ const ctx = canvas.getContext("2d");
5132
+ ctx.scale(dpr, dpr);
5133
+ ctx.drawImage(img, 0, 0, width, height);
5134
+ if (appended) img.remove();
4380
5135
  return canvas;
4381
5136
  }
4382
- async function toPng(node2, options = {}) {
4383
- const canvas = await toCanvas(node2, options);
4384
- return canvas.toDataURL();
5137
+ async function toBlob(url2, {
5138
+ type = "svg",
5139
+ scale = 1,
5140
+ backgroundColor = "#fff",
5141
+ quality
5142
+ } = {}) {
5143
+ const mime = {
5144
+ jpg: "image/jpeg",
5145
+ jpeg: "image/jpeg",
5146
+ png: "image/png",
5147
+ webp: "image/webp"
5148
+ }[type] || "image/png";
5149
+ if (type === "svg") {
5150
+ const svgText = decodeURIComponent(url2.split(",")[1]);
5151
+ return new Blob([svgText], { type: "image/svg+xml" });
5152
+ }
5153
+ const canvas = await createBackground(url2, { dpr: 1, scale }, backgroundColor);
5154
+ return new Promise((resolve2) => {
5155
+ canvas.toBlob((blob2) => resolve2(blob2), `${mime}`, quality);
5156
+ });
5157
+ }
5158
+ async function createBackground(url2, { dpr = 1, scale = 1 }, backgroundColor) {
5159
+ const baseCanvas = await toCanvas(url2, { dpr, scale });
5160
+ if (!backgroundColor) return baseCanvas;
5161
+ const temp = document.createElement("canvas");
5162
+ temp.width = baseCanvas.width;
5163
+ temp.height = baseCanvas.height;
5164
+ const ctx = temp.getContext("2d");
5165
+ ctx.fillStyle = backgroundColor;
5166
+ ctx.fillRect(0, 0, temp.width, temp.height);
5167
+ ctx.drawImage(baseCanvas, 0, 0);
5168
+ return temp;
5169
+ }
5170
+ async function toRasterImg(url2, { dpr = 1, scale = 1, backgroundColor, quality }, format = "png") {
5171
+ const defaultBg = ["jpg", "jpeg", "webp"].includes(format) ? "#fff" : void 0;
5172
+ const finalBg = backgroundColor ?? defaultBg;
5173
+ const canvas = await createBackground(url2, { dpr, scale }, finalBg);
5174
+ const img = new Image();
5175
+ img.src = canvas.toDataURL(`image/${format}`, quality);
5176
+ await img.decode();
5177
+ img.style.width = `${canvas.width / dpr}px`;
5178
+ img.style.height = `${canvas.height / dpr}px`;
5179
+ return img;
5180
+ }
5181
+ async function download(url2, { dpr = 1, scale = 1, backgroundColor, format = "png", filename = "snapDOM" } = {}) {
5182
+ if (format === "svg") {
5183
+ const blob2 = await toBlob(url2);
5184
+ const objectURL = URL.createObjectURL(blob2);
5185
+ const a2 = document.createElement("a");
5186
+ a2.href = objectURL;
5187
+ a2.download = `${filename}.svg`;
5188
+ a2.click();
5189
+ URL.revokeObjectURL(objectURL);
5190
+ return;
5191
+ }
5192
+ const defaultBg = ["jpg", "jpeg", "webp"].includes(format) ? "#fff" : void 0;
5193
+ const finalBg = backgroundColor ?? defaultBg;
5194
+ const canvas = await createBackground(url2, { dpr, scale }, finalBg);
5195
+ const mime = {
5196
+ jpg: "image/jpeg",
5197
+ jpeg: "image/jpeg",
5198
+ png: "image/png",
5199
+ webp: "image/webp"
5200
+ }[format] || "image/png";
5201
+ const dataURL = canvas.toDataURL(mime);
5202
+ const a = document.createElement("a");
5203
+ a.href = dataURL;
5204
+ a.download = `${filename}.${format}`;
5205
+ a.click();
5206
+ }
5207
+ async function snapdom(element, options = {}) {
5208
+ options = { scale: 1, ...options };
5209
+ if (!element) throw new Error("Element cannot be null or undefined");
5210
+ if (options.iconFonts) {
5211
+ extendIconFonts(options.iconFonts);
5212
+ }
5213
+ return await snapdom.capture(element, options);
5214
+ }
5215
+ snapdom.capture = async (el, options = {}) => {
5216
+ const url2 = await captureDOM(el, options);
5217
+ const dpr = options.dpr ?? (window.devicePixelRatio || 1);
5218
+ const scale = options.scale || 1;
5219
+ return {
5220
+ url: url2,
5221
+ options,
5222
+ toRaw: () => url2,
5223
+ toImg: (opts = {}) => toImg(url2, { scale, ...opts }),
5224
+ toCanvas: (opts = {}) => toCanvas(url2, { dpr, scale, ...opts }),
5225
+ toBlob: (opts = {}) => toBlob(url2, { scale, ...opts }),
5226
+ toPng: (opts = {}) => toRasterImg(url2, { dpr, scale, ...opts }, "png"),
5227
+ toJpg: (opts = {}) => toRasterImg(url2, { dpr, scale, ...opts }, "jpeg"),
5228
+ toWebp: (opts = {}) => toRasterImg(url2, { dpr, scale, ...opts }, "webp"),
5229
+ download: ({ format = "png", filename = "snapDOM", backgroundColor, ...opts } = {}) => download(url2, { dpr, scale, format, filename, backgroundColor, ...opts })
5230
+ };
5231
+ };
5232
+ snapdom.toRaw = async (el, options) => (await snapdom.capture(el, options)).toRaw();
5233
+ snapdom.toImg = async (el, options) => (await snapdom.capture(el, options)).toImg();
5234
+ snapdom.toCanvas = async (el, options) => (await snapdom.capture(el, options)).toCanvas();
5235
+ snapdom.toBlob = async (el, options) => (await snapdom.capture(el, options)).toBlob(options);
5236
+ snapdom.toPng = async (el, options) => (await snapdom.capture(el, options)).toPng(options);
5237
+ snapdom.toJpg = async (el, options) => (await snapdom.capture(el, options)).toJpg(options);
5238
+ snapdom.toWebp = async (el, options) => (await snapdom.capture(el, options)).toWebp(options);
5239
+ snapdom.download = async (el, options = {}) => {
5240
+ const {
5241
+ format = "png",
5242
+ filename = "capture",
5243
+ backgroundColor,
5244
+ ...rest
5245
+ } = options;
5246
+ const capture = await snapdom.capture(el, rest);
5247
+ return await capture.download({ format, filename, backgroundColor });
5248
+ };
5249
+ const MAX_WIDTH = 1920;
5250
+ const MAX_HEIGHT = 1080;
5251
+ function getScaledDimensions(originalWidth, originalHeight) {
5252
+ if (originalWidth <= MAX_WIDTH && originalHeight <= MAX_HEIGHT) {
5253
+ return { width: originalWidth, height: originalHeight };
5254
+ }
5255
+ const widthScale = MAX_WIDTH / originalWidth;
5256
+ const heightScale = MAX_HEIGHT / originalHeight;
5257
+ const scale = Math.min(widthScale, heightScale);
5258
+ return {
5259
+ width: Math.round(originalWidth * scale),
5260
+ height: Math.round(originalHeight * scale)
5261
+ };
4385
5262
  }
4386
5263
  async function captureScreenshot(domBody) {
4387
- const height = document.documentElement.scrollHeight;
4388
- const width = document.documentElement.scrollWidth;
5264
+ const originalHeight = document.documentElement.scrollHeight;
5265
+ const originalWidth = document.documentElement.scrollWidth;
5266
+ const { width, height } = getScaledDimensions(originalWidth, originalHeight);
4389
5267
  const styles = Array.from(document.styleSheets).map((styleSheet) => {
4390
5268
  try {
4391
5269
  return Array.from(styleSheet.cssRules).map((rule2) => rule2.cssText).join("");
@@ -4396,37 +5274,17 @@ async function captureScreenshot(domBody) {
4396
5274
  const styleElement = document.createElement("style");
4397
5275
  styleElement.textContent = styles;
4398
5276
  domBody.appendChild(styleElement);
4399
- const filter = (node2) => {
4400
- const exclusionClasses = ["bf-nocapture"];
4401
- return !exclusionClasses.some((classname) => node2.classList?.contains(classname));
4402
- };
4403
- const image = await toPng(domBody, {
4404
- quality: 0.7,
4405
- // Set quality to 0.7
4406
- width,
4407
- // Set width to the scroll width
4408
- height,
4409
- // Set height to the scroll height
4410
- style: {
4411
- transform: "scale(1)"
4412
- // Set scale to 1 to avoid scaling
4413
- },
4414
- skipFonts: true,
4415
- // Avoid embedding web fonts to bypass SecurityError
4416
- cacheBust: true,
4417
- // Prevent caching
4418
- backgroundColor: "white",
4419
- // Set background color to white
4420
- fetchRequestInit: {
4421
- mode: "cors"
4422
- // Enable CORS
4423
- },
4424
- filter,
4425
- imagePlaceholder: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFdwI2QJIhFwAAAABJRU5ErkJggg=="
4426
- // Transparent placeholder for CORS-blocked images
4427
- });
4428
- domBody.removeChild(styleElement);
4429
- return image;
5277
+ try {
5278
+ const canvas = await snapdom.toCanvas(domBody, {
5279
+ quality: 0.7,
5280
+ width,
5281
+ height,
5282
+ exclude: [".bf-nocapture"]
5283
+ });
5284
+ return canvas.toDataURL("image/webp", 0.7);
5285
+ } finally {
5286
+ domBody.removeChild(styleElement);
5287
+ }
4430
5288
  }
4431
5289
  const _wellKnownNames = [
4432
5290
  "aaron",
@@ -16913,7 +17771,7 @@ function serializeNode(n2, options) {
16913
17771
  maskTextFn,
16914
17772
  maskInputFn,
16915
17773
  dataURLOptions = {},
16916
- inlineImages,
17774
+ inlineImages: inlineImages2,
16917
17775
  recordCanvas,
16918
17776
  keepIframeSrcFn,
16919
17777
  newlyAddedElement = false,
@@ -16952,7 +17810,7 @@ function serializeNode(n2, options) {
16952
17810
  maskInputOptions,
16953
17811
  maskInputFn,
16954
17812
  dataURLOptions,
16955
- inlineImages,
17813
+ inlineImages: inlineImages2,
16956
17814
  recordCanvas,
16957
17815
  keepIframeSrcFn,
16958
17816
  newlyAddedElement,
@@ -17020,7 +17878,7 @@ function serializeElementNode(n2, options) {
17020
17878
  maskInputOptions = {},
17021
17879
  maskInputFn,
17022
17880
  dataURLOptions = {},
17023
- inlineImages,
17881
+ inlineImages: inlineImages2,
17024
17882
  recordCanvas,
17025
17883
  keepIframeSrcFn,
17026
17884
  newlyAddedElement = false,
@@ -17117,7 +17975,7 @@ function serializeElementNode(n2, options) {
17117
17975
  }
17118
17976
  }
17119
17977
  }
17120
- if (tagName === "img" && inlineImages) {
17978
+ if (tagName === "img" && inlineImages2) {
17121
17979
  if (!canvasService) {
17122
17980
  canvasService = doc.createElement("canvas");
17123
17981
  canvasCtx = canvasService.getContext("2d");
@@ -17256,7 +18114,7 @@ function serializeNodeWithId(n2, options) {
17256
18114
  maskInputFn,
17257
18115
  slimDOMOptions,
17258
18116
  dataURLOptions = {},
17259
- inlineImages = false,
18117
+ inlineImages: inlineImages2 = false,
17260
18118
  recordCanvas = false,
17261
18119
  onSerialize,
17262
18120
  onIframeLoad,
@@ -17289,7 +18147,7 @@ function serializeNodeWithId(n2, options) {
17289
18147
  maskTextFn,
17290
18148
  maskInputFn,
17291
18149
  dataURLOptions,
17292
- inlineImages,
18150
+ inlineImages: inlineImages2,
17293
18151
  recordCanvas,
17294
18152
  keepIframeSrcFn,
17295
18153
  newlyAddedElement,
@@ -17342,7 +18200,7 @@ function serializeNodeWithId(n2, options) {
17342
18200
  maskInputFn,
17343
18201
  slimDOMOptions,
17344
18202
  dataURLOptions,
17345
- inlineImages,
18203
+ inlineImages: inlineImages2,
17346
18204
  recordCanvas,
17347
18205
  preserveWhiteSpace,
17348
18206
  onSerialize,
@@ -17401,7 +18259,7 @@ function serializeNodeWithId(n2, options) {
17401
18259
  maskInputFn,
17402
18260
  slimDOMOptions,
17403
18261
  dataURLOptions,
17404
- inlineImages,
18262
+ inlineImages: inlineImages2,
17405
18263
  recordCanvas,
17406
18264
  preserveWhiteSpace,
17407
18265
  onSerialize,
@@ -17442,7 +18300,7 @@ function serializeNodeWithId(n2, options) {
17442
18300
  maskInputFn,
17443
18301
  slimDOMOptions,
17444
18302
  dataURLOptions,
17445
- inlineImages,
18303
+ inlineImages: inlineImages2,
17446
18304
  recordCanvas,
17447
18305
  preserveWhiteSpace,
17448
18306
  onSerialize,
@@ -17473,7 +18331,7 @@ function snapshot(n2, options) {
17473
18331
  maskTextClass = "rr-mask",
17474
18332
  maskTextSelector = null,
17475
18333
  inlineStylesheet = true,
17476
- inlineImages = false,
18334
+ inlineImages: inlineImages2 = false,
17477
18335
  recordCanvas = false,
17478
18336
  maskAllInputs = false,
17479
18337
  maskTextFn,
@@ -17538,7 +18396,7 @@ function snapshot(n2, options) {
17538
18396
  maskInputFn,
17539
18397
  slimDOMOptions,
17540
18398
  dataURLOptions,
17541
- inlineImages,
18399
+ inlineImages: inlineImages2,
17542
18400
  recordCanvas,
17543
18401
  preserveWhiteSpace,
17544
18402
  onSerialize,
@@ -27864,7 +28722,7 @@ function record(options = {}) {
27864
28722
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
27865
28723
  userTriggeredOnInput = false,
27866
28724
  collectFonts = false,
27867
- inlineImages = false,
28725
+ inlineImages: inlineImages2 = false,
27868
28726
  plugins,
27869
28727
  keepIframeSrcFn = () => false,
27870
28728
  ignoreCSSAttributes = /* @__PURE__ */ new Set([]),
@@ -28048,7 +28906,7 @@ function record(options = {}) {
28048
28906
  maskTextFn,
28049
28907
  maskInputFn,
28050
28908
  recordCanvas,
28051
- inlineImages,
28909
+ inlineImages: inlineImages2,
28052
28910
  sampling,
28053
28911
  slimDOMOptions,
28054
28912
  iframeManager,
@@ -28090,7 +28948,7 @@ function record(options = {}) {
28090
28948
  slimDOM: slimDOMOptions,
28091
28949
  dataURLOptions,
28092
28950
  recordCanvas,
28093
- inlineImages,
28951
+ inlineImages: inlineImages2,
28094
28952
  onSerialize: (n2) => {
28095
28953
  if (isSerializedIframe$1(n2, mirror)) {
28096
28954
  iframeManager.addIframe(n2);
@@ -28224,7 +29082,7 @@ function record(options = {}) {
28224
29082
  sampling,
28225
29083
  recordDOM,
28226
29084
  recordCanvas,
28227
- inlineImages,
29085
+ inlineImages: inlineImages2,
28228
29086
  userTriggeredOnInput,
28229
29087
  collectFonts,
28230
29088
  doc,
@@ -37509,7 +38367,7 @@ function toCamelCase(str) {
37509
38367
  ($1) => $1.toUpperCase().replace("-", "").replace("_", "")
37510
38368
  );
37511
38369
  }
37512
- const VERSION = "0.0.19";
38370
+ const VERSION = "0.0.20";
37513
38371
  class Tracker extends TrackerSdk {
37514
38372
  // 750KB
37515
38373
  constructor(options) {