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