@zumer/snapdom 2.0.0-dev.2 → 2.0.0-dev.4
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/README.md +50 -9
- package/dist/modules/chunks/chunk-23Q2XEDO.js +7 -0
- package/dist/modules/chunks/chunk-2FBS3OGP.js +7 -0
- package/dist/modules/chunks/chunk-43NWAKSV.js +7 -0
- package/dist/modules/chunks/chunk-5EIHY65Z.js +7 -0
- package/dist/modules/chunks/chunk-6FO4Y3OG.js +10 -0
- package/dist/modules/chunks/chunk-AOOOETXS.js +7 -0
- package/dist/modules/chunks/chunk-DV3SVS7E.js +10 -0
- package/dist/modules/chunks/chunk-FPOOW4FJ.js +7 -0
- package/dist/modules/chunks/chunk-FQFF5KMG.js +7 -0
- package/dist/modules/chunks/chunk-GU6SHZKJ.js +7 -0
- package/dist/modules/chunks/chunk-JUQT3OWD.js +7 -0
- package/dist/modules/chunks/chunk-LO3HOQZX.js +8 -0
- package/dist/modules/chunks/chunk-MI3WCG7V.js +8 -0
- package/dist/modules/chunks/chunk-R6NINER4.js +7 -0
- package/dist/modules/chunks/chunk-TWGUMDQG.js +10 -0
- package/dist/modules/chunks/chunk-UI252PBJ.js +7 -0
- package/dist/modules/chunks/chunk-XUQSCYHL.js +10 -0
- package/dist/modules/chunks/chunk-ZHRUQIHZ.js +7 -0
- package/dist/modules/chunks/rasterize-NVVIIRN2.js +7 -0
- package/dist/modules/chunks/rasterize-XGEI2GKT.js +7 -0
- package/dist/modules/exporters/download.js +7 -0
- package/dist/modules/exporters/toBlob.js +7 -0
- package/dist/modules/exporters/toCanvas.js +7 -0
- package/dist/modules/exporters/toImg.js +7 -0
- package/dist/modules/exporters/toJpg.js +7 -0
- package/dist/modules/exporters/toPng.js +7 -0
- package/dist/modules/exporters/toSvg.js +7 -0
- package/dist/modules/exporters/toWebp.js +7 -0
- package/dist/modules/plugins.js +7 -0
- package/dist/modules/preCache.js +7 -0
- package/dist/modules/snapdom.js +7 -0
- package/dist/snapdom.js +13 -5030
- package/dist/snapdom.mjs +2284 -1886
- package/package.json +29 -24
- package/dist/snapdom.min.js +0 -8
- package/dist/snapdom.min.mjs +0 -8
package/dist/snapdom.mjs
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
|
-
|
|
2
1
|
/*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Author Juan Martin Muda
|
|
6
|
-
* License MIT
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
* SnapDOM
|
|
3
|
+
* v2.0.0-dev.4
|
|
4
|
+
* Author: Juan Martin Muda
|
|
5
|
+
* License: MIT
|
|
6
|
+
*/
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
9
16
|
|
|
10
17
|
// src/core/cache.js
|
|
11
|
-
var cache = {
|
|
12
|
-
image: /* @__PURE__ */ new Map(),
|
|
13
|
-
background: /* @__PURE__ */ new Map(),
|
|
14
|
-
resource: /* @__PURE__ */ new Map(),
|
|
15
|
-
defaultStyle: /* @__PURE__ */ new Map(),
|
|
16
|
-
baseStyle: /* @__PURE__ */ new Map(),
|
|
17
|
-
computedStyle: /* @__PURE__ */ new WeakMap(),
|
|
18
|
-
font: /* @__PURE__ */ new Set(),
|
|
19
|
-
session: {
|
|
20
|
-
styleMap: /* @__PURE__ */ new Map(),
|
|
21
|
-
styleCache: /* @__PURE__ */ new WeakMap(),
|
|
22
|
-
nodeMap: /* @__PURE__ */ new Map()
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
18
|
function applyCachePolicy(policy = "soft") {
|
|
26
19
|
cache.session.__counterEpoch = (cache.session.__counterEpoch || 0) + 1;
|
|
27
20
|
switch (policy) {
|
|
@@ -60,6 +53,25 @@ function applyCachePolicy(policy = "soft") {
|
|
|
60
53
|
}
|
|
61
54
|
}
|
|
62
55
|
}
|
|
56
|
+
var cache;
|
|
57
|
+
var init_cache = __esm({
|
|
58
|
+
"src/core/cache.js"() {
|
|
59
|
+
cache = {
|
|
60
|
+
image: /* @__PURE__ */ new Map(),
|
|
61
|
+
background: /* @__PURE__ */ new Map(),
|
|
62
|
+
resource: /* @__PURE__ */ new Map(),
|
|
63
|
+
defaultStyle: /* @__PURE__ */ new Map(),
|
|
64
|
+
baseStyle: /* @__PURE__ */ new Map(),
|
|
65
|
+
computedStyle: /* @__PURE__ */ new WeakMap(),
|
|
66
|
+
font: /* @__PURE__ */ new Set(),
|
|
67
|
+
session: {
|
|
68
|
+
styleMap: /* @__PURE__ */ new Map(),
|
|
69
|
+
styleCache: /* @__PURE__ */ new WeakMap(),
|
|
70
|
+
nodeMap: /* @__PURE__ */ new Map()
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
63
75
|
|
|
64
76
|
// src/utils/helpers.js
|
|
65
77
|
function extractURL(value) {
|
|
@@ -96,6 +108,10 @@ function safeEncodeURI(uri) {
|
|
|
96
108
|
return uri;
|
|
97
109
|
}
|
|
98
110
|
}
|
|
111
|
+
var init_helpers = __esm({
|
|
112
|
+
"src/utils/helpers.js"() {
|
|
113
|
+
}
|
|
114
|
+
});
|
|
99
115
|
|
|
100
116
|
// src/modules/snapFetch.js
|
|
101
117
|
function createSnapLogger(prefix = "[snapDOM]", { ttlMs = 5 * 6e4, maxEntries = 12 } = {}) {
|
|
@@ -124,9 +140,6 @@ function createSnapLogger(prefix = "[snapDOM]", { ttlMs = 5 * 6e4, maxEntries =
|
|
|
124
140
|
}
|
|
125
141
|
};
|
|
126
142
|
}
|
|
127
|
-
var snapLogger = createSnapLogger("[snapDOM]", { ttlMs: 3 * 6e4, maxEntries: 10 });
|
|
128
|
-
var _inflight = /* @__PURE__ */ new Map();
|
|
129
|
-
var _errorCache = /* @__PURE__ */ new Map();
|
|
130
143
|
function isSpecialURL(url) {
|
|
131
144
|
return /^data:|^blob:|^about:blank$/i.test(url);
|
|
132
145
|
}
|
|
@@ -326,6 +339,15 @@ async function snapFetch(url, options = {}) {
|
|
|
326
339
|
_inflight.set(key, p);
|
|
327
340
|
return p;
|
|
328
341
|
}
|
|
342
|
+
var snapLogger, _inflight, _errorCache;
|
|
343
|
+
var init_snapFetch = __esm({
|
|
344
|
+
"src/modules/snapFetch.js"() {
|
|
345
|
+
init_helpers();
|
|
346
|
+
snapLogger = createSnapLogger("[snapDOM]", { ttlMs: 3 * 6e4, maxEntries: 10 });
|
|
347
|
+
_inflight = /* @__PURE__ */ new Map();
|
|
348
|
+
_errorCache = /* @__PURE__ */ new Map();
|
|
349
|
+
}
|
|
350
|
+
});
|
|
329
351
|
|
|
330
352
|
// src/utils/image.js
|
|
331
353
|
async function inlineSingleBackgroundEntry(entry, options = {}) {
|
|
@@ -355,78 +377,15 @@ async function inlineSingleBackgroundEntry(entry, options = {}) {
|
|
|
355
377
|
return "none";
|
|
356
378
|
}
|
|
357
379
|
}
|
|
380
|
+
var init_image = __esm({
|
|
381
|
+
"src/utils/image.js"() {
|
|
382
|
+
init_cache();
|
|
383
|
+
init_helpers();
|
|
384
|
+
init_snapFetch();
|
|
385
|
+
}
|
|
386
|
+
});
|
|
358
387
|
|
|
359
388
|
// src/utils/css.js
|
|
360
|
-
var NO_CAPTURE_TAGS = /* @__PURE__ */ new Set([
|
|
361
|
-
"meta",
|
|
362
|
-
"script",
|
|
363
|
-
"noscript",
|
|
364
|
-
"title",
|
|
365
|
-
"link",
|
|
366
|
-
"template"
|
|
367
|
-
]);
|
|
368
|
-
var NO_DEFAULTS_TAGS = /* @__PURE__ */ new Set([
|
|
369
|
-
// non-painting / head stuff
|
|
370
|
-
"meta",
|
|
371
|
-
"link",
|
|
372
|
-
"style",
|
|
373
|
-
"title",
|
|
374
|
-
"noscript",
|
|
375
|
-
"script",
|
|
376
|
-
"template",
|
|
377
|
-
// SVG whole namespace (safe for LeaderLine/presentation attrs)
|
|
378
|
-
"g",
|
|
379
|
-
"defs",
|
|
380
|
-
"use",
|
|
381
|
-
"marker",
|
|
382
|
-
"mask",
|
|
383
|
-
"clipPath",
|
|
384
|
-
"pattern",
|
|
385
|
-
"path",
|
|
386
|
-
"polygon",
|
|
387
|
-
"polyline",
|
|
388
|
-
"line",
|
|
389
|
-
"circle",
|
|
390
|
-
"ellipse",
|
|
391
|
-
"rect",
|
|
392
|
-
"filter",
|
|
393
|
-
"lineargradient",
|
|
394
|
-
"radialgradient",
|
|
395
|
-
"stop"
|
|
396
|
-
]);
|
|
397
|
-
var commonTags = [
|
|
398
|
-
"div",
|
|
399
|
-
"span",
|
|
400
|
-
"p",
|
|
401
|
-
"a",
|
|
402
|
-
"img",
|
|
403
|
-
"ul",
|
|
404
|
-
"li",
|
|
405
|
-
"button",
|
|
406
|
-
"input",
|
|
407
|
-
"select",
|
|
408
|
-
"textarea",
|
|
409
|
-
"label",
|
|
410
|
-
"section",
|
|
411
|
-
"article",
|
|
412
|
-
"header",
|
|
413
|
-
"footer",
|
|
414
|
-
"nav",
|
|
415
|
-
"main",
|
|
416
|
-
"aside",
|
|
417
|
-
"h1",
|
|
418
|
-
"h2",
|
|
419
|
-
"h3",
|
|
420
|
-
"h4",
|
|
421
|
-
"h5",
|
|
422
|
-
"h6",
|
|
423
|
-
"table",
|
|
424
|
-
"thead",
|
|
425
|
-
"tbody",
|
|
426
|
-
"tr",
|
|
427
|
-
"td",
|
|
428
|
-
"th"
|
|
429
|
-
];
|
|
430
389
|
function precacheCommonTags() {
|
|
431
390
|
for (let tag of commonTags) {
|
|
432
391
|
const t = String(tag).toLowerCase();
|
|
@@ -473,26 +432,6 @@ function getDefaultStyleForTag(tagName) {
|
|
|
473
432
|
cache.defaultStyle.set(tagName, defaults);
|
|
474
433
|
return defaults;
|
|
475
434
|
}
|
|
476
|
-
var NO_PAINT_TOKEN = /(?:^|-)(animation|transition)(?:-|$)/i;
|
|
477
|
-
var NO_PAINT_PREFIX = /^(--|view-timeline|scroll-timeline|animation-trigger|offset-|position-try|app-region|interactivity|overlay|view-transition|-webkit-locale|-webkit-user-(?:drag|modify)|-webkit-tap-highlight-color|-webkit-text-security)$/i;
|
|
478
|
-
var NO_PAINT_EXACT = /* @__PURE__ */ new Set([
|
|
479
|
-
// Interaction hints
|
|
480
|
-
"cursor",
|
|
481
|
-
"pointer-events",
|
|
482
|
-
"touch-action",
|
|
483
|
-
"user-select",
|
|
484
|
-
// Printing/speech/reading-mode hints
|
|
485
|
-
"print-color-adjust",
|
|
486
|
-
"speak",
|
|
487
|
-
"reading-flow",
|
|
488
|
-
"reading-order",
|
|
489
|
-
// Anchoring/container/timeline scopes (metadata for layout queries)
|
|
490
|
-
"anchor-name",
|
|
491
|
-
"anchor-scope",
|
|
492
|
-
"container-name",
|
|
493
|
-
"container-type",
|
|
494
|
-
"timeline-scope"
|
|
495
|
-
]);
|
|
496
435
|
function shouldIgnoreProp(prop) {
|
|
497
436
|
const p = String(prop).toLowerCase();
|
|
498
437
|
if (NO_PAINT_EXACT.has(p)) return true;
|
|
@@ -592,6 +531,102 @@ function splitBackgroundImage(bg) {
|
|
|
592
531
|
parts.push(bg.slice(lastIndex).trim());
|
|
593
532
|
return parts;
|
|
594
533
|
}
|
|
534
|
+
var NO_CAPTURE_TAGS, NO_DEFAULTS_TAGS, commonTags, NO_PAINT_TOKEN, NO_PAINT_PREFIX, NO_PAINT_EXACT;
|
|
535
|
+
var init_css = __esm({
|
|
536
|
+
"src/utils/css.js"() {
|
|
537
|
+
init_cache();
|
|
538
|
+
NO_CAPTURE_TAGS = /* @__PURE__ */ new Set([
|
|
539
|
+
"meta",
|
|
540
|
+
"script",
|
|
541
|
+
"noscript",
|
|
542
|
+
"title",
|
|
543
|
+
"link",
|
|
544
|
+
"template"
|
|
545
|
+
]);
|
|
546
|
+
NO_DEFAULTS_TAGS = /* @__PURE__ */ new Set([
|
|
547
|
+
// non-painting / head stuff
|
|
548
|
+
"meta",
|
|
549
|
+
"link",
|
|
550
|
+
"style",
|
|
551
|
+
"title",
|
|
552
|
+
"noscript",
|
|
553
|
+
"script",
|
|
554
|
+
"template",
|
|
555
|
+
// SVG whole namespace (safe for LeaderLine/presentation attrs)
|
|
556
|
+
"g",
|
|
557
|
+
"defs",
|
|
558
|
+
"use",
|
|
559
|
+
"marker",
|
|
560
|
+
"mask",
|
|
561
|
+
"clipPath",
|
|
562
|
+
"pattern",
|
|
563
|
+
"path",
|
|
564
|
+
"polygon",
|
|
565
|
+
"polyline",
|
|
566
|
+
"line",
|
|
567
|
+
"circle",
|
|
568
|
+
"ellipse",
|
|
569
|
+
"rect",
|
|
570
|
+
"filter",
|
|
571
|
+
"lineargradient",
|
|
572
|
+
"radialgradient",
|
|
573
|
+
"stop"
|
|
574
|
+
]);
|
|
575
|
+
commonTags = [
|
|
576
|
+
"div",
|
|
577
|
+
"span",
|
|
578
|
+
"p",
|
|
579
|
+
"a",
|
|
580
|
+
"img",
|
|
581
|
+
"ul",
|
|
582
|
+
"li",
|
|
583
|
+
"button",
|
|
584
|
+
"input",
|
|
585
|
+
"select",
|
|
586
|
+
"textarea",
|
|
587
|
+
"label",
|
|
588
|
+
"section",
|
|
589
|
+
"article",
|
|
590
|
+
"header",
|
|
591
|
+
"footer",
|
|
592
|
+
"nav",
|
|
593
|
+
"main",
|
|
594
|
+
"aside",
|
|
595
|
+
"h1",
|
|
596
|
+
"h2",
|
|
597
|
+
"h3",
|
|
598
|
+
"h4",
|
|
599
|
+
"h5",
|
|
600
|
+
"h6",
|
|
601
|
+
"table",
|
|
602
|
+
"thead",
|
|
603
|
+
"tbody",
|
|
604
|
+
"tr",
|
|
605
|
+
"td",
|
|
606
|
+
"th"
|
|
607
|
+
];
|
|
608
|
+
NO_PAINT_TOKEN = /(?:^|-)(animation|transition)(?:-|$)/i;
|
|
609
|
+
NO_PAINT_PREFIX = /^(--|view-timeline|scroll-timeline|animation-trigger|offset-|position-try|app-region|interactivity|overlay|view-transition|-webkit-locale|-webkit-user-(?:drag|modify)|-webkit-tap-highlight-color|-webkit-text-security)$/i;
|
|
610
|
+
NO_PAINT_EXACT = /* @__PURE__ */ new Set([
|
|
611
|
+
// Interaction hints
|
|
612
|
+
"cursor",
|
|
613
|
+
"pointer-events",
|
|
614
|
+
"touch-action",
|
|
615
|
+
"user-select",
|
|
616
|
+
// Printing/speech/reading-mode hints
|
|
617
|
+
"print-color-adjust",
|
|
618
|
+
"speak",
|
|
619
|
+
"reading-flow",
|
|
620
|
+
"reading-order",
|
|
621
|
+
// Anchoring/container/timeline scopes (metadata for layout queries)
|
|
622
|
+
"anchor-name",
|
|
623
|
+
"anchor-scope",
|
|
624
|
+
"container-name",
|
|
625
|
+
"container-type",
|
|
626
|
+
"timeline-scope"
|
|
627
|
+
]);
|
|
628
|
+
}
|
|
629
|
+
});
|
|
595
630
|
|
|
596
631
|
// src/utils/browser.js
|
|
597
632
|
function idle(fn, { fast = false } = {}) {
|
|
@@ -618,915 +653,1392 @@ function isSafari() {
|
|
|
618
653
|
const isIOSWebKit = /ipad|iphone|ipod/.test(uaLower) && isWebKit;
|
|
619
654
|
return isSafariUA || isUIWebView || isWeChatUA || isBaiduUA || isIOSWebKit;
|
|
620
655
|
}
|
|
656
|
+
var init_browser = __esm({
|
|
657
|
+
"src/utils/browser.js"() {
|
|
658
|
+
}
|
|
659
|
+
});
|
|
621
660
|
|
|
622
|
-
// src/
|
|
623
|
-
var
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
661
|
+
// src/utils/index.js
|
|
662
|
+
var init_utils = __esm({
|
|
663
|
+
"src/utils/index.js"() {
|
|
664
|
+
init_image();
|
|
665
|
+
init_css();
|
|
666
|
+
init_browser();
|
|
667
|
+
init_helpers();
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
// src/exporters/toCanvas.js
|
|
672
|
+
var toCanvas_exports = {};
|
|
673
|
+
__export(toCanvas_exports, {
|
|
674
|
+
toCanvas: () => toCanvas
|
|
675
|
+
});
|
|
676
|
+
function isSvgDataURL(u) {
|
|
677
|
+
return typeof u === "string" && /^data:image\/svg\+xml/i.test(u);
|
|
628
678
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
679
|
+
function decodeSvgFromDataURL(u) {
|
|
680
|
+
const i = u.indexOf(",");
|
|
681
|
+
return i >= 0 ? decodeURIComponent(u.slice(i + 1)) : "";
|
|
682
|
+
}
|
|
683
|
+
function encodeSvgToDataURL(svgText) {
|
|
684
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
|
|
685
|
+
}
|
|
686
|
+
function splitDecls(s) {
|
|
687
|
+
let parts = [], buf = "", depth = 0;
|
|
688
|
+
for (let i = 0; i < s.length; i++) {
|
|
689
|
+
const ch = s[i];
|
|
690
|
+
if (ch === "(") depth++;
|
|
691
|
+
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
692
|
+
if (ch === ";" && depth === 0) {
|
|
693
|
+
parts.push(buf);
|
|
694
|
+
buf = "";
|
|
695
|
+
} else buf += ch;
|
|
637
696
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
697
|
+
if (buf.trim()) parts.push(buf);
|
|
698
|
+
return parts.map((x) => x.trim()).filter(Boolean);
|
|
699
|
+
}
|
|
700
|
+
function boxShadowToDropShadow(value) {
|
|
701
|
+
const layers = [];
|
|
702
|
+
let buf = "", depth = 0;
|
|
703
|
+
for (let i = 0; i < value.length; i++) {
|
|
704
|
+
const ch = value[i];
|
|
705
|
+
if (ch === "(") depth++;
|
|
706
|
+
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
707
|
+
if (ch === "," && depth === 0) {
|
|
708
|
+
layers.push(buf.trim());
|
|
709
|
+
buf = "";
|
|
710
|
+
} else buf += ch;
|
|
642
711
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
712
|
+
if (buf.trim()) layers.push(buf.trim());
|
|
713
|
+
const fns = [];
|
|
714
|
+
for (const layer of layers) {
|
|
715
|
+
if (/\binset\b/i.test(layer)) continue;
|
|
716
|
+
const nums = layer.match(/-?\d+(?:\.\d+)?px/gi) || [];
|
|
717
|
+
const [ox = "0px", oy = "0px", blur = "0px"] = nums;
|
|
718
|
+
let color = layer.replace(/-?\d+(?:\.\d+)?px/gi, "").replace(/\binset\b/ig, "").trim().replace(/\s{2,}/g, " ");
|
|
719
|
+
const hasColor = !!color && color !== ",";
|
|
720
|
+
fns.push(`drop-shadow(${ox} ${oy} ${blur}${hasColor ? ` ${color}` : ""})`);
|
|
651
721
|
}
|
|
722
|
+
return fns.join(" ");
|
|
652
723
|
}
|
|
653
|
-
function
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if (
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
"text-decoration-color",
|
|
667
|
-
"text-decoration-style",
|
|
668
|
-
"text-decoration-thickness",
|
|
669
|
-
"text-underline-offset",
|
|
670
|
-
"text-decoration-skip-ink"
|
|
671
|
-
];
|
|
672
|
-
for (const prop of EXTRA_TEXT_DECORATION_PROPS) {
|
|
673
|
-
if (out[prop]) continue;
|
|
674
|
-
try {
|
|
675
|
-
const v = style.getPropertyValue(prop);
|
|
676
|
-
if (v) out[prop] = v;
|
|
677
|
-
} catch {
|
|
678
|
-
}
|
|
724
|
+
function rewriteDeclList(list) {
|
|
725
|
+
const decls = splitDecls(list);
|
|
726
|
+
let filter = null, wfilter = null, box = null;
|
|
727
|
+
const rest = [];
|
|
728
|
+
for (const d of decls) {
|
|
729
|
+
const idx = d.indexOf(":");
|
|
730
|
+
if (idx < 0) continue;
|
|
731
|
+
const prop = d.slice(0, idx).trim().toLowerCase();
|
|
732
|
+
const val = d.slice(idx + 1).trim();
|
|
733
|
+
if (prop === "box-shadow") box = val;
|
|
734
|
+
else if (prop === "filter") filter = val;
|
|
735
|
+
else if (prop === "-webkit-filter") wfilter = val;
|
|
736
|
+
else rest.push([prop, val]);
|
|
679
737
|
}
|
|
680
|
-
if (
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
"font-variant",
|
|
686
|
-
"font-variant-ligatures",
|
|
687
|
-
"font-optical-sizing"
|
|
688
|
-
];
|
|
689
|
-
for (const prop of EXTRA_FONT_PROPS) {
|
|
690
|
-
if (out[prop]) continue;
|
|
691
|
-
try {
|
|
692
|
-
const v = style.getPropertyValue(prop);
|
|
693
|
-
if (v) out[prop] = v;
|
|
694
|
-
} catch {
|
|
695
|
-
}
|
|
738
|
+
if (box) {
|
|
739
|
+
const ds = boxShadowToDropShadow(box);
|
|
740
|
+
if (ds) {
|
|
741
|
+
filter = filter ? `${filter} ${ds}` : ds;
|
|
742
|
+
wfilter = wfilter ? `${wfilter} ${ds}` : ds;
|
|
696
743
|
}
|
|
697
744
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
function styleSignature(snap) {
|
|
703
|
-
let sig = __snapshotSig.get(snap);
|
|
704
|
-
if (sig) return sig;
|
|
705
|
-
const entries = Object.entries(snap).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
706
|
-
sig = entries.map(([k, v]) => `${k}:${v}`).join(";");
|
|
707
|
-
__snapshotSig.set(snap, sig);
|
|
708
|
-
return sig;
|
|
745
|
+
const out = [...rest];
|
|
746
|
+
if (filter) out.push(["filter", filter]);
|
|
747
|
+
if (wfilter) out.push(["-webkit-filter", wfilter]);
|
|
748
|
+
return out.map(([k, v]) => `${k}:${v}`).join(";");
|
|
709
749
|
}
|
|
710
|
-
function
|
|
711
|
-
|
|
712
|
-
if (rec && rec.epoch === __epoch) return rec.snapshot;
|
|
713
|
-
const style = preStyle || getComputedStyle(el);
|
|
714
|
-
const snap = snapshotComputedStyleFull(style, options);
|
|
715
|
-
stripHeightForWrappers(el, style, snap);
|
|
716
|
-
snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
|
|
717
|
-
return snap;
|
|
750
|
+
function rewriteCssBlock(css) {
|
|
751
|
+
return css.replace(/([^{}]+)\{([^}]*)\}/g, (_m, sel, body) => `${sel}{${rewriteDeclList(body)}}`);
|
|
718
752
|
}
|
|
719
|
-
function
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
resource: cache.resource,
|
|
730
|
-
background: cache.background,
|
|
731
|
-
font: cache.font
|
|
732
|
-
},
|
|
733
|
-
options: opts || {}
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
return {
|
|
737
|
-
session: cache.session,
|
|
738
|
-
persist: {
|
|
739
|
-
snapshotKeyCache,
|
|
740
|
-
defaultStyle: cache.defaultStyle,
|
|
741
|
-
baseStyle: cache.baseStyle,
|
|
742
|
-
image: cache.image,
|
|
743
|
-
resource: cache.resource,
|
|
744
|
-
background: cache.background,
|
|
745
|
-
font: cache.font
|
|
746
|
-
},
|
|
747
|
-
options: sessionOrCtx || opts || {}
|
|
748
|
-
};
|
|
753
|
+
function rewriteSvgBoxShadowToDropShadow(svgText) {
|
|
754
|
+
svgText = svgText.replace(
|
|
755
|
+
/<style[^>]*>([\s\S]*?)<\/style>/gi,
|
|
756
|
+
(m, css) => m.replace(css, rewriteCssBlock(css))
|
|
757
|
+
);
|
|
758
|
+
svgText = svgText.replace(
|
|
759
|
+
/style=(['"])([\s\S]*?)\1/gi,
|
|
760
|
+
(m, q, body) => `style=${q}${rewriteDeclList(body)}${q}`
|
|
761
|
+
);
|
|
762
|
+
return svgText;
|
|
749
763
|
}
|
|
750
|
-
|
|
751
|
-
if (
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
ctx.session.__bumpedForDisabled = true;
|
|
759
|
-
}
|
|
760
|
-
if (NO_DEFAULTS_TAGS.has(source.tagName?.toLowerCase())) {
|
|
761
|
-
const author = source.getAttribute?.("style");
|
|
762
|
-
if (author) clone.setAttribute("style", author);
|
|
763
|
-
}
|
|
764
|
-
const { session, persist } = ctx;
|
|
765
|
-
if (!session.styleCache.has(source)) {
|
|
766
|
-
session.styleCache.set(source, getComputedStyle(source));
|
|
767
|
-
}
|
|
768
|
-
const pre = session.styleCache.get(source);
|
|
769
|
-
const snap = getSnapshot(source, pre, ctx.options);
|
|
770
|
-
const sig = styleSignature(snap);
|
|
771
|
-
let key = persist.snapshotKeyCache.get(sig);
|
|
772
|
-
if (!key) {
|
|
773
|
-
const tag = source.tagName?.toLowerCase() || "div";
|
|
774
|
-
key = getStyleKey(snap, tag);
|
|
775
|
-
persist.snapshotKeyCache.set(sig, key);
|
|
764
|
+
function maybeConvertBoxShadowForSafari(url) {
|
|
765
|
+
if (!isSafari() || !isSvgDataURL(url)) return url;
|
|
766
|
+
try {
|
|
767
|
+
const svg = decodeSvgFromDataURL(url);
|
|
768
|
+
const fixed = rewriteSvgBoxShadowToDropShadow(svg);
|
|
769
|
+
return encodeSvgToDataURL(fixed);
|
|
770
|
+
} catch {
|
|
771
|
+
return url;
|
|
776
772
|
}
|
|
777
|
-
session.styleMap.set(clone, key);
|
|
778
|
-
}
|
|
779
|
-
function isReplaced(el) {
|
|
780
|
-
return el instanceof HTMLImageElement || el instanceof HTMLCanvasElement || el instanceof HTMLVideoElement || el instanceof HTMLIFrameElement || el instanceof SVGElement || el instanceof HTMLObjectElement || el instanceof HTMLEmbedElement;
|
|
781
773
|
}
|
|
782
|
-
function
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
if (
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
if (isReplaced(el)) return;
|
|
813
|
-
const pos = cs.position;
|
|
814
|
-
if (pos === "absolute" || pos === "fixed" || pos === "sticky") return;
|
|
815
|
-
if (cs.transform !== "none") return;
|
|
816
|
-
if (hasBox(cs)) return;
|
|
817
|
-
if (isFlexOrGridItem(el)) return;
|
|
818
|
-
if (!hasFlowFast(el, cs)) return;
|
|
819
|
-
delete snap.height;
|
|
820
|
-
delete snap["block-size"];
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
// src/modules/CSSVar.js
|
|
824
|
-
var KEY_PROPS = ["fill", "stroke", "color", "background-color", "stop-color"];
|
|
825
|
-
var __BASELINE_CACHE = /* @__PURE__ */ new Map();
|
|
826
|
-
function getBaselineComputed(tagName, ns) {
|
|
827
|
-
const key = ns + "::" + tagName.toLowerCase();
|
|
828
|
-
let entry = __BASELINE_CACHE.get(key);
|
|
829
|
-
if (entry) return entry;
|
|
830
|
-
const doc = document;
|
|
831
|
-
const el = ns === "http://www.w3.org/2000/svg" ? doc.createElementNS(ns, tagName) : doc.createElement(tagName);
|
|
832
|
-
const holder = doc.createElement("div");
|
|
833
|
-
holder.style.cssText = "position:absolute;left:-99999px;top:-99999px;contain:strict;display:block;";
|
|
834
|
-
holder.appendChild(el);
|
|
835
|
-
doc.documentElement.appendChild(holder);
|
|
836
|
-
const cs = getComputedStyle(el);
|
|
837
|
-
const base = {};
|
|
838
|
-
for (const p of KEY_PROPS) {
|
|
839
|
-
base[p] = cs.getPropertyValue(p) || "";
|
|
774
|
+
async function toCanvas(url, options) {
|
|
775
|
+
let { width: optW, height: optH, scale = 1, dpr = 1, meta = {}, backgroundColor } = options;
|
|
776
|
+
url = maybeConvertBoxShadowForSafari(url);
|
|
777
|
+
const img = new Image();
|
|
778
|
+
img.loading = "eager";
|
|
779
|
+
img.decoding = "sync";
|
|
780
|
+
img.crossOrigin = "anonymous";
|
|
781
|
+
img.src = url;
|
|
782
|
+
await img.decode();
|
|
783
|
+
const natW = img.naturalWidth;
|
|
784
|
+
const natH = img.naturalHeight;
|
|
785
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : natW;
|
|
786
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : natH;
|
|
787
|
+
let outW, outH;
|
|
788
|
+
const hasW = Number.isFinite(optW);
|
|
789
|
+
const hasH = Number.isFinite(optH);
|
|
790
|
+
if (hasW && hasH) {
|
|
791
|
+
outW = Math.max(1, optW);
|
|
792
|
+
outH = Math.max(1, optH);
|
|
793
|
+
} else if (hasW) {
|
|
794
|
+
const k = optW / Math.max(1, refW);
|
|
795
|
+
outW = optW;
|
|
796
|
+
outH = refH * k;
|
|
797
|
+
} else if (hasH) {
|
|
798
|
+
const k = optH / Math.max(1, refH);
|
|
799
|
+
outH = optH;
|
|
800
|
+
outW = refW * k;
|
|
801
|
+
} else {
|
|
802
|
+
outW = natW;
|
|
803
|
+
outH = natH;
|
|
840
804
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
if (
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
break;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
805
|
+
outW = outW * scale;
|
|
806
|
+
outH = outH * scale;
|
|
807
|
+
const canvas = document.createElement("canvas");
|
|
808
|
+
canvas.width = outW * dpr;
|
|
809
|
+
canvas.height = outH * dpr;
|
|
810
|
+
canvas.style.width = `${outW}px`;
|
|
811
|
+
canvas.style.height = `${outH}px`;
|
|
812
|
+
const ctx = canvas.getContext("2d");
|
|
813
|
+
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
814
|
+
if (backgroundColor) {
|
|
815
|
+
ctx.save();
|
|
816
|
+
ctx.fillStyle = backgroundColor;
|
|
817
|
+
ctx.fillRect(0, 0, outW, outH);
|
|
818
|
+
ctx.restore();
|
|
858
819
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
820
|
+
ctx.drawImage(img, 0, 0, outW, outH);
|
|
821
|
+
return canvas;
|
|
822
|
+
}
|
|
823
|
+
var init_toCanvas = __esm({
|
|
824
|
+
"src/exporters/toCanvas.js"() {
|
|
825
|
+
init_browser();
|
|
865
826
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
// src/modules/rasterize.js
|
|
830
|
+
var rasterize_exports = {};
|
|
831
|
+
__export(rasterize_exports, {
|
|
832
|
+
rasterize: () => rasterize
|
|
833
|
+
});
|
|
834
|
+
async function rasterize(url, options) {
|
|
835
|
+
const canvas = await toCanvas(url, options);
|
|
836
|
+
const img = new Image();
|
|
837
|
+
img.src = canvas.toDataURL(`image/${options.format}`, options.quality);
|
|
838
|
+
await img.decode();
|
|
839
|
+
img.style.width = `${canvas.width / options.dpr}px`;
|
|
840
|
+
img.style.height = `${canvas.height / options.dpr}px`;
|
|
841
|
+
return img;
|
|
842
|
+
}
|
|
843
|
+
var init_rasterize = __esm({
|
|
844
|
+
"src/modules/rasterize.js"() {
|
|
845
|
+
init_toCanvas();
|
|
882
846
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// src/exporters/toImg.js
|
|
850
|
+
var toImg_exports = {};
|
|
851
|
+
__export(toImg_exports, {
|
|
852
|
+
toImg: () => toImg,
|
|
853
|
+
toSvg: () => toImg
|
|
854
|
+
});
|
|
855
|
+
async function toImg(url, options) {
|
|
856
|
+
const { scale = 1, width, height, meta = {} } = options;
|
|
857
|
+
const hasW = Number.isFinite(width);
|
|
858
|
+
const hasH = Number.isFinite(height);
|
|
859
|
+
const wantsScale = Number.isFinite(scale) && scale !== 1 || hasW || hasH;
|
|
860
|
+
if (isSafari() && wantsScale) {
|
|
861
|
+
const pngUrl = await rasterize(url, { ...options, format: "png", quality: 1, meta });
|
|
862
|
+
return pngUrl;
|
|
897
863
|
}
|
|
898
|
-
|
|
899
|
-
|
|
864
|
+
const img = new Image();
|
|
865
|
+
img.decoding = "sync";
|
|
866
|
+
img.loading = "eager";
|
|
867
|
+
img.src = url;
|
|
868
|
+
await img.decode();
|
|
869
|
+
if (hasW && hasH) {
|
|
870
|
+
img.style.width = `${width}px`;
|
|
871
|
+
img.style.height = `${height}px`;
|
|
872
|
+
} else if (hasW) {
|
|
873
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
874
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
875
|
+
const k = width / Math.max(1, refW);
|
|
876
|
+
img.style.width = `${width}px`;
|
|
877
|
+
img.style.height = `${Math.round(refH * k)}px`;
|
|
878
|
+
} else if (hasH) {
|
|
879
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
880
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
881
|
+
const k = height / Math.max(1, refH);
|
|
882
|
+
img.style.height = `${height}px`;
|
|
883
|
+
img.style.width = `${Math.round(refW * k)}px`;
|
|
884
|
+
} else {
|
|
885
|
+
const cssW = Math.round(img.naturalWidth * scale);
|
|
886
|
+
const cssH = Math.round(img.naturalHeight * scale);
|
|
887
|
+
img.style.width = `${cssW}px`;
|
|
888
|
+
img.style.height = `${cssH}px`;
|
|
889
|
+
if (typeof url === "string" && url.startsWith("data:image/svg+xml")) {
|
|
900
890
|
try {
|
|
901
|
-
|
|
891
|
+
const decoded = decodeURIComponent(url.split(",")[1]);
|
|
892
|
+
const patched = decoded.replace(/width="[^"]*"/, `width="${cssW}"`).replace(/height="[^"]*"/, `height="${cssH}"`);
|
|
893
|
+
url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(patched)}`;
|
|
894
|
+
img.src = url;
|
|
902
895
|
} catch {
|
|
903
|
-
cs = null;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
if (!cs) return;
|
|
907
|
-
const ns = sourceEl.namespaceURI || "html";
|
|
908
|
-
const base = getBaselineComputed(sourceEl.tagName, ns);
|
|
909
|
-
for (const prop of KEY_PROPS) {
|
|
910
|
-
const v = cs.getPropertyValue(prop) || "";
|
|
911
|
-
const b = base[prop] || "";
|
|
912
|
-
if (v && v !== b) {
|
|
913
|
-
try {
|
|
914
|
-
cloneEl.style.setProperty(prop, v.trim());
|
|
915
|
-
} catch {
|
|
916
|
-
}
|
|
917
896
|
}
|
|
918
897
|
}
|
|
919
898
|
}
|
|
899
|
+
return img;
|
|
920
900
|
}
|
|
901
|
+
var init_toImg = __esm({
|
|
902
|
+
"src/exporters/toImg.js"() {
|
|
903
|
+
init_utils();
|
|
904
|
+
init_rasterize();
|
|
905
|
+
}
|
|
906
|
+
});
|
|
921
907
|
|
|
922
|
-
// src/
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
});
|
|
938
|
-
}));
|
|
908
|
+
// src/exporters/toBlob.js
|
|
909
|
+
var toBlob_exports = {};
|
|
910
|
+
__export(toBlob_exports, {
|
|
911
|
+
toBlob: () => toBlob
|
|
912
|
+
});
|
|
913
|
+
async function toBlob(url, options) {
|
|
914
|
+
const type = options.type;
|
|
915
|
+
if (type === "svg") {
|
|
916
|
+
const svgText = decodeURIComponent(url.split(",")[1]);
|
|
917
|
+
return new Blob([svgText], { type: "image/svg+xml" });
|
|
918
|
+
}
|
|
919
|
+
const canvas = await toCanvas(url, options);
|
|
920
|
+
return new Promise(
|
|
921
|
+
(resolve) => canvas.toBlob((blob) => resolve(blob), `image/${type}`, options.quality)
|
|
922
|
+
);
|
|
939
923
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
if (/:not\(\s*\[data-sd-slotted\]\s*\)\s*$/.test(sel)) return sel;
|
|
944
|
-
return `${sel}:not([data-sd-slotted])`;
|
|
945
|
-
}
|
|
946
|
-
function wrapWithScope(selectorList, scopeSelector, excludeSlotted = true) {
|
|
947
|
-
return selectorList.split(",").map((s) => s.trim()).filter(Boolean).map((s) => {
|
|
948
|
-
if (s.startsWith(":where(")) return s;
|
|
949
|
-
if (s.startsWith("@")) return s;
|
|
950
|
-
const body = excludeSlotted ? addNotSlottedRightmost(s) : s;
|
|
951
|
-
return `:where(${scopeSelector} ${body})`;
|
|
952
|
-
}).join(", ");
|
|
953
|
-
}
|
|
954
|
-
function rewriteShadowCSS(cssText, scopeSelector) {
|
|
955
|
-
if (!cssText) return "";
|
|
956
|
-
cssText = cssText.replace(/:host\(([^)]+)\)/g, (_, sel) => {
|
|
957
|
-
return `:where(${scopeSelector}:is(${sel.trim()}))`;
|
|
958
|
-
});
|
|
959
|
-
cssText = cssText.replace(/:host\b/g, `:where(${scopeSelector})`);
|
|
960
|
-
cssText = cssText.replace(/:host-context\(([^)]+)\)/g, (_, sel) => {
|
|
961
|
-
return `:where(:where(${sel.trim()}) ${scopeSelector})`;
|
|
962
|
-
});
|
|
963
|
-
cssText = cssText.replace(/::slotted\(([^)]+)\)/g, (_, sel) => {
|
|
964
|
-
return `:where(${scopeSelector} ${sel.trim()})`;
|
|
965
|
-
});
|
|
966
|
-
cssText = cssText.replace(/(^|})(\s*)([^@}{]+){/g, (_, brace, ws, selectorList) => {
|
|
967
|
-
const wrapped = wrapWithScope(
|
|
968
|
-
selectorList,
|
|
969
|
-
scopeSelector,
|
|
970
|
-
/*excludeSlotted*/
|
|
971
|
-
true
|
|
972
|
-
);
|
|
973
|
-
return `${brace}${ws}${wrapped}{`;
|
|
974
|
-
});
|
|
975
|
-
return cssText;
|
|
976
|
-
}
|
|
977
|
-
function nextShadowScopeId(sessionCache) {
|
|
978
|
-
sessionCache.shadowScopeSeq = (sessionCache.shadowScopeSeq || 0) + 1;
|
|
979
|
-
return `s${sessionCache.shadowScopeSeq}`;
|
|
980
|
-
}
|
|
981
|
-
function extractShadowCSS(sr) {
|
|
982
|
-
let css = "";
|
|
983
|
-
try {
|
|
984
|
-
sr.querySelectorAll("style").forEach((s) => {
|
|
985
|
-
css += (s.textContent || "") + "\n";
|
|
986
|
-
});
|
|
987
|
-
const sheets = sr.adoptedStyleSheets || [];
|
|
988
|
-
for (const sh of sheets) {
|
|
989
|
-
try {
|
|
990
|
-
if (sh && sh.cssRules) {
|
|
991
|
-
for (const rule of sh.cssRules) css += rule.cssText + "\n";
|
|
992
|
-
}
|
|
993
|
-
} catch {
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
} catch {
|
|
924
|
+
var init_toBlob = __esm({
|
|
925
|
+
"src/exporters/toBlob.js"() {
|
|
926
|
+
init_toCanvas();
|
|
997
927
|
}
|
|
998
|
-
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
// src/exporters/download.js
|
|
931
|
+
var download_exports = {};
|
|
932
|
+
__export(download_exports, {
|
|
933
|
+
download: () => download
|
|
934
|
+
});
|
|
935
|
+
async function download(url, options) {
|
|
936
|
+
options.dpr = 1;
|
|
937
|
+
if (options.format === "svg") {
|
|
938
|
+
const blob = await toBlob(url, { ...options, type: "svg" });
|
|
939
|
+
const objectURL = URL.createObjectURL(blob);
|
|
940
|
+
const a2 = document.createElement("a");
|
|
941
|
+
a2.href = objectURL;
|
|
942
|
+
a2.download = options.filename;
|
|
943
|
+
a2.click();
|
|
944
|
+
URL.revokeObjectURL(objectURL);
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
const canvas = await toCanvas(url, options);
|
|
948
|
+
const a = document.createElement("a");
|
|
949
|
+
a.href = canvas.toDataURL(`image/${options.format}`, options.quality);
|
|
950
|
+
a.download = options.filename;
|
|
951
|
+
a.click();
|
|
999
952
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
953
|
+
var init_download = __esm({
|
|
954
|
+
"src/exporters/download.js"() {
|
|
955
|
+
init_toBlob();
|
|
956
|
+
init_toCanvas();
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
// src/core/prepare.js
|
|
961
|
+
init_utils();
|
|
962
|
+
|
|
963
|
+
// src/modules/styles.js
|
|
964
|
+
init_utils();
|
|
965
|
+
init_cache();
|
|
966
|
+
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
967
|
+
var snapshotKeyCache = /* @__PURE__ */ new Map();
|
|
968
|
+
var __epoch = 0;
|
|
969
|
+
function bumpEpoch() {
|
|
970
|
+
__epoch++;
|
|
1006
971
|
}
|
|
1007
|
-
|
|
972
|
+
var __wired = false;
|
|
973
|
+
function setupInvalidationOnce(root = document.documentElement) {
|
|
974
|
+
if (__wired) return;
|
|
975
|
+
__wired = true;
|
|
1008
976
|
try {
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
cloned.setAttribute("src", chosen);
|
|
1012
|
-
cloned.removeAttribute("srcset");
|
|
1013
|
-
cloned.removeAttribute("sizes");
|
|
1014
|
-
cloned.loading = "eager";
|
|
1015
|
-
cloned.decoding = "sync";
|
|
977
|
+
const domObs = new MutationObserver(() => bumpEpoch());
|
|
978
|
+
domObs.observe(root, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
1016
979
|
} catch {
|
|
1017
980
|
}
|
|
1018
|
-
}
|
|
1019
|
-
function collectCustomPropsFromCSS(cssText) {
|
|
1020
|
-
const out = /* @__PURE__ */ new Set();
|
|
1021
|
-
if (!cssText) return out;
|
|
1022
|
-
const re = /var\(\s*(--[A-Za-z0-9_-]+)\b/g;
|
|
1023
|
-
let m;
|
|
1024
|
-
while (m = re.exec(cssText)) out.add(m[1]);
|
|
1025
|
-
return out;
|
|
1026
|
-
}
|
|
1027
|
-
function resolveCustomProp(el, name) {
|
|
1028
981
|
try {
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1031
|
-
if (v) return v;
|
|
982
|
+
const headObs = new MutationObserver(() => bumpEpoch());
|
|
983
|
+
headObs.observe(document.head, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
1032
984
|
} catch {
|
|
1033
985
|
}
|
|
1034
986
|
try {
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
987
|
+
const f = document.fonts;
|
|
988
|
+
if (f) {
|
|
989
|
+
f.addEventListener?.("loadingdone", bumpEpoch);
|
|
990
|
+
f.ready?.then(() => bumpEpoch()).catch(() => {
|
|
991
|
+
});
|
|
992
|
+
}
|
|
1038
993
|
} catch {
|
|
1039
994
|
}
|
|
1040
|
-
return "";
|
|
1041
|
-
}
|
|
1042
|
-
function buildSeedCustomPropsRule(hostEl, names, scopeSelector) {
|
|
1043
|
-
const decls = [];
|
|
1044
|
-
for (const name of names) {
|
|
1045
|
-
const val = resolveCustomProp(hostEl, name);
|
|
1046
|
-
if (val) decls.push(`${name}: ${val};`);
|
|
1047
|
-
}
|
|
1048
|
-
if (!decls.length) return "";
|
|
1049
|
-
return `${scopeSelector}{${decls.join("")}}
|
|
1050
|
-
`;
|
|
1051
|
-
}
|
|
1052
|
-
function markSlottedSubtree(root) {
|
|
1053
|
-
if (!root) return;
|
|
1054
|
-
if (root.nodeType === Node.ELEMENT_NODE) {
|
|
1055
|
-
root.setAttribute("data-sd-slotted", "");
|
|
1056
|
-
}
|
|
1057
|
-
if (root.querySelectorAll) {
|
|
1058
|
-
root.querySelectorAll("*").forEach((el) => el.setAttribute("data-sd-slotted", ""));
|
|
1059
|
-
}
|
|
1060
995
|
}
|
|
1061
|
-
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
996
|
+
function snapshotComputedStyleFull(style, options = {}) {
|
|
997
|
+
const out = {};
|
|
998
|
+
const vis = style.getPropertyValue("visibility");
|
|
999
|
+
for (let i = 0; i < style.length; i++) {
|
|
1000
|
+
const prop = style[i];
|
|
1001
|
+
let val = style.getPropertyValue(prop);
|
|
1002
|
+
if ((prop === "background-image" || prop === "content") && val.includes("url(") && !val.includes("data:")) {
|
|
1003
|
+
val = "none";
|
|
1067
1004
|
}
|
|
1068
|
-
|
|
1069
|
-
let doc = probe();
|
|
1070
|
-
let i = 0;
|
|
1071
|
-
while (i < attempts && (!doc || !doc.body && !doc.documentElement)) {
|
|
1072
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
1073
|
-
doc = probe();
|
|
1074
|
-
i++;
|
|
1075
|
-
}
|
|
1076
|
-
return doc && (doc.body || doc.documentElement) ? doc : null;
|
|
1077
|
-
}
|
|
1078
|
-
function measureContentBox(el) {
|
|
1079
|
-
const rect = el.getBoundingClientRect();
|
|
1080
|
-
let bl = 0, br = 0, bt = 0, bb = 0;
|
|
1081
|
-
try {
|
|
1082
|
-
const cs = getComputedStyle(el);
|
|
1083
|
-
bl = parseFloat(cs.borderLeftWidth) || 0;
|
|
1084
|
-
br = parseFloat(cs.borderRightWidth) || 0;
|
|
1085
|
-
bt = parseFloat(cs.borderTopWidth) || 0;
|
|
1086
|
-
bb = parseFloat(cs.borderBottomWidth) || 0;
|
|
1087
|
-
} catch {
|
|
1005
|
+
out[prop] = val;
|
|
1088
1006
|
}
|
|
1089
|
-
const
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
(
|
|
1098
|
-
|
|
1007
|
+
const EXTRA_TEXT_DECORATION_PROPS = [
|
|
1008
|
+
"text-decoration-line",
|
|
1009
|
+
"text-decoration-color",
|
|
1010
|
+
"text-decoration-style",
|
|
1011
|
+
"text-decoration-thickness",
|
|
1012
|
+
"text-underline-offset",
|
|
1013
|
+
"text-decoration-skip-ink"
|
|
1014
|
+
];
|
|
1015
|
+
for (const prop of EXTRA_TEXT_DECORATION_PROPS) {
|
|
1016
|
+
if (out[prop]) continue;
|
|
1099
1017
|
try {
|
|
1100
|
-
style.
|
|
1018
|
+
const v = style.getPropertyValue(prop);
|
|
1019
|
+
if (v) out[prop] = v;
|
|
1101
1020
|
} catch {
|
|
1102
1021
|
}
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
|
-
async function rasterizeIframe(iframe, sessionCache, options) {
|
|
1106
|
-
const doc = await getAccessibleIframeDocument(iframe, 3);
|
|
1107
|
-
if (!doc) throw new Error("iframe document not accessible/ready");
|
|
1108
|
-
const { contentWidth, contentHeight, rect } = measureContentBox(iframe);
|
|
1109
|
-
const snap = options?.snap;
|
|
1110
|
-
if (!snap || typeof snap.toPng !== "function") {
|
|
1111
|
-
throw new Error("snapdom.toPng not available in iframe or window");
|
|
1112
1022
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1023
|
+
if (options.embedFonts) {
|
|
1024
|
+
const EXTRA_FONT_PROPS = [
|
|
1025
|
+
"font-feature-settings",
|
|
1026
|
+
"font-variation-settings",
|
|
1027
|
+
"font-kerning",
|
|
1028
|
+
"font-variant",
|
|
1029
|
+
"font-variant-ligatures",
|
|
1030
|
+
"font-optical-sizing"
|
|
1031
|
+
];
|
|
1032
|
+
for (const prop of EXTRA_FONT_PROPS) {
|
|
1033
|
+
if (out[prop]) continue;
|
|
1034
|
+
try {
|
|
1035
|
+
const v = style.getPropertyValue(prop);
|
|
1036
|
+
if (v) out[prop] = v;
|
|
1037
|
+
} catch {
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1120
1040
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
imgEl.style.height = `${contentHeight}px`;
|
|
1124
|
-
const wrapper = document.createElement("div");
|
|
1125
|
-
sessionCache.nodeMap.set(wrapper, iframe);
|
|
1126
|
-
inlineAllStyles(iframe, wrapper, sessionCache, options);
|
|
1127
|
-
wrapper.style.overflow = "hidden";
|
|
1128
|
-
wrapper.style.display = "block";
|
|
1129
|
-
if (!wrapper.style.width) wrapper.style.width = `${Math.round(rect.width)}px`;
|
|
1130
|
-
if (!wrapper.style.height) wrapper.style.height = `${Math.round(rect.height)}px`;
|
|
1131
|
-
wrapper.appendChild(imgEl);
|
|
1132
|
-
return wrapper;
|
|
1041
|
+
if (vis === "hidden") out.opacity = "0";
|
|
1042
|
+
return out;
|
|
1133
1043
|
}
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1044
|
+
var __snapshotSig = /* @__PURE__ */ new WeakMap();
|
|
1045
|
+
function styleSignature(snap) {
|
|
1046
|
+
let sig = __snapshotSig.get(snap);
|
|
1047
|
+
if (sig) return sig;
|
|
1048
|
+
const entries = Object.entries(snap).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
1049
|
+
sig = entries.map(([k, v]) => `${k}:${v}`).join(";");
|
|
1050
|
+
__snapshotSig.set(snap, sig);
|
|
1051
|
+
return sig;
|
|
1052
|
+
}
|
|
1053
|
+
function getSnapshot(el, preStyle = null, options = {}) {
|
|
1054
|
+
const rec = snapshotCache.get(el);
|
|
1055
|
+
if (rec && rec.epoch === __epoch) return rec.snapshot;
|
|
1056
|
+
const style = preStyle || getComputedStyle(el);
|
|
1057
|
+
const snap = snapshotComputedStyleFull(style, options);
|
|
1058
|
+
stripHeightForWrappers(el, style, snap);
|
|
1059
|
+
snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
|
|
1060
|
+
return snap;
|
|
1061
|
+
}
|
|
1062
|
+
function _resolveCtx(sessionOrCtx, opts) {
|
|
1063
|
+
if (sessionOrCtx && sessionOrCtx.session && sessionOrCtx.persist) return sessionOrCtx;
|
|
1064
|
+
if (sessionOrCtx && (sessionOrCtx.styleMap || sessionOrCtx.styleCache || sessionOrCtx.nodeMap)) {
|
|
1065
|
+
return {
|
|
1066
|
+
session: sessionOrCtx,
|
|
1067
|
+
persist: {
|
|
1068
|
+
snapshotKeyCache,
|
|
1069
|
+
defaultStyle: cache.defaultStyle,
|
|
1070
|
+
baseStyle: cache.baseStyle,
|
|
1071
|
+
image: cache.image,
|
|
1072
|
+
resource: cache.resource,
|
|
1073
|
+
background: cache.background,
|
|
1074
|
+
font: cache.font
|
|
1075
|
+
},
|
|
1076
|
+
options: opts || {}
|
|
1077
|
+
};
|
|
1147
1078
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1079
|
+
return {
|
|
1080
|
+
session: cache.session,
|
|
1081
|
+
persist: {
|
|
1082
|
+
snapshotKeyCache,
|
|
1083
|
+
defaultStyle: cache.defaultStyle,
|
|
1084
|
+
baseStyle: cache.baseStyle,
|
|
1085
|
+
image: cache.image,
|
|
1086
|
+
resource: cache.resource,
|
|
1087
|
+
background: cache.background,
|
|
1088
|
+
font: cache.font
|
|
1089
|
+
},
|
|
1090
|
+
options: sessionOrCtx || opts || {}
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
async function inlineAllStyles(source, clone, sessionOrCtx, opts) {
|
|
1094
|
+
if (source.tagName === "STYLE") return;
|
|
1095
|
+
const ctx = _resolveCtx(sessionOrCtx, opts);
|
|
1096
|
+
const resetMode = ctx.options && ctx.options.cache || "auto";
|
|
1097
|
+
if (resetMode !== "disabled") setupInvalidationOnce(document.documentElement);
|
|
1098
|
+
if (resetMode === "disabled" && !ctx.session.__bumpedForDisabled) {
|
|
1099
|
+
bumpEpoch();
|
|
1100
|
+
snapshotKeyCache.clear();
|
|
1101
|
+
ctx.session.__bumpedForDisabled = true;
|
|
1150
1102
|
}
|
|
1151
|
-
if (
|
|
1152
|
-
|
|
1103
|
+
if (NO_DEFAULTS_TAGS.has(source.tagName?.toLowerCase())) {
|
|
1104
|
+
const author = source.getAttribute?.("style");
|
|
1105
|
+
if (author) clone.setAttribute("style", author);
|
|
1153
1106
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1107
|
+
const { session, persist } = ctx;
|
|
1108
|
+
if (!session.styleCache.has(source)) {
|
|
1109
|
+
session.styleCache.set(source, getComputedStyle(source));
|
|
1110
|
+
}
|
|
1111
|
+
const pre = session.styleCache.get(source);
|
|
1112
|
+
const snap = getSnapshot(source, pre, ctx.options);
|
|
1113
|
+
const sig = styleSignature(snap);
|
|
1114
|
+
let key = persist.snapshotKeyCache.get(sig);
|
|
1115
|
+
if (!key) {
|
|
1116
|
+
const tag = source.tagName?.toLowerCase() || "div";
|
|
1117
|
+
key = getStyleKey(snap, tag);
|
|
1118
|
+
persist.snapshotKeyCache.set(sig, key);
|
|
1119
|
+
}
|
|
1120
|
+
session.styleMap.set(clone, key);
|
|
1121
|
+
}
|
|
1122
|
+
function isReplaced(el) {
|
|
1123
|
+
return el instanceof HTMLImageElement || el instanceof HTMLCanvasElement || el instanceof HTMLVideoElement || el instanceof HTMLIFrameElement || el instanceof SVGElement || el instanceof HTMLObjectElement || el instanceof HTMLEmbedElement;
|
|
1124
|
+
}
|
|
1125
|
+
function hasBox(cs) {
|
|
1126
|
+
if (cs.backgroundImage && cs.backgroundImage !== "none") return true;
|
|
1127
|
+
if (cs.backgroundColor && cs.backgroundColor !== "rgba(0, 0, 0, 0)" && cs.backgroundColor !== "transparent") return true;
|
|
1128
|
+
if ((parseFloat(cs.borderTopWidth) || 0) > 0) return true;
|
|
1129
|
+
if ((parseFloat(cs.borderBottomWidth) || 0) > 0) return true;
|
|
1130
|
+
if ((parseFloat(cs.paddingTop) || 0) > 0) return true;
|
|
1131
|
+
if ((parseFloat(cs.paddingBottom) || 0) > 0) return true;
|
|
1132
|
+
const ob = cs.overflowBlock || cs.overflowY || "visible";
|
|
1133
|
+
return ob !== "visible";
|
|
1134
|
+
}
|
|
1135
|
+
function isFlexOrGridItem(el) {
|
|
1136
|
+
const p = el.parentElement;
|
|
1137
|
+
if (!p) return false;
|
|
1138
|
+
const pd = getComputedStyle(p).display || "";
|
|
1139
|
+
return pd.includes("flex") || pd.includes("grid");
|
|
1140
|
+
}
|
|
1141
|
+
function hasFlowFast(el, cs) {
|
|
1142
|
+
if (el.textContent && /\S/.test(el.textContent)) return true;
|
|
1143
|
+
const f = el.firstElementChild, l = el.lastElementChild;
|
|
1144
|
+
if (f && f.tagName === "BR" || l && l.tagName === "BR") return true;
|
|
1145
|
+
const sh = el.scrollHeight;
|
|
1146
|
+
if (sh === 0) return false;
|
|
1147
|
+
const pt = parseFloat(cs.paddingTop) || 0;
|
|
1148
|
+
const pb = parseFloat(cs.paddingBottom) || 0;
|
|
1149
|
+
return sh > pt + pb;
|
|
1150
|
+
}
|
|
1151
|
+
function stripHeightForWrappers(el, cs, snap) {
|
|
1152
|
+
if (el instanceof HTMLElement && el.style && el.style.height) return;
|
|
1153
|
+
const tag = el.tagName && el.tagName.toLowerCase();
|
|
1154
|
+
if (!tag || tag !== "div" && tag !== "section" && tag !== "article" && tag !== "main" && tag !== "aside" && tag !== "header" && tag !== "footer" && tag !== "nav") {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
const disp = cs.display || "";
|
|
1158
|
+
if (disp.includes("flex") || disp.includes("grid")) return;
|
|
1159
|
+
if (isReplaced(el)) return;
|
|
1160
|
+
const pos = cs.position;
|
|
1161
|
+
if (pos === "absolute" || pos === "fixed" || pos === "sticky") return;
|
|
1162
|
+
if (cs.transform !== "none") return;
|
|
1163
|
+
if (hasBox(cs)) return;
|
|
1164
|
+
if (isFlexOrGridItem(el)) return;
|
|
1165
|
+
const overflowX = cs.overflowX || cs.overflow || "visible";
|
|
1166
|
+
const overflowY = cs.overflowY || cs.overflow || "visible";
|
|
1167
|
+
if (overflowX !== "visible" || overflowY !== "visible") return;
|
|
1168
|
+
const clip = cs.clip;
|
|
1169
|
+
if (clip && clip !== "auto" && clip !== "rect(auto, auto, auto, auto)") return;
|
|
1170
|
+
if (cs.visibility === "hidden" || cs.opacity === "0") return;
|
|
1171
|
+
if (!hasFlowFast(el, cs)) return;
|
|
1172
|
+
delete snap.height;
|
|
1173
|
+
delete snap["block-size"];
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// src/core/clone.js
|
|
1177
|
+
init_css();
|
|
1178
|
+
|
|
1179
|
+
// src/modules/CSSVar.js
|
|
1180
|
+
var KEY_PROPS = ["fill", "stroke", "color", "background-color", "stop-color"];
|
|
1181
|
+
var __BASELINE_CACHE = /* @__PURE__ */ new Map();
|
|
1182
|
+
function getBaselineComputed(tagName, ns) {
|
|
1183
|
+
const key = ns + "::" + tagName.toLowerCase();
|
|
1184
|
+
let entry = __BASELINE_CACHE.get(key);
|
|
1185
|
+
if (entry) return entry;
|
|
1186
|
+
const doc = document;
|
|
1187
|
+
const el = ns === "http://www.w3.org/2000/svg" ? doc.createElementNS(ns, tagName) : doc.createElement(tagName);
|
|
1188
|
+
const holder = doc.createElement("div");
|
|
1189
|
+
holder.style.cssText = "position:absolute;left:-99999px;top:-99999px;contain:strict;display:block;";
|
|
1190
|
+
holder.appendChild(el);
|
|
1191
|
+
doc.documentElement.appendChild(holder);
|
|
1192
|
+
const cs = getComputedStyle(el);
|
|
1193
|
+
const base = {};
|
|
1194
|
+
for (const p of KEY_PROPS) {
|
|
1195
|
+
base[p] = cs.getPropertyValue(p) || "";
|
|
1196
|
+
}
|
|
1197
|
+
holder.remove();
|
|
1198
|
+
__BASELINE_CACHE.set(key, base);
|
|
1199
|
+
return base;
|
|
1200
|
+
}
|
|
1201
|
+
function resolveCSSVars(sourceEl, cloneEl) {
|
|
1202
|
+
if (!(sourceEl instanceof Element) || !(cloneEl instanceof Element)) return;
|
|
1203
|
+
const styleAttr = sourceEl.getAttribute?.("style");
|
|
1204
|
+
let hasVar = !!(styleAttr && styleAttr.includes("var("));
|
|
1205
|
+
if (!hasVar && sourceEl.attributes?.length) {
|
|
1206
|
+
const attrs = sourceEl.attributes;
|
|
1207
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
1208
|
+
const a = attrs[i];
|
|
1209
|
+
if (a && typeof a.value === "string" && a.value.includes("var(")) {
|
|
1210
|
+
hasVar = true;
|
|
1211
|
+
break;
|
|
1212
|
+
}
|
|
1162
1213
|
}
|
|
1163
1214
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1215
|
+
let cs = null;
|
|
1216
|
+
if (hasVar) {
|
|
1217
|
+
try {
|
|
1218
|
+
cs = getComputedStyle(sourceEl);
|
|
1219
|
+
} catch {
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (hasVar) {
|
|
1223
|
+
const author = sourceEl.style;
|
|
1224
|
+
if (author && author.length) {
|
|
1225
|
+
for (let i = 0; i < author.length; i++) {
|
|
1226
|
+
const prop = author[i];
|
|
1227
|
+
const val = author.getPropertyValue(prop);
|
|
1228
|
+
if (!val || !val.includes("var(")) continue;
|
|
1229
|
+
const resolved = cs && cs.getPropertyValue(prop);
|
|
1230
|
+
if (resolved) {
|
|
1231
|
+
try {
|
|
1232
|
+
cloneEl.style.setProperty(prop, resolved.trim(), author.getPropertyPriority(prop));
|
|
1233
|
+
} catch {
|
|
1175
1234
|
}
|
|
1176
1235
|
}
|
|
1177
|
-
} catch (err) {
|
|
1178
|
-
console.warn(`Invalid selector in exclude option: ${selector}`, err);
|
|
1179
1236
|
}
|
|
1180
1237
|
}
|
|
1181
1238
|
}
|
|
1182
|
-
if (
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1239
|
+
if (hasVar && sourceEl.attributes?.length) {
|
|
1240
|
+
const attrs = sourceEl.attributes;
|
|
1241
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
1242
|
+
const a = attrs[i];
|
|
1243
|
+
if (!a || typeof a.value !== "string" || !a.value.includes("var(")) continue;
|
|
1244
|
+
const propName = a.name;
|
|
1245
|
+
const resolved = cs && cs.getPropertyValue(propName);
|
|
1246
|
+
if (resolved) {
|
|
1247
|
+
try {
|
|
1248
|
+
cloneEl.style.setProperty(propName, resolved.trim());
|
|
1249
|
+
} catch {
|
|
1192
1250
|
}
|
|
1193
1251
|
}
|
|
1194
|
-
} catch (err) {
|
|
1195
|
-
console.warn("Error in filter function:", err);
|
|
1196
1252
|
}
|
|
1197
1253
|
}
|
|
1198
|
-
if (
|
|
1199
|
-
|
|
1200
|
-
try {
|
|
1201
|
-
sameOrigin = !!(node.contentDocument || node.contentWindow?.document);
|
|
1202
|
-
} catch {
|
|
1203
|
-
sameOrigin = false;
|
|
1204
|
-
}
|
|
1205
|
-
if (sameOrigin) {
|
|
1254
|
+
if (!hasVar) {
|
|
1255
|
+
if (!cs) {
|
|
1206
1256
|
try {
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
console.warn("[SnapDOM] iframe rasterization failed, fallback:", err);
|
|
1257
|
+
cs = getComputedStyle(sourceEl);
|
|
1258
|
+
} catch {
|
|
1259
|
+
cs = null;
|
|
1211
1260
|
}
|
|
1212
1261
|
}
|
|
1213
|
-
if (
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
const spacer = document.createElement("div");
|
|
1221
|
-
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1222
|
-
inlineAllStyles(node, spacer, sessionCache, options);
|
|
1223
|
-
return spacer;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
if (node.getAttribute("data-capture") === "placeholder") {
|
|
1227
|
-
const clone2 = node.cloneNode(false);
|
|
1228
|
-
sessionCache.nodeMap.set(clone2, node);
|
|
1229
|
-
inlineAllStyles(node, clone2, sessionCache, options);
|
|
1230
|
-
const placeholder = document.createElement("div");
|
|
1231
|
-
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
1232
|
-
placeholder.style.cssText = "color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;";
|
|
1233
|
-
clone2.appendChild(placeholder);
|
|
1234
|
-
return clone2;
|
|
1235
|
-
}
|
|
1236
|
-
if (node.tagName === "CANVAS") {
|
|
1237
|
-
let url = "";
|
|
1238
|
-
try {
|
|
1239
|
-
const ctx = node.getContext("2d", { willReadFrequently: true });
|
|
1240
|
-
try {
|
|
1241
|
-
ctx && ctx.getImageData(0, 0, 1, 1);
|
|
1242
|
-
} catch {
|
|
1243
|
-
}
|
|
1244
|
-
await new Promise((r) => requestAnimationFrame(r));
|
|
1245
|
-
url = node.toDataURL("image/png");
|
|
1246
|
-
if (!url || url === "data:,") {
|
|
1262
|
+
if (!cs) return;
|
|
1263
|
+
const ns = sourceEl.namespaceURI || "html";
|
|
1264
|
+
const base = getBaselineComputed(sourceEl.tagName, ns);
|
|
1265
|
+
for (const prop of KEY_PROPS) {
|
|
1266
|
+
const v = cs.getPropertyValue(prop) || "";
|
|
1267
|
+
const b = base[prop] || "";
|
|
1268
|
+
if (v && v !== b) {
|
|
1247
1269
|
try {
|
|
1248
|
-
|
|
1270
|
+
cloneEl.style.setProperty(prop, v.trim());
|
|
1249
1271
|
} catch {
|
|
1250
1272
|
}
|
|
1251
|
-
await new Promise((r) => requestAnimationFrame(r));
|
|
1252
|
-
url = node.toDataURL("image/png");
|
|
1253
|
-
if (!url || url === "data:,") {
|
|
1254
|
-
const scratch = document.createElement("canvas");
|
|
1255
|
-
scratch.width = node.width;
|
|
1256
|
-
scratch.height = node.height;
|
|
1257
|
-
const sctx = scratch.getContext("2d");
|
|
1258
|
-
if (sctx) {
|
|
1259
|
-
sctx.drawImage(node, 0, 0);
|
|
1260
|
-
url = scratch.toDataURL("image/png");
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
1273
|
}
|
|
1264
|
-
} catch {
|
|
1265
|
-
}
|
|
1266
|
-
const img = document.createElement("img");
|
|
1267
|
-
try {
|
|
1268
|
-
img.decoding = "sync";
|
|
1269
|
-
img.loading = "eager";
|
|
1270
|
-
} catch {
|
|
1271
|
-
}
|
|
1272
|
-
if (url) img.src = url;
|
|
1273
|
-
img.width = node.width;
|
|
1274
|
-
img.height = node.height;
|
|
1275
|
-
try {
|
|
1276
|
-
const cs = getComputedStyle(node);
|
|
1277
|
-
if (cs.width) img.style.width = cs.width;
|
|
1278
|
-
if (cs.height) img.style.height = cs.height;
|
|
1279
|
-
} catch {
|
|
1280
1274
|
}
|
|
1281
|
-
sessionCache.nodeMap.set(img, node);
|
|
1282
|
-
inlineAllStyles(node, img, sessionCache, options);
|
|
1283
|
-
return img;
|
|
1284
1275
|
}
|
|
1285
|
-
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// src/utils/clone.helpers.js
|
|
1279
|
+
init_utils();
|
|
1280
|
+
init_cache();
|
|
1281
|
+
init_snapFetch();
|
|
1282
|
+
function idleCallback(childList, callback, fast) {
|
|
1283
|
+
return Promise.all(childList.map((child) => {
|
|
1284
|
+
return new Promise((resolve) => {
|
|
1285
|
+
function deal() {
|
|
1286
|
+
idle((deadline) => {
|
|
1287
|
+
const hasIdleBudget = deadline && typeof deadline.timeRemaining === "function" ? deadline.timeRemaining() > 0 : true;
|
|
1288
|
+
if (hasIdleBudget) {
|
|
1289
|
+
callback(child, resolve);
|
|
1290
|
+
} else {
|
|
1291
|
+
deal();
|
|
1292
|
+
}
|
|
1293
|
+
}, { fast });
|
|
1294
|
+
}
|
|
1295
|
+
deal();
|
|
1296
|
+
});
|
|
1297
|
+
}));
|
|
1298
|
+
}
|
|
1299
|
+
function addNotSlottedRightmost(sel) {
|
|
1300
|
+
sel = sel.trim();
|
|
1301
|
+
if (!sel) return sel;
|
|
1302
|
+
if (/:not\(\s*\[data-sd-slotted\]\s*\)\s*$/.test(sel)) return sel;
|
|
1303
|
+
return `${sel}:not([data-sd-slotted])`;
|
|
1304
|
+
}
|
|
1305
|
+
function wrapWithScope(selectorList, scopeSelector, excludeSlotted = true) {
|
|
1306
|
+
return selectorList.split(",").map((s) => s.trim()).filter(Boolean).map((s) => {
|
|
1307
|
+
if (s.startsWith(":where(")) return s;
|
|
1308
|
+
if (s.startsWith("@")) return s;
|
|
1309
|
+
const body = excludeSlotted ? addNotSlottedRightmost(s) : s;
|
|
1310
|
+
return `:where(${scopeSelector} ${body})`;
|
|
1311
|
+
}).join(", ");
|
|
1312
|
+
}
|
|
1313
|
+
function rewriteShadowCSS(cssText, scopeSelector) {
|
|
1314
|
+
if (!cssText) return "";
|
|
1315
|
+
cssText = cssText.replace(/:host\(([^)]+)\)/g, (_, sel) => {
|
|
1316
|
+
return `:where(${scopeSelector}:is(${sel.trim()}))`;
|
|
1317
|
+
});
|
|
1318
|
+
cssText = cssText.replace(/:host\b/g, `:where(${scopeSelector})`);
|
|
1319
|
+
cssText = cssText.replace(/:host-context\(([^)]+)\)/g, (_, sel) => {
|
|
1320
|
+
return `:where(:where(${sel.trim()}) ${scopeSelector})`;
|
|
1321
|
+
});
|
|
1322
|
+
cssText = cssText.replace(/::slotted\(([^)]+)\)/g, (_, sel) => {
|
|
1323
|
+
return `:where(${scopeSelector} ${sel.trim()})`;
|
|
1324
|
+
});
|
|
1325
|
+
cssText = cssText.replace(/(^|})(\s*)([^@}{]+){/g, (_, brace, ws, selectorList) => {
|
|
1326
|
+
const wrapped = wrapWithScope(
|
|
1327
|
+
selectorList,
|
|
1328
|
+
scopeSelector,
|
|
1329
|
+
/*excludeSlotted*/
|
|
1330
|
+
true
|
|
1331
|
+
);
|
|
1332
|
+
return `${brace}${ws}${wrapped}{`;
|
|
1333
|
+
});
|
|
1334
|
+
return cssText;
|
|
1335
|
+
}
|
|
1336
|
+
function nextShadowScopeId(sessionCache) {
|
|
1337
|
+
sessionCache.shadowScopeSeq = (sessionCache.shadowScopeSeq || 0) + 1;
|
|
1338
|
+
return `s${sessionCache.shadowScopeSeq}`;
|
|
1339
|
+
}
|
|
1340
|
+
function extractShadowCSS(sr) {
|
|
1341
|
+
let css = "";
|
|
1286
1342
|
try {
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1343
|
+
sr.querySelectorAll("style").forEach((s) => {
|
|
1344
|
+
css += (s.textContent || "") + "\n";
|
|
1345
|
+
});
|
|
1346
|
+
const sheets = sr.adoptedStyleSheets || [];
|
|
1347
|
+
for (const sh of sheets) {
|
|
1292
1348
|
try {
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
let h = Math.round(rect.height || 0);
|
|
1296
|
-
if (!w || !h) {
|
|
1297
|
-
const computed = window.getComputedStyle(node);
|
|
1298
|
-
const cssW = parseFloat(computed.width) || 0;
|
|
1299
|
-
const cssH = parseFloat(computed.height) || 0;
|
|
1300
|
-
const attrW = parseInt(node.getAttribute("width") || "", 10) || 0;
|
|
1301
|
-
const attrH = parseInt(node.getAttribute("height") || "", 10) || 0;
|
|
1302
|
-
const propW = node.width || node.naturalWidth || 0;
|
|
1303
|
-
const propH = node.height || node.naturalHeight || 0;
|
|
1304
|
-
w = Math.round(w || cssW || attrW || propW || 0);
|
|
1305
|
-
h = Math.round(h || cssH || attrH || propH || 0);
|
|
1349
|
+
if (sh && sh.cssRules) {
|
|
1350
|
+
for (const rule of sh.cssRules) css += rule.cssText + "\n";
|
|
1306
1351
|
}
|
|
1307
|
-
if (w) clone.dataset.snapdomWidth = String(w);
|
|
1308
|
-
if (h) clone.dataset.snapdomHeight = String(h);
|
|
1309
1352
|
} catch {
|
|
1310
1353
|
}
|
|
1311
1354
|
}
|
|
1312
|
-
} catch
|
|
1313
|
-
console.error("[Snapdom] Failed to clone node:", node, err);
|
|
1314
|
-
throw err;
|
|
1355
|
+
} catch {
|
|
1315
1356
|
}
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1357
|
+
return css;
|
|
1358
|
+
}
|
|
1359
|
+
function injectScopedStyle(hostClone, cssText, scopeId) {
|
|
1360
|
+
if (!cssText) return;
|
|
1361
|
+
const style = document.createElement("style");
|
|
1362
|
+
style.setAttribute("data-sd", scopeId);
|
|
1363
|
+
style.textContent = cssText;
|
|
1364
|
+
hostClone.insertBefore(style, hostClone.firstChild || null);
|
|
1365
|
+
}
|
|
1366
|
+
function freezeImgSrcset(original, cloned) {
|
|
1367
|
+
try {
|
|
1368
|
+
const chosen = original.currentSrc || original.src || "";
|
|
1369
|
+
if (!chosen) return;
|
|
1370
|
+
cloned.setAttribute("src", chosen);
|
|
1371
|
+
cloned.removeAttribute("srcset");
|
|
1372
|
+
cloned.removeAttribute("sizes");
|
|
1373
|
+
cloned.loading = "eager";
|
|
1374
|
+
cloned.decoding = "sync";
|
|
1375
|
+
} catch {
|
|
1320
1376
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1377
|
+
}
|
|
1378
|
+
function collectCustomPropsFromCSS(cssText) {
|
|
1379
|
+
const out = /* @__PURE__ */ new Set();
|
|
1380
|
+
if (!cssText) return out;
|
|
1381
|
+
const re = /var\(\s*(--[A-Za-z0-9_-]+)\b/g;
|
|
1382
|
+
let m;
|
|
1383
|
+
while (m = re.exec(cssText)) out.add(m[1]);
|
|
1384
|
+
return out;
|
|
1385
|
+
}
|
|
1386
|
+
function resolveCustomProp(el, name) {
|
|
1387
|
+
try {
|
|
1388
|
+
const cs = getComputedStyle(el);
|
|
1389
|
+
let v = cs.getPropertyValue(name).trim();
|
|
1390
|
+
if (v) return v;
|
|
1391
|
+
} catch {
|
|
1329
1392
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1393
|
+
try {
|
|
1394
|
+
const rootCS = getComputedStyle(document.documentElement);
|
|
1395
|
+
let v = rootCS.getPropertyValue(name).trim();
|
|
1396
|
+
if (v) return v;
|
|
1397
|
+
} catch {
|
|
1332
1398
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1399
|
+
return "";
|
|
1400
|
+
}
|
|
1401
|
+
function buildSeedCustomPropsRule(hostEl, names, scopeSelector) {
|
|
1402
|
+
const decls = [];
|
|
1403
|
+
for (const name of names) {
|
|
1404
|
+
const val = resolveCustomProp(hostEl, name);
|
|
1405
|
+
if (val) decls.push(`${name}: ${val};`);
|
|
1335
1406
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1407
|
+
if (!decls.length) return "";
|
|
1408
|
+
return `${scopeSelector}{${decls.join("")}}
|
|
1409
|
+
`;
|
|
1410
|
+
}
|
|
1411
|
+
function markSlottedSubtree(root) {
|
|
1412
|
+
if (!root) return;
|
|
1413
|
+
if (root.nodeType === Node.ELEMENT_NODE) {
|
|
1414
|
+
root.setAttribute("data-sd-slotted", "");
|
|
1415
|
+
}
|
|
1416
|
+
if (root.querySelectorAll) {
|
|
1417
|
+
root.querySelectorAll("*").forEach((el) => el.setAttribute("data-sd-slotted", ""));
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
async function getAccessibleIframeDocument(iframe, attempts = 3) {
|
|
1421
|
+
const probe = () => {
|
|
1349
1422
|
try {
|
|
1350
|
-
|
|
1351
|
-
for (const s of slots) {
|
|
1352
|
-
let assigned = [];
|
|
1353
|
-
try {
|
|
1354
|
-
assigned = s.assignedNodes?.({ flatten: true }) || s.assignedNodes?.() || [];
|
|
1355
|
-
} catch {
|
|
1356
|
-
assigned = s.assignedNodes?.() || [];
|
|
1357
|
-
}
|
|
1358
|
-
for (const an of assigned) clonedAssignedNodes.add(an);
|
|
1359
|
-
}
|
|
1360
|
-
} catch {
|
|
1361
|
-
}
|
|
1362
|
-
const scopeId = nextShadowScopeId(sessionCache);
|
|
1363
|
-
const scopeSelector = `[data-sd="${scopeId}"]`;
|
|
1364
|
-
try {
|
|
1365
|
-
clone.setAttribute("data-sd", scopeId);
|
|
1423
|
+
return iframe.contentDocument || iframe.contentWindow?.document || null;
|
|
1366
1424
|
} catch {
|
|
1425
|
+
return null;
|
|
1367
1426
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
shadowFrag.append(...cloneList2.filter((clonedChild) => !!clonedChild));
|
|
1376
|
-
clone.appendChild(shadowFrag);
|
|
1377
|
-
}
|
|
1378
|
-
if (node.tagName === "SLOT") {
|
|
1379
|
-
let callback2 = function(child, resolve) {
|
|
1380
|
-
deepClone(child, sessionCache, options).then((clonedChild) => {
|
|
1381
|
-
if (clonedChild) {
|
|
1382
|
-
markSlottedSubtree(clonedChild);
|
|
1383
|
-
}
|
|
1384
|
-
resolve(clonedChild || null);
|
|
1385
|
-
}).catch(() => {
|
|
1386
|
-
resolve(null);
|
|
1387
|
-
});
|
|
1388
|
-
};
|
|
1389
|
-
const assigned = node.assignedNodes?.({ flatten: true }) || [];
|
|
1390
|
-
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
1391
|
-
const fragment = document.createDocumentFragment();
|
|
1392
|
-
const cloneList2 = await idleCallback(Array.from(nodesToClone), callback2, options.fast);
|
|
1393
|
-
fragment.append(...cloneList2.filter((clonedChild) => !!clonedChild));
|
|
1394
|
-
return fragment;
|
|
1395
|
-
}
|
|
1396
|
-
function callback(child, resolve) {
|
|
1397
|
-
if (clonedAssignedNodes.has(child)) return resolve(null);
|
|
1398
|
-
deepClone(child, sessionCache, options).then((clonedChild) => {
|
|
1399
|
-
resolve(clonedChild || null);
|
|
1400
|
-
}).catch(() => {
|
|
1401
|
-
resolve(null);
|
|
1402
|
-
});
|
|
1403
|
-
}
|
|
1404
|
-
const cloneList = await idleCallback(Array.from(node.childNodes), callback, options.fast);
|
|
1405
|
-
clone.append(...cloneList.filter((clonedChild) => !!clonedChild));
|
|
1406
|
-
if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
|
|
1407
|
-
clone.value = pendingSelectValue;
|
|
1408
|
-
for (const opt of clone.options) {
|
|
1409
|
-
if (opt.value === pendingSelectValue) {
|
|
1410
|
-
opt.setAttribute("selected", "");
|
|
1411
|
-
} else {
|
|
1412
|
-
opt.removeAttribute("selected");
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
if (pendingTextAreaValue !== null && clone instanceof HTMLTextAreaElement) {
|
|
1417
|
-
clone.textContent = pendingTextAreaValue;
|
|
1418
|
-
}
|
|
1419
|
-
return clone;
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
// src/modules/iconFonts.js
|
|
1423
|
-
var defaultIconFonts = [
|
|
1424
|
-
// /uicons/i,
|
|
1425
|
-
/font\s*awesome/i,
|
|
1426
|
-
/material\s*icons/i,
|
|
1427
|
-
/ionicons/i,
|
|
1428
|
-
/glyphicons/i,
|
|
1429
|
-
/feather/i,
|
|
1430
|
-
/bootstrap\s*icons/i,
|
|
1431
|
-
/remix\s*icons/i,
|
|
1432
|
-
/heroicons/i,
|
|
1433
|
-
/layui/i,
|
|
1434
|
-
/lucide/i
|
|
1435
|
-
];
|
|
1436
|
-
var ICON_FONT_URLS = Object.assign({
|
|
1437
|
-
materialIconsFilled: "https://fonts.gstatic.com/s/materialicons/v48/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2",
|
|
1438
|
-
materialIconsOutlined: "https://fonts.gstatic.com/s/materialiconsoutlined/v110/gok-H7zzDkdnRel8-DQ6KAXJ69wP1tGnf4ZGhUcel5euIg.woff2",
|
|
1439
|
-
materialIconsRound: "https://fonts.gstatic.com/s/materialiconsround/v109/LDItaoyNOAY6Uewc665JcIzCKsKc_M9flwmPq_HTTw.woff2",
|
|
1440
|
-
materialIconsSharp: "https://fonts.gstatic.com/s/materialiconssharp/v110/oPWQ_lt5nv4pWNJpghLP75WiFR4kLh3kvmvRImcycg.woff2"
|
|
1441
|
-
}, typeof window !== "undefined" && window.__SNAPDOM_ICON_FONTS__ || {});
|
|
1442
|
-
var userIconFonts = [];
|
|
1443
|
-
function extendIconFonts(fonts) {
|
|
1444
|
-
const list = Array.isArray(fonts) ? fonts : [fonts];
|
|
1445
|
-
for (const f of list) {
|
|
1446
|
-
if (f instanceof RegExp) userIconFonts.push(f);
|
|
1447
|
-
else if (typeof f === "string") userIconFonts.push(new RegExp(f, "i"));
|
|
1448
|
-
else console.warn("[snapdom] Ignored invalid iconFont value:", f);
|
|
1427
|
+
};
|
|
1428
|
+
let doc = probe();
|
|
1429
|
+
let i = 0;
|
|
1430
|
+
while (i < attempts && (!doc || !doc.body && !doc.documentElement)) {
|
|
1431
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
1432
|
+
doc = probe();
|
|
1433
|
+
i++;
|
|
1449
1434
|
}
|
|
1435
|
+
return doc && (doc.body || doc.documentElement) ? doc : null;
|
|
1450
1436
|
}
|
|
1451
|
-
function
|
|
1452
|
-
const
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1437
|
+
function measureContentBox(el) {
|
|
1438
|
+
const rect = el.getBoundingClientRect();
|
|
1439
|
+
let bl = 0, br = 0, bt = 0, bb = 0;
|
|
1440
|
+
try {
|
|
1441
|
+
const cs = getComputedStyle(el);
|
|
1442
|
+
bl = parseFloat(cs.borderLeftWidth) || 0;
|
|
1443
|
+
br = parseFloat(cs.borderRightWidth) || 0;
|
|
1444
|
+
bt = parseFloat(cs.borderTopWidth) || 0;
|
|
1445
|
+
bb = parseFloat(cs.borderBottomWidth) || 0;
|
|
1446
|
+
} catch {
|
|
1456
1447
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
}
|
|
1460
|
-
function isMaterialFamily(family = "") {
|
|
1461
|
-
const s = String(family).toLowerCase();
|
|
1462
|
-
return /\bmaterial\s*icons\b/.test(s) || /\bmaterial\s*symbols\b/.test(s);
|
|
1448
|
+
const contentWidth = Math.max(0, Math.round(rect.width - (bl + br)));
|
|
1449
|
+
const contentHeight = Math.max(0, Math.round(rect.height - (bt + bb)));
|
|
1450
|
+
return { contentWidth, contentHeight, rect };
|
|
1463
1451
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1452
|
+
function pinIframeViewport(doc, w, h) {
|
|
1453
|
+
const style = doc.createElement("style");
|
|
1454
|
+
style.setAttribute("data-sd-iframe-pin", "");
|
|
1455
|
+
style.textContent = `html, body {margin: 0 !important;padding: 0 !important;width: ${w}px !important;height: ${h}px !important;min-width: ${w}px !important;min-height: ${h}px !important;box-sizing: border-box !important;overflow: hidden !important;background-clip: border-box !important;}`;
|
|
1456
|
+
(doc.head || doc.documentElement).appendChild(style);
|
|
1457
|
+
return () => {
|
|
1458
|
+
try {
|
|
1459
|
+
style.remove();
|
|
1460
|
+
} catch {
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1472
1463
|
}
|
|
1473
|
-
async function
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1464
|
+
async function rasterizeIframe(iframe, sessionCache, options) {
|
|
1465
|
+
const doc = await getAccessibleIframeDocument(iframe, 3);
|
|
1466
|
+
if (!doc) throw new Error("iframe document not accessible/ready");
|
|
1467
|
+
const { contentWidth, contentHeight, rect } = measureContentBox(iframe);
|
|
1468
|
+
const snap = options?.snap;
|
|
1469
|
+
if (!snap || typeof snap.toPng !== "function") {
|
|
1470
|
+
throw new Error("snapdom.toPng not available in iframe or window");
|
|
1479
1471
|
}
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
|
|
1472
|
+
const nested = { ...options, scale: 1 };
|
|
1473
|
+
const unpin = pinIframeViewport(doc, contentWidth, contentHeight);
|
|
1474
|
+
let imgEl;
|
|
1475
|
+
try {
|
|
1476
|
+
imgEl = await snap.toPng(doc.documentElement, nested);
|
|
1477
|
+
} finally {
|
|
1478
|
+
unpin();
|
|
1483
1479
|
}
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1480
|
+
imgEl.style.display = "block";
|
|
1481
|
+
imgEl.style.width = `${contentWidth}px`;
|
|
1482
|
+
imgEl.style.height = `${contentHeight}px`;
|
|
1483
|
+
const wrapper = document.createElement("div");
|
|
1484
|
+
sessionCache.nodeMap.set(wrapper, iframe);
|
|
1485
|
+
inlineAllStyles(iframe, wrapper, sessionCache, options);
|
|
1486
|
+
wrapper.style.overflow = "hidden";
|
|
1487
|
+
wrapper.style.display = "block";
|
|
1488
|
+
if (!wrapper.style.width) wrapper.style.width = `${Math.round(rect.width)}px`;
|
|
1489
|
+
if (!wrapper.style.height) wrapper.style.height = `${Math.round(rect.height)}px`;
|
|
1490
|
+
wrapper.appendChild(imgEl);
|
|
1491
|
+
return wrapper;
|
|
1492
|
+
}
|
|
1493
|
+
var _blobToDataUrlCache = /* @__PURE__ */ new Map();
|
|
1494
|
+
async function blobUrlToDataUrl(blobUrl) {
|
|
1495
|
+
if (cache.resource?.has(blobUrl)) return cache.resource.get(blobUrl);
|
|
1496
|
+
if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl);
|
|
1497
|
+
const p = (async () => {
|
|
1498
|
+
const r = await snapFetch(blobUrl, { as: "dataURL", silent: true });
|
|
1499
|
+
if (!r.ok || typeof r.data !== "string") {
|
|
1500
|
+
throw new Error(`[snapDOM] Failed to read blob URL: ${blobUrl}`);
|
|
1498
1501
|
}
|
|
1502
|
+
cache.resource?.set(blobUrl, r.data);
|
|
1503
|
+
return r.data;
|
|
1504
|
+
})();
|
|
1505
|
+
_blobToDataUrlCache.set(blobUrl, p);
|
|
1506
|
+
try {
|
|
1507
|
+
const data = await p;
|
|
1508
|
+
_blobToDataUrlCache.set(blobUrl, data);
|
|
1509
|
+
return data;
|
|
1510
|
+
} catch (e) {
|
|
1511
|
+
_blobToDataUrlCache.delete(blobUrl);
|
|
1512
|
+
throw e;
|
|
1499
1513
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
if (!
|
|
1514
|
+
}
|
|
1515
|
+
var BLOB_URL_RE = /\bblob:[^)"'\s]+/g;
|
|
1516
|
+
async function replaceBlobUrlsInCssText(cssText) {
|
|
1517
|
+
if (!cssText || cssText.indexOf("blob:") === -1) return cssText;
|
|
1518
|
+
const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || []));
|
|
1519
|
+
if (uniques.length === 0) return cssText;
|
|
1520
|
+
let out = cssText;
|
|
1521
|
+
for (const u of uniques) {
|
|
1504
1522
|
try {
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1507
|
-
await ff.load();
|
|
1508
|
-
loadedCanvasFamilies.set(pick.alias, true);
|
|
1523
|
+
const d = await blobUrlToDataUrl(u);
|
|
1524
|
+
out = out.split(u).join(d);
|
|
1509
1525
|
} catch {
|
|
1510
|
-
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
1511
1526
|
}
|
|
1512
1527
|
}
|
|
1513
|
-
|
|
1514
|
-
return { familyForMeasure: quoted, familyForCanvas: quoted };
|
|
1528
|
+
return out;
|
|
1515
1529
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
await Promise.all([
|
|
1519
|
-
document.fonts.load(`400 ${px}px "${String(family).replace(/["']/g, "")}"`),
|
|
1520
|
-
document.fonts.ready
|
|
1521
|
-
]);
|
|
1522
|
-
} catch {
|
|
1523
|
-
}
|
|
1530
|
+
function isBlobUrl(u) {
|
|
1531
|
+
return typeof u === "string" && u.startsWith("blob:");
|
|
1524
1532
|
}
|
|
1525
|
-
function
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1533
|
+
function parseSrcset(srcset) {
|
|
1534
|
+
return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => {
|
|
1535
|
+
const m = item.match(/^(\S+)(\s+.+)?$/);
|
|
1536
|
+
return m ? { url: m[1], desc: m[2] || "" } : null;
|
|
1537
|
+
}).filter(Boolean);
|
|
1538
|
+
}
|
|
1539
|
+
function stringifySrcset(parts) {
|
|
1540
|
+
return parts.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", ");
|
|
1541
|
+
}
|
|
1542
|
+
async function resolveBlobUrlsInTree(root) {
|
|
1543
|
+
if (!root) return;
|
|
1544
|
+
const imgs = root.querySelectorAll ? root.querySelectorAll("img") : [];
|
|
1545
|
+
for (const img of imgs) {
|
|
1546
|
+
try {
|
|
1547
|
+
const srcAttr = img.getAttribute("src");
|
|
1548
|
+
const effective = srcAttr || img.currentSrc || "";
|
|
1549
|
+
if (isBlobUrl(effective)) {
|
|
1550
|
+
const data = await blobUrlToDataUrl(effective);
|
|
1551
|
+
img.setAttribute("src", data);
|
|
1552
|
+
}
|
|
1553
|
+
const srcset = img.getAttribute("srcset");
|
|
1554
|
+
if (srcset && srcset.includes("blob:")) {
|
|
1555
|
+
const parts = parseSrcset(srcset);
|
|
1556
|
+
let changed = false;
|
|
1557
|
+
for (const p of parts) {
|
|
1558
|
+
if (isBlobUrl(p.url)) {
|
|
1559
|
+
try {
|
|
1560
|
+
p.url = await blobUrlToDataUrl(p.url);
|
|
1561
|
+
changed = true;
|
|
1562
|
+
} catch {
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
if (changed) img.setAttribute("srcset", stringifySrcset(parts));
|
|
1567
|
+
}
|
|
1568
|
+
} catch {
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : [];
|
|
1572
|
+
for (const node of svgImages) {
|
|
1573
|
+
try {
|
|
1574
|
+
const XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
1575
|
+
const href = node.getAttribute("href") || node.getAttributeNS?.(XLINK_NS, "href");
|
|
1576
|
+
if (isBlobUrl(href)) {
|
|
1577
|
+
const d = await blobUrlToDataUrl(href);
|
|
1578
|
+
node.setAttribute("href", d);
|
|
1579
|
+
node.removeAttributeNS?.(XLINK_NS, "href");
|
|
1580
|
+
}
|
|
1581
|
+
} catch {
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
const styled = root.querySelectorAll ? root.querySelectorAll("[style*='blob:']") : [];
|
|
1585
|
+
for (const el of styled) {
|
|
1586
|
+
try {
|
|
1587
|
+
const styleText = el.getAttribute("style");
|
|
1588
|
+
if (styleText && styleText.includes("blob:")) {
|
|
1589
|
+
const replaced = await replaceBlobUrlsInCssText(styleText);
|
|
1590
|
+
el.setAttribute("style", replaced);
|
|
1591
|
+
}
|
|
1592
|
+
} catch {
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
const styleTags = root.querySelectorAll ? root.querySelectorAll("style") : [];
|
|
1596
|
+
for (const s of styleTags) {
|
|
1597
|
+
try {
|
|
1598
|
+
const css = s.textContent || "";
|
|
1599
|
+
if (css.includes("blob:")) {
|
|
1600
|
+
s.textContent = await replaceBlobUrlsInCssText(css);
|
|
1601
|
+
}
|
|
1602
|
+
} catch {
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
const urlAttrs = ["poster"];
|
|
1606
|
+
for (const attr of urlAttrs) {
|
|
1607
|
+
const nodes = root.querySelectorAll ? root.querySelectorAll(`[${attr}^='blob:']`) : [];
|
|
1608
|
+
for (const n of nodes) {
|
|
1609
|
+
try {
|
|
1610
|
+
const u = n.getAttribute(attr);
|
|
1611
|
+
if (isBlobUrl(u)) {
|
|
1612
|
+
n.setAttribute(attr, await blobUrlToDataUrl(u));
|
|
1613
|
+
}
|
|
1614
|
+
} catch {
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// src/core/clone.js
|
|
1621
|
+
async function deepClone(node, sessionCache, options) {
|
|
1622
|
+
if (!node) throw new Error("Invalid node");
|
|
1623
|
+
const clonedAssignedNodes = /* @__PURE__ */ new Set();
|
|
1624
|
+
let pendingSelectValue = null;
|
|
1625
|
+
let pendingTextAreaValue = null;
|
|
1626
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1627
|
+
const tag = (node.localName || node.tagName || "").toLowerCase();
|
|
1628
|
+
if (node.id === "snapdom-sandbox" || node.hasAttribute("data-snapdom-sandbox")) {
|
|
1629
|
+
return null;
|
|
1630
|
+
}
|
|
1631
|
+
if (NO_CAPTURE_TAGS.has(tag)) {
|
|
1632
|
+
return null;
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
1636
|
+
return node.cloneNode(true);
|
|
1637
|
+
}
|
|
1638
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
1639
|
+
return node.cloneNode(true);
|
|
1640
|
+
}
|
|
1641
|
+
if (node.getAttribute("data-capture") === "exclude") {
|
|
1642
|
+
if (options.excludeMode === "hide") {
|
|
1643
|
+
const spacer = document.createElement("div");
|
|
1644
|
+
const rect = node.getBoundingClientRect();
|
|
1645
|
+
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1646
|
+
return spacer;
|
|
1647
|
+
} else if (options.excludeMode === "remove") {
|
|
1648
|
+
return null;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
if (options.exclude && Array.isArray(options.exclude)) {
|
|
1652
|
+
for (const selector of options.exclude) {
|
|
1653
|
+
try {
|
|
1654
|
+
if (node.matches?.(selector)) {
|
|
1655
|
+
if (options.excludeMode === "hide") {
|
|
1656
|
+
const spacer = document.createElement("div");
|
|
1657
|
+
const rect = node.getBoundingClientRect();
|
|
1658
|
+
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1659
|
+
return spacer;
|
|
1660
|
+
} else if (options.excludeMode === "remove") {
|
|
1661
|
+
return null;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
} catch (err) {
|
|
1665
|
+
console.warn(`Invalid selector in exclude option: ${selector}`, err);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
if (typeof options.filter === "function") {
|
|
1670
|
+
try {
|
|
1671
|
+
if (!options.filter(node)) {
|
|
1672
|
+
if (options.filterMode === "hide") {
|
|
1673
|
+
const spacer = document.createElement("div");
|
|
1674
|
+
const rect = node.getBoundingClientRect();
|
|
1675
|
+
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1676
|
+
return spacer;
|
|
1677
|
+
} else if (options.filterMode === "remove") {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
} catch (err) {
|
|
1682
|
+
console.warn("Error in filter function:", err);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
if (node.tagName === "IFRAME") {
|
|
1686
|
+
let sameOrigin = false;
|
|
1687
|
+
try {
|
|
1688
|
+
sameOrigin = !!(node.contentDocument || node.contentWindow?.document);
|
|
1689
|
+
} catch {
|
|
1690
|
+
sameOrigin = false;
|
|
1691
|
+
}
|
|
1692
|
+
if (sameOrigin) {
|
|
1693
|
+
try {
|
|
1694
|
+
const wrapper = await rasterizeIframe(node, sessionCache, options);
|
|
1695
|
+
return wrapper;
|
|
1696
|
+
} catch (err) {
|
|
1697
|
+
console.warn("[SnapDOM] iframe rasterization failed, fallback:", err);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
if (options.placeholders) {
|
|
1701
|
+
const fallback = document.createElement("div");
|
|
1702
|
+
fallback.style.cssText = `width:${node.offsetWidth}px;height:${node.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`;
|
|
1703
|
+
inlineAllStyles(node, fallback, sessionCache, options);
|
|
1704
|
+
return fallback;
|
|
1705
|
+
} else {
|
|
1706
|
+
const rect = node.getBoundingClientRect();
|
|
1707
|
+
const spacer = document.createElement("div");
|
|
1708
|
+
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1709
|
+
inlineAllStyles(node, spacer, sessionCache, options);
|
|
1710
|
+
return spacer;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
if (node.getAttribute("data-capture") === "placeholder") {
|
|
1714
|
+
const clone2 = node.cloneNode(false);
|
|
1715
|
+
sessionCache.nodeMap.set(clone2, node);
|
|
1716
|
+
inlineAllStyles(node, clone2, sessionCache, options);
|
|
1717
|
+
const placeholder = document.createElement("div");
|
|
1718
|
+
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
1719
|
+
placeholder.style.cssText = "color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;";
|
|
1720
|
+
clone2.appendChild(placeholder);
|
|
1721
|
+
return clone2;
|
|
1722
|
+
}
|
|
1723
|
+
if (node.tagName === "CANVAS") {
|
|
1724
|
+
let url = "";
|
|
1725
|
+
try {
|
|
1726
|
+
const ctx = node.getContext("2d", { willReadFrequently: true });
|
|
1727
|
+
try {
|
|
1728
|
+
ctx && ctx.getImageData(0, 0, 1, 1);
|
|
1729
|
+
} catch {
|
|
1730
|
+
}
|
|
1731
|
+
await new Promise((r) => requestAnimationFrame(r));
|
|
1732
|
+
url = node.toDataURL("image/png");
|
|
1733
|
+
if (!url || url === "data:,") {
|
|
1734
|
+
try {
|
|
1735
|
+
ctx && ctx.getImageData(0, 0, 1, 1);
|
|
1736
|
+
} catch {
|
|
1737
|
+
}
|
|
1738
|
+
await new Promise((r) => requestAnimationFrame(r));
|
|
1739
|
+
url = node.toDataURL("image/png");
|
|
1740
|
+
if (!url || url === "data:,") {
|
|
1741
|
+
const scratch = document.createElement("canvas");
|
|
1742
|
+
scratch.width = node.width;
|
|
1743
|
+
scratch.height = node.height;
|
|
1744
|
+
const sctx = scratch.getContext("2d");
|
|
1745
|
+
if (sctx) {
|
|
1746
|
+
sctx.drawImage(node, 0, 0);
|
|
1747
|
+
url = scratch.toDataURL("image/png");
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
} catch {
|
|
1752
|
+
}
|
|
1753
|
+
const img = document.createElement("img");
|
|
1754
|
+
try {
|
|
1755
|
+
img.decoding = "sync";
|
|
1756
|
+
img.loading = "eager";
|
|
1757
|
+
} catch {
|
|
1758
|
+
}
|
|
1759
|
+
if (url) img.src = url;
|
|
1760
|
+
img.width = node.width;
|
|
1761
|
+
img.height = node.height;
|
|
1762
|
+
try {
|
|
1763
|
+
const cs = getComputedStyle(node);
|
|
1764
|
+
if (cs.width) img.style.width = cs.width;
|
|
1765
|
+
if (cs.height) img.style.height = cs.height;
|
|
1766
|
+
} catch {
|
|
1767
|
+
}
|
|
1768
|
+
sessionCache.nodeMap.set(img, node);
|
|
1769
|
+
inlineAllStyles(node, img, sessionCache, options);
|
|
1770
|
+
return img;
|
|
1771
|
+
}
|
|
1772
|
+
let clone;
|
|
1773
|
+
try {
|
|
1774
|
+
clone = node.cloneNode(false);
|
|
1775
|
+
resolveCSSVars(node, clone);
|
|
1776
|
+
sessionCache.nodeMap.set(clone, node);
|
|
1777
|
+
if (node.tagName === "IMG") {
|
|
1778
|
+
freezeImgSrcset(node, clone);
|
|
1779
|
+
try {
|
|
1780
|
+
const rect = node.getBoundingClientRect();
|
|
1781
|
+
let w = Math.round(rect.width || 0);
|
|
1782
|
+
let h = Math.round(rect.height || 0);
|
|
1783
|
+
if (!w || !h) {
|
|
1784
|
+
const computed = window.getComputedStyle(node);
|
|
1785
|
+
const cssW = parseFloat(computed.width) || 0;
|
|
1786
|
+
const cssH = parseFloat(computed.height) || 0;
|
|
1787
|
+
const attrW = parseInt(node.getAttribute("width") || "", 10) || 0;
|
|
1788
|
+
const attrH = parseInt(node.getAttribute("height") || "", 10) || 0;
|
|
1789
|
+
const propW = node.width || node.naturalWidth || 0;
|
|
1790
|
+
const propH = node.height || node.naturalHeight || 0;
|
|
1791
|
+
w = Math.round(w || cssW || attrW || propW || 0);
|
|
1792
|
+
h = Math.round(h || cssH || attrH || propH || 0);
|
|
1793
|
+
}
|
|
1794
|
+
if (w) clone.dataset.snapdomWidth = String(w);
|
|
1795
|
+
if (h) clone.dataset.snapdomHeight = String(h);
|
|
1796
|
+
} catch {
|
|
1797
|
+
}
|
|
1798
|
+
try {
|
|
1799
|
+
const authored = node.getAttribute("style") || "";
|
|
1800
|
+
const cs = window.getComputedStyle(node);
|
|
1801
|
+
const usesPercentOrAuto = (prop) => {
|
|
1802
|
+
const a = authored.match(new RegExp(`${prop}\\s*:\\s*([^;]+)`, "i"));
|
|
1803
|
+
const v = a ? a[1].trim() : cs.getPropertyValue(prop);
|
|
1804
|
+
return /%|auto/i.test(String(v || ""));
|
|
1805
|
+
};
|
|
1806
|
+
const w = parseInt(clone.dataset.snapdomWidth || "0", 10);
|
|
1807
|
+
const h = parseInt(clone.dataset.snapdomHeight || "0", 10);
|
|
1808
|
+
const needFreezeW = usesPercentOrAuto("width") || !w;
|
|
1809
|
+
const needFreezeH = usesPercentOrAuto("height") || !h;
|
|
1810
|
+
if (needFreezeW && w) clone.style.width = `${w}px`;
|
|
1811
|
+
if (needFreezeH && h) clone.style.height = `${h}px`;
|
|
1812
|
+
if (w) clone.style.minWidth = `${w}px`;
|
|
1813
|
+
if (h) clone.style.minHeight = `${h}px`;
|
|
1814
|
+
} catch {
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
} catch (err) {
|
|
1818
|
+
console.error("[Snapdom] Failed to clone node:", node, err);
|
|
1819
|
+
throw err;
|
|
1820
|
+
}
|
|
1821
|
+
if (node instanceof HTMLTextAreaElement) {
|
|
1822
|
+
const rect = node.getBoundingClientRect();
|
|
1823
|
+
clone.style.width = `${rect.width}px`;
|
|
1824
|
+
clone.style.height = `${rect.height}px`;
|
|
1825
|
+
}
|
|
1826
|
+
if (node instanceof HTMLInputElement) {
|
|
1827
|
+
clone.value = node.value;
|
|
1828
|
+
clone.setAttribute("value", node.value);
|
|
1829
|
+
if (node.checked !== void 0) {
|
|
1830
|
+
clone.checked = node.checked;
|
|
1831
|
+
if (node.checked) clone.setAttribute("checked", "");
|
|
1832
|
+
if (node.indeterminate) clone.indeterminate = node.indeterminate;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
if (node instanceof HTMLSelectElement) {
|
|
1836
|
+
pendingSelectValue = node.value;
|
|
1837
|
+
}
|
|
1838
|
+
if (node instanceof HTMLTextAreaElement) {
|
|
1839
|
+
pendingTextAreaValue = node.value;
|
|
1840
|
+
}
|
|
1841
|
+
inlineAllStyles(node, clone, sessionCache, options);
|
|
1842
|
+
if (node.shadowRoot) {
|
|
1843
|
+
let callback2 = function(child, resolve) {
|
|
1844
|
+
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
|
|
1845
|
+
return resolve(null);
|
|
1846
|
+
} else {
|
|
1847
|
+
deepClone(child, sessionCache, options).then((clonedChild) => {
|
|
1848
|
+
resolve(clonedChild || null);
|
|
1849
|
+
}).catch(() => {
|
|
1850
|
+
resolve(null);
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1854
|
+
try {
|
|
1855
|
+
const slots = node.shadowRoot.querySelectorAll("slot");
|
|
1856
|
+
for (const s of slots) {
|
|
1857
|
+
let assigned = [];
|
|
1858
|
+
try {
|
|
1859
|
+
assigned = s.assignedNodes?.({ flatten: true }) || s.assignedNodes?.() || [];
|
|
1860
|
+
} catch {
|
|
1861
|
+
assigned = s.assignedNodes?.() || [];
|
|
1862
|
+
}
|
|
1863
|
+
for (const an of assigned) clonedAssignedNodes.add(an);
|
|
1864
|
+
}
|
|
1865
|
+
} catch {
|
|
1866
|
+
}
|
|
1867
|
+
const scopeId = nextShadowScopeId(sessionCache);
|
|
1868
|
+
const scopeSelector = `[data-sd="${scopeId}"]`;
|
|
1869
|
+
try {
|
|
1870
|
+
clone.setAttribute("data-sd", scopeId);
|
|
1871
|
+
} catch {
|
|
1872
|
+
}
|
|
1873
|
+
const rawCSS = extractShadowCSS(node.shadowRoot);
|
|
1874
|
+
const rewritten = rewriteShadowCSS(rawCSS, scopeSelector);
|
|
1875
|
+
const neededVars = collectCustomPropsFromCSS(rawCSS);
|
|
1876
|
+
const seed = buildSeedCustomPropsRule(node, neededVars, scopeSelector);
|
|
1877
|
+
injectScopedStyle(clone, seed + rewritten, scopeId);
|
|
1878
|
+
const shadowFrag = document.createDocumentFragment();
|
|
1879
|
+
const cloneList2 = await idleCallback(Array.from(node.shadowRoot.childNodes), callback2, options.fast);
|
|
1880
|
+
shadowFrag.append(...cloneList2.filter((clonedChild) => !!clonedChild));
|
|
1881
|
+
clone.appendChild(shadowFrag);
|
|
1882
|
+
}
|
|
1883
|
+
if (node.tagName === "SLOT") {
|
|
1884
|
+
let callback2 = function(child, resolve) {
|
|
1885
|
+
deepClone(child, sessionCache, options).then((clonedChild) => {
|
|
1886
|
+
if (clonedChild) {
|
|
1887
|
+
markSlottedSubtree(clonedChild);
|
|
1888
|
+
}
|
|
1889
|
+
resolve(clonedChild || null);
|
|
1890
|
+
}).catch(() => {
|
|
1891
|
+
resolve(null);
|
|
1892
|
+
});
|
|
1893
|
+
};
|
|
1894
|
+
const assigned = node.assignedNodes?.({ flatten: true }) || [];
|
|
1895
|
+
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
1896
|
+
const fragment = document.createDocumentFragment();
|
|
1897
|
+
const cloneList2 = await idleCallback(Array.from(nodesToClone), callback2, options.fast);
|
|
1898
|
+
fragment.append(...cloneList2.filter((clonedChild) => !!clonedChild));
|
|
1899
|
+
return fragment;
|
|
1900
|
+
}
|
|
1901
|
+
function callback(child, resolve) {
|
|
1902
|
+
if (clonedAssignedNodes.has(child)) return resolve(null);
|
|
1903
|
+
deepClone(child, sessionCache, options).then((clonedChild) => {
|
|
1904
|
+
resolve(clonedChild || null);
|
|
1905
|
+
}).catch(() => {
|
|
1906
|
+
resolve(null);
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
const cloneList = await idleCallback(Array.from(node.childNodes), callback, options.fast);
|
|
1910
|
+
clone.append(...cloneList.filter((clonedChild) => !!clonedChild));
|
|
1911
|
+
if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
|
|
1912
|
+
clone.value = pendingSelectValue;
|
|
1913
|
+
for (const opt of clone.options) {
|
|
1914
|
+
if (opt.value === pendingSelectValue) {
|
|
1915
|
+
opt.setAttribute("selected", "");
|
|
1916
|
+
} else {
|
|
1917
|
+
opt.removeAttribute("selected");
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
if (pendingTextAreaValue !== null && clone instanceof HTMLTextAreaElement) {
|
|
1922
|
+
clone.textContent = pendingTextAreaValue;
|
|
1923
|
+
}
|
|
1924
|
+
return clone;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
// src/modules/pseudo.js
|
|
1928
|
+
init_utils();
|
|
1929
|
+
|
|
1930
|
+
// src/modules/fonts.js
|
|
1931
|
+
init_helpers();
|
|
1932
|
+
init_cache();
|
|
1933
|
+
|
|
1934
|
+
// src/modules/iconFonts.js
|
|
1935
|
+
var defaultIconFonts = [
|
|
1936
|
+
// /uicons/i,
|
|
1937
|
+
/font\s*awesome/i,
|
|
1938
|
+
/material\s*icons/i,
|
|
1939
|
+
/ionicons/i,
|
|
1940
|
+
/glyphicons/i,
|
|
1941
|
+
/feather/i,
|
|
1942
|
+
/bootstrap\s*icons/i,
|
|
1943
|
+
/remix\s*icons/i,
|
|
1944
|
+
/heroicons/i,
|
|
1945
|
+
/layui/i,
|
|
1946
|
+
/lucide/i
|
|
1947
|
+
];
|
|
1948
|
+
var ICON_FONT_URLS = Object.assign({
|
|
1949
|
+
materialIconsFilled: "https://fonts.gstatic.com/s/materialicons/v48/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2",
|
|
1950
|
+
materialIconsOutlined: "https://fonts.gstatic.com/s/materialiconsoutlined/v110/gok-H7zzDkdnRel8-DQ6KAXJ69wP1tGnf4ZGhUcel5euIg.woff2",
|
|
1951
|
+
materialIconsRound: "https://fonts.gstatic.com/s/materialiconsround/v109/LDItaoyNOAY6Uewc665JcIzCKsKc_M9flwmPq_HTTw.woff2",
|
|
1952
|
+
materialIconsSharp: "https://fonts.gstatic.com/s/materialiconssharp/v110/oPWQ_lt5nv4pWNJpghLP75WiFR4kLh3kvmvRImcycg.woff2"
|
|
1953
|
+
}, typeof window !== "undefined" && window.__SNAPDOM_ICON_FONTS__ || {});
|
|
1954
|
+
var userIconFonts = [];
|
|
1955
|
+
function extendIconFonts(fonts) {
|
|
1956
|
+
const list = Array.isArray(fonts) ? fonts : [fonts];
|
|
1957
|
+
for (const f of list) {
|
|
1958
|
+
if (f instanceof RegExp) userIconFonts.push(f);
|
|
1959
|
+
else if (typeof f === "string") userIconFonts.push(new RegExp(f, "i"));
|
|
1960
|
+
else console.warn("[snapdom] Ignored invalid iconFont value:", f);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
function isIconFont2(input) {
|
|
1964
|
+
const text = typeof input === "string" ? input : "";
|
|
1965
|
+
const candidates = [...defaultIconFonts, ...userIconFonts];
|
|
1966
|
+
for (const rx of candidates) {
|
|
1967
|
+
if (rx instanceof RegExp && rx.test(text)) return true;
|
|
1968
|
+
}
|
|
1969
|
+
if (/icon/i.test(text) || /glyph/i.test(text) || /symbols/i.test(text) || /feather/i.test(text) || /fontawesome/i.test(text)) return true;
|
|
1970
|
+
return false;
|
|
1971
|
+
}
|
|
1972
|
+
function isMaterialFamily(family = "") {
|
|
1973
|
+
const s = String(family).toLowerCase();
|
|
1974
|
+
return /\bmaterial\s*icons\b/.test(s) || /\bmaterial\s*symbols\b/.test(s);
|
|
1975
|
+
}
|
|
1976
|
+
var loadedCanvasFamilies = /* @__PURE__ */ new Map();
|
|
1977
|
+
function parseAxes(variation = "") {
|
|
1978
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1979
|
+
const v = String(variation || "");
|
|
1980
|
+
const rx = /['"]?\s*([A-Za-z]{3,4})\s*['"]?\s*([+-]?\d+(?:\.\d+)?)\s*/g;
|
|
1981
|
+
let m;
|
|
1982
|
+
while (m = rx.exec(v)) out[m[1].toUpperCase()] = Number(m[2]);
|
|
1983
|
+
return out;
|
|
1984
|
+
}
|
|
1985
|
+
async function ensureLigatureCanvasFont(cssFamily, className, axes) {
|
|
1986
|
+
const fam = String(cssFamily || "");
|
|
1987
|
+
const lowerFam = fam.toLowerCase();
|
|
1988
|
+
const cls = String(className || "").toLowerCase();
|
|
1989
|
+
if (/\bmaterial\s*icons\b/.test(lowerFam) && !/\bsymbols\b/.test(lowerFam)) {
|
|
1990
|
+
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
1991
|
+
}
|
|
1992
|
+
const isSymbols = /\bmaterial\s*symbols\b/.test(lowerFam);
|
|
1993
|
+
if (!isSymbols) {
|
|
1994
|
+
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
1995
|
+
}
|
|
1996
|
+
const FILL = axes && (axes.FILL ?? axes.fill);
|
|
1997
|
+
let style = "outlined";
|
|
1998
|
+
if (/\brounded\b/.test(cls) || /\bround\b/.test(cls)) style = "rounded";
|
|
1999
|
+
else if (/\bsharp\b/.test(cls)) style = "sharp";
|
|
2000
|
+
else if (/\boutlined\b/.test(cls)) style = "outlined";
|
|
2001
|
+
const filled = FILL === 1;
|
|
2002
|
+
let pick = null;
|
|
2003
|
+
if (filled) {
|
|
2004
|
+
if (style === "outlined" && ICON_FONT_URLS.materialIconsFilled) {
|
|
2005
|
+
pick = { url: ICON_FONT_URLS.materialIconsFilled, alias: "snapdom-mi-filled" };
|
|
2006
|
+
} else if (style === "rounded" && ICON_FONT_URLS.materialIconsRound) {
|
|
2007
|
+
pick = { url: ICON_FONT_URLS.materialIconsRound, alias: "snapdom-mi-round" };
|
|
2008
|
+
} else if (style === "sharp" && ICON_FONT_URLS.materialIconsSharp) {
|
|
2009
|
+
pick = { url: ICON_FONT_URLS.materialIconsSharp, alias: "snapdom-mi-sharp" };
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
if (!pick) {
|
|
2013
|
+
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
2014
|
+
}
|
|
2015
|
+
if (!loadedCanvasFamilies.has(pick.alias)) {
|
|
2016
|
+
try {
|
|
2017
|
+
const ff = new FontFace(pick.alias, `url(${pick.url})`, { style: "normal", weight: "400" });
|
|
2018
|
+
document.fonts.add(ff);
|
|
2019
|
+
await ff.load();
|
|
2020
|
+
loadedCanvasFamilies.set(pick.alias, true);
|
|
2021
|
+
} catch {
|
|
2022
|
+
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
const quoted = `"${pick.alias}"`;
|
|
2026
|
+
return { familyForMeasure: quoted, familyForCanvas: quoted };
|
|
2027
|
+
}
|
|
2028
|
+
async function ensureMaterialFontsReady(family = "Material Icons", px = 24) {
|
|
2029
|
+
try {
|
|
2030
|
+
await Promise.all([
|
|
2031
|
+
document.fonts.load(`400 ${px}px "${String(family).replace(/["']/g, "")}"`),
|
|
2032
|
+
document.fonts.ready
|
|
2033
|
+
]);
|
|
2034
|
+
} catch {
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
function resolvePaintColor(cs) {
|
|
2038
|
+
let fill = cs.getPropertyValue("-webkit-text-fill-color")?.trim() || "";
|
|
2039
|
+
const isTransparent = /^transparent$/i.test(fill) || /rgba?\(\s*0\s*,\s*0\s*,\s*0\s*,\s*0\s*\)/i.test(fill);
|
|
2040
|
+
if (fill && !isTransparent && fill.toLowerCase() !== "currentcolor") return fill;
|
|
2041
|
+
const c = cs.color?.trim();
|
|
1530
2042
|
return c && c !== "inherit" ? c : "#000";
|
|
1531
2043
|
}
|
|
1532
2044
|
async function materialIconToImage(ligatureText, {
|
|
@@ -1630,6 +2142,7 @@ async function ligatureIconToImage(cloneRoot, sourceRoot) {
|
|
|
1630
2142
|
}
|
|
1631
2143
|
|
|
1632
2144
|
// src/modules/fonts.js
|
|
2145
|
+
init_snapFetch();
|
|
1633
2146
|
async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
1634
2147
|
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
1635
2148
|
const dpr = window.devicePixelRatio || 1;
|
|
@@ -2115,6 +2628,18 @@ async function embedCustomFonts({
|
|
|
2115
2628
|
if (nearWeight && sOk && tOk) return true;
|
|
2116
2629
|
}
|
|
2117
2630
|
}
|
|
2631
|
+
if (!faceIsRange && ss.kind === "normal") {
|
|
2632
|
+
const hasItalicRequest = need.some((r) => normStyle(r.s) !== "normal");
|
|
2633
|
+
if (hasItalicRequest) {
|
|
2634
|
+
for (const r of need) {
|
|
2635
|
+
const nearWeight = Math.abs(faceSingleW - r.w) <= 300;
|
|
2636
|
+
const stretchOK = r.st >= ts.min && r.st <= ts.max;
|
|
2637
|
+
if (nearWeight && stretchOK) {
|
|
2638
|
+
return true;
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2118
2643
|
return false;
|
|
2119
2644
|
}
|
|
2120
2645
|
const simpleExcluder = buildSimpleExcluder(exclude);
|
|
@@ -2399,6 +2924,7 @@ async function ensureFontsReady(families, warmupRepetitions = 2) {
|
|
|
2399
2924
|
}
|
|
2400
2925
|
|
|
2401
2926
|
// src/modules/counter.js
|
|
2927
|
+
init_cache();
|
|
2402
2928
|
function hasCounters(input) {
|
|
2403
2929
|
return /\bcounter\s*\(|\bcounters\s*\(/.test(input || "");
|
|
2404
2930
|
}
|
|
@@ -2595,6 +3121,154 @@ function resolveCountersInContent(raw, node, ctx) {
|
|
|
2595
3121
|
}
|
|
2596
3122
|
|
|
2597
3123
|
// src/modules/pseudo.js
|
|
3124
|
+
init_snapFetch();
|
|
3125
|
+
init_cache();
|
|
3126
|
+
var __preflightMemo = /* @__PURE__ */ new WeakMap();
|
|
3127
|
+
var CSS_RULE_SCAN_BUDGET = 300;
|
|
3128
|
+
function preflightWithFp(doc, sessionCache) {
|
|
3129
|
+
const fp = styleFingerprint(doc);
|
|
3130
|
+
if (!sessionCache) return shouldProcessPseudos(doc);
|
|
3131
|
+
if (sessionCache.__pseudoPreflightFp !== fp) {
|
|
3132
|
+
sessionCache.__pseudoPreflight = shouldProcessPseudos(doc);
|
|
3133
|
+
sessionCache.__pseudoPreflightFp = fp;
|
|
3134
|
+
}
|
|
3135
|
+
return !!sessionCache.__pseudoPreflight;
|
|
3136
|
+
}
|
|
3137
|
+
function safeRules(sheet) {
|
|
3138
|
+
try {
|
|
3139
|
+
return sheet && sheet.cssRules ? sheet.cssRules : null;
|
|
3140
|
+
} catch {
|
|
3141
|
+
return null;
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
function styleFingerprint(doc) {
|
|
3145
|
+
const nodes = doc.querySelectorAll('style,link[rel~="stylesheet"]');
|
|
3146
|
+
let fp = `n:${nodes.length}|`;
|
|
3147
|
+
let totalRules = 0;
|
|
3148
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
3149
|
+
const n = nodes[i];
|
|
3150
|
+
if (n.tagName === "STYLE") {
|
|
3151
|
+
const len = n.textContent ? n.textContent.length : 0;
|
|
3152
|
+
fp += `S${len}|`;
|
|
3153
|
+
const sheet = (
|
|
3154
|
+
/** @type {HTMLStyleElement} */
|
|
3155
|
+
n.sheet
|
|
3156
|
+
);
|
|
3157
|
+
const rules = sheet ? safeRules(sheet) : null;
|
|
3158
|
+
if (rules) totalRules += rules.length;
|
|
3159
|
+
} else {
|
|
3160
|
+
const href = n.getAttribute("href") || "";
|
|
3161
|
+
const media = n.getAttribute("media") || "all";
|
|
3162
|
+
fp += `L${href}|m:${media}|`;
|
|
3163
|
+
const sheet = (
|
|
3164
|
+
/** @type {HTMLLinkElement} */
|
|
3165
|
+
n.sheet
|
|
3166
|
+
);
|
|
3167
|
+
const rules = sheet ? safeRules(sheet) : null;
|
|
3168
|
+
if (rules) totalRules += rules.length;
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
const ass = (
|
|
3172
|
+
/** @type {any} */
|
|
3173
|
+
doc.adoptedStyleSheets
|
|
3174
|
+
);
|
|
3175
|
+
fp += `ass:${Array.isArray(ass) ? ass.length : 0}|tr:${totalRules}`;
|
|
3176
|
+
return fp;
|
|
3177
|
+
}
|
|
3178
|
+
function sheetHasNeedles(sheet, needles, state) {
|
|
3179
|
+
const rules = safeRules(sheet);
|
|
3180
|
+
if (!rules) return false;
|
|
3181
|
+
for (let i = 0; i < rules.length; i++) {
|
|
3182
|
+
if (state.budget <= 0) return false;
|
|
3183
|
+
const rule = rules[i];
|
|
3184
|
+
const css = rule && rule.cssText ? rule.cssText : "";
|
|
3185
|
+
state.budget--;
|
|
3186
|
+
for (const k of needles) {
|
|
3187
|
+
if (css.includes(k)) return true;
|
|
3188
|
+
}
|
|
3189
|
+
if (rule && rule.cssRules && rule.cssRules.length) {
|
|
3190
|
+
for (let j = 0; j < rule.cssRules.length && state.budget > 0; j++) {
|
|
3191
|
+
const inner = rule.cssRules[j];
|
|
3192
|
+
const innerCss = inner && inner.cssText ? inner.cssText : "";
|
|
3193
|
+
state.budget--;
|
|
3194
|
+
for (const k of needles) {
|
|
3195
|
+
if (innerCss.includes(k)) return true;
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
if (state.budget <= 0) return false;
|
|
3200
|
+
}
|
|
3201
|
+
return false;
|
|
3202
|
+
}
|
|
3203
|
+
function shouldProcessPseudos(doc = document) {
|
|
3204
|
+
const fp = styleFingerprint(doc);
|
|
3205
|
+
const memo = __preflightMemo.get(doc);
|
|
3206
|
+
if (memo && memo.fingerprint === fp) return memo.result;
|
|
3207
|
+
const NEEDLES = [
|
|
3208
|
+
// double-colon
|
|
3209
|
+
"::before",
|
|
3210
|
+
"::after",
|
|
3211
|
+
"::first-letter",
|
|
3212
|
+
// single-colon robustness
|
|
3213
|
+
":before",
|
|
3214
|
+
":after",
|
|
3215
|
+
":first-letter",
|
|
3216
|
+
// counters
|
|
3217
|
+
"counter(",
|
|
3218
|
+
"counters(",
|
|
3219
|
+
"counter-increment",
|
|
3220
|
+
"counter-reset"
|
|
3221
|
+
];
|
|
3222
|
+
const styleEls = doc.querySelectorAll("style");
|
|
3223
|
+
for (let i = 0; i < styleEls.length; i++) {
|
|
3224
|
+
const t = styleEls[i].textContent || "";
|
|
3225
|
+
for (const k of NEEDLES) if (t.includes(k)) {
|
|
3226
|
+
__preflightMemo.set(doc, { fingerprint: fp, result: true });
|
|
3227
|
+
return true;
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
const ass = (
|
|
3231
|
+
/** @type {any} */
|
|
3232
|
+
doc.adoptedStyleSheets
|
|
3233
|
+
);
|
|
3234
|
+
if (Array.isArray(ass) && ass.length) {
|
|
3235
|
+
const state = { budget: CSS_RULE_SCAN_BUDGET };
|
|
3236
|
+
try {
|
|
3237
|
+
for (const sheet of ass) {
|
|
3238
|
+
if (sheetHasNeedles(sheet, NEEDLES, state)) {
|
|
3239
|
+
__preflightMemo.set(doc, { fingerprint: fp, result: true });
|
|
3240
|
+
return true;
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
} catch {
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
{
|
|
3247
|
+
const nodes = doc.querySelectorAll('style,link[rel~="stylesheet"]');
|
|
3248
|
+
const state = { budget: CSS_RULE_SCAN_BUDGET };
|
|
3249
|
+
for (let i = 0; i < nodes.length && state.budget > 0; i++) {
|
|
3250
|
+
const n = nodes[i];
|
|
3251
|
+
let sheet = null;
|
|
3252
|
+
if (n.tagName === "STYLE") {
|
|
3253
|
+
sheet = /** @type {HTMLStyleElement} */
|
|
3254
|
+
n.sheet || null;
|
|
3255
|
+
} else {
|
|
3256
|
+
sheet = /** @type {HTMLLinkElement} */
|
|
3257
|
+
n.sheet || null;
|
|
3258
|
+
}
|
|
3259
|
+
if (sheet && sheetHasNeedles(sheet, NEEDLES, state)) {
|
|
3260
|
+
__preflightMemo.set(doc, { fingerprint: fp, result: true });
|
|
3261
|
+
return true;
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
if (doc.querySelector('[style*="counter("], [style*="counters("]')) {
|
|
3266
|
+
__preflightMemo.set(doc, { fingerprint: fp, result: true });
|
|
3267
|
+
return true;
|
|
3268
|
+
}
|
|
3269
|
+
__preflightMemo.set(doc, { fingerprint: fp, result: false });
|
|
3270
|
+
return false;
|
|
3271
|
+
}
|
|
2598
3272
|
var __siblingCounters = /* @__PURE__ */ new WeakMap();
|
|
2599
3273
|
var __pseudoEpoch = -1;
|
|
2600
3274
|
function unquoteDoubleStrings2(s) {
|
|
@@ -2693,6 +3367,10 @@ function resolvePseudoContentAndIncs(node, pseudo, baseCtx) {
|
|
|
2693
3367
|
}
|
|
2694
3368
|
async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
2695
3369
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
3370
|
+
const doc = source.ownerDocument || document;
|
|
3371
|
+
if (!preflightWithFp(doc, sessionCache)) {
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
2696
3374
|
const epoch = cache?.session?.__counterEpoch ?? 0;
|
|
2697
3375
|
if (__pseudoEpoch !== epoch) {
|
|
2698
3376
|
__siblingCounters = /* @__PURE__ */ new WeakMap();
|
|
@@ -2709,7 +3387,7 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2709
3387
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
2710
3388
|
try {
|
|
2711
3389
|
const style = getStyle(source, pseudo);
|
|
2712
|
-
if (!style
|
|
3390
|
+
if (!style) continue;
|
|
2713
3391
|
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";
|
|
2714
3392
|
if (isEmptyPseudo) continue;
|
|
2715
3393
|
if (pseudo === "::first-letter") {
|
|
@@ -2736,7 +3414,8 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2736
3414
|
clone.insertBefore(span, restNode);
|
|
2737
3415
|
continue;
|
|
2738
3416
|
}
|
|
2739
|
-
const rawContent = style.content;
|
|
3417
|
+
const rawContent = style.content ?? "";
|
|
3418
|
+
const isNoExplicitContent = rawContent === "" || rawContent === "none" || rawContent === "normal";
|
|
2740
3419
|
const { text: cleanContent, incs } = resolvePseudoContentAndIncs(source, pseudo, counterCtx);
|
|
2741
3420
|
const bg = style.backgroundImage;
|
|
2742
3421
|
const bgColor = style.backgroundColor;
|
|
@@ -2748,7 +3427,7 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2748
3427
|
const borderWidth = parseFloat(style.borderWidth);
|
|
2749
3428
|
const transform = style.transform;
|
|
2750
3429
|
const isIconFont22 = isIconFont2(fontFamily);
|
|
2751
|
-
const hasExplicitContent =
|
|
3430
|
+
const hasExplicitContent = !isNoExplicitContent && cleanContent !== "";
|
|
2752
3431
|
const hasBg = bg && bg !== "none";
|
|
2753
3432
|
const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
|
|
2754
3433
|
const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
|
|
@@ -2770,8 +3449,6 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2770
3449
|
}
|
|
2771
3450
|
const pseudoEl = document.createElement("span");
|
|
2772
3451
|
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
2773
|
-
pseudoEl.style.display = "inline";
|
|
2774
|
-
pseudoEl.style.verticalAlign = "baseline";
|
|
2775
3452
|
pseudoEl.style.pointerEvents = "none";
|
|
2776
3453
|
const snapshot = snapshotComputedStyle(style);
|
|
2777
3454
|
const key = getStyleKey(snapshot, "span");
|
|
@@ -2877,31 +3554,55 @@ function inlineExternalDefsAndSymbols(element, lookupRoot) {
|
|
|
2877
3554
|
"marker-end"
|
|
2878
3555
|
];
|
|
2879
3556
|
const cssEscape = (s) => window.CSS && CSS.escape ? CSS.escape(s) : s.replace(/[^a-zA-Z0-9_-]/g, "\\$&");
|
|
3557
|
+
const XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
3558
|
+
const getHrefAttr = (el) => {
|
|
3559
|
+
if (!el || !el.getAttribute) return null;
|
|
3560
|
+
let href = el.getAttribute("href") || el.getAttribute("xlink:href") || (typeof el.getAttributeNS === "function" ? el.getAttributeNS(XLINK_NS, "href") : null);
|
|
3561
|
+
if (href) return href;
|
|
3562
|
+
const attrs = el.attributes;
|
|
3563
|
+
if (!attrs) return null;
|
|
3564
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
3565
|
+
const a = attrs[i];
|
|
3566
|
+
if (!a || !a.name) continue;
|
|
3567
|
+
if (a.name === "href") return a.value;
|
|
3568
|
+
const idx = a.name.indexOf(":");
|
|
3569
|
+
if (idx !== -1 && a.name.slice(idx + 1) === "href") {
|
|
3570
|
+
return a.value;
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
return null;
|
|
3574
|
+
};
|
|
2880
3575
|
const globalExistingIds = new Set(
|
|
2881
3576
|
Array.from(element.querySelectorAll("[id]")).map((n) => n.id)
|
|
2882
3577
|
);
|
|
2883
3578
|
const neededIds = /* @__PURE__ */ new Set();
|
|
2884
3579
|
let sawAnyReference = false;
|
|
2885
|
-
const addUrlIdsFromValue = (val) => {
|
|
3580
|
+
const addUrlIdsFromValue = (val, queueForResolve = null) => {
|
|
2886
3581
|
if (!val) return;
|
|
2887
3582
|
URL_ID_RE.lastIndex = 0;
|
|
2888
3583
|
let m;
|
|
2889
3584
|
while (m = URL_ID_RE.exec(val)) {
|
|
2890
3585
|
sawAnyReference = true;
|
|
2891
3586
|
const id = (m[1] || "").trim();
|
|
2892
|
-
if (
|
|
3587
|
+
if (!id) continue;
|
|
3588
|
+
if (!globalExistingIds.has(id)) {
|
|
3589
|
+
neededIds.add(id);
|
|
3590
|
+
if (queueForResolve && !queueForResolve.has(id)) {
|
|
3591
|
+
queueForResolve.add(id);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
2893
3594
|
}
|
|
2894
3595
|
};
|
|
2895
3596
|
const collectReferencesInSvg = (rootSvg) => {
|
|
2896
|
-
const uses = rootSvg.querySelectorAll(
|
|
3597
|
+
const uses = rootSvg.querySelectorAll("use");
|
|
2897
3598
|
for (const u of uses) {
|
|
2898
|
-
const href =
|
|
3599
|
+
const href = getHrefAttr(u);
|
|
2899
3600
|
if (!href || !href.startsWith("#")) continue;
|
|
2900
3601
|
sawAnyReference = true;
|
|
2901
3602
|
const id = href.slice(1).trim();
|
|
2902
3603
|
if (id && !globalExistingIds.has(id)) neededIds.add(id);
|
|
2903
3604
|
}
|
|
2904
|
-
|
|
3605
|
+
const query = '*[style*="url("],*[fill^="url("], *[stroke^="url("],*[filter^="url("],*[clip-path^="url("],*[mask^="url("],*[marker^="url("],*[marker-start^="url("],*[marker-mid^="url("],*[marker-end^="url("]';
|
|
2905
3606
|
const candidates = rootSvg.querySelectorAll(query);
|
|
2906
3607
|
for (const el of candidates) {
|
|
2907
3608
|
addUrlIdsFromValue(el.getAttribute("style") || "");
|
|
@@ -2952,21 +3653,26 @@ function inlineExternalDefsAndSymbols(element, lookupRoot) {
|
|
|
2952
3653
|
globalExistingIds.add(id);
|
|
2953
3654
|
const walk = [clone, ...clone.querySelectorAll("*")];
|
|
2954
3655
|
for (const node of walk) {
|
|
2955
|
-
const
|
|
2956
|
-
if (
|
|
2957
|
-
const ref =
|
|
2958
|
-
if (ref && !globalExistingIds.has(ref) && !inlined.has(ref))
|
|
3656
|
+
const href = getHrefAttr(node);
|
|
3657
|
+
if (href && href.startsWith("#")) {
|
|
3658
|
+
const ref = href.slice(1).trim();
|
|
3659
|
+
if (ref && !globalExistingIds.has(ref) && !inlined.has(ref)) {
|
|
3660
|
+
queued.add(ref);
|
|
3661
|
+
}
|
|
2959
3662
|
}
|
|
2960
3663
|
const style = node.getAttribute?.("style") || "";
|
|
2961
|
-
if (style) addUrlIdsFromValue(style);
|
|
3664
|
+
if (style) addUrlIdsFromValue(style, queued);
|
|
2962
3665
|
for (const a of URL_ATTRS) {
|
|
2963
3666
|
const v = node.getAttribute?.(a);
|
|
2964
|
-
if (v) addUrlIdsFromValue(v);
|
|
3667
|
+
if (v) addUrlIdsFromValue(v, queued);
|
|
2965
3668
|
}
|
|
2966
3669
|
}
|
|
2967
3670
|
}
|
|
2968
3671
|
}
|
|
2969
3672
|
|
|
3673
|
+
// src/core/prepare.js
|
|
3674
|
+
init_cache();
|
|
3675
|
+
|
|
2970
3676
|
// src/modules/changeCSS.js
|
|
2971
3677
|
function freezeSticky(originalRoot, cloneRoot) {
|
|
2972
3678
|
if (!originalRoot || !cloneRoot) return;
|
|
@@ -3063,6 +3769,20 @@ function _childrenWithoutPlaceholders(el, phAttr) {
|
|
|
3063
3769
|
return out;
|
|
3064
3770
|
}
|
|
3065
3771
|
|
|
3772
|
+
// src/utils/prepare.helpers.js
|
|
3773
|
+
function stabilizeLayout(element) {
|
|
3774
|
+
const style = getComputedStyle(element);
|
|
3775
|
+
const outlineStyle = style.outlineStyle;
|
|
3776
|
+
const outlineWidth = style.outlineWidth;
|
|
3777
|
+
const borderStyle = style.borderStyle;
|
|
3778
|
+
const borderWidth = style.borderWidth;
|
|
3779
|
+
const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0;
|
|
3780
|
+
const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0;
|
|
3781
|
+
if (outlineVisible && borderAbsent) {
|
|
3782
|
+
element.style.border = `${outlineWidth} solid transparent`;
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3066
3786
|
// src/core/prepare.js
|
|
3067
3787
|
async function prepareClone(element, options = {}) {
|
|
3068
3788
|
const sessionCache = {
|
|
@@ -3091,218 +3811,92 @@ async function prepareClone(element, options = {}) {
|
|
|
3091
3811
|
console.warn("inlinePseudoElements failed:", e);
|
|
3092
3812
|
}
|
|
3093
3813
|
await resolveBlobUrlsInTree(clone);
|
|
3094
|
-
try {
|
|
3095
|
-
const styleNodes = clone.querySelectorAll("style[data-sd]");
|
|
3096
|
-
for (const s of styleNodes) {
|
|
3097
|
-
shadowScopedCSS += s.textContent || "";
|
|
3098
|
-
s.remove();
|
|
3099
|
-
}
|
|
3100
|
-
} catch {
|
|
3101
|
-
}
|
|
3102
|
-
const keyToClass = generateCSSClasses(sessionCache.styleMap);
|
|
3103
|
-
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
3104
|
-
classCSS = shadowScopedCSS + classCSS;
|
|
3105
|
-
for (const [node, key] of sessionCache.styleMap.entries()) {
|
|
3106
|
-
if (node.tagName === "STYLE") continue;
|
|
3107
|
-
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
|
|
3108
|
-
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
3109
|
-
continue;
|
|
3110
|
-
}
|
|
3111
|
-
const className = keyToClass.get(key);
|
|
3112
|
-
if (className) node.classList.add(className);
|
|
3113
|
-
const bgImage = node.style?.backgroundImage;
|
|
3114
|
-
const hasIcon = node.dataset?.snapdomHasIcon;
|
|
3115
|
-
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
3116
|
-
if (hasIcon) {
|
|
3117
|
-
node.style.verticalAlign = "middle";
|
|
3118
|
-
node.style.display = "inline";
|
|
3119
|
-
}
|
|
3120
|
-
}
|
|
3121
|
-
for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) {
|
|
3122
|
-
const scrollX = originalNode.scrollLeft;
|
|
3123
|
-
const scrollY = originalNode.scrollTop;
|
|
3124
|
-
const hasScroll = scrollX || scrollY;
|
|
3125
|
-
if (hasScroll && cloneNode instanceof HTMLElement) {
|
|
3126
|
-
cloneNode.style.overflow = "hidden";
|
|
3127
|
-
cloneNode.style.scrollbarWidth = "none";
|
|
3128
|
-
cloneNode.style.msOverflowStyle = "none";
|
|
3129
|
-
const inner = document.createElement("div");
|
|
3130
|
-
inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`;
|
|
3131
|
-
inner.style.willChange = "transform";
|
|
3132
|
-
inner.style.display = "inline-block";
|
|
3133
|
-
inner.style.width = "100%";
|
|
3134
|
-
while (cloneNode.firstChild) {
|
|
3135
|
-
inner.appendChild(cloneNode.firstChild);
|
|
3136
|
-
}
|
|
3137
|
-
cloneNode.appendChild(inner);
|
|
3138
|
-
}
|
|
3139
|
-
}
|
|
3140
|
-
const contentRoot = clone instanceof HTMLElement && clone.firstElementChild instanceof HTMLElement ? clone.firstElementChild : clone;
|
|
3141
|
-
freezeSticky(element, contentRoot);
|
|
3142
|
-
if (element === sessionCache.nodeMap.get(clone)) {
|
|
3143
|
-
const computed = sessionCache.styleCache.get(element) || window.getComputedStyle(element);
|
|
3144
|
-
sessionCache.styleCache.set(element, computed);
|
|
3145
|
-
const transform = stripTranslate(computed.transform);
|
|
3146
|
-
clone.style.margin = "0";
|
|
3147
|
-
clone.style.top = "auto";
|
|
3148
|
-
clone.style.left = "auto";
|
|
3149
|
-
clone.style.right = "auto";
|
|
3150
|
-
clone.style.bottom = "auto";
|
|
3151
|
-
clone.style.animation = "none";
|
|
3152
|
-
clone.style.transition = "none";
|
|
3153
|
-
clone.style.willChange = "auto";
|
|
3154
|
-
clone.style.float = "none";
|
|
3155
|
-
clone.style.clear = "none";
|
|
3156
|
-
clone.style.transform = transform || "";
|
|
3157
|
-
}
|
|
3158
|
-
for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) {
|
|
3159
|
-
if (originalNode.tagName === "PRE") {
|
|
3160
|
-
cloneNode.style.marginTop = "0";
|
|
3161
|
-
cloneNode.style.marginBlockStart = "0";
|
|
3162
|
-
}
|
|
3163
|
-
}
|
|
3164
|
-
return { clone, classCSS, styleCache: sessionCache.styleCache };
|
|
3165
|
-
}
|
|
3166
|
-
function stabilizeLayout(element) {
|
|
3167
|
-
const style = getComputedStyle(element);
|
|
3168
|
-
const outlineStyle = style.outlineStyle;
|
|
3169
|
-
const outlineWidth = style.outlineWidth;
|
|
3170
|
-
const borderStyle = style.borderStyle;
|
|
3171
|
-
const borderWidth = style.borderWidth;
|
|
3172
|
-
const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0;
|
|
3173
|
-
const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0;
|
|
3174
|
-
if (outlineVisible && borderAbsent) {
|
|
3175
|
-
element.style.border = `${outlineWidth} solid transparent`;
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
var _blobToDataUrlCache = /* @__PURE__ */ new Map();
|
|
3179
|
-
async function blobUrlToDataUrl(blobUrl) {
|
|
3180
|
-
if (cache.resource?.has(blobUrl)) return cache.resource.get(blobUrl);
|
|
3181
|
-
if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl);
|
|
3182
|
-
const p = (async () => {
|
|
3183
|
-
const r = await snapFetch(blobUrl, { as: "dataURL", silent: true });
|
|
3184
|
-
if (!r.ok || typeof r.data !== "string") {
|
|
3185
|
-
throw new Error(`[snapDOM] Failed to read blob URL: ${blobUrl}`);
|
|
3186
|
-
}
|
|
3187
|
-
cache.resource?.set(blobUrl, r.data);
|
|
3188
|
-
return r.data;
|
|
3189
|
-
})();
|
|
3190
|
-
_blobToDataUrlCache.set(blobUrl, p);
|
|
3191
|
-
try {
|
|
3192
|
-
const data = await p;
|
|
3193
|
-
_blobToDataUrlCache.set(blobUrl, data);
|
|
3194
|
-
return data;
|
|
3195
|
-
} catch (e) {
|
|
3196
|
-
_blobToDataUrlCache.delete(blobUrl);
|
|
3197
|
-
throw e;
|
|
3198
|
-
}
|
|
3199
|
-
}
|
|
3200
|
-
var BLOB_URL_RE = /\bblob:[^)"'\s]+/g;
|
|
3201
|
-
async function replaceBlobUrlsInCssText(cssText) {
|
|
3202
|
-
if (!cssText || cssText.indexOf("blob:") === -1) return cssText;
|
|
3203
|
-
const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || []));
|
|
3204
|
-
if (uniques.length === 0) return cssText;
|
|
3205
|
-
let out = cssText;
|
|
3206
|
-
for (const u of uniques) {
|
|
3207
|
-
try {
|
|
3208
|
-
const d = await blobUrlToDataUrl(u);
|
|
3209
|
-
out = out.split(u).join(d);
|
|
3210
|
-
} catch {
|
|
3211
|
-
}
|
|
3212
|
-
}
|
|
3213
|
-
return out;
|
|
3214
|
-
}
|
|
3215
|
-
function isBlobUrl(u) {
|
|
3216
|
-
return typeof u === "string" && u.startsWith("blob:");
|
|
3217
|
-
}
|
|
3218
|
-
function parseSrcset(srcset) {
|
|
3219
|
-
return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => {
|
|
3220
|
-
const m = item.match(/^(\S+)(\s+.+)?$/);
|
|
3221
|
-
return m ? { url: m[1], desc: m[2] || "" } : null;
|
|
3222
|
-
}).filter(Boolean);
|
|
3223
|
-
}
|
|
3224
|
-
function stringifySrcset(parts) {
|
|
3225
|
-
return parts.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", ");
|
|
3226
|
-
}
|
|
3227
|
-
async function resolveBlobUrlsInTree(root) {
|
|
3228
|
-
if (!root) return;
|
|
3229
|
-
const imgs = root.querySelectorAll ? root.querySelectorAll("img") : [];
|
|
3230
|
-
for (const img of imgs) {
|
|
3231
|
-
try {
|
|
3232
|
-
const srcAttr = img.getAttribute("src");
|
|
3233
|
-
const effective = srcAttr || img.currentSrc || "";
|
|
3234
|
-
if (isBlobUrl(effective)) {
|
|
3235
|
-
const data = await blobUrlToDataUrl(effective);
|
|
3236
|
-
img.setAttribute("src", data);
|
|
3237
|
-
}
|
|
3238
|
-
const srcset = img.getAttribute("srcset");
|
|
3239
|
-
if (srcset && srcset.includes("blob:")) {
|
|
3240
|
-
const parts = parseSrcset(srcset);
|
|
3241
|
-
let changed = false;
|
|
3242
|
-
for (const p of parts) {
|
|
3243
|
-
if (isBlobUrl(p.url)) {
|
|
3244
|
-
try {
|
|
3245
|
-
p.url = await blobUrlToDataUrl(p.url);
|
|
3246
|
-
changed = true;
|
|
3247
|
-
} catch {
|
|
3248
|
-
}
|
|
3249
|
-
}
|
|
3250
|
-
}
|
|
3251
|
-
if (changed) img.setAttribute("srcset", stringifySrcset(parts));
|
|
3252
|
-
}
|
|
3253
|
-
} catch {
|
|
3254
|
-
}
|
|
3255
|
-
}
|
|
3256
|
-
const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : [];
|
|
3257
|
-
for (const node of svgImages) {
|
|
3258
|
-
try {
|
|
3259
|
-
const XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
3260
|
-
const href = node.getAttribute("href") || node.getAttributeNS?.(XLINK_NS, "href");
|
|
3261
|
-
if (isBlobUrl(href)) {
|
|
3262
|
-
const d = await blobUrlToDataUrl(href);
|
|
3263
|
-
node.setAttribute("href", d);
|
|
3264
|
-
node.removeAttributeNS?.(XLINK_NS, "href");
|
|
3265
|
-
}
|
|
3266
|
-
} catch {
|
|
3814
|
+
try {
|
|
3815
|
+
const styleNodes = clone.querySelectorAll("style[data-sd]");
|
|
3816
|
+
for (const s of styleNodes) {
|
|
3817
|
+
shadowScopedCSS += s.textContent || "";
|
|
3818
|
+
s.remove();
|
|
3267
3819
|
}
|
|
3820
|
+
} catch {
|
|
3268
3821
|
}
|
|
3269
|
-
const
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
}
|
|
3822
|
+
const keyToClass = generateCSSClasses(sessionCache.styleMap);
|
|
3823
|
+
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
3824
|
+
classCSS = shadowScopedCSS + classCSS;
|
|
3825
|
+
for (const [node, key] of sessionCache.styleMap.entries()) {
|
|
3826
|
+
if (node.tagName === "STYLE") continue;
|
|
3827
|
+
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
|
|
3828
|
+
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
3829
|
+
continue;
|
|
3830
|
+
}
|
|
3831
|
+
const className = keyToClass.get(key);
|
|
3832
|
+
if (className) node.classList.add(className);
|
|
3833
|
+
const bgImage = node.style?.backgroundImage;
|
|
3834
|
+
const hasIcon = node.dataset?.snapdomHasIcon;
|
|
3835
|
+
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
3836
|
+
if (hasIcon) {
|
|
3837
|
+
node.style.verticalAlign = "middle";
|
|
3838
|
+
node.style.display = "inline";
|
|
3278
3839
|
}
|
|
3279
3840
|
}
|
|
3280
|
-
const
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3841
|
+
for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) {
|
|
3842
|
+
const scrollX = originalNode.scrollLeft;
|
|
3843
|
+
const scrollY = originalNode.scrollTop;
|
|
3844
|
+
const hasScroll = scrollX || scrollY;
|
|
3845
|
+
if (hasScroll && cloneNode instanceof HTMLElement) {
|
|
3846
|
+
cloneNode.style.overflow = "hidden";
|
|
3847
|
+
cloneNode.style.scrollbarWidth = "none";
|
|
3848
|
+
cloneNode.style.msOverflowStyle = "none";
|
|
3849
|
+
const inner = document.createElement("div");
|
|
3850
|
+
inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`;
|
|
3851
|
+
inner.style.willChange = "transform";
|
|
3852
|
+
inner.style.display = "inline-block";
|
|
3853
|
+
inner.style.width = "100%";
|
|
3854
|
+
while (cloneNode.firstChild) {
|
|
3855
|
+
inner.appendChild(cloneNode.firstChild);
|
|
3286
3856
|
}
|
|
3287
|
-
|
|
3857
|
+
cloneNode.appendChild(inner);
|
|
3288
3858
|
}
|
|
3289
3859
|
}
|
|
3290
|
-
const
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3860
|
+
const contentRoot = clone instanceof HTMLElement && clone.firstElementChild instanceof HTMLElement ? clone.firstElementChild : clone;
|
|
3861
|
+
freezeSticky(element, contentRoot);
|
|
3862
|
+
if (element === sessionCache.nodeMap.get(clone)) {
|
|
3863
|
+
const computed = sessionCache.styleCache.get(element) || window.getComputedStyle(element);
|
|
3864
|
+
sessionCache.styleCache.set(element, computed);
|
|
3865
|
+
const transform = stripTranslate(computed.transform);
|
|
3866
|
+
clone.style.margin = "0";
|
|
3867
|
+
clone.style.top = "auto";
|
|
3868
|
+
clone.style.left = "auto";
|
|
3869
|
+
clone.style.right = "auto";
|
|
3870
|
+
clone.style.bottom = "auto";
|
|
3871
|
+
clone.style.animation = "none";
|
|
3872
|
+
clone.style.transition = "none";
|
|
3873
|
+
clone.style.willChange = "auto";
|
|
3874
|
+
clone.style.float = "none";
|
|
3875
|
+
clone.style.clear = "none";
|
|
3876
|
+
clone.style.transform = transform || "";
|
|
3877
|
+
}
|
|
3878
|
+
for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) {
|
|
3879
|
+
if (originalNode.tagName === "PRE") {
|
|
3880
|
+
cloneNode.style.marginTop = "0";
|
|
3881
|
+
cloneNode.style.marginBlockStart = "0";
|
|
3301
3882
|
}
|
|
3302
3883
|
}
|
|
3884
|
+
return { clone, classCSS, styleCache: sessionCache.styleCache };
|
|
3303
3885
|
}
|
|
3304
3886
|
|
|
3305
3887
|
// src/modules/images.js
|
|
3888
|
+
init_snapFetch();
|
|
3889
|
+
function extractImageDimensions(img) {
|
|
3890
|
+
const dsW = parseInt(img.dataset?.snapdomWidth || "", 10) || 0;
|
|
3891
|
+
const dsH = parseInt(img.dataset?.snapdomHeight || "", 10) || 0;
|
|
3892
|
+
const attrW = parseInt(img.getAttribute("width") || "", 10) || 0;
|
|
3893
|
+
const attrH = parseInt(img.getAttribute("height") || "", 10) || 0;
|
|
3894
|
+
const styleW = parseFloat(img.style?.width || "") || 0;
|
|
3895
|
+
const styleH = parseFloat(img.style?.height || "") || 0;
|
|
3896
|
+
const w = dsW || styleW || attrW || img.width || img.naturalWidth || 100;
|
|
3897
|
+
const h = dsH || styleH || attrH || img.height || img.naturalHeight || 100;
|
|
3898
|
+
return { width: w, height: h };
|
|
3899
|
+
}
|
|
3306
3900
|
async function inlineImages(clone, options = {}) {
|
|
3307
3901
|
const imgs = Array.from(clone.querySelectorAll("img"));
|
|
3308
3902
|
const processImg = async (img) => {
|
|
@@ -3321,41 +3915,30 @@ async function inlineImages(clone, options = {}) {
|
|
|
3321
3915
|
if (!img.height) img.height = img.naturalHeight || 100;
|
|
3322
3916
|
return;
|
|
3323
3917
|
}
|
|
3918
|
+
const { width: fbW, height: fbH } = extractImageDimensions(img);
|
|
3324
3919
|
const { fallbackURL } = options || {};
|
|
3325
3920
|
if (fallbackURL) {
|
|
3326
3921
|
try {
|
|
3327
|
-
const dsW = parseInt(img.dataset?.snapdomWidth || "", 10) || 0;
|
|
3328
|
-
const dsH = parseInt(img.dataset?.snapdomHeight || "", 10) || 0;
|
|
3329
|
-
const attrW = parseInt(img.getAttribute("width") || "", 10) || 0;
|
|
3330
|
-
const attrH = parseInt(img.getAttribute("height") || "", 10) || 0;
|
|
3331
|
-
const styleW = parseFloat(img.style?.width || "") || 0;
|
|
3332
|
-
const styleH = parseFloat(img.style?.height || "") || 0;
|
|
3333
|
-
const fbW = dsW || styleW || attrW || img.width || void 0;
|
|
3334
|
-
const fbH = dsH || styleH || attrH || img.height || void 0;
|
|
3335
3922
|
const fallbackUrl = typeof fallbackURL === "function" ? await fallbackURL({ width: fbW, height: fbH, src, element: img }) : fallbackURL;
|
|
3336
3923
|
if (fallbackUrl) {
|
|
3337
3924
|
const fallbackData = await snapFetch(fallbackUrl, { as: "dataURL", useProxy: options.useProxy });
|
|
3338
3925
|
img.src = fallbackData.data;
|
|
3339
|
-
if (!img.width
|
|
3340
|
-
if (!img.height
|
|
3341
|
-
if (!img.width) img.width = img.naturalWidth || 100;
|
|
3342
|
-
if (!img.height) img.height = img.naturalHeight || 100;
|
|
3926
|
+
if (!img.width) img.width = fbW;
|
|
3927
|
+
if (!img.height) img.height = fbH;
|
|
3343
3928
|
return;
|
|
3344
3929
|
}
|
|
3345
3930
|
} catch {
|
|
3346
3931
|
}
|
|
3347
3932
|
}
|
|
3348
|
-
const w = img.width || img.naturalWidth || 100;
|
|
3349
|
-
const h = img.height || img.naturalHeight || 100;
|
|
3350
3933
|
if (options.placeholders !== false) {
|
|
3351
3934
|
const fallback = document.createElement("div");
|
|
3352
3935
|
fallback.style.cssText = [
|
|
3353
|
-
`width:${
|
|
3354
|
-
`height:${
|
|
3936
|
+
`width:${fbW}px`,
|
|
3937
|
+
`height:${fbH}px`,
|
|
3355
3938
|
"background:#ccc",
|
|
3356
3939
|
"display:inline-block",
|
|
3357
3940
|
"text-align:center",
|
|
3358
|
-
`line-height:${
|
|
3941
|
+
`line-height:${fbH}px`,
|
|
3359
3942
|
"color:#666",
|
|
3360
3943
|
"font-size:12px",
|
|
3361
3944
|
"overflow:hidden"
|
|
@@ -3364,7 +3947,7 @@ async function inlineImages(clone, options = {}) {
|
|
|
3364
3947
|
img.replaceWith(fallback);
|
|
3365
3948
|
} else {
|
|
3366
3949
|
const spacer = document.createElement("div");
|
|
3367
|
-
spacer.style.cssText = `display:inline-block;width:${
|
|
3950
|
+
spacer.style.cssText = `display:inline-block;width:${fbW}px;height:${fbH}px;visibility:hidden;`;
|
|
3368
3951
|
img.replaceWith(spacer);
|
|
3369
3952
|
}
|
|
3370
3953
|
};
|
|
@@ -3375,6 +3958,7 @@ async function inlineImages(clone, options = {}) {
|
|
|
3375
3958
|
}
|
|
3376
3959
|
|
|
3377
3960
|
// src/modules/background.js
|
|
3961
|
+
init_utils();
|
|
3378
3962
|
async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
|
|
3379
3963
|
const queue = [[source, clone]];
|
|
3380
3964
|
const URL_PROPS = [
|
|
@@ -3472,6 +4056,10 @@ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
|
|
|
3472
4056
|
}
|
|
3473
4057
|
}
|
|
3474
4058
|
|
|
4059
|
+
// src/core/capture.js
|
|
4060
|
+
init_utils();
|
|
4061
|
+
init_cache();
|
|
4062
|
+
|
|
3475
4063
|
// src/modules/lineClamp.js
|
|
3476
4064
|
function lineClamp(el) {
|
|
3477
4065
|
if (!el) return () => {
|
|
@@ -3604,7 +4192,7 @@ function attachSessionPlugins(context, localDefs, force = false) {
|
|
|
3604
4192
|
return context;
|
|
3605
4193
|
}
|
|
3606
4194
|
|
|
3607
|
-
// src/
|
|
4195
|
+
// src/utils/capture.helpers.js
|
|
3608
4196
|
function stripRootShadows(originalEl, cloneRoot) {
|
|
3609
4197
|
if (!originalEl || !cloneRoot || !cloneRoot.style) return;
|
|
3610
4198
|
const cs = getComputedStyle(originalEl);
|
|
@@ -3633,268 +4221,43 @@ function removeAllComments(root) {
|
|
|
3633
4221
|
while (it.nextNode()) toRemove.push(it.currentNode);
|
|
3634
4222
|
for (const n of toRemove) n.remove();
|
|
3635
4223
|
}
|
|
3636
|
-
function sanitizeAttributesForXHTML(root, opts = {}) {
|
|
3637
|
-
const { stripFrameworkDirectives = true } = opts;
|
|
3638
|
-
const ALLOWED_PREFIXES = /* @__PURE__ */ new Set(["xml", "xlink"]);
|
|
3639
|
-
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
3640
|
-
while (walker.nextNode()) {
|
|
3641
|
-
const el = walker.currentNode;
|
|
3642
|
-
for (const attr of Array.from(el.attributes)) {
|
|
3643
|
-
const name = attr.name;
|
|
3644
|
-
if (name.includes("@")) {
|
|
3645
|
-
el.removeAttribute(name);
|
|
3646
|
-
continue;
|
|
3647
|
-
}
|
|
3648
|
-
if (name.includes(":")) {
|
|
3649
|
-
const prefix = name.split(":", 1)[0];
|
|
3650
|
-
if (!ALLOWED_PREFIXES.has(prefix)) {
|
|
3651
|
-
el.removeAttribute(name);
|
|
3652
|
-
continue;
|
|
3653
|
-
}
|
|
3654
|
-
}
|
|
3655
|
-
if (!stripFrameworkDirectives) continue;
|
|
3656
|
-
if (name.startsWith("x-") || // Alpine
|
|
3657
|
-
name.startsWith("v-") || // Vue
|
|
3658
|
-
name.startsWith(":") || // Vue/Alpine shorthand
|
|
3659
|
-
name.startsWith("on:") || // Svelte
|
|
3660
|
-
name.startsWith("bind:") || // Svelte
|
|
3661
|
-
name.startsWith("let:") || // Svelte
|
|
3662
|
-
name.startsWith("class:")) {
|
|
3663
|
-
el.removeAttribute(name);
|
|
3664
|
-
continue;
|
|
3665
|
-
}
|
|
3666
|
-
}
|
|
3667
|
-
}
|
|
3668
|
-
}
|
|
3669
|
-
function sanitizeCloneForXHTML(root, opts = {}) {
|
|
3670
|
-
if (!root) return;
|
|
3671
|
-
sanitizeAttributesForXHTML(root, opts);
|
|
3672
|
-
removeAllComments(root);
|
|
3673
|
-
}
|
|
3674
|
-
async function captureDOM(element, options) {
|
|
3675
|
-
if (!element) throw new Error("Element cannot be null or undefined");
|
|
3676
|
-
applyCachePolicy(options.cache);
|
|
3677
|
-
const fast = options.fast;
|
|
3678
|
-
const outerTransforms = options.outerTransforms !== false;
|
|
3679
|
-
const outerShadows = !!options.outerShadows;
|
|
3680
|
-
let state = { element, options, plugins: options.plugins };
|
|
3681
|
-
let clone, classCSS, styleCache;
|
|
3682
|
-
let fontsCSS = "";
|
|
3683
|
-
let baseCSS = "";
|
|
3684
|
-
let dataURL;
|
|
3685
|
-
let svgString;
|
|
3686
|
-
let rootTransform2D = null;
|
|
3687
|
-
await runHook("beforeSnap", state);
|
|
3688
|
-
await runHook("beforeClone", state);
|
|
3689
|
-
const undoClamp = lineClamp(state.element);
|
|
3690
|
-
try {
|
|
3691
|
-
({ clone, classCSS, styleCache } = await prepareClone(state.element, state.options));
|
|
3692
|
-
if (!outerTransforms && clone) {
|
|
3693
|
-
rootTransform2D = normalizeRootTransforms(state.element, clone);
|
|
3694
|
-
}
|
|
3695
|
-
if (!outerShadows && clone) {
|
|
3696
|
-
stripRootShadows(state.element, clone);
|
|
3697
|
-
}
|
|
3698
|
-
} finally {
|
|
3699
|
-
undoClamp();
|
|
3700
|
-
}
|
|
3701
|
-
state = { clone, classCSS, styleCache, ...state };
|
|
3702
|
-
await runHook("afterClone", state);
|
|
3703
|
-
sanitizeCloneForXHTML(state.clone);
|
|
3704
|
-
if (state.options?.excludeMode === "remove") {
|
|
3705
|
-
try {
|
|
3706
|
-
shrinkAutoSizeBoxes(state.element, state.clone, state.styleCache);
|
|
3707
|
-
} catch (e) {
|
|
3708
|
-
console.warn("[snapdom] shrink pass failed:", e);
|
|
3709
|
-
}
|
|
3710
|
-
}
|
|
3711
|
-
try {
|
|
3712
|
-
await ligatureIconToImage(state.clone, state.element);
|
|
3713
|
-
} catch {
|
|
3714
|
-
}
|
|
3715
|
-
await new Promise((resolve) => {
|
|
3716
|
-
idle(async () => {
|
|
3717
|
-
await inlineImages(state.clone, state.options);
|
|
3718
|
-
resolve();
|
|
3719
|
-
}, { fast });
|
|
3720
|
-
});
|
|
3721
|
-
await new Promise((resolve) => {
|
|
3722
|
-
idle(async () => {
|
|
3723
|
-
await inlineBackgroundImages(state.element, state.clone, state.styleCache, state.options);
|
|
3724
|
-
resolve();
|
|
3725
|
-
}, { fast });
|
|
3726
|
-
});
|
|
3727
|
-
if (options.embedFonts) {
|
|
3728
|
-
await new Promise((resolve) => {
|
|
3729
|
-
idle(async () => {
|
|
3730
|
-
const required = collectUsedFontVariants(state.element);
|
|
3731
|
-
const usedCodepoints = collectUsedCodepoints(state.element);
|
|
3732
|
-
if (isSafari()) {
|
|
3733
|
-
const families = new Set(
|
|
3734
|
-
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
3735
|
-
);
|
|
3736
|
-
await ensureFontsReady(families, 1);
|
|
3737
|
-
}
|
|
3738
|
-
fontsCSS = await embedCustomFonts({
|
|
3739
|
-
required,
|
|
3740
|
-
usedCodepoints,
|
|
3741
|
-
preCached: false,
|
|
3742
|
-
exclude: state.options.excludeFonts,
|
|
3743
|
-
useProxy: state.options.useProxy
|
|
3744
|
-
});
|
|
3745
|
-
resolve();
|
|
3746
|
-
}, { fast });
|
|
3747
|
-
});
|
|
3748
|
-
}
|
|
3749
|
-
const usedTags = collectUsedTagNames(state.clone).sort();
|
|
3750
|
-
const tagKey = usedTags.join(",");
|
|
3751
|
-
if (cache.baseStyle.has(tagKey)) {
|
|
3752
|
-
baseCSS = cache.baseStyle.get(tagKey);
|
|
3753
|
-
} else {
|
|
3754
|
-
await new Promise((resolve) => {
|
|
3755
|
-
idle(() => {
|
|
3756
|
-
baseCSS = generateDedupedBaseCSS(usedTags);
|
|
3757
|
-
cache.baseStyle.set(tagKey, baseCSS);
|
|
3758
|
-
resolve();
|
|
3759
|
-
}, { fast });
|
|
3760
|
-
});
|
|
3761
|
-
}
|
|
3762
|
-
state = { fontsCSS, baseCSS, ...state };
|
|
3763
|
-
await runHook("beforeRender", state);
|
|
3764
|
-
await new Promise((resolve) => {
|
|
3765
|
-
idle(() => {
|
|
3766
|
-
const csEl = getComputedStyle(state.element);
|
|
3767
|
-
const rect = state.element.getBoundingClientRect();
|
|
3768
|
-
let w0 = Math.max(1, limitDecimals(state.element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
|
|
3769
|
-
let h0 = Math.max(1, limitDecimals(state.element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
|
|
3770
|
-
if (state.options?.excludeMode === "remove") {
|
|
3771
|
-
const hEst = estimateKeptHeight(state.element, state.options);
|
|
3772
|
-
const EPS = 1;
|
|
3773
|
-
if (Number.isFinite(hEst) && hEst > 0) {
|
|
3774
|
-
h0 = Math.max(1, Math.min(h0, limitDecimals(hEst + EPS)));
|
|
3775
|
-
}
|
|
3776
|
-
}
|
|
3777
|
-
const coerceNum = (v, def = NaN) => {
|
|
3778
|
-
const n = typeof v === "string" ? parseFloat(v) : v;
|
|
3779
|
-
return Number.isFinite(n) ? n : def;
|
|
3780
|
-
};
|
|
3781
|
-
const optW = coerceNum(state.options.width);
|
|
3782
|
-
const optH = coerceNum(state.options.height);
|
|
3783
|
-
let w = w0, h = h0;
|
|
3784
|
-
const hasW = Number.isFinite(optW);
|
|
3785
|
-
const hasH = Number.isFinite(optH);
|
|
3786
|
-
const aspect0 = h0 > 0 ? w0 / h0 : 1;
|
|
3787
|
-
if (hasW && hasH) {
|
|
3788
|
-
w = Math.max(1, limitDecimals(optW));
|
|
3789
|
-
h = Math.max(1, limitDecimals(optH));
|
|
3790
|
-
} else if (hasW) {
|
|
3791
|
-
w = Math.max(1, limitDecimals(optW));
|
|
3792
|
-
h = Math.max(1, limitDecimals(w / (aspect0 || 1)));
|
|
3793
|
-
} else if (hasH) {
|
|
3794
|
-
h = Math.max(1, limitDecimals(optH));
|
|
3795
|
-
w = Math.max(1, limitDecimals(h * (aspect0 || 1)));
|
|
3796
|
-
} else {
|
|
3797
|
-
w = w0;
|
|
3798
|
-
h = h0;
|
|
4224
|
+
function sanitizeAttributesForXHTML(root, opts = {}) {
|
|
4225
|
+
const { stripFrameworkDirectives = true } = opts;
|
|
4226
|
+
const ALLOWED_PREFIXES = /* @__PURE__ */ new Set(["xml", "xlink"]);
|
|
4227
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
4228
|
+
while (walker.nextNode()) {
|
|
4229
|
+
const el = walker.currentNode;
|
|
4230
|
+
for (const attr of Array.from(el.attributes)) {
|
|
4231
|
+
const name = attr.name;
|
|
4232
|
+
if (name.includes("@")) {
|
|
4233
|
+
el.removeAttribute(name);
|
|
4234
|
+
continue;
|
|
3799
4235
|
}
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
c: rootTransform2D.c || 0,
|
|
3806
|
-
d: rootTransform2D.d || 1,
|
|
3807
|
-
e: 0,
|
|
3808
|
-
f: 0
|
|
3809
|
-
};
|
|
3810
|
-
const bb2 = bboxWithOriginFull(w0, h0, M2, 0, 0);
|
|
3811
|
-
minX = limitDecimals(bb2.minX);
|
|
3812
|
-
minY = limitDecimals(bb2.minY);
|
|
3813
|
-
maxX = limitDecimals(bb2.maxX);
|
|
3814
|
-
maxY = limitDecimals(bb2.maxY);
|
|
3815
|
-
} else {
|
|
3816
|
-
const useTFBBox = outerTransforms && hasTFBBox(state.element);
|
|
3817
|
-
if (useTFBBox) {
|
|
3818
|
-
const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
|
|
3819
|
-
const ind2 = readIndividualTransforms(state.element);
|
|
3820
|
-
const TOTAL = readTotalTransformMatrix({
|
|
3821
|
-
baseTransform: baseTransform2,
|
|
3822
|
-
rotate: ind2.rotate || "0deg",
|
|
3823
|
-
scale: ind2.scale,
|
|
3824
|
-
translate: ind2.translate
|
|
3825
|
-
});
|
|
3826
|
-
const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
|
|
3827
|
-
const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
|
|
3828
|
-
const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
|
|
3829
|
-
minX = limitDecimals(bb.minX);
|
|
3830
|
-
minY = limitDecimals(bb.minY);
|
|
3831
|
-
maxX = limitDecimals(bb.maxX);
|
|
3832
|
-
maxY = limitDecimals(bb.maxY);
|
|
4236
|
+
if (name.includes(":")) {
|
|
4237
|
+
const prefix = name.split(":", 1)[0];
|
|
4238
|
+
if (!ALLOWED_PREFIXES.has(prefix)) {
|
|
4239
|
+
el.removeAttribute(name);
|
|
4240
|
+
continue;
|
|
3833
4241
|
}
|
|
3834
4242
|
}
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
const outW = Math.max(1, limitDecimals(vbW0 * scaleW));
|
|
3854
|
-
const outH = Math.max(1, limitDecimals(vbH0 * scaleH));
|
|
3855
|
-
const svgNS = "http://www.w3.org/2000/svg";
|
|
3856
|
-
const basePad = isSafari() ? 1 : 0;
|
|
3857
|
-
const extraPad = !outerTransforms ? 1 : 0;
|
|
3858
|
-
const pad = limitDecimals(basePad + extraPad);
|
|
3859
|
-
const fo = document.createElementNS(svgNS, "foreignObject");
|
|
3860
|
-
const vbMinX = limitDecimals(minX);
|
|
3861
|
-
const vbMinY = limitDecimals(minY);
|
|
3862
|
-
fo.setAttribute("x", String(limitDecimals(-(vbMinX - pad))));
|
|
3863
|
-
fo.setAttribute("y", String(limitDecimals(-(vbMinY - pad))));
|
|
3864
|
-
fo.setAttribute("width", String(limitDecimals(w0 + pad * 2)));
|
|
3865
|
-
fo.setAttribute("height", String(limitDecimals(h0 + pad * 2)));
|
|
3866
|
-
fo.style.overflow = "visible";
|
|
3867
|
-
const styleTag = document.createElement("style");
|
|
3868
|
-
styleTag.textContent = state.baseCSS + state.fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + state.classCSS;
|
|
3869
|
-
fo.appendChild(styleTag);
|
|
3870
|
-
const container = document.createElement("div");
|
|
3871
|
-
container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
3872
|
-
container.style.width = `${limitDecimals(w0)}px`;
|
|
3873
|
-
container.style.height = `${limitDecimals(h0)}px`;
|
|
3874
|
-
container.style.overflow = "visible";
|
|
3875
|
-
state.clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
3876
|
-
container.appendChild(state.clone);
|
|
3877
|
-
fo.appendChild(container);
|
|
3878
|
-
const serializer = new XMLSerializer();
|
|
3879
|
-
const foString = serializer.serializeToString(fo);
|
|
3880
|
-
const vbW = limitDecimals(vbW0 + pad * 2);
|
|
3881
|
-
const vbH = limitDecimals(vbH0 + pad * 2);
|
|
3882
|
-
const wantsSize = hasW || hasH;
|
|
3883
|
-
options.meta = { w0, h0, vbW, vbH, targetW: w, targetH: h };
|
|
3884
|
-
const svgOutW = isSafari() && wantsSize ? vbW : limitDecimals(outW + pad * 2);
|
|
3885
|
-
const svgOutH = isSafari() && wantsSize ? vbH : limitDecimals(outH + pad * 2);
|
|
3886
|
-
const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
|
|
3887
|
-
const svgFooter = "</svg>";
|
|
3888
|
-
svgString = svgHeader + foString + svgFooter;
|
|
3889
|
-
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
3890
|
-
state = { svgString, dataURL, ...state };
|
|
3891
|
-
resolve();
|
|
3892
|
-
}, { fast });
|
|
3893
|
-
});
|
|
3894
|
-
await runHook("afterRender", state);
|
|
3895
|
-
const sandbox = document.getElementById("snapdom-sandbox");
|
|
3896
|
-
if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
|
|
3897
|
-
return state.dataURL;
|
|
4243
|
+
if (!stripFrameworkDirectives) continue;
|
|
4244
|
+
if (name.startsWith("x-") || // Alpine
|
|
4245
|
+
name.startsWith("v-") || // Vue
|
|
4246
|
+
name.startsWith(":") || // Vue/Alpine shorthand
|
|
4247
|
+
name.startsWith("on:") || // Svelte
|
|
4248
|
+
name.startsWith("bind:") || // Svelte
|
|
4249
|
+
name.startsWith("let:") || // Svelte
|
|
4250
|
+
name.startsWith("class:")) {
|
|
4251
|
+
el.removeAttribute(name);
|
|
4252
|
+
continue;
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
function sanitizeCloneForXHTML(root, opts = {}) {
|
|
4258
|
+
if (!root) return;
|
|
4259
|
+
sanitizeAttributesForXHTML(root, opts);
|
|
4260
|
+
removeAllComments(root);
|
|
3898
4261
|
}
|
|
3899
4262
|
function authorHasExplicitSize(el) {
|
|
3900
4263
|
try {
|
|
@@ -3947,7 +4310,83 @@ function shrinkAutoSizeBoxes(sourceRoot, cloneRoot, styleCache = /* @__PURE__ */
|
|
|
3947
4310
|
}
|
|
3948
4311
|
walk(sourceRoot, cloneRoot);
|
|
3949
4312
|
}
|
|
4313
|
+
function isInNormalFlow(el) {
|
|
4314
|
+
const cs = getComputedStyle(el);
|
|
4315
|
+
if (cs.display === "none") return false;
|
|
4316
|
+
if (cs.position === "absolute" || cs.position === "fixed" || cs.position === "sticky") return false;
|
|
4317
|
+
if ((cs.cssFloat || cs.float || "none") !== "none") return false;
|
|
4318
|
+
if (cs.transform && cs.transform !== "none") return false;
|
|
4319
|
+
return true;
|
|
4320
|
+
}
|
|
4321
|
+
function willBeExcluded(el, options) {
|
|
4322
|
+
if (!(el instanceof Element)) return false;
|
|
4323
|
+
if (el.getAttribute("data-capture") === "exclude" && options?.excludeMode === "remove") return true;
|
|
4324
|
+
if (Array.isArray(options?.exclude)) {
|
|
4325
|
+
for (const sel of options.exclude) {
|
|
4326
|
+
try {
|
|
4327
|
+
if (el.matches(sel)) return options.excludeMode === "remove";
|
|
4328
|
+
} catch {
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
return false;
|
|
4333
|
+
}
|
|
4334
|
+
function estimateKeptHeight(container, options) {
|
|
4335
|
+
const csC = getComputedStyle(container);
|
|
4336
|
+
const rC = container.getBoundingClientRect();
|
|
4337
|
+
let minTop = Infinity;
|
|
4338
|
+
let maxBottom = -Infinity;
|
|
4339
|
+
let found = false;
|
|
4340
|
+
const kids = Array.from(container.children);
|
|
4341
|
+
for (const k of kids) {
|
|
4342
|
+
if (willBeExcluded(k, options)) continue;
|
|
4343
|
+
if (!isInNormalFlow(k)) continue;
|
|
4344
|
+
const rk = k.getBoundingClientRect();
|
|
4345
|
+
const top = rk.top - rC.top;
|
|
4346
|
+
const bottom = rk.bottom - rC.top;
|
|
4347
|
+
if (bottom <= top) continue;
|
|
4348
|
+
if (top < minTop) minTop = top;
|
|
4349
|
+
if (bottom > maxBottom) maxBottom = bottom;
|
|
4350
|
+
found = true;
|
|
4351
|
+
}
|
|
4352
|
+
const contentSpan = found ? Math.max(0, maxBottom - minTop) : 0;
|
|
4353
|
+
const bt = parseFloat(csC.borderTopWidth) || 0;
|
|
4354
|
+
const bb = parseFloat(csC.borderBottomWidth) || 0;
|
|
4355
|
+
const pt = parseFloat(csC.paddingTop) || 0;
|
|
4356
|
+
const pb = parseFloat(csC.paddingBottom) || 0;
|
|
4357
|
+
return bt + bb + pt + pb + contentSpan;
|
|
4358
|
+
}
|
|
3950
4359
|
var limitDecimals = (v, n = 3) => Number.isFinite(v) ? Math.round(v * 10 ** n) / 10 ** n : v;
|
|
4360
|
+
|
|
4361
|
+
// src/utils/transforms.helpers.js
|
|
4362
|
+
function parseBoxShadow(cs) {
|
|
4363
|
+
const v = cs.boxShadow || "";
|
|
4364
|
+
if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
4365
|
+
const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
|
|
4366
|
+
let t = 0, r = 0, b2 = 0, l = 0;
|
|
4367
|
+
for (const part of parts) {
|
|
4368
|
+
const nums = part.match(/-?\d+(\.\d+)?px/g)?.map((n) => parseFloat(n)) || [];
|
|
4369
|
+
if (nums.length < 2) continue;
|
|
4370
|
+
const [ox2, oy2, blur = 0, spread = 0] = nums;
|
|
4371
|
+
const extX = Math.abs(ox2) + blur + spread;
|
|
4372
|
+
const extY = Math.abs(oy2) + blur + spread;
|
|
4373
|
+
r = Math.max(r, extX + Math.max(ox2, 0));
|
|
4374
|
+
l = Math.max(l, extX + Math.max(-ox2, 0));
|
|
4375
|
+
b2 = Math.max(b2, extY + Math.max(oy2, 0));
|
|
4376
|
+
t = Math.max(t, extY + Math.max(-oy2, 0));
|
|
4377
|
+
}
|
|
4378
|
+
return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
|
|
4379
|
+
}
|
|
4380
|
+
function parseFilterBlur(cs) {
|
|
4381
|
+
const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
|
|
4382
|
+
const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
|
|
4383
|
+
return { top: b2, right: b2, bottom: b2, left: b2 };
|
|
4384
|
+
}
|
|
4385
|
+
function parseOutline(cs) {
|
|
4386
|
+
if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
4387
|
+
const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
|
|
4388
|
+
return { top: w2, right: w2, bottom: w2, left: w2 };
|
|
4389
|
+
}
|
|
3951
4390
|
function parseFilterDropShadows(cs) {
|
|
3952
4391
|
const raw = `${cs.filter || ""} ${cs.webkitFilter || ""}`.trim();
|
|
3953
4392
|
if (!raw || raw === "none") {
|
|
@@ -4036,34 +4475,6 @@ function normalizeRootTransforms(originalEl, cloneRoot) {
|
|
|
4036
4475
|
return null;
|
|
4037
4476
|
}
|
|
4038
4477
|
}
|
|
4039
|
-
function parseBoxShadow(cs) {
|
|
4040
|
-
const v = cs.boxShadow || "";
|
|
4041
|
-
if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
4042
|
-
const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
|
|
4043
|
-
let t = 0, r = 0, b2 = 0, l = 0;
|
|
4044
|
-
for (const part of parts) {
|
|
4045
|
-
const nums = part.match(/-?\d+(\.\d+)?px/g)?.map((n) => parseFloat(n)) || [];
|
|
4046
|
-
if (nums.length < 2) continue;
|
|
4047
|
-
const [ox2, oy2, blur = 0, spread = 0] = nums;
|
|
4048
|
-
const extX = Math.abs(ox2) + blur + spread;
|
|
4049
|
-
const extY = Math.abs(oy2) + blur + spread;
|
|
4050
|
-
r = Math.max(r, extX + Math.max(ox2, 0));
|
|
4051
|
-
l = Math.max(l, extX + Math.max(-ox2, 0));
|
|
4052
|
-
b2 = Math.max(b2, extY + Math.max(oy2, 0));
|
|
4053
|
-
t = Math.max(t, extY + Math.max(-oy2, 0));
|
|
4054
|
-
}
|
|
4055
|
-
return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
|
|
4056
|
-
}
|
|
4057
|
-
function parseFilterBlur(cs) {
|
|
4058
|
-
const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
|
|
4059
|
-
const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
|
|
4060
|
-
return { top: b2, right: b2, bottom: b2, left: b2 };
|
|
4061
|
-
}
|
|
4062
|
-
function parseOutline(cs) {
|
|
4063
|
-
if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
4064
|
-
const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
|
|
4065
|
-
return { top: w2, right: w2, bottom: w2, left: w2 };
|
|
4066
|
-
}
|
|
4067
4478
|
function bboxWithOriginFull(w2, h2, M, ox2, oy2) {
|
|
4068
4479
|
const a2 = M.a, b2 = M.b, c2 = M.c, d2 = M.d, e2 = M.e || 0, f2 = M.f || 0;
|
|
4069
4480
|
function pt(x, y) {
|
|
@@ -4083,17 +4494,24 @@ function bboxWithOriginFull(w2, h2, M, ox2, oy2) {
|
|
|
4083
4494
|
}
|
|
4084
4495
|
return { minX: minX2, minY: minY2, maxX: maxX2, maxY: maxY2, width: maxX2 - minX2, height: maxY2 - minY2 };
|
|
4085
4496
|
}
|
|
4086
|
-
function
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
return
|
|
4096
|
-
|
|
4497
|
+
function parseTransformOriginPx(cs, w, h) {
|
|
4498
|
+
const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/);
|
|
4499
|
+
const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"];
|
|
4500
|
+
const toPx = (token, size) => {
|
|
4501
|
+
const t = token.toLowerCase();
|
|
4502
|
+
if (t === "left" || t === "top") return 0;
|
|
4503
|
+
if (t === "center") return size / 2;
|
|
4504
|
+
if (t === "right") return size;
|
|
4505
|
+
if (t === "bottom") return size;
|
|
4506
|
+
if (t.endsWith("px")) return parseFloat(t) || 0;
|
|
4507
|
+
if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100;
|
|
4508
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0;
|
|
4509
|
+
return 0;
|
|
4510
|
+
};
|
|
4511
|
+
return {
|
|
4512
|
+
ox: toPx(oxRaw, w),
|
|
4513
|
+
oy: toPx(oyRaw, h)
|
|
4514
|
+
};
|
|
4097
4515
|
}
|
|
4098
4516
|
function readIndividualTransforms(el) {
|
|
4099
4517
|
const out = { rotate: "0deg", scale: null, translate: null };
|
|
@@ -4133,438 +4551,373 @@ function readIndividualTransforms(el) {
|
|
|
4133
4551
|
}
|
|
4134
4552
|
const tr = safeGet("translate");
|
|
4135
4553
|
if (tr) {
|
|
4136
|
-
const tx = "x" in tr && "value" in tr.x ? tr.x.value : Array.isArray(tr) ? tr[0]?.value : 0;
|
|
4137
|
-
const ty = "y" in tr && "value" in tr.y ? tr.y.value : Array.isArray(tr) ? tr[1]?.value : 0;
|
|
4138
|
-
const ux = "x" in tr && tr.x?.unit ? tr.x.unit : "px";
|
|
4139
|
-
const uy = "y" in tr && tr.y?.unit ? tr.y.unit : "px";
|
|
4140
|
-
out.translate = `${tx}${ux} ${ty}${uy}`;
|
|
4141
|
-
} else {
|
|
4142
|
-
const cs2 = getComputedStyle(el);
|
|
4143
|
-
out.translate = cs2.translate && cs2.translate !== "none" ? cs2.translate : null;
|
|
4144
|
-
}
|
|
4145
|
-
return out;
|
|
4146
|
-
}
|
|
4147
|
-
const cs = getComputedStyle(el);
|
|
4148
|
-
out.rotate = cs.rotate && cs.rotate !== "none" ? cs.rotate : "0deg";
|
|
4149
|
-
out.scale = cs.scale && cs.scale !== "none" ? cs.scale : null;
|
|
4150
|
-
out.translate = cs.translate && cs.translate !== "none" ? cs.translate : null;
|
|
4151
|
-
return out;
|
|
4152
|
-
}
|
|
4153
|
-
function hasBBoxAffectingTransform(el) {
|
|
4154
|
-
const cs = getComputedStyle(el);
|
|
4155
|
-
const t = cs.transform || "none";
|
|
4156
|
-
const hasMatrix = t !== "none" && !/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,\s*0\s*,\s*0\s*\)$/i.test(t);
|
|
4157
|
-
if (hasMatrix) return true;
|
|
4158
|
-
const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg";
|
|
4159
|
-
const s = cs.scale && cs.scale !== "none" && cs.scale !== "1";
|
|
4160
|
-
const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px";
|
|
4161
|
-
return Boolean(r || s || tr);
|
|
4162
|
-
}
|
|
4163
|
-
function parseTransformOriginPx(cs, w, h) {
|
|
4164
|
-
const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/);
|
|
4165
|
-
const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"];
|
|
4166
|
-
const toPx = (token, size) => {
|
|
4167
|
-
const t = token.toLowerCase();
|
|
4168
|
-
if (t === "left" || t === "top") return 0;
|
|
4169
|
-
if (t === "center") return size / 2;
|
|
4170
|
-
if (t === "right") return size;
|
|
4171
|
-
if (t === "bottom") return size;
|
|
4172
|
-
if (t.endsWith("px")) return parseFloat(t) || 0;
|
|
4173
|
-
if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100;
|
|
4174
|
-
if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0;
|
|
4175
|
-
return 0;
|
|
4176
|
-
};
|
|
4177
|
-
return {
|
|
4178
|
-
ox: toPx(oxRaw, w),
|
|
4179
|
-
oy: toPx(oyRaw, h)
|
|
4180
|
-
};
|
|
4181
|
-
}
|
|
4182
|
-
var __measureHost = null;
|
|
4183
|
-
function getMeasureHost() {
|
|
4184
|
-
if (__measureHost) return __measureHost;
|
|
4185
|
-
const n = document.createElement("div");
|
|
4186
|
-
n.id = "snapdom-measure-slot";
|
|
4187
|
-
n.setAttribute("aria-hidden", "true");
|
|
4188
|
-
Object.assign(n.style, {
|
|
4189
|
-
position: "absolute",
|
|
4190
|
-
left: "-99999px",
|
|
4191
|
-
top: "0px",
|
|
4192
|
-
width: "0px",
|
|
4193
|
-
height: "0px",
|
|
4194
|
-
overflow: "hidden",
|
|
4195
|
-
opacity: "0",
|
|
4196
|
-
pointerEvents: "none",
|
|
4197
|
-
contain: "size layout style"
|
|
4198
|
-
});
|
|
4199
|
-
document.documentElement.appendChild(n);
|
|
4200
|
-
__measureHost = n;
|
|
4201
|
-
return n;
|
|
4202
|
-
}
|
|
4203
|
-
function readTotalTransformMatrix(t) {
|
|
4204
|
-
const host = getMeasureHost();
|
|
4205
|
-
const tmp = document.createElement("div");
|
|
4206
|
-
tmp.style.transformOrigin = "0 0";
|
|
4207
|
-
if (t.baseTransform) tmp.style.transform = t.baseTransform;
|
|
4208
|
-
if (t.rotate) tmp.style.rotate = t.rotate;
|
|
4209
|
-
if (t.scale) tmp.style.scale = t.scale;
|
|
4210
|
-
if (t.translate) tmp.style.translate = t.translate;
|
|
4211
|
-
host.appendChild(tmp);
|
|
4212
|
-
const M = matrixFromComputed(tmp);
|
|
4213
|
-
host.removeChild(tmp);
|
|
4214
|
-
return M;
|
|
4215
|
-
}
|
|
4216
|
-
function isInNormalFlow(el) {
|
|
4217
|
-
const cs = getComputedStyle(el);
|
|
4218
|
-
if (cs.display === "none") return false;
|
|
4219
|
-
if (cs.position === "absolute" || cs.position === "fixed" || cs.position === "sticky") return false;
|
|
4220
|
-
if ((cs.cssFloat || cs.float || "none") !== "none") return false;
|
|
4221
|
-
if (cs.transform && cs.transform !== "none") return false;
|
|
4222
|
-
return true;
|
|
4223
|
-
}
|
|
4224
|
-
function willBeExcluded(el, options) {
|
|
4225
|
-
if (!(el instanceof Element)) return false;
|
|
4226
|
-
if (el.getAttribute("data-capture") === "exclude" && options?.excludeMode === "remove") return true;
|
|
4227
|
-
if (Array.isArray(options?.exclude)) {
|
|
4228
|
-
for (const sel of options.exclude) {
|
|
4229
|
-
try {
|
|
4230
|
-
if (el.matches(sel)) return options.excludeMode === "remove";
|
|
4231
|
-
} catch {
|
|
4232
|
-
}
|
|
4233
|
-
}
|
|
4234
|
-
}
|
|
4235
|
-
return false;
|
|
4236
|
-
}
|
|
4237
|
-
function estimateKeptHeight(container, options) {
|
|
4238
|
-
const csC = getComputedStyle(container);
|
|
4239
|
-
const rC = container.getBoundingClientRect();
|
|
4240
|
-
let minTop = Infinity;
|
|
4241
|
-
let maxBottom = -Infinity;
|
|
4242
|
-
let found = false;
|
|
4243
|
-
const kids = Array.from(container.children);
|
|
4244
|
-
for (const k of kids) {
|
|
4245
|
-
if (willBeExcluded(k, options)) continue;
|
|
4246
|
-
if (!isInNormalFlow(k)) continue;
|
|
4247
|
-
const rk = k.getBoundingClientRect();
|
|
4248
|
-
const top = rk.top - rC.top;
|
|
4249
|
-
const bottom = rk.bottom - rC.top;
|
|
4250
|
-
if (bottom <= top) continue;
|
|
4251
|
-
if (top < minTop) minTop = top;
|
|
4252
|
-
if (bottom > maxBottom) maxBottom = bottom;
|
|
4253
|
-
found = true;
|
|
4254
|
-
}
|
|
4255
|
-
const contentSpan = found ? Math.max(0, maxBottom - minTop) : 0;
|
|
4256
|
-
const bt = parseFloat(csC.borderTopWidth) || 0;
|
|
4257
|
-
const bb = parseFloat(csC.borderBottomWidth) || 0;
|
|
4258
|
-
const pt = parseFloat(csC.paddingTop) || 0;
|
|
4259
|
-
const pb = parseFloat(csC.paddingBottom) || 0;
|
|
4260
|
-
return bt + bb + pt + pb + contentSpan;
|
|
4261
|
-
}
|
|
4262
|
-
|
|
4263
|
-
// src/core/context.js
|
|
4264
|
-
function normalizeCachePolicy(v) {
|
|
4265
|
-
if (typeof v === "string") {
|
|
4266
|
-
const s = v.toLowerCase().trim();
|
|
4267
|
-
if (s === "disabled" || s === "full" || s === "auto" || s === "soft") return (
|
|
4268
|
-
/** @type {CachePolicy} */
|
|
4269
|
-
s
|
|
4270
|
-
);
|
|
4271
|
-
}
|
|
4272
|
-
return "soft";
|
|
4273
|
-
}
|
|
4274
|
-
function createContext(options = {}) {
|
|
4275
|
-
let resolvedFormat = options.format ?? "png";
|
|
4276
|
-
if (resolvedFormat === "jpg") resolvedFormat = "jpeg";
|
|
4277
|
-
const cachePolicy = normalizeCachePolicy(options.cache);
|
|
4278
|
-
return {
|
|
4279
|
-
// Debug & perf
|
|
4280
|
-
debug: options.debug ?? false,
|
|
4281
|
-
fast: options.fast ?? true,
|
|
4282
|
-
scale: options.scale ?? 1,
|
|
4283
|
-
// DOM filters
|
|
4284
|
-
exclude: options.exclude ?? [],
|
|
4285
|
-
excludeMode: options.excludeMode ?? "hide",
|
|
4286
|
-
filter: options.filter ?? null,
|
|
4287
|
-
filterMode: options.filterMode ?? "hide",
|
|
4288
|
-
// Placeholders
|
|
4289
|
-
placeholders: options.placeholders !== false,
|
|
4290
|
-
// default true
|
|
4291
|
-
// Fonts
|
|
4292
|
-
embedFonts: options.embedFonts ?? false,
|
|
4293
|
-
iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
|
|
4294
|
-
localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
|
|
4295
|
-
excludeFonts: options.excludeFonts ?? void 0,
|
|
4296
|
-
fallbackURL: options.fallbackURL ?? void 0,
|
|
4297
|
-
/** @type {CachePolicy} */
|
|
4298
|
-
cache: cachePolicy,
|
|
4299
|
-
// Network
|
|
4300
|
-
useProxy: typeof options.useProxy === "string" ? options.useProxy : "",
|
|
4301
|
-
// Output
|
|
4302
|
-
width: options.width ?? null,
|
|
4303
|
-
height: options.height ?? null,
|
|
4304
|
-
format: resolvedFormat,
|
|
4305
|
-
type: options.type ?? "svg",
|
|
4306
|
-
quality: options.quality ?? 0.92,
|
|
4307
|
-
dpr: options.dpr ?? (window.devicePixelRatio || 1),
|
|
4308
|
-
backgroundColor: options.backgroundColor ?? (["jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null),
|
|
4309
|
-
filename: options.filename ?? "snapDOM",
|
|
4310
|
-
// NEW flags (user-friendly)
|
|
4311
|
-
outerTransforms: options.outerTransforms ?? true,
|
|
4312
|
-
outerShadows: options.outerShadows ?? false
|
|
4313
|
-
// Plugins (reservado)
|
|
4314
|
-
// plugins: normalizePlugins(...),
|
|
4315
|
-
};
|
|
4316
|
-
}
|
|
4317
|
-
|
|
4318
|
-
// src/exporters/toCanvas.js
|
|
4319
|
-
function isSvgDataURL(u) {
|
|
4320
|
-
return typeof u === "string" && /^data:image\/svg\+xml/i.test(u);
|
|
4321
|
-
}
|
|
4322
|
-
function decodeSvgFromDataURL(u) {
|
|
4323
|
-
const i = u.indexOf(",");
|
|
4324
|
-
return i >= 0 ? decodeURIComponent(u.slice(i + 1)) : "";
|
|
4325
|
-
}
|
|
4326
|
-
function encodeSvgToDataURL(svgText) {
|
|
4327
|
-
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
|
|
4328
|
-
}
|
|
4329
|
-
function splitDecls(s) {
|
|
4330
|
-
let parts = [], buf = "", depth = 0;
|
|
4331
|
-
for (let i = 0; i < s.length; i++) {
|
|
4332
|
-
const ch = s[i];
|
|
4333
|
-
if (ch === "(") depth++;
|
|
4334
|
-
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
4335
|
-
if (ch === ";" && depth === 0) {
|
|
4336
|
-
parts.push(buf);
|
|
4337
|
-
buf = "";
|
|
4338
|
-
} else buf += ch;
|
|
4339
|
-
}
|
|
4340
|
-
if (buf.trim()) parts.push(buf);
|
|
4341
|
-
return parts.map((x) => x.trim()).filter(Boolean);
|
|
4342
|
-
}
|
|
4343
|
-
function boxShadowToDropShadow(value) {
|
|
4344
|
-
const layers = [];
|
|
4345
|
-
let buf = "", depth = 0;
|
|
4346
|
-
for (let i = 0; i < value.length; i++) {
|
|
4347
|
-
const ch = value[i];
|
|
4348
|
-
if (ch === "(") depth++;
|
|
4349
|
-
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
4350
|
-
if (ch === "," && depth === 0) {
|
|
4351
|
-
layers.push(buf.trim());
|
|
4352
|
-
buf = "";
|
|
4353
|
-
} else buf += ch;
|
|
4354
|
-
}
|
|
4355
|
-
if (buf.trim()) layers.push(buf.trim());
|
|
4356
|
-
const fns = [];
|
|
4357
|
-
for (const layer of layers) {
|
|
4358
|
-
if (/\binset\b/i.test(layer)) continue;
|
|
4359
|
-
const nums = layer.match(/-?\d+(?:\.\d+)?px/gi) || [];
|
|
4360
|
-
const [ox = "0px", oy = "0px", blur = "0px"] = nums;
|
|
4361
|
-
let color = layer.replace(/-?\d+(?:\.\d+)?px/gi, "").replace(/\binset\b/ig, "").trim().replace(/\s{2,}/g, " ");
|
|
4362
|
-
const hasColor = !!color && color !== ",";
|
|
4363
|
-
fns.push(`drop-shadow(${ox} ${oy} ${blur}${hasColor ? ` ${color}` : ""})`);
|
|
4364
|
-
}
|
|
4365
|
-
return fns.join(" ");
|
|
4366
|
-
}
|
|
4367
|
-
function rewriteDeclList(list) {
|
|
4368
|
-
const decls = splitDecls(list);
|
|
4369
|
-
let filter = null, wfilter = null, box = null;
|
|
4370
|
-
const rest = [];
|
|
4371
|
-
for (const d of decls) {
|
|
4372
|
-
const idx = d.indexOf(":");
|
|
4373
|
-
if (idx < 0) continue;
|
|
4374
|
-
const prop = d.slice(0, idx).trim().toLowerCase();
|
|
4375
|
-
const val = d.slice(idx + 1).trim();
|
|
4376
|
-
if (prop === "box-shadow") box = val;
|
|
4377
|
-
else if (prop === "filter") filter = val;
|
|
4378
|
-
else if (prop === "-webkit-filter") wfilter = val;
|
|
4379
|
-
else rest.push([prop, val]);
|
|
4380
|
-
}
|
|
4381
|
-
if (box) {
|
|
4382
|
-
const ds = boxShadowToDropShadow(box);
|
|
4383
|
-
if (ds) {
|
|
4384
|
-
filter = filter ? `${filter} ${ds}` : ds;
|
|
4385
|
-
wfilter = wfilter ? `${wfilter} ${ds}` : ds;
|
|
4554
|
+
const tx = "x" in tr && "value" in tr.x ? tr.x.value : Array.isArray(tr) ? tr[0]?.value : 0;
|
|
4555
|
+
const ty = "y" in tr && "value" in tr.y ? tr.y.value : Array.isArray(tr) ? tr[1]?.value : 0;
|
|
4556
|
+
const ux = "x" in tr && tr.x?.unit ? tr.x.unit : "px";
|
|
4557
|
+
const uy = "y" in tr && tr.y?.unit ? tr.y.unit : "px";
|
|
4558
|
+
out.translate = `${tx}${ux} ${ty}${uy}`;
|
|
4559
|
+
} else {
|
|
4560
|
+
const cs2 = getComputedStyle(el);
|
|
4561
|
+
out.translate = cs2.translate && cs2.translate !== "none" ? cs2.translate : null;
|
|
4386
4562
|
}
|
|
4563
|
+
return out;
|
|
4387
4564
|
}
|
|
4388
|
-
const
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4565
|
+
const cs = getComputedStyle(el);
|
|
4566
|
+
out.rotate = cs.rotate && cs.rotate !== "none" ? cs.rotate : "0deg";
|
|
4567
|
+
out.scale = cs.scale && cs.scale !== "none" ? cs.scale : null;
|
|
4568
|
+
out.translate = cs.translate && cs.translate !== "none" ? cs.translate : null;
|
|
4569
|
+
return out;
|
|
4392
4570
|
}
|
|
4393
|
-
|
|
4394
|
-
|
|
4571
|
+
var __measureHost = null;
|
|
4572
|
+
function getMeasureHost() {
|
|
4573
|
+
if (__measureHost) return __measureHost;
|
|
4574
|
+
const n = document.createElement("div");
|
|
4575
|
+
n.id = "snapdom-measure-slot";
|
|
4576
|
+
n.setAttribute("aria-hidden", "true");
|
|
4577
|
+
Object.assign(n.style, {
|
|
4578
|
+
position: "absolute",
|
|
4579
|
+
left: "-99999px",
|
|
4580
|
+
top: "0px",
|
|
4581
|
+
width: "0px",
|
|
4582
|
+
height: "0px",
|
|
4583
|
+
overflow: "hidden",
|
|
4584
|
+
opacity: "0",
|
|
4585
|
+
pointerEvents: "none",
|
|
4586
|
+
contain: "size layout style"
|
|
4587
|
+
});
|
|
4588
|
+
document.documentElement.appendChild(n);
|
|
4589
|
+
__measureHost = n;
|
|
4590
|
+
return n;
|
|
4395
4591
|
}
|
|
4396
|
-
function
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
);
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
);
|
|
4405
|
-
|
|
4592
|
+
function readTotalTransformMatrix(t) {
|
|
4593
|
+
const host = getMeasureHost();
|
|
4594
|
+
const tmp = document.createElement("div");
|
|
4595
|
+
tmp.style.transformOrigin = "0 0";
|
|
4596
|
+
if (t.baseTransform) tmp.style.transform = t.baseTransform;
|
|
4597
|
+
if (t.rotate) tmp.style.rotate = t.rotate;
|
|
4598
|
+
if (t.scale) tmp.style.scale = t.scale;
|
|
4599
|
+
if (t.translate) tmp.style.translate = t.translate;
|
|
4600
|
+
host.appendChild(tmp);
|
|
4601
|
+
const M = matrixFromComputed(tmp);
|
|
4602
|
+
host.removeChild(tmp);
|
|
4603
|
+
return M;
|
|
4406
4604
|
}
|
|
4407
|
-
function
|
|
4408
|
-
|
|
4605
|
+
function hasBBoxAffectingTransform(el) {
|
|
4606
|
+
const cs = getComputedStyle(el);
|
|
4607
|
+
const t = cs.transform || "none";
|
|
4608
|
+
const hasMatrix = t !== "none" && !/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,\s*0\s*,\s*0\s*\)$/i.test(t);
|
|
4609
|
+
if (hasMatrix) return true;
|
|
4610
|
+
const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg";
|
|
4611
|
+
const s = cs.scale && cs.scale !== "none" && cs.scale !== "1";
|
|
4612
|
+
const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px";
|
|
4613
|
+
return Boolean(r || s || tr);
|
|
4614
|
+
}
|
|
4615
|
+
function matrixFromComputed(el) {
|
|
4616
|
+
const tr = getComputedStyle(el).transform;
|
|
4617
|
+
if (!tr || tr === "none") return new DOMMatrix();
|
|
4409
4618
|
try {
|
|
4410
|
-
|
|
4411
|
-
const fixed = rewriteSvgBoxShadowToDropShadow(svg);
|
|
4412
|
-
return encodeSvgToDataURL(fixed);
|
|
4619
|
+
return new DOMMatrix(tr);
|
|
4413
4620
|
} catch {
|
|
4414
|
-
return
|
|
4415
|
-
}
|
|
4416
|
-
}
|
|
4417
|
-
async function toCanvas(url, options) {
|
|
4418
|
-
let { width: optW, height: optH, scale = 1, dpr = 1, meta = {}, backgroundColor } = options;
|
|
4419
|
-
url = maybeConvertBoxShadowForSafari(url);
|
|
4420
|
-
const img = new Image();
|
|
4421
|
-
img.loading = "eager";
|
|
4422
|
-
img.decoding = "sync";
|
|
4423
|
-
img.crossOrigin = "anonymous";
|
|
4424
|
-
img.src = url;
|
|
4425
|
-
await img.decode();
|
|
4426
|
-
const natW = img.naturalWidth;
|
|
4427
|
-
const natH = img.naturalHeight;
|
|
4428
|
-
const refW = Number.isFinite(meta.w0) ? meta.w0 : natW;
|
|
4429
|
-
const refH = Number.isFinite(meta.h0) ? meta.h0 : natH;
|
|
4430
|
-
let outW, outH;
|
|
4431
|
-
const hasW = Number.isFinite(optW);
|
|
4432
|
-
const hasH = Number.isFinite(optH);
|
|
4433
|
-
if (hasW && hasH) {
|
|
4434
|
-
outW = Math.max(1, optW);
|
|
4435
|
-
outH = Math.max(1, optH);
|
|
4436
|
-
} else if (hasW) {
|
|
4437
|
-
const k = optW / Math.max(1, refW);
|
|
4438
|
-
outW = optW;
|
|
4439
|
-
outH = refH * k;
|
|
4440
|
-
} else if (hasH) {
|
|
4441
|
-
const k = optH / Math.max(1, refH);
|
|
4442
|
-
outH = optH;
|
|
4443
|
-
outW = refW * k;
|
|
4444
|
-
} else {
|
|
4445
|
-
outW = natW;
|
|
4446
|
-
outH = natH;
|
|
4447
|
-
}
|
|
4448
|
-
outW = outW * scale;
|
|
4449
|
-
outH = outH * scale;
|
|
4450
|
-
const canvas = document.createElement("canvas");
|
|
4451
|
-
canvas.width = outW * dpr;
|
|
4452
|
-
canvas.height = outH * dpr;
|
|
4453
|
-
canvas.style.width = `${outW}px`;
|
|
4454
|
-
canvas.style.height = `${outH}px`;
|
|
4455
|
-
const ctx = canvas.getContext("2d");
|
|
4456
|
-
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
4457
|
-
if (backgroundColor) {
|
|
4458
|
-
ctx.save();
|
|
4459
|
-
ctx.fillStyle = backgroundColor;
|
|
4460
|
-
ctx.fillRect(0, 0, outW, outH);
|
|
4461
|
-
ctx.restore();
|
|
4621
|
+
return new WebKitCSSMatrix(tr);
|
|
4462
4622
|
}
|
|
4463
|
-
ctx.drawImage(img, 0, 0, outW, outH);
|
|
4464
|
-
return canvas;
|
|
4465
|
-
}
|
|
4466
|
-
|
|
4467
|
-
// src/modules/rasterize.js
|
|
4468
|
-
async function rasterize(url, options) {
|
|
4469
|
-
const canvas = await toCanvas(url, options);
|
|
4470
|
-
const img = new Image();
|
|
4471
|
-
img.src = canvas.toDataURL(`image/${options.format}`, options.quality);
|
|
4472
|
-
await img.decode();
|
|
4473
|
-
img.style.width = `${canvas.width / options.dpr}px`;
|
|
4474
|
-
img.style.height = `${canvas.height / options.dpr}px`;
|
|
4475
|
-
return img;
|
|
4476
4623
|
}
|
|
4477
4624
|
|
|
4478
|
-
// src/
|
|
4479
|
-
async function
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
const
|
|
4483
|
-
const
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4625
|
+
// src/core/capture.js
|
|
4626
|
+
async function captureDOM(element, options) {
|
|
4627
|
+
if (!element) throw new Error("Element cannot be null or undefined");
|
|
4628
|
+
applyCachePolicy(options.cache);
|
|
4629
|
+
const fast = options.fast;
|
|
4630
|
+
const outerTransforms = options.outerTransforms !== false;
|
|
4631
|
+
const outerShadows = !!options.outerShadows;
|
|
4632
|
+
let state = { element, options, plugins: options.plugins };
|
|
4633
|
+
let clone, classCSS, styleCache;
|
|
4634
|
+
let fontsCSS = "";
|
|
4635
|
+
let baseCSS = "";
|
|
4636
|
+
let dataURL;
|
|
4637
|
+
let svgString;
|
|
4638
|
+
let rootTransform2D = null;
|
|
4639
|
+
await runHook("beforeSnap", state);
|
|
4640
|
+
await runHook("beforeClone", state);
|
|
4641
|
+
const undoClamp = lineClamp(state.element);
|
|
4642
|
+
try {
|
|
4643
|
+
({ clone, classCSS, styleCache } = await prepareClone(state.element, state.options));
|
|
4644
|
+
if (!outerTransforms && clone) {
|
|
4645
|
+
rootTransform2D = normalizeRootTransforms(state.element, clone);
|
|
4646
|
+
}
|
|
4647
|
+
if (!outerShadows && clone) {
|
|
4648
|
+
stripRootShadows(state.element, clone);
|
|
4649
|
+
}
|
|
4650
|
+
} finally {
|
|
4651
|
+
undoClamp();
|
|
4487
4652
|
}
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
} else if (hasW) {
|
|
4497
|
-
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
4498
|
-
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
4499
|
-
const k = width / Math.max(1, refW);
|
|
4500
|
-
img.style.width = `${width}px`;
|
|
4501
|
-
img.style.height = `${Math.round(refH * k)}px`;
|
|
4502
|
-
} else if (hasH) {
|
|
4503
|
-
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
4504
|
-
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
4505
|
-
const k = height / Math.max(1, refH);
|
|
4506
|
-
img.style.height = `${height}px`;
|
|
4507
|
-
img.style.width = `${Math.round(refW * k)}px`;
|
|
4508
|
-
} else {
|
|
4509
|
-
const cssW = Math.round(img.naturalWidth * scale);
|
|
4510
|
-
const cssH = Math.round(img.naturalHeight * scale);
|
|
4511
|
-
img.style.width = `${cssW}px`;
|
|
4512
|
-
img.style.height = `${cssH}px`;
|
|
4513
|
-
if (typeof url === "string" && url.startsWith("data:image/svg+xml")) {
|
|
4514
|
-
try {
|
|
4515
|
-
const decoded = decodeURIComponent(url.split(",")[1]);
|
|
4516
|
-
const patched = decoded.replace(/width="[^"]*"/, `width="${cssW}"`).replace(/height="[^"]*"/, `height="${cssH}"`);
|
|
4517
|
-
url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(patched)}`;
|
|
4518
|
-
img.src = url;
|
|
4519
|
-
} catch {
|
|
4520
|
-
}
|
|
4653
|
+
state = { clone, classCSS, styleCache, ...state };
|
|
4654
|
+
await runHook("afterClone", state);
|
|
4655
|
+
sanitizeCloneForXHTML(state.clone);
|
|
4656
|
+
if (state.options?.excludeMode === "remove") {
|
|
4657
|
+
try {
|
|
4658
|
+
shrinkAutoSizeBoxes(state.element, state.clone, state.styleCache);
|
|
4659
|
+
} catch (e) {
|
|
4660
|
+
console.warn("[snapdom] shrink pass failed:", e);
|
|
4521
4661
|
}
|
|
4522
4662
|
}
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4663
|
+
try {
|
|
4664
|
+
await ligatureIconToImage(state.clone, state.element);
|
|
4665
|
+
} catch {
|
|
4666
|
+
}
|
|
4667
|
+
await new Promise((resolve) => {
|
|
4668
|
+
idle(async () => {
|
|
4669
|
+
await inlineImages(state.clone, state.options);
|
|
4670
|
+
resolve();
|
|
4671
|
+
}, { fast });
|
|
4672
|
+
});
|
|
4673
|
+
await new Promise((resolve) => {
|
|
4674
|
+
idle(async () => {
|
|
4675
|
+
await inlineBackgroundImages(state.element, state.clone, state.styleCache, state.options);
|
|
4676
|
+
resolve();
|
|
4677
|
+
}, { fast });
|
|
4678
|
+
});
|
|
4679
|
+
if (options.embedFonts) {
|
|
4680
|
+
await new Promise((resolve) => {
|
|
4681
|
+
idle(async () => {
|
|
4682
|
+
const required = collectUsedFontVariants(state.element);
|
|
4683
|
+
const usedCodepoints = collectUsedCodepoints(state.element);
|
|
4684
|
+
if (isSafari()) {
|
|
4685
|
+
const families = new Set(
|
|
4686
|
+
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
4687
|
+
);
|
|
4688
|
+
await ensureFontsReady(families, 1);
|
|
4689
|
+
}
|
|
4690
|
+
fontsCSS = await embedCustomFonts({
|
|
4691
|
+
required,
|
|
4692
|
+
usedCodepoints,
|
|
4693
|
+
preCached: false,
|
|
4694
|
+
exclude: state.options.excludeFonts,
|
|
4695
|
+
useProxy: state.options.useProxy
|
|
4696
|
+
});
|
|
4697
|
+
resolve();
|
|
4698
|
+
}, { fast });
|
|
4699
|
+
});
|
|
4700
|
+
}
|
|
4701
|
+
const usedTags = collectUsedTagNames(state.clone).sort();
|
|
4702
|
+
const tagKey = usedTags.join(",");
|
|
4703
|
+
if (cache.baseStyle.has(tagKey)) {
|
|
4704
|
+
baseCSS = cache.baseStyle.get(tagKey);
|
|
4705
|
+
} else {
|
|
4706
|
+
await new Promise((resolve) => {
|
|
4707
|
+
idle(() => {
|
|
4708
|
+
baseCSS = generateDedupedBaseCSS(usedTags);
|
|
4709
|
+
cache.baseStyle.set(tagKey, baseCSS);
|
|
4710
|
+
resolve();
|
|
4711
|
+
}, { fast });
|
|
4712
|
+
});
|
|
4532
4713
|
}
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4714
|
+
state = { fontsCSS, baseCSS, ...state };
|
|
4715
|
+
await runHook("beforeRender", state);
|
|
4716
|
+
await new Promise((resolve) => {
|
|
4717
|
+
idle(() => {
|
|
4718
|
+
const csEl = getComputedStyle(state.element);
|
|
4719
|
+
const rect = state.element.getBoundingClientRect();
|
|
4720
|
+
let w0 = Math.max(1, limitDecimals(state.element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
|
|
4721
|
+
let h0 = Math.max(1, limitDecimals(state.element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
|
|
4722
|
+
if (state.options?.excludeMode === "remove") {
|
|
4723
|
+
const hEst = estimateKeptHeight(state.element, state.options);
|
|
4724
|
+
const EPS = 1;
|
|
4725
|
+
if (Number.isFinite(hEst) && hEst > 0) {
|
|
4726
|
+
h0 = Math.max(1, Math.min(h0, limitDecimals(hEst + EPS)));
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
const coerceNum = (v, def = NaN) => {
|
|
4730
|
+
const n = typeof v === "string" ? parseFloat(v) : v;
|
|
4731
|
+
return Number.isFinite(n) ? n : def;
|
|
4732
|
+
};
|
|
4733
|
+
const optW = coerceNum(state.options.width);
|
|
4734
|
+
const optH = coerceNum(state.options.height);
|
|
4735
|
+
let w = w0, h = h0;
|
|
4736
|
+
const hasW = Number.isFinite(optW);
|
|
4737
|
+
const hasH = Number.isFinite(optH);
|
|
4738
|
+
const aspect0 = h0 > 0 ? w0 / h0 : 1;
|
|
4739
|
+
if (hasW && hasH) {
|
|
4740
|
+
w = Math.max(1, limitDecimals(optW));
|
|
4741
|
+
h = Math.max(1, limitDecimals(optH));
|
|
4742
|
+
} else if (hasW) {
|
|
4743
|
+
w = Math.max(1, limitDecimals(optW));
|
|
4744
|
+
h = Math.max(1, limitDecimals(w / (aspect0 || 1)));
|
|
4745
|
+
} else if (hasH) {
|
|
4746
|
+
h = Math.max(1, limitDecimals(optH));
|
|
4747
|
+
w = Math.max(1, limitDecimals(h * (aspect0 || 1)));
|
|
4748
|
+
} else {
|
|
4749
|
+
w = w0;
|
|
4750
|
+
h = h0;
|
|
4751
|
+
}
|
|
4752
|
+
let minX = 0, minY = 0, maxX = w0, maxY = h0;
|
|
4753
|
+
if (!outerTransforms && rootTransform2D && Number.isFinite(rootTransform2D.a)) {
|
|
4754
|
+
const M2 = {
|
|
4755
|
+
a: rootTransform2D.a,
|
|
4756
|
+
b: rootTransform2D.b || 0,
|
|
4757
|
+
c: rootTransform2D.c || 0,
|
|
4758
|
+
d: rootTransform2D.d || 1,
|
|
4759
|
+
e: 0,
|
|
4760
|
+
f: 0
|
|
4761
|
+
};
|
|
4762
|
+
const bb2 = bboxWithOriginFull(w0, h0, M2, 0, 0);
|
|
4763
|
+
minX = limitDecimals(bb2.minX);
|
|
4764
|
+
minY = limitDecimals(bb2.minY);
|
|
4765
|
+
maxX = limitDecimals(bb2.maxX);
|
|
4766
|
+
maxY = limitDecimals(bb2.maxY);
|
|
4767
|
+
} else {
|
|
4768
|
+
const useTFBBox = outerTransforms && hasTFBBox(state.element);
|
|
4769
|
+
if (useTFBBox) {
|
|
4770
|
+
const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
|
|
4771
|
+
const ind2 = readIndividualTransforms(state.element);
|
|
4772
|
+
const TOTAL = readTotalTransformMatrix({
|
|
4773
|
+
baseTransform: baseTransform2,
|
|
4774
|
+
rotate: ind2.rotate || "0deg",
|
|
4775
|
+
scale: ind2.scale,
|
|
4776
|
+
translate: ind2.translate
|
|
4777
|
+
});
|
|
4778
|
+
const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
|
|
4779
|
+
const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
|
|
4780
|
+
const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
|
|
4781
|
+
minX = limitDecimals(bb.minX);
|
|
4782
|
+
minY = limitDecimals(bb.minY);
|
|
4783
|
+
maxX = limitDecimals(bb.maxX);
|
|
4784
|
+
maxY = limitDecimals(bb.maxY);
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
const bleedShadow = parseBoxShadow(csEl);
|
|
4788
|
+
const bleedBlur = parseFilterBlur(csEl);
|
|
4789
|
+
const bleedOutline = parseOutline(csEl);
|
|
4790
|
+
const drop = parseFilterDropShadows(csEl);
|
|
4791
|
+
const bleed = !outerShadows ? { top: 0, right: 0, bottom: 0, left: 0 } : {
|
|
4792
|
+
top: limitDecimals(bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top),
|
|
4793
|
+
right: limitDecimals(bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right),
|
|
4794
|
+
bottom: limitDecimals(bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom),
|
|
4795
|
+
left: limitDecimals(bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left)
|
|
4796
|
+
};
|
|
4797
|
+
minX = limitDecimals(minX - bleed.left);
|
|
4798
|
+
minY = limitDecimals(minY - bleed.top);
|
|
4799
|
+
maxX = limitDecimals(maxX + bleed.right);
|
|
4800
|
+
maxY = limitDecimals(maxY + bleed.bottom);
|
|
4801
|
+
const vbW0 = Math.max(1, limitDecimals(maxX - minX));
|
|
4802
|
+
const vbH0 = Math.max(1, limitDecimals(maxY - minY));
|
|
4803
|
+
const scaleW = hasW || hasH ? limitDecimals(w / w0) : 1;
|
|
4804
|
+
const scaleH = hasH || hasW ? limitDecimals(h / h0) : 1;
|
|
4805
|
+
const outW = Math.max(1, limitDecimals(vbW0 * scaleW));
|
|
4806
|
+
const outH = Math.max(1, limitDecimals(vbH0 * scaleH));
|
|
4807
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
4808
|
+
const basePad = isSafari() ? 1 : 0;
|
|
4809
|
+
const extraPad = !outerTransforms ? 1 : 0;
|
|
4810
|
+
const pad = limitDecimals(basePad + extraPad);
|
|
4811
|
+
const fo = document.createElementNS(svgNS, "foreignObject");
|
|
4812
|
+
const vbMinX = limitDecimals(minX);
|
|
4813
|
+
const vbMinY = limitDecimals(minY);
|
|
4814
|
+
fo.setAttribute("x", String(limitDecimals(-(vbMinX - pad))));
|
|
4815
|
+
fo.setAttribute("y", String(limitDecimals(-(vbMinY - pad))));
|
|
4816
|
+
fo.setAttribute("width", String(limitDecimals(w0 + pad * 2)));
|
|
4817
|
+
fo.setAttribute("height", String(limitDecimals(h0 + pad * 2)));
|
|
4818
|
+
fo.style.overflow = "visible";
|
|
4819
|
+
const styleTag = document.createElement("style");
|
|
4820
|
+
styleTag.textContent = state.baseCSS + state.fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + state.classCSS;
|
|
4821
|
+
fo.appendChild(styleTag);
|
|
4822
|
+
const container = document.createElement("div");
|
|
4823
|
+
container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
4824
|
+
container.style.width = `${limitDecimals(w0)}px`;
|
|
4825
|
+
container.style.height = `${limitDecimals(h0)}px`;
|
|
4826
|
+
container.style.overflow = "visible";
|
|
4827
|
+
state.clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
4828
|
+
container.appendChild(state.clone);
|
|
4829
|
+
fo.appendChild(container);
|
|
4830
|
+
const serializer = new XMLSerializer();
|
|
4831
|
+
const foString = serializer.serializeToString(fo);
|
|
4832
|
+
const vbW = limitDecimals(vbW0 + pad * 2);
|
|
4833
|
+
const vbH = limitDecimals(vbH0 + pad * 2);
|
|
4834
|
+
const wantsSize = hasW || hasH;
|
|
4835
|
+
options.meta = { w0, h0, vbW, vbH, targetW: w, targetH: h };
|
|
4836
|
+
const svgOutW = isSafari() && wantsSize ? vbW : limitDecimals(outW + pad * 2);
|
|
4837
|
+
const svgOutH = isSafari() && wantsSize ? vbH : limitDecimals(outH + pad * 2);
|
|
4838
|
+
const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
|
|
4839
|
+
const svgFooter = "</svg>";
|
|
4840
|
+
svgString = svgHeader + foString + svgFooter;
|
|
4841
|
+
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
4842
|
+
state = { svgString, dataURL, ...state };
|
|
4843
|
+
resolve();
|
|
4844
|
+
}, { fast });
|
|
4845
|
+
});
|
|
4846
|
+
await runHook("afterRender", state);
|
|
4847
|
+
const sandbox = document.getElementById("snapdom-sandbox");
|
|
4848
|
+
if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
|
|
4849
|
+
return state.dataURL;
|
|
4850
|
+
}
|
|
4851
|
+
function hasTFBBox(el) {
|
|
4852
|
+
return hasBBoxAffectingTransform(el);
|
|
4537
4853
|
}
|
|
4538
4854
|
|
|
4539
|
-
// src/
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
a2.download = options.filename;
|
|
4548
|
-
a2.click();
|
|
4549
|
-
URL.revokeObjectURL(objectURL);
|
|
4550
|
-
return;
|
|
4855
|
+
// src/core/context.js
|
|
4856
|
+
function normalizeCachePolicy(v) {
|
|
4857
|
+
if (typeof v === "string") {
|
|
4858
|
+
const s = v.toLowerCase().trim();
|
|
4859
|
+
if (s === "disabled" || s === "full" || s === "auto" || s === "soft") return (
|
|
4860
|
+
/** @type {CachePolicy} */
|
|
4861
|
+
s
|
|
4862
|
+
);
|
|
4551
4863
|
}
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4864
|
+
return "soft";
|
|
4865
|
+
}
|
|
4866
|
+
function createContext(options = {}) {
|
|
4867
|
+
let resolvedFormat = options.format ?? "png";
|
|
4868
|
+
if (resolvedFormat === "jpg") resolvedFormat = "jpeg";
|
|
4869
|
+
const cachePolicy = normalizeCachePolicy(options.cache);
|
|
4870
|
+
return {
|
|
4871
|
+
// Debug & perf
|
|
4872
|
+
debug: options.debug ?? false,
|
|
4873
|
+
fast: options.fast ?? true,
|
|
4874
|
+
scale: options.scale ?? 1,
|
|
4875
|
+
// DOM filters
|
|
4876
|
+
exclude: options.exclude ?? [],
|
|
4877
|
+
excludeMode: options.excludeMode ?? "hide",
|
|
4878
|
+
filter: options.filter ?? null,
|
|
4879
|
+
filterMode: options.filterMode ?? "hide",
|
|
4880
|
+
// Placeholders
|
|
4881
|
+
placeholders: options.placeholders !== false,
|
|
4882
|
+
// default true
|
|
4883
|
+
// Fonts
|
|
4884
|
+
embedFonts: options.embedFonts ?? false,
|
|
4885
|
+
iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
|
|
4886
|
+
localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
|
|
4887
|
+
excludeFonts: options.excludeFonts ?? void 0,
|
|
4888
|
+
fallbackURL: options.fallbackURL ?? void 0,
|
|
4889
|
+
/** @type {CachePolicy} */
|
|
4890
|
+
cache: cachePolicy,
|
|
4891
|
+
// Network
|
|
4892
|
+
useProxy: typeof options.useProxy === "string" ? options.useProxy : "",
|
|
4893
|
+
// Output
|
|
4894
|
+
width: options.width ?? null,
|
|
4895
|
+
height: options.height ?? null,
|
|
4896
|
+
format: resolvedFormat,
|
|
4897
|
+
type: options.type ?? "svg",
|
|
4898
|
+
quality: options.quality ?? 0.92,
|
|
4899
|
+
dpr: options.dpr ?? (window.devicePixelRatio || 1),
|
|
4900
|
+
backgroundColor: options.backgroundColor ?? (["jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null),
|
|
4901
|
+
filename: options.filename ?? "snapDOM",
|
|
4902
|
+
// NEW flags (user-friendly)
|
|
4903
|
+
outerTransforms: options.outerTransforms ?? true,
|
|
4904
|
+
outerShadows: options.outerShadows ?? false
|
|
4905
|
+
// Plugins (reservado)
|
|
4906
|
+
// plugins: normalizePlugins(...),
|
|
4907
|
+
};
|
|
4557
4908
|
}
|
|
4558
4909
|
|
|
4559
4910
|
// src/api/snapdom.js
|
|
4560
|
-
|
|
4911
|
+
init_browser();
|
|
4912
|
+
function plugins(...defs) {
|
|
4561
4913
|
registerPlugins(...defs);
|
|
4562
4914
|
return snapdom;
|
|
4563
|
-
}
|
|
4915
|
+
}
|
|
4916
|
+
var snapdom = Object.assign(main, { plugins });
|
|
4564
4917
|
var INTERNAL_TOKEN = Symbol("snapdom.internal");
|
|
4565
4918
|
var INTERNAL_EXPORT_TOKEN = Symbol("snapdom.internal.silent");
|
|
4566
4919
|
var _safariWarmup = false;
|
|
4567
|
-
async function
|
|
4920
|
+
async function main(element, userOptions) {
|
|
4568
4921
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
4569
4922
|
const context = createContext(userOptions);
|
|
4570
4923
|
attachSessionPlugins(context, userOptions && userOptions.plugins);
|
|
@@ -4572,7 +4925,6 @@ async function snapdom(element, userOptions) {
|
|
|
4572
4925
|
for (let i = 0; i < 3; i++) {
|
|
4573
4926
|
try {
|
|
4574
4927
|
await safariWarmup(element, userOptions);
|
|
4575
|
-
console.log("safariWarmup:", i);
|
|
4576
4928
|
_safariWarmup = false;
|
|
4577
4929
|
} catch {
|
|
4578
4930
|
}
|
|
@@ -4587,32 +4939,76 @@ async function snapdom(element, userOptions) {
|
|
|
4587
4939
|
}
|
|
4588
4940
|
return snapdom.capture(element, context, INTERNAL_TOKEN);
|
|
4589
4941
|
}
|
|
4590
|
-
snapdom.plugins = (...defs) => {
|
|
4591
|
-
registerPlugins(...defs);
|
|
4592
|
-
return snapdom;
|
|
4593
|
-
};
|
|
4594
4942
|
snapdom.capture = async (el, context, _token) => {
|
|
4595
4943
|
if (_token !== INTERNAL_TOKEN) throw new Error("[snapdom.capture] is internal. Use snapdom(...) instead.");
|
|
4596
4944
|
const url = await captureDOM(el, context);
|
|
4597
4945
|
const coreExports = {
|
|
4598
|
-
img: async (ctx, opts) =>
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4946
|
+
img: async (ctx, opts) => {
|
|
4947
|
+
const { toImg: toImg2 } = await Promise.resolve().then(() => (init_toImg(), toImg_exports));
|
|
4948
|
+
return toImg2(url, { ...ctx, ...opts || {} });
|
|
4949
|
+
},
|
|
4950
|
+
svg: async (ctx, opts) => {
|
|
4951
|
+
const { toSvg } = await Promise.resolve().then(() => (init_toImg(), toImg_exports));
|
|
4952
|
+
return toSvg(url, { ...ctx, ...opts || {} });
|
|
4953
|
+
},
|
|
4954
|
+
canvas: async (ctx, opts) => {
|
|
4955
|
+
const { toCanvas: toCanvas2 } = await Promise.resolve().then(() => (init_toCanvas(), toCanvas_exports));
|
|
4956
|
+
return toCanvas2(url, { ...ctx, ...opts || {} });
|
|
4957
|
+
},
|
|
4958
|
+
blob: async (ctx, opts) => {
|
|
4959
|
+
const { toBlob: toBlob2 } = await Promise.resolve().then(() => (init_toBlob(), toBlob_exports));
|
|
4960
|
+
return toBlob2(url, { ...ctx, ...opts || {} });
|
|
4961
|
+
},
|
|
4962
|
+
png: async (ctx, opts) => {
|
|
4963
|
+
const { rasterize: rasterize2 } = await Promise.resolve().then(() => (init_rasterize(), rasterize_exports));
|
|
4964
|
+
return rasterize2(url, { ...ctx, ...opts || {}, format: "png" });
|
|
4965
|
+
},
|
|
4966
|
+
jpeg: async (ctx, opts) => {
|
|
4967
|
+
const { rasterize: rasterize2 } = await Promise.resolve().then(() => (init_rasterize(), rasterize_exports));
|
|
4968
|
+
return rasterize2(url, { ...ctx, ...opts || {}, format: "jpeg" });
|
|
4969
|
+
},
|
|
4970
|
+
webp: async (ctx, opts) => {
|
|
4971
|
+
const { rasterize: rasterize2 } = await Promise.resolve().then(() => (init_rasterize(), rasterize_exports));
|
|
4972
|
+
return rasterize2(url, { ...ctx, ...opts || {}, format: "webp" });
|
|
4973
|
+
},
|
|
4974
|
+
download: async (ctx, opts) => {
|
|
4975
|
+
const { download: download2 } = await Promise.resolve().then(() => (init_download(), download_exports));
|
|
4976
|
+
return download2(url, { ...ctx, ...opts || {} });
|
|
4977
|
+
}
|
|
4606
4978
|
};
|
|
4607
4979
|
const _pluginExports = {
|
|
4608
|
-
svg: (opts) =>
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4980
|
+
svg: async (opts) => {
|
|
4981
|
+
const { toSvg } = await Promise.resolve().then(() => (init_toImg(), toImg_exports));
|
|
4982
|
+
return toSvg(url, { ...context, ...opts || {}, [INTERNAL_EXPORT_TOKEN]: true });
|
|
4983
|
+
},
|
|
4984
|
+
canvas: async (opts) => {
|
|
4985
|
+
const { toCanvas: toCanvas2 } = await Promise.resolve().then(() => (init_toCanvas(), toCanvas_exports));
|
|
4986
|
+
return toCanvas2(url, { ...context, ...opts || {}, [INTERNAL_EXPORT_TOKEN]: true });
|
|
4987
|
+
},
|
|
4988
|
+
png: async (opts) => {
|
|
4989
|
+
const { rasterize: rasterize2 } = await Promise.resolve().then(() => (init_rasterize(), rasterize_exports));
|
|
4990
|
+
return rasterize2(url, { ...context, ...opts || {}, format: "png", [INTERNAL_EXPORT_TOKEN]: true });
|
|
4991
|
+
},
|
|
4992
|
+
jpeg: async (opts) => {
|
|
4993
|
+
const { rasterize: rasterize2 } = await Promise.resolve().then(() => (init_rasterize(), rasterize_exports));
|
|
4994
|
+
return rasterize2(url, { ...context, ...opts || {}, format: "jpeg", [INTERNAL_EXPORT_TOKEN]: true });
|
|
4995
|
+
},
|
|
4996
|
+
jpg: async (opts) => {
|
|
4997
|
+
const { rasterize: rasterize2 } = await Promise.resolve().then(() => (init_rasterize(), rasterize_exports));
|
|
4998
|
+
return rasterize2(url, { ...context, ...opts || {}, format: "jpeg", [INTERNAL_EXPORT_TOKEN]: true });
|
|
4999
|
+
},
|
|
5000
|
+
webp: async (opts) => {
|
|
5001
|
+
const { rasterize: rasterize2 } = await Promise.resolve().then(() => (init_rasterize(), rasterize_exports));
|
|
5002
|
+
return rasterize2(url, { ...context, ...opts || {}, format: "webp", [INTERNAL_EXPORT_TOKEN]: true });
|
|
5003
|
+
},
|
|
5004
|
+
blob: async (opts) => {
|
|
5005
|
+
const { toBlob: toBlob2 } = await Promise.resolve().then(() => (init_toBlob(), toBlob_exports));
|
|
5006
|
+
return toBlob2(url, { ...context, ...opts || {}, [INTERNAL_EXPORT_TOKEN]: true });
|
|
5007
|
+
},
|
|
5008
|
+
img: async (opts) => {
|
|
5009
|
+
const { toImg: toImg2 } = await Promise.resolve().then(() => (init_toImg(), toImg_exports));
|
|
5010
|
+
return toImg2(url, { ...context, ...opts || {}, [INTERNAL_EXPORT_TOKEN]: true });
|
|
5011
|
+
}
|
|
4616
5012
|
};
|
|
4617
5013
|
const _defineCtx = { ...context, export: { url }, exports: _pluginExports };
|
|
4618
5014
|
const providedMaps = await runAll("defineExports", _defineCtx);
|
|
@@ -4659,10 +5055,9 @@ snapdom.capture = async (el, context, _token) => {
|
|
|
4659
5055
|
toBlob: (opts) => runExport("blob", opts),
|
|
4660
5056
|
toPng: (opts) => runExport("png", opts),
|
|
4661
5057
|
toJpg: (opts) => runExport("jpg", opts),
|
|
4662
|
-
//
|
|
5058
|
+
// alias requerido por tests
|
|
4663
5059
|
toWebp: (opts) => runExport("webp", opts),
|
|
4664
5060
|
download: (opts) => runExport("download", opts)
|
|
4665
|
-
// ← método directo (no toDownload)
|
|
4666
5061
|
};
|
|
4667
5062
|
for (const key of Object.keys(exportsMap)) {
|
|
4668
5063
|
const helper = "to" + key.charAt(0).toUpperCase() + key.slice(1);
|
|
@@ -4752,6 +5147,9 @@ function hasBackgroundOrMask(el) {
|
|
|
4752
5147
|
}
|
|
4753
5148
|
|
|
4754
5149
|
// src/api/preCache.js
|
|
5150
|
+
init_utils();
|
|
5151
|
+
init_snapFetch();
|
|
5152
|
+
init_cache();
|
|
4755
5153
|
async function preCache(root = document, options = {}) {
|
|
4756
5154
|
const {
|
|
4757
5155
|
embedFonts = true,
|