@brainfish-ai/web-tracker 0.0.18 → 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.
- package/dist/tracker.cjs.js +1 -1
- package/dist/tracker.cjs.js.gz +0 -0
- package/dist/tracker.cjs.js.map +1 -1
- package/dist/tracker.es.js +1590 -730
- package/dist/tracker.es.js.gz +0 -0
- package/dist/tracker.es.js.map +1 -1
- package/dist/tracker.js +1 -1
- package/dist/tracker.js.gz +0 -0
- package/dist/tracker.js.map +1 -1
- package/package.json +5 -5
package/dist/tracker.es.js
CHANGED
|
@@ -43,7 +43,7 @@ const encodeBlobAsBase64 = (data, callback) => {
|
|
|
43
43
|
};
|
|
44
44
|
return fileReader.readAsDataURL(data);
|
|
45
45
|
};
|
|
46
|
-
function toArray
|
|
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
|
|
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
|
|
60
|
+
return callback(toArray(packet.data));
|
|
61
61
|
}
|
|
62
62
|
encodePacket(packet, false, (encoded) => {
|
|
63
63
|
if (!TEXT_ENCODER) {
|
|
@@ -3518,7 +3518,7 @@ class TrackerSdk {
|
|
|
3518
3518
|
...properties
|
|
3519
3519
|
};
|
|
3520
3520
|
}
|
|
3521
|
-
async track(name, properties) {
|
|
3521
|
+
async track(name, properties, widgetSourceOverrides) {
|
|
3522
3522
|
return this.send({
|
|
3523
3523
|
type: "event.track",
|
|
3524
3524
|
payload: {
|
|
@@ -3526,7 +3526,9 @@ class TrackerSdk {
|
|
|
3526
3526
|
userId: properties?.userId ?? this.userId,
|
|
3527
3527
|
properties: {
|
|
3528
3528
|
...this.global ?? {},
|
|
3529
|
-
...properties ?? {}
|
|
3529
|
+
...properties ?? {},
|
|
3530
|
+
...widgetSourceOverrides ? { _bfOverrides: widgetSourceOverrides } : {}
|
|
3531
|
+
// only include jwt overrides if they are provided
|
|
3530
3532
|
}
|
|
3531
3533
|
}
|
|
3532
3534
|
});
|
|
@@ -3609,781 +3611,1659 @@ class TrackerSdk {
|
|
|
3609
3611
|
this.queue = [];
|
|
3610
3612
|
}
|
|
3611
3613
|
}
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
const
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
)
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
}
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
const
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
}
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
}
|
|
3678
|
-
function getImageSize(targetNode, options = {}) {
|
|
3679
|
-
const width = options.width || getNodeWidth(targetNode);
|
|
3680
|
-
const height = options.height || getNodeHeight(targetNode);
|
|
3681
|
-
return { width, height };
|
|
3682
|
-
}
|
|
3683
|
-
function getPixelRatio() {
|
|
3684
|
-
let ratio;
|
|
3685
|
-
let FINAL_PROCESS;
|
|
3686
|
-
try {
|
|
3687
|
-
FINAL_PROCESS = process;
|
|
3688
|
-
} catch (e) {
|
|
3689
|
-
}
|
|
3690
|
-
const val = FINAL_PROCESS && FINAL_PROCESS.env ? FINAL_PROCESS.env.devicePixelRatio : null;
|
|
3691
|
-
if (val) {
|
|
3692
|
-
ratio = parseInt(val, 10);
|
|
3693
|
-
if (Number.isNaN(ratio)) {
|
|
3694
|
-
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
|
+
}
|
|
3695
3680
|
}
|
|
3696
3681
|
}
|
|
3697
|
-
return
|
|
3682
|
+
return entries.sort().join(";");
|
|
3698
3683
|
}
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
if (
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
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)})`;
|
|
3713
3732
|
} else {
|
|
3714
|
-
|
|
3715
|
-
|
|
3733
|
+
const dataUrl = await fetchImage(encodedUrl, { useProxy: options.useProxy });
|
|
3734
|
+
cache.background.set(encodedUrl, dataUrl);
|
|
3735
|
+
return options.skipInline ? void 0 : `url("${dataUrl}")`;
|
|
3716
3736
|
}
|
|
3717
3737
|
}
|
|
3738
|
+
if (isGradient || entry === "none") {
|
|
3739
|
+
return entry;
|
|
3740
|
+
}
|
|
3741
|
+
return entry;
|
|
3718
3742
|
}
|
|
3719
|
-
function
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
};
|
|
3727
|
-
img.onerror = reject;
|
|
3728
|
-
img.crossOrigin = "anonymous";
|
|
3729
|
-
img.decoding = "async";
|
|
3730
|
-
img.src = url2;
|
|
3731
|
-
});
|
|
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
|
+
}
|
|
3732
3750
|
}
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
async function nodeToDataURL(node2, width, height) {
|
|
3737
|
-
const xmlns = "http://www.w3.org/2000/svg";
|
|
3738
|
-
const svg = document.createElementNS(xmlns, "svg");
|
|
3739
|
-
const foreignObject = document.createElementNS(xmlns, "foreignObject");
|
|
3740
|
-
svg.setAttribute("width", `${width}`);
|
|
3741
|
-
svg.setAttribute("height", `${height}`);
|
|
3742
|
-
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
3743
|
-
foreignObject.setAttribute("width", "100%");
|
|
3744
|
-
foreignObject.setAttribute("height", "100%");
|
|
3745
|
-
foreignObject.setAttribute("x", "0");
|
|
3746
|
-
foreignObject.setAttribute("y", "0");
|
|
3747
|
-
foreignObject.setAttribute("externalResourcesRequired", "true");
|
|
3748
|
-
svg.appendChild(foreignObject);
|
|
3749
|
-
foreignObject.appendChild(node2);
|
|
3750
|
-
return svgToDataURL(svg);
|
|
3751
|
-
}
|
|
3752
|
-
const isInstanceOfElement = (node2, instance) => {
|
|
3753
|
-
if (node2 instanceof instance)
|
|
3754
|
-
return true;
|
|
3755
|
-
const nodePrototype = Object.getPrototypeOf(node2);
|
|
3756
|
-
if (nodePrototype === null)
|
|
3757
|
-
return false;
|
|
3758
|
-
return nodePrototype.constructor.name === instance.name || isInstanceOfElement(nodePrototype, instance);
|
|
3759
|
-
};
|
|
3760
|
-
function formatCSSText(style) {
|
|
3761
|
-
const content = style.getPropertyValue("content");
|
|
3762
|
-
return `${style.cssText} content: '${content.replace(/'|"/g, "")}';`;
|
|
3763
|
-
}
|
|
3764
|
-
function formatCSSProperties(style, options) {
|
|
3765
|
-
return getStyleProperties(options).map((name) => {
|
|
3766
|
-
const value2 = style.getPropertyValue(name);
|
|
3767
|
-
const priority = style.getPropertyPriority(name);
|
|
3768
|
-
return `${name}: ${value2}${priority ? " !important" : ""};`;
|
|
3769
|
-
}).join(" ");
|
|
3770
|
-
}
|
|
3771
|
-
function getPseudoElementStyle(className, pseudo, style, options) {
|
|
3772
|
-
const selector = `.${className}:${pseudo}`;
|
|
3773
|
-
const cssText = style.cssText ? formatCSSText(style) : formatCSSProperties(style, options);
|
|
3774
|
-
return document.createTextNode(`${selector}{${cssText}}`);
|
|
3775
|
-
}
|
|
3776
|
-
function clonePseudoElement(nativeNode, clonedNode, pseudo, options) {
|
|
3777
|
-
const style = window.getComputedStyle(nativeNode, pseudo);
|
|
3778
|
-
const content = style.getPropertyValue("content");
|
|
3779
|
-
if (content === "" || content === "none") {
|
|
3780
|
-
return;
|
|
3751
|
+
function getStyle(el, pseudo = null) {
|
|
3752
|
+
if (!(el instanceof Element)) {
|
|
3753
|
+
return window.getComputedStyle(el, pseudo);
|
|
3781
3754
|
}
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
return;
|
|
3755
|
+
let map = cache.computedStyle.get(el);
|
|
3756
|
+
if (!map) {
|
|
3757
|
+
map = /* @__PURE__ */ new Map();
|
|
3758
|
+
cache.computedStyle.set(el, map);
|
|
3787
3759
|
}
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
}
|
|
3792
|
-
|
|
3793
|
-
clonePseudoElement(nativeNode, clonedNode, ":before", options);
|
|
3794
|
-
clonePseudoElement(nativeNode, clonedNode, ":after", options);
|
|
3795
|
-
}
|
|
3796
|
-
const WOFF = "application/font-woff";
|
|
3797
|
-
const JPEG = "image/jpeg";
|
|
3798
|
-
const mimes = {
|
|
3799
|
-
woff: WOFF,
|
|
3800
|
-
woff2: WOFF,
|
|
3801
|
-
ttf: "application/font-truetype",
|
|
3802
|
-
eot: "application/vnd.ms-fontobject",
|
|
3803
|
-
png: "image/png",
|
|
3804
|
-
jpg: JPEG,
|
|
3805
|
-
jpeg: JPEG,
|
|
3806
|
-
gif: "image/gif",
|
|
3807
|
-
tiff: "image/tiff",
|
|
3808
|
-
svg: "image/svg+xml",
|
|
3809
|
-
webp: "image/webp"
|
|
3810
|
-
};
|
|
3811
|
-
function getExtension(url2) {
|
|
3812
|
-
const match2 = /\.([^./]*?)$/g.exec(url2);
|
|
3813
|
-
return match2 ? match2[1] : "";
|
|
3814
|
-
}
|
|
3815
|
-
function getMimeType(url2) {
|
|
3816
|
-
const extension = getExtension(url2).toLowerCase();
|
|
3817
|
-
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);
|
|
3818
3765
|
}
|
|
3819
|
-
function
|
|
3820
|
-
|
|
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;
|
|
3821
3776
|
}
|
|
3822
|
-
function
|
|
3823
|
-
|
|
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;
|
|
3824
3783
|
}
|
|
3825
|
-
function
|
|
3826
|
-
|
|
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
|
+
}
|
|
3827
3799
|
}
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
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
|
+
}
|
|
3832
3810
|
}
|
|
3833
|
-
const
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
}
|
|
3841
|
-
|
|
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
|
+
});
|
|
3842
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);
|
|
3843
3912
|
};
|
|
3844
|
-
|
|
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(", ")})`;
|
|
3845
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, " ");
|
|
3846
3991
|
}
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
if (/ttf|otf|eot|woff2?/i.test(key)) {
|
|
3854
|
-
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;
|
|
3855
3998
|
}
|
|
3856
|
-
return contentType ? `[${contentType}]${key}` : key;
|
|
3857
3999
|
}
|
|
3858
|
-
|
|
3859
|
-
const
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
if (!contentType) {
|
|
3870
|
-
contentType = res.headers.get("Content-Type") || "";
|
|
3871
|
-
}
|
|
3872
|
-
return getContentFromDataUrl(result2);
|
|
3873
|
-
});
|
|
3874
|
-
dataURL = makeDataUrl(content, contentType);
|
|
3875
|
-
} catch (error) {
|
|
3876
|
-
dataURL = options.imagePlaceholder || "";
|
|
3877
|
-
let msg = `Failed to fetch resource: ${resourceUrl}`;
|
|
3878
|
-
if (error) {
|
|
3879
|
-
msg = typeof error === "string" ? error : error.message;
|
|
3880
|
-
}
|
|
3881
|
-
if (msg) {
|
|
3882
|
-
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;
|
|
3883
4011
|
}
|
|
3884
4012
|
}
|
|
3885
|
-
|
|
3886
|
-
return
|
|
4013
|
+
parts2.push(bg.slice(lastIndex).trim());
|
|
4014
|
+
return parts2;
|
|
3887
4015
|
}
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
const ctx = canvas.getContext("2d");
|
|
3899
|
-
canvas.width = video.clientWidth;
|
|
3900
|
-
canvas.height = video.clientHeight;
|
|
3901
|
-
ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
3902
|
-
const dataURL2 = canvas.toDataURL();
|
|
3903
|
-
return createImage(dataURL2);
|
|
3904
|
-
}
|
|
3905
|
-
const poster = video.poster;
|
|
3906
|
-
const contentType = getMimeType(poster);
|
|
3907
|
-
const dataURL = await resourceToDataURL(poster, contentType, options);
|
|
3908
|
-
return createImage(dataURL);
|
|
3909
|
-
}
|
|
3910
|
-
async function cloneIFrameElement(iframe, options) {
|
|
3911
|
-
var _a2;
|
|
3912
|
-
try {
|
|
3913
|
-
if ((_a2 = iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument) === null || _a2 === void 0 ? void 0 : _a2.body) {
|
|
3914
|
-
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";
|
|
3915
4026
|
}
|
|
3916
|
-
|
|
4027
|
+
result2[prop] = val;
|
|
4028
|
+
}
|
|
4029
|
+
if (computedVisibility === "hidden") {
|
|
4030
|
+
result2.opacity = "0";
|
|
3917
4031
|
}
|
|
3918
|
-
return
|
|
4032
|
+
return result2;
|
|
3919
4033
|
}
|
|
3920
|
-
|
|
3921
|
-
if (
|
|
3922
|
-
|
|
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));
|
|
3923
4038
|
}
|
|
3924
|
-
|
|
3925
|
-
|
|
4039
|
+
const style = cache2.get(source);
|
|
4040
|
+
if (!snapshotCache.has(source)) {
|
|
4041
|
+
const snapshot22 = snapshotComputedStyleFull(style);
|
|
4042
|
+
snapshotCache.set(source, snapshot22);
|
|
3926
4043
|
}
|
|
3927
|
-
|
|
3928
|
-
|
|
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;
|
|
3929
4049
|
}
|
|
3930
|
-
|
|
4050
|
+
const tagName = source.tagName?.toLowerCase() || "div";
|
|
4051
|
+
const key = getStyleKey(snapshot2, tagName, compress);
|
|
4052
|
+
snapshotKeyCache.set(hash, key);
|
|
4053
|
+
styleMap.set(clone, key);
|
|
3931
4054
|
}
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
} else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && ((_a2 = nativeNode.contentDocument) === null || _a2 === void 0 ? void 0 : _a2.body)) {
|
|
3943
|
-
children = toArray(nativeNode.contentDocument.body.childNodes);
|
|
3944
|
-
} else {
|
|
3945
|
-
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 {
|
|
3946
4065
|
}
|
|
3947
|
-
|
|
3948
|
-
|
|
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);
|
|
3949
4073
|
}
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
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
|
+
}
|
|
3953
4095
|
}
|
|
3954
|
-
}), Promise.resolve());
|
|
3955
|
-
return clonedNode;
|
|
3956
|
-
}
|
|
3957
|
-
function cloneCSSStyle(nativeNode, clonedNode, options) {
|
|
3958
|
-
const targetStyle = clonedNode.style;
|
|
3959
|
-
if (!targetStyle) {
|
|
3960
|
-
return;
|
|
3961
4096
|
}
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
if (name === "font-size" && value2.endsWith("px")) {
|
|
3970
|
-
const reducedFont = Math.floor(parseFloat(value2.substring(0, value2.length - 2))) - 0.1;
|
|
3971
|
-
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;
|
|
3972
4104
|
}
|
|
3973
|
-
|
|
3974
|
-
|
|
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
|
+
}
|
|
3975
4176
|
}
|
|
3976
|
-
|
|
3977
|
-
|
|
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);
|
|
3978
4189
|
}
|
|
3979
|
-
|
|
3980
|
-
}
|
|
4190
|
+
clone.appendChild(shadowFrag);
|
|
4191
|
+
}
|
|
3981
4192
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
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;
|
|
3986
4202
|
}
|
|
3987
|
-
|
|
3988
|
-
|
|
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);
|
|
3989
4207
|
}
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
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
|
+
}
|
|
3997
4216
|
}
|
|
3998
4217
|
}
|
|
4218
|
+
return clone;
|
|
3999
4219
|
}
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
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
|
+
}
|
|
4006
4244
|
}
|
|
4007
|
-
return clonedNode;
|
|
4008
4245
|
}
|
|
4009
|
-
|
|
4010
|
-
const
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
const processedDefs = {};
|
|
4015
|
-
for (let i = 0; i < uses.length; i++) {
|
|
4016
|
-
const use = uses[i];
|
|
4017
|
-
const id = use.getAttribute("xlink:href");
|
|
4018
|
-
if (id) {
|
|
4019
|
-
const exist = clone.querySelector(id);
|
|
4020
|
-
const definition = document.querySelector(id);
|
|
4021
|
-
if (!exist && definition && !processedDefs[id]) {
|
|
4022
|
-
processedDefs[id] = await cloneNode$3(definition, options, true);
|
|
4023
|
-
}
|
|
4024
|
-
}
|
|
4025
|
-
}
|
|
4026
|
-
const nodes = Object.values(processedDefs);
|
|
4027
|
-
if (nodes.length) {
|
|
4028
|
-
const ns = "http://www.w3.org/1999/xhtml";
|
|
4029
|
-
const svg = document.createElementNS(ns, "svg");
|
|
4030
|
-
svg.setAttribute("xmlns", ns);
|
|
4031
|
-
svg.style.position = "absolute";
|
|
4032
|
-
svg.style.width = "0";
|
|
4033
|
-
svg.style.height = "0";
|
|
4034
|
-
svg.style.overflow = "hidden";
|
|
4035
|
-
svg.style.display = "none";
|
|
4036
|
-
const defs = document.createElementNS(ns, "defs");
|
|
4037
|
-
svg.appendChild(defs);
|
|
4038
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
4039
|
-
defs.appendChild(nodes[i]);
|
|
4040
|
-
}
|
|
4041
|
-
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;
|
|
4042
4251
|
}
|
|
4043
|
-
return
|
|
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;
|
|
4044
4254
|
}
|
|
4045
|
-
async function
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
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
|
+
};
|
|
4050
4290
|
}
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
const FONT_SRC_REGEX = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
|
|
4054
|
-
function toRegex(url2) {
|
|
4055
|
-
const escaped = url2.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
|
|
4056
|
-
return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
|
|
4291
|
+
function isStylesheetLoaded(href) {
|
|
4292
|
+
return Array.from(document.styleSheets).some((sheet) => sheet.href === href);
|
|
4057
4293
|
}
|
|
4058
|
-
function
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
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);
|
|
4063
4304
|
});
|
|
4064
|
-
return urls.filter((url2) => !isDataUrl(url2));
|
|
4065
4305
|
}
|
|
4066
|
-
async function
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
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);
|
|
4074
4313
|
}
|
|
4075
|
-
return
|
|
4076
|
-
} catch (error) {
|
|
4314
|
+
return cache.resource.get("fonts-embed-css");
|
|
4077
4315
|
}
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
const [src, , format] = URL_WITH_FORMAT_REGEX.exec(match2) || [];
|
|
4084
|
-
if (!format) {
|
|
4085
|
-
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"}`);
|
|
4086
4321
|
}
|
|
4087
|
-
|
|
4088
|
-
|
|
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);
|
|
4089
4335
|
}
|
|
4090
4336
|
}
|
|
4091
|
-
});
|
|
4092
|
-
}
|
|
4093
|
-
function shouldEmbed(url2) {
|
|
4094
|
-
return url2.search(URL_REGEX) !== -1;
|
|
4095
|
-
}
|
|
4096
|
-
async function embedResources(cssText, baseUrl, options) {
|
|
4097
|
-
if (!shouldEmbed(cssText)) {
|
|
4098
|
-
return cssText;
|
|
4099
4337
|
}
|
|
4100
|
-
|
|
4101
|
-
const
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
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
|
+
}
|
|
4111
4404
|
}
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
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
|
+
}
|
|
4122
4491
|
}
|
|
4123
|
-
const
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
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:")) {
|
|
4128
4498
|
try {
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
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;
|
|
4132
4511
|
}
|
|
4133
|
-
} : reject;
|
|
4134
|
-
const image = clonedNode;
|
|
4135
|
-
if (image.decode) {
|
|
4136
|
-
image.decode = resolve2;
|
|
4137
|
-
}
|
|
4138
|
-
if (image.loading === "lazy") {
|
|
4139
|
-
image.loading = "eager";
|
|
4140
|
-
}
|
|
4141
|
-
if (isImageElement) {
|
|
4142
|
-
clonedNode.srcset = "";
|
|
4143
|
-
clonedNode.src = dataURL;
|
|
4144
4512
|
} else {
|
|
4145
|
-
|
|
4513
|
+
cache.resource.set(src, b64);
|
|
4514
|
+
cache.font.add(src);
|
|
4146
4515
|
}
|
|
4147
|
-
|
|
4148
|
-
}
|
|
4149
|
-
async function embedChildren(clonedNode, options) {
|
|
4150
|
-
const children = toArray(clonedNode.childNodes);
|
|
4151
|
-
const deferreds = children.map((child) => embedImages(child, options));
|
|
4152
|
-
await Promise.all(deferreds).then(() => clonedNode);
|
|
4153
|
-
}
|
|
4154
|
-
async function embedImages(clonedNode, options) {
|
|
4155
|
-
if (isInstanceOfElement(clonedNode, Element)) {
|
|
4156
|
-
await embedBackground(clonedNode, options);
|
|
4157
|
-
await embedImageNode(clonedNode, options);
|
|
4158
|
-
await embedChildren(clonedNode, options);
|
|
4516
|
+
finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};}`;
|
|
4159
4517
|
}
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
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
|
+
}
|
|
4168
4526
|
}
|
|
4169
|
-
|
|
4170
|
-
|
|
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
|
+
}
|
|
4171
4634
|
}
|
|
4172
|
-
const
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
});
|
|
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);
|
|
4177
4639
|
}
|
|
4178
|
-
return node2;
|
|
4179
4640
|
}
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
let
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
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
|
+
}
|
|
4205
4687
|
});
|
|
4206
|
-
return Promise.all(loadFonts).then(() => cssText);
|
|
4207
4688
|
}
|
|
4208
|
-
function
|
|
4209
|
-
|
|
4210
|
-
|
|
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);
|
|
4211
4700
|
}
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
const matches = keyframesRegex.exec(cssText);
|
|
4218
|
-
if (matches === null) {
|
|
4219
|
-
break;
|
|
4220
|
-
}
|
|
4221
|
-
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;
|
|
4222
4706
|
}
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
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";
|
|
4235
4730
|
}
|
|
4236
|
-
} else {
|
|
4237
|
-
unifiedRegex.lastIndex = importRegex.lastIndex;
|
|
4238
4731
|
}
|
|
4239
|
-
|
|
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
|
+
}
|
|
4240
4823
|
}
|
|
4241
|
-
return
|
|
4824
|
+
return out;
|
|
4242
4825
|
}
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
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
|
+
}
|
|
4266
4860
|
}
|
|
4267
|
-
});
|
|
4268
|
-
} catch (e) {
|
|
4269
|
-
const inline = styleSheets2.find((a) => a.href == null) || document.styleSheets[0];
|
|
4270
|
-
if (sheet.href != null) {
|
|
4271
|
-
deferreds.push(fetchCSS(sheet.href).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule2) => {
|
|
4272
|
-
inline.insertRule(rule2, inline.cssRules.length);
|
|
4273
|
-
})).catch((err) => {
|
|
4274
|
-
console.error("Error loading remote stylesheet", err);
|
|
4275
|
-
}));
|
|
4276
4861
|
}
|
|
4277
|
-
|
|
4862
|
+
if (changed) img.setAttribute("srcset", stringifySrcset(parts2));
|
|
4278
4863
|
}
|
|
4864
|
+
} catch {
|
|
4279
4865
|
}
|
|
4280
|
-
}
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
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));
|
|
4290
4909
|
}
|
|
4910
|
+
} catch {
|
|
4291
4911
|
}
|
|
4292
|
-
}
|
|
4293
|
-
return ret;
|
|
4294
|
-
});
|
|
4295
|
-
}
|
|
4296
|
-
function getWebFontRules(cssRules) {
|
|
4297
|
-
return cssRules.filter((rule2) => rule2.type === CSSRule.FONT_FACE_RULE).filter((rule2) => shouldEmbed(rule2.style.getPropertyValue("src")));
|
|
4298
|
-
}
|
|
4299
|
-
async function parseWebFontRules(node2, options) {
|
|
4300
|
-
if (node2.ownerDocument == null) {
|
|
4301
|
-
throw new Error("Provided element is not within a Document");
|
|
4912
|
+
}
|
|
4302
4913
|
}
|
|
4303
|
-
const styleSheets2 = toArray(node2.ownerDocument.styleSheets);
|
|
4304
|
-
const cssRules = await getCSSRules(styleSheets2, options);
|
|
4305
|
-
return getWebFontRules(cssRules);
|
|
4306
4914
|
}
|
|
4307
|
-
function
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
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;
|
|
4320
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 });
|
|
4321
5022
|
});
|
|
4322
5023
|
}
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
const usedFonts = getUsedFonts(node2);
|
|
4329
|
-
const cssTexts = await Promise.all(rules.filter((rule2) => usedFonts.has(normalizeFontFamily(rule2.style.fontFamily))).map((rule2) => {
|
|
4330
|
-
const baseUrl = rule2.parentStyleSheet ? rule2.parentStyleSheet.href : null;
|
|
4331
|
-
return embedResources(rule2.cssText, baseUrl, options);
|
|
4332
|
-
}));
|
|
4333
|
-
return cssTexts.join("\n");
|
|
4334
|
-
}
|
|
4335
|
-
async function embedWebFonts(clonedNode, options) {
|
|
4336
|
-
const cssText = options.fontEmbedCSS != null ? options.fontEmbedCSS : options.skipFonts ? null : await getWebFontCSS(clonedNode, options);
|
|
4337
|
-
if (cssText) {
|
|
4338
|
-
const styleNode = document.createElement("style");
|
|
4339
|
-
const sytleContent = document.createTextNode(cssText);
|
|
4340
|
-
styleNode.appendChild(sytleContent);
|
|
4341
|
-
if (clonedNode.firstChild) {
|
|
4342
|
-
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);
|
|
4343
5029
|
} else {
|
|
4344
|
-
|
|
5030
|
+
await new Promise((resolve2) => {
|
|
5031
|
+
idle(() => {
|
|
5032
|
+
baseCSS = generateDedupedBaseCSS(usedTags);
|
|
5033
|
+
cache.baseStyle.set(tagKey, baseCSS);
|
|
5034
|
+
resolve2();
|
|
5035
|
+
}, { fast });
|
|
5036
|
+
});
|
|
4345
5037
|
}
|
|
4346
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;
|
|
4347
5093
|
}
|
|
4348
|
-
async function
|
|
4349
|
-
const
|
|
4350
|
-
|
|
4351
|
-
await
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
const
|
|
4360
|
-
|
|
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;
|
|
4361
5126
|
const canvas = document.createElement("canvas");
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
}
|
|
4371
|
-
canvas.style.width = `${canvasWidth}`;
|
|
4372
|
-
canvas.style.height = `${canvasHeight}`;
|
|
4373
|
-
if (options.backgroundColor) {
|
|
4374
|
-
context.fillStyle = options.backgroundColor;
|
|
4375
|
-
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
4376
|
-
}
|
|
4377
|
-
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();
|
|
4378
5135
|
return canvas;
|
|
4379
5136
|
}
|
|
4380
|
-
async function
|
|
4381
|
-
|
|
4382
|
-
|
|
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
|
+
};
|
|
4383
5262
|
}
|
|
4384
5263
|
async function captureScreenshot(domBody) {
|
|
4385
|
-
const
|
|
4386
|
-
const
|
|
5264
|
+
const originalHeight = document.documentElement.scrollHeight;
|
|
5265
|
+
const originalWidth = document.documentElement.scrollWidth;
|
|
5266
|
+
const { width, height } = getScaledDimensions(originalWidth, originalHeight);
|
|
4387
5267
|
const styles = Array.from(document.styleSheets).map((styleSheet) => {
|
|
4388
5268
|
try {
|
|
4389
5269
|
return Array.from(styleSheet.cssRules).map((rule2) => rule2.cssText).join("");
|
|
@@ -4394,37 +5274,17 @@ async function captureScreenshot(domBody) {
|
|
|
4394
5274
|
const styleElement = document.createElement("style");
|
|
4395
5275
|
styleElement.textContent = styles;
|
|
4396
5276
|
domBody.appendChild(styleElement);
|
|
4397
|
-
|
|
4398
|
-
const
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
style: {
|
|
4409
|
-
transform: "scale(1)"
|
|
4410
|
-
// Set scale to 1 to avoid scaling
|
|
4411
|
-
},
|
|
4412
|
-
skipFonts: true,
|
|
4413
|
-
// Avoid embedding web fonts to bypass SecurityError
|
|
4414
|
-
cacheBust: true,
|
|
4415
|
-
// Prevent caching
|
|
4416
|
-
backgroundColor: "white",
|
|
4417
|
-
// Set background color to white
|
|
4418
|
-
fetchRequestInit: {
|
|
4419
|
-
mode: "cors"
|
|
4420
|
-
// Enable CORS
|
|
4421
|
-
},
|
|
4422
|
-
filter,
|
|
4423
|
-
imagePlaceholder: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFdwI2QJIhFwAAAABJRU5ErkJggg=="
|
|
4424
|
-
// Transparent placeholder for CORS-blocked images
|
|
4425
|
-
});
|
|
4426
|
-
domBody.removeChild(styleElement);
|
|
4427
|
-
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
|
+
}
|
|
4428
5288
|
}
|
|
4429
5289
|
const _wellKnownNames = [
|
|
4430
5290
|
"aaron",
|
|
@@ -16911,7 +17771,7 @@ function serializeNode(n2, options) {
|
|
|
16911
17771
|
maskTextFn,
|
|
16912
17772
|
maskInputFn,
|
|
16913
17773
|
dataURLOptions = {},
|
|
16914
|
-
inlineImages,
|
|
17774
|
+
inlineImages: inlineImages2,
|
|
16915
17775
|
recordCanvas,
|
|
16916
17776
|
keepIframeSrcFn,
|
|
16917
17777
|
newlyAddedElement = false,
|
|
@@ -16950,7 +17810,7 @@ function serializeNode(n2, options) {
|
|
|
16950
17810
|
maskInputOptions,
|
|
16951
17811
|
maskInputFn,
|
|
16952
17812
|
dataURLOptions,
|
|
16953
|
-
inlineImages,
|
|
17813
|
+
inlineImages: inlineImages2,
|
|
16954
17814
|
recordCanvas,
|
|
16955
17815
|
keepIframeSrcFn,
|
|
16956
17816
|
newlyAddedElement,
|
|
@@ -17018,7 +17878,7 @@ function serializeElementNode(n2, options) {
|
|
|
17018
17878
|
maskInputOptions = {},
|
|
17019
17879
|
maskInputFn,
|
|
17020
17880
|
dataURLOptions = {},
|
|
17021
|
-
inlineImages,
|
|
17881
|
+
inlineImages: inlineImages2,
|
|
17022
17882
|
recordCanvas,
|
|
17023
17883
|
keepIframeSrcFn,
|
|
17024
17884
|
newlyAddedElement = false,
|
|
@@ -17115,7 +17975,7 @@ function serializeElementNode(n2, options) {
|
|
|
17115
17975
|
}
|
|
17116
17976
|
}
|
|
17117
17977
|
}
|
|
17118
|
-
if (tagName === "img" &&
|
|
17978
|
+
if (tagName === "img" && inlineImages2) {
|
|
17119
17979
|
if (!canvasService) {
|
|
17120
17980
|
canvasService = doc.createElement("canvas");
|
|
17121
17981
|
canvasCtx = canvasService.getContext("2d");
|
|
@@ -17254,7 +18114,7 @@ function serializeNodeWithId(n2, options) {
|
|
|
17254
18114
|
maskInputFn,
|
|
17255
18115
|
slimDOMOptions,
|
|
17256
18116
|
dataURLOptions = {},
|
|
17257
|
-
inlineImages = false,
|
|
18117
|
+
inlineImages: inlineImages2 = false,
|
|
17258
18118
|
recordCanvas = false,
|
|
17259
18119
|
onSerialize,
|
|
17260
18120
|
onIframeLoad,
|
|
@@ -17287,7 +18147,7 @@ function serializeNodeWithId(n2, options) {
|
|
|
17287
18147
|
maskTextFn,
|
|
17288
18148
|
maskInputFn,
|
|
17289
18149
|
dataURLOptions,
|
|
17290
|
-
inlineImages,
|
|
18150
|
+
inlineImages: inlineImages2,
|
|
17291
18151
|
recordCanvas,
|
|
17292
18152
|
keepIframeSrcFn,
|
|
17293
18153
|
newlyAddedElement,
|
|
@@ -17340,7 +18200,7 @@ function serializeNodeWithId(n2, options) {
|
|
|
17340
18200
|
maskInputFn,
|
|
17341
18201
|
slimDOMOptions,
|
|
17342
18202
|
dataURLOptions,
|
|
17343
|
-
inlineImages,
|
|
18203
|
+
inlineImages: inlineImages2,
|
|
17344
18204
|
recordCanvas,
|
|
17345
18205
|
preserveWhiteSpace,
|
|
17346
18206
|
onSerialize,
|
|
@@ -17399,7 +18259,7 @@ function serializeNodeWithId(n2, options) {
|
|
|
17399
18259
|
maskInputFn,
|
|
17400
18260
|
slimDOMOptions,
|
|
17401
18261
|
dataURLOptions,
|
|
17402
|
-
inlineImages,
|
|
18262
|
+
inlineImages: inlineImages2,
|
|
17403
18263
|
recordCanvas,
|
|
17404
18264
|
preserveWhiteSpace,
|
|
17405
18265
|
onSerialize,
|
|
@@ -17440,7 +18300,7 @@ function serializeNodeWithId(n2, options) {
|
|
|
17440
18300
|
maskInputFn,
|
|
17441
18301
|
slimDOMOptions,
|
|
17442
18302
|
dataURLOptions,
|
|
17443
|
-
inlineImages,
|
|
18303
|
+
inlineImages: inlineImages2,
|
|
17444
18304
|
recordCanvas,
|
|
17445
18305
|
preserveWhiteSpace,
|
|
17446
18306
|
onSerialize,
|
|
@@ -17471,7 +18331,7 @@ function snapshot(n2, options) {
|
|
|
17471
18331
|
maskTextClass = "rr-mask",
|
|
17472
18332
|
maskTextSelector = null,
|
|
17473
18333
|
inlineStylesheet = true,
|
|
17474
|
-
inlineImages = false,
|
|
18334
|
+
inlineImages: inlineImages2 = false,
|
|
17475
18335
|
recordCanvas = false,
|
|
17476
18336
|
maskAllInputs = false,
|
|
17477
18337
|
maskTextFn,
|
|
@@ -17536,7 +18396,7 @@ function snapshot(n2, options) {
|
|
|
17536
18396
|
maskInputFn,
|
|
17537
18397
|
slimDOMOptions,
|
|
17538
18398
|
dataURLOptions,
|
|
17539
|
-
inlineImages,
|
|
18399
|
+
inlineImages: inlineImages2,
|
|
17540
18400
|
recordCanvas,
|
|
17541
18401
|
preserveWhiteSpace,
|
|
17542
18402
|
onSerialize,
|
|
@@ -27862,7 +28722,7 @@ function record(options = {}) {
|
|
|
27862
28722
|
recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
|
|
27863
28723
|
userTriggeredOnInput = false,
|
|
27864
28724
|
collectFonts = false,
|
|
27865
|
-
inlineImages = false,
|
|
28725
|
+
inlineImages: inlineImages2 = false,
|
|
27866
28726
|
plugins,
|
|
27867
28727
|
keepIframeSrcFn = () => false,
|
|
27868
28728
|
ignoreCSSAttributes = /* @__PURE__ */ new Set([]),
|
|
@@ -28046,7 +28906,7 @@ function record(options = {}) {
|
|
|
28046
28906
|
maskTextFn,
|
|
28047
28907
|
maskInputFn,
|
|
28048
28908
|
recordCanvas,
|
|
28049
|
-
inlineImages,
|
|
28909
|
+
inlineImages: inlineImages2,
|
|
28050
28910
|
sampling,
|
|
28051
28911
|
slimDOMOptions,
|
|
28052
28912
|
iframeManager,
|
|
@@ -28088,7 +28948,7 @@ function record(options = {}) {
|
|
|
28088
28948
|
slimDOM: slimDOMOptions,
|
|
28089
28949
|
dataURLOptions,
|
|
28090
28950
|
recordCanvas,
|
|
28091
|
-
inlineImages,
|
|
28951
|
+
inlineImages: inlineImages2,
|
|
28092
28952
|
onSerialize: (n2) => {
|
|
28093
28953
|
if (isSerializedIframe$1(n2, mirror)) {
|
|
28094
28954
|
iframeManager.addIframe(n2);
|
|
@@ -28222,7 +29082,7 @@ function record(options = {}) {
|
|
|
28222
29082
|
sampling,
|
|
28223
29083
|
recordDOM,
|
|
28224
29084
|
recordCanvas,
|
|
28225
|
-
inlineImages,
|
|
29085
|
+
inlineImages: inlineImages2,
|
|
28226
29086
|
userTriggeredOnInput,
|
|
28227
29087
|
collectFonts,
|
|
28228
29088
|
doc,
|
|
@@ -37507,7 +38367,7 @@ function toCamelCase(str) {
|
|
|
37507
38367
|
($1) => $1.toUpperCase().replace("-", "").replace("_", "")
|
|
37508
38368
|
);
|
|
37509
38369
|
}
|
|
37510
|
-
const VERSION = "0.0.
|
|
38370
|
+
const VERSION = "0.0.20";
|
|
37511
38371
|
class Tracker extends TrackerSdk {
|
|
37512
38372
|
// 750KB
|
|
37513
38373
|
constructor(options) {
|