@zumer/snapdom 2.0.0-dev.3 → 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 -5187
- package/dist/snapdom.mjs +1569 -1328
- 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,845 +653,1187 @@ 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
|
-
var
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
if (
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
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
|
+
};
|
|
1154
1078
|
}
|
|
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
|
+
};
|
|
1155
1092
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
const
|
|
1160
|
-
if (
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
out = out.split(u).join(d);
|
|
1166
|
-
} catch {
|
|
1167
|
-
}
|
|
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;
|
|
1168
1102
|
}
|
|
1169
|
-
|
|
1103
|
+
if (NO_DEFAULTS_TAGS.has(source.tagName?.toLowerCase())) {
|
|
1104
|
+
const author = source.getAttribute?.("style");
|
|
1105
|
+
if (author) clone.setAttribute("style", author);
|
|
1106
|
+
}
|
|
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);
|
|
1170
1121
|
}
|
|
1171
|
-
function
|
|
1172
|
-
return
|
|
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;
|
|
1173
1124
|
}
|
|
1174
|
-
function
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
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";
|
|
1179
1134
|
}
|
|
1180
|
-
function
|
|
1181
|
-
|
|
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");
|
|
1182
1140
|
}
|
|
1183
|
-
|
|
1184
|
-
if (
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
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;
|
|
1208
1212
|
}
|
|
1209
|
-
} catch {
|
|
1210
1213
|
}
|
|
1211
1214
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1215
|
+
let cs = null;
|
|
1216
|
+
if (hasVar) {
|
|
1214
1217
|
try {
|
|
1215
|
-
|
|
1216
|
-
const href = node.getAttribute("href") || node.getAttributeNS?.(XLINK_NS, "href");
|
|
1217
|
-
if (isBlobUrl(href)) {
|
|
1218
|
-
const d = await blobUrlToDataUrl(href);
|
|
1219
|
-
node.setAttribute("href", d);
|
|
1220
|
-
node.removeAttributeNS?.(XLINK_NS, "href");
|
|
1221
|
-
}
|
|
1218
|
+
cs = getComputedStyle(sourceEl);
|
|
1222
1219
|
} catch {
|
|
1223
1220
|
}
|
|
1224
1221
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
const
|
|
1231
|
-
|
|
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 {
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1232
1236
|
}
|
|
1233
|
-
} catch {
|
|
1234
1237
|
}
|
|
1235
1238
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
const
|
|
1240
|
-
if (
|
|
1241
|
-
|
|
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 {
|
|
1250
|
+
}
|
|
1242
1251
|
}
|
|
1243
|
-
} catch {
|
|
1244
1252
|
}
|
|
1245
1253
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
const nodes = root.querySelectorAll ? root.querySelectorAll(`[${attr}^='blob:']`) : [];
|
|
1249
|
-
for (const n of nodes) {
|
|
1254
|
+
if (!hasVar) {
|
|
1255
|
+
if (!cs) {
|
|
1250
1256
|
try {
|
|
1251
|
-
|
|
1252
|
-
if (isBlobUrl(u)) {
|
|
1253
|
-
n.setAttribute(attr, await blobUrlToDataUrl(u));
|
|
1254
|
-
}
|
|
1257
|
+
cs = getComputedStyle(sourceEl);
|
|
1255
1258
|
} catch {
|
|
1259
|
+
cs = null;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
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) {
|
|
1269
|
+
try {
|
|
1270
|
+
cloneEl.style.setProperty(prop, v.trim());
|
|
1271
|
+
} catch {
|
|
1272
|
+
}
|
|
1256
1273
|
}
|
|
1257
1274
|
}
|
|
1258
1275
|
}
|
|
1259
1276
|
}
|
|
1260
1277
|
|
|
1261
|
-
// src/
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
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 = "";
|
|
1342
|
+
try {
|
|
1343
|
+
sr.querySelectorAll("style").forEach((s) => {
|
|
1344
|
+
css += (s.textContent || "") + "\n";
|
|
1345
|
+
});
|
|
1346
|
+
const sheets = sr.adoptedStyleSheets || [];
|
|
1347
|
+
for (const sh of sheets) {
|
|
1348
|
+
try {
|
|
1349
|
+
if (sh && sh.cssRules) {
|
|
1350
|
+
for (const rule of sh.cssRules) css += rule.cssText + "\n";
|
|
1351
|
+
}
|
|
1352
|
+
} catch {
|
|
1353
|
+
}
|
|
1274
1354
|
}
|
|
1355
|
+
} catch {
|
|
1275
1356
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
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 {
|
|
1278
1376
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
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 {
|
|
1281
1392
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
return spacer;
|
|
1288
|
-
} else if (options.excludeMode === "remove") {
|
|
1289
|
-
return null;
|
|
1290
|
-
}
|
|
1393
|
+
try {
|
|
1394
|
+
const rootCS = getComputedStyle(document.documentElement);
|
|
1395
|
+
let v = rootCS.getPropertyValue(name).trim();
|
|
1396
|
+
if (v) return v;
|
|
1397
|
+
} catch {
|
|
1291
1398
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1300
|
-
return spacer;
|
|
1301
|
-
} else if (options.excludeMode === "remove") {
|
|
1302
|
-
return null;
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
} catch (err) {
|
|
1306
|
-
console.warn(`Invalid selector in exclude option: ${selector}`, err);
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
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};`);
|
|
1309
1406
|
}
|
|
1310
|
-
if (
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
} else if (options.filterMode === "remove") {
|
|
1319
|
-
return null;
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
} catch (err) {
|
|
1323
|
-
console.warn("Error in filter function:", err);
|
|
1324
|
-
}
|
|
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", "");
|
|
1325
1415
|
}
|
|
1326
|
-
if (
|
|
1327
|
-
|
|
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 = () => {
|
|
1328
1422
|
try {
|
|
1329
|
-
|
|
1423
|
+
return iframe.contentDocument || iframe.contentWindow?.document || null;
|
|
1330
1424
|
} catch {
|
|
1331
|
-
|
|
1332
|
-
}
|
|
1333
|
-
if (sameOrigin) {
|
|
1334
|
-
try {
|
|
1335
|
-
const wrapper = await rasterizeIframe(node, sessionCache, options);
|
|
1336
|
-
return wrapper;
|
|
1337
|
-
} catch (err) {
|
|
1338
|
-
console.warn("[SnapDOM] iframe rasterization failed, fallback:", err);
|
|
1339
|
-
}
|
|
1425
|
+
return null;
|
|
1340
1426
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
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++;
|
|
1434
|
+
}
|
|
1435
|
+
return doc && (doc.body || doc.documentElement) ? doc : null;
|
|
1436
|
+
}
|
|
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 {
|
|
1447
|
+
}
|
|
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 };
|
|
1451
|
+
}
|
|
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 {
|
|
1352
1461
|
}
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
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");
|
|
1353
1471
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
clone2.appendChild(placeholder);
|
|
1362
|
-
return clone2;
|
|
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();
|
|
1363
1479
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
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}`);
|
|
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;
|
|
1513
|
+
}
|
|
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) {
|
|
1366
1522
|
try {
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1523
|
+
const d = await blobUrlToDataUrl(u);
|
|
1524
|
+
out = out.split(u).join(d);
|
|
1525
|
+
} catch {
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
return out;
|
|
1529
|
+
}
|
|
1530
|
+
function isBlobUrl(u) {
|
|
1531
|
+
return typeof u === "string" && u.startsWith("blob:");
|
|
1532
|
+
}
|
|
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);
|
|
1371
1552
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
scratch.width = node.width;
|
|
1384
|
-
scratch.height = node.height;
|
|
1385
|
-
const sctx = scratch.getContext("2d");
|
|
1386
|
-
if (sctx) {
|
|
1387
|
-
sctx.drawImage(node, 0, 0);
|
|
1388
|
-
url = scratch.toDataURL("image/png");
|
|
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
|
+
}
|
|
1389
1564
|
}
|
|
1390
1565
|
}
|
|
1566
|
+
if (changed) img.setAttribute("srcset", stringifySrcset(parts));
|
|
1391
1567
|
}
|
|
1392
1568
|
} catch {
|
|
1393
1569
|
}
|
|
1394
|
-
|
|
1570
|
+
}
|
|
1571
|
+
const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : [];
|
|
1572
|
+
for (const node of svgImages) {
|
|
1395
1573
|
try {
|
|
1396
|
-
|
|
1397
|
-
|
|
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
|
+
}
|
|
1398
1581
|
} catch {
|
|
1399
1582
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1583
|
+
}
|
|
1584
|
+
const styled = root.querySelectorAll ? root.querySelectorAll("[style*='blob:']") : [];
|
|
1585
|
+
for (const el of styled) {
|
|
1403
1586
|
try {
|
|
1404
|
-
const
|
|
1405
|
-
if (
|
|
1406
|
-
|
|
1587
|
+
const styleText = el.getAttribute("style");
|
|
1588
|
+
if (styleText && styleText.includes("blob:")) {
|
|
1589
|
+
const replaced = await replaceBlobUrlsInCssText(styleText);
|
|
1590
|
+
el.setAttribute("style", replaced);
|
|
1591
|
+
}
|
|
1407
1592
|
} catch {
|
|
1408
1593
|
}
|
|
1409
|
-
sessionCache.nodeMap.set(img, node);
|
|
1410
|
-
inlineAllStyles(node, img, sessionCache, options);
|
|
1411
|
-
return img;
|
|
1412
1594
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
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) {
|
|
1420
1609
|
try {
|
|
1421
|
-
const
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
if (!w || !h) {
|
|
1425
|
-
const computed = window.getComputedStyle(node);
|
|
1426
|
-
const cssW = parseFloat(computed.width) || 0;
|
|
1427
|
-
const cssH = parseFloat(computed.height) || 0;
|
|
1428
|
-
const attrW = parseInt(node.getAttribute("width") || "", 10) || 0;
|
|
1429
|
-
const attrH = parseInt(node.getAttribute("height") || "", 10) || 0;
|
|
1430
|
-
const propW = node.width || node.naturalWidth || 0;
|
|
1431
|
-
const propH = node.height || node.naturalHeight || 0;
|
|
1432
|
-
w = Math.round(w || cssW || attrW || propW || 0);
|
|
1433
|
-
h = Math.round(h || cssH || attrH || propH || 0);
|
|
1610
|
+
const u = n.getAttribute(attr);
|
|
1611
|
+
if (isBlobUrl(u)) {
|
|
1612
|
+
n.setAttribute(attr, await blobUrlToDataUrl(u));
|
|
1434
1613
|
}
|
|
1435
|
-
if (w) clone.dataset.snapdomWidth = String(w);
|
|
1436
|
-
if (h) clone.dataset.snapdomHeight = String(h);
|
|
1437
1614
|
} catch {
|
|
1438
1615
|
}
|
|
1439
1616
|
}
|
|
1440
|
-
} catch (err) {
|
|
1441
|
-
console.error("[Snapdom] Failed to clone node:", node, err);
|
|
1442
|
-
throw err;
|
|
1443
|
-
}
|
|
1444
|
-
if (node instanceof HTMLTextAreaElement) {
|
|
1445
|
-
const rect = node.getBoundingClientRect();
|
|
1446
|
-
clone.style.width = `${rect.width}px`;
|
|
1447
|
-
clone.style.height = `${rect.height}px`;
|
|
1448
|
-
}
|
|
1449
|
-
if (node instanceof HTMLInputElement) {
|
|
1450
|
-
clone.value = node.value;
|
|
1451
|
-
clone.setAttribute("value", node.value);
|
|
1452
|
-
if (node.checked !== void 0) {
|
|
1453
|
-
clone.checked = node.checked;
|
|
1454
|
-
if (node.checked) clone.setAttribute("checked", "");
|
|
1455
|
-
if (node.indeterminate) clone.indeterminate = node.indeterminate;
|
|
1456
|
-
}
|
|
1457
1617
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
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;
|
|
1460
1837
|
}
|
|
1461
1838
|
if (node instanceof HTMLTextAreaElement) {
|
|
1462
1839
|
pendingTextAreaValue = node.value;
|
|
@@ -1547,6 +1924,13 @@ async function deepClone(node, sessionCache, options) {
|
|
|
1547
1924
|
return clone;
|
|
1548
1925
|
}
|
|
1549
1926
|
|
|
1927
|
+
// src/modules/pseudo.js
|
|
1928
|
+
init_utils();
|
|
1929
|
+
|
|
1930
|
+
// src/modules/fonts.js
|
|
1931
|
+
init_helpers();
|
|
1932
|
+
init_cache();
|
|
1933
|
+
|
|
1550
1934
|
// src/modules/iconFonts.js
|
|
1551
1935
|
var defaultIconFonts = [
|
|
1552
1936
|
// /uicons/i,
|
|
@@ -1758,6 +2142,7 @@ async function ligatureIconToImage(cloneRoot, sourceRoot) {
|
|
|
1758
2142
|
}
|
|
1759
2143
|
|
|
1760
2144
|
// src/modules/fonts.js
|
|
2145
|
+
init_snapFetch();
|
|
1761
2146
|
async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
1762
2147
|
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
1763
2148
|
const dpr = window.devicePixelRatio || 1;
|
|
@@ -2243,6 +2628,18 @@ async function embedCustomFonts({
|
|
|
2243
2628
|
if (nearWeight && sOk && tOk) return true;
|
|
2244
2629
|
}
|
|
2245
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
|
+
}
|
|
2246
2643
|
return false;
|
|
2247
2644
|
}
|
|
2248
2645
|
const simpleExcluder = buildSimpleExcluder(exclude);
|
|
@@ -2527,6 +2924,7 @@ async function ensureFontsReady(families, warmupRepetitions = 2) {
|
|
|
2527
2924
|
}
|
|
2528
2925
|
|
|
2529
2926
|
// src/modules/counter.js
|
|
2927
|
+
init_cache();
|
|
2530
2928
|
function hasCounters(input) {
|
|
2531
2929
|
return /\bcounter\s*\(|\bcounters\s*\(/.test(input || "");
|
|
2532
2930
|
}
|
|
@@ -2723,6 +3121,8 @@ function resolveCountersInContent(raw, node, ctx) {
|
|
|
2723
3121
|
}
|
|
2724
3122
|
|
|
2725
3123
|
// src/modules/pseudo.js
|
|
3124
|
+
init_snapFetch();
|
|
3125
|
+
init_cache();
|
|
2726
3126
|
var __preflightMemo = /* @__PURE__ */ new WeakMap();
|
|
2727
3127
|
var CSS_RULE_SCAN_BUDGET = 300;
|
|
2728
3128
|
function preflightWithFp(doc, sessionCache) {
|
|
@@ -3154,31 +3554,55 @@ function inlineExternalDefsAndSymbols(element, lookupRoot) {
|
|
|
3154
3554
|
"marker-end"
|
|
3155
3555
|
];
|
|
3156
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
|
+
};
|
|
3157
3575
|
const globalExistingIds = new Set(
|
|
3158
3576
|
Array.from(element.querySelectorAll("[id]")).map((n) => n.id)
|
|
3159
3577
|
);
|
|
3160
3578
|
const neededIds = /* @__PURE__ */ new Set();
|
|
3161
3579
|
let sawAnyReference = false;
|
|
3162
|
-
const addUrlIdsFromValue = (val) => {
|
|
3580
|
+
const addUrlIdsFromValue = (val, queueForResolve = null) => {
|
|
3163
3581
|
if (!val) return;
|
|
3164
3582
|
URL_ID_RE.lastIndex = 0;
|
|
3165
3583
|
let m;
|
|
3166
3584
|
while (m = URL_ID_RE.exec(val)) {
|
|
3167
3585
|
sawAnyReference = true;
|
|
3168
3586
|
const id = (m[1] || "").trim();
|
|
3169
|
-
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
|
+
}
|
|
3170
3594
|
}
|
|
3171
3595
|
};
|
|
3172
3596
|
const collectReferencesInSvg = (rootSvg) => {
|
|
3173
|
-
const uses = rootSvg.querySelectorAll(
|
|
3597
|
+
const uses = rootSvg.querySelectorAll("use");
|
|
3174
3598
|
for (const u of uses) {
|
|
3175
|
-
const href =
|
|
3599
|
+
const href = getHrefAttr(u);
|
|
3176
3600
|
if (!href || !href.startsWith("#")) continue;
|
|
3177
3601
|
sawAnyReference = true;
|
|
3178
3602
|
const id = href.slice(1).trim();
|
|
3179
3603
|
if (id && !globalExistingIds.has(id)) neededIds.add(id);
|
|
3180
3604
|
}
|
|
3181
|
-
|
|
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("]';
|
|
3182
3606
|
const candidates = rootSvg.querySelectorAll(query);
|
|
3183
3607
|
for (const el of candidates) {
|
|
3184
3608
|
addUrlIdsFromValue(el.getAttribute("style") || "");
|
|
@@ -3229,21 +3653,26 @@ function inlineExternalDefsAndSymbols(element, lookupRoot) {
|
|
|
3229
3653
|
globalExistingIds.add(id);
|
|
3230
3654
|
const walk = [clone, ...clone.querySelectorAll("*")];
|
|
3231
3655
|
for (const node of walk) {
|
|
3232
|
-
const
|
|
3233
|
-
if (
|
|
3234
|
-
const ref =
|
|
3235
|
-
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
|
+
}
|
|
3236
3662
|
}
|
|
3237
3663
|
const style = node.getAttribute?.("style") || "";
|
|
3238
|
-
if (style) addUrlIdsFromValue(style);
|
|
3664
|
+
if (style) addUrlIdsFromValue(style, queued);
|
|
3239
3665
|
for (const a of URL_ATTRS) {
|
|
3240
3666
|
const v = node.getAttribute?.(a);
|
|
3241
|
-
if (v) addUrlIdsFromValue(v);
|
|
3667
|
+
if (v) addUrlIdsFromValue(v, queued);
|
|
3242
3668
|
}
|
|
3243
3669
|
}
|
|
3244
3670
|
}
|
|
3245
3671
|
}
|
|
3246
3672
|
|
|
3673
|
+
// src/core/prepare.js
|
|
3674
|
+
init_cache();
|
|
3675
|
+
|
|
3247
3676
|
// src/modules/changeCSS.js
|
|
3248
3677
|
function freezeSticky(originalRoot, cloneRoot) {
|
|
3249
3678
|
if (!originalRoot || !cloneRoot) return;
|
|
@@ -3456,6 +3885,7 @@ async function prepareClone(element, options = {}) {
|
|
|
3456
3885
|
}
|
|
3457
3886
|
|
|
3458
3887
|
// src/modules/images.js
|
|
3888
|
+
init_snapFetch();
|
|
3459
3889
|
function extractImageDimensions(img) {
|
|
3460
3890
|
const dsW = parseInt(img.dataset?.snapdomWidth || "", 10) || 0;
|
|
3461
3891
|
const dsH = parseInt(img.dataset?.snapdomHeight || "", 10) || 0;
|
|
@@ -3528,6 +3958,7 @@ async function inlineImages(clone, options = {}) {
|
|
|
3528
3958
|
}
|
|
3529
3959
|
|
|
3530
3960
|
// src/modules/background.js
|
|
3961
|
+
init_utils();
|
|
3531
3962
|
async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
|
|
3532
3963
|
const queue = [[source, clone]];
|
|
3533
3964
|
const URL_PROPS = [
|
|
@@ -3625,6 +4056,10 @@ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
|
|
|
3625
4056
|
}
|
|
3626
4057
|
}
|
|
3627
4058
|
|
|
4059
|
+
// src/core/capture.js
|
|
4060
|
+
init_utils();
|
|
4061
|
+
init_cache();
|
|
4062
|
+
|
|
3628
4063
|
// src/modules/lineClamp.js
|
|
3629
4064
|
function lineClamp(el) {
|
|
3630
4065
|
if (!el) return () => {
|
|
@@ -4260,468 +4695,229 @@ async function captureDOM(element, options) {
|
|
|
4260
4695
|
useProxy: state.options.useProxy
|
|
4261
4696
|
});
|
|
4262
4697
|
resolve();
|
|
4263
|
-
}, { fast });
|
|
4264
|
-
});
|
|
4265
|
-
}
|
|
4266
|
-
const usedTags = collectUsedTagNames(state.clone).sort();
|
|
4267
|
-
const tagKey = usedTags.join(",");
|
|
4268
|
-
if (cache.baseStyle.has(tagKey)) {
|
|
4269
|
-
baseCSS = cache.baseStyle.get(tagKey);
|
|
4270
|
-
} else {
|
|
4271
|
-
await new Promise((resolve) => {
|
|
4272
|
-
idle(() => {
|
|
4273
|
-
baseCSS = generateDedupedBaseCSS(usedTags);
|
|
4274
|
-
cache.baseStyle.set(tagKey, baseCSS);
|
|
4275
|
-
resolve();
|
|
4276
|
-
}, { fast });
|
|
4277
|
-
});
|
|
4278
|
-
}
|
|
4279
|
-
state = { fontsCSS, baseCSS, ...state };
|
|
4280
|
-
await runHook("beforeRender", state);
|
|
4281
|
-
await new Promise((resolve) => {
|
|
4282
|
-
idle(() => {
|
|
4283
|
-
const csEl = getComputedStyle(state.element);
|
|
4284
|
-
const rect = state.element.getBoundingClientRect();
|
|
4285
|
-
let w0 = Math.max(1, limitDecimals(state.element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
|
|
4286
|
-
let h0 = Math.max(1, limitDecimals(state.element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
|
|
4287
|
-
if (state.options?.excludeMode === "remove") {
|
|
4288
|
-
const hEst = estimateKeptHeight(state.element, state.options);
|
|
4289
|
-
const EPS = 1;
|
|
4290
|
-
if (Number.isFinite(hEst) && hEst > 0) {
|
|
4291
|
-
h0 = Math.max(1, Math.min(h0, limitDecimals(hEst + EPS)));
|
|
4292
|
-
}
|
|
4293
|
-
}
|
|
4294
|
-
const coerceNum = (v, def = NaN) => {
|
|
4295
|
-
const n = typeof v === "string" ? parseFloat(v) : v;
|
|
4296
|
-
return Number.isFinite(n) ? n : def;
|
|
4297
|
-
};
|
|
4298
|
-
const optW = coerceNum(state.options.width);
|
|
4299
|
-
const optH = coerceNum(state.options.height);
|
|
4300
|
-
let w = w0, h = h0;
|
|
4301
|
-
const hasW = Number.isFinite(optW);
|
|
4302
|
-
const hasH = Number.isFinite(optH);
|
|
4303
|
-
const aspect0 = h0 > 0 ? w0 / h0 : 1;
|
|
4304
|
-
if (hasW && hasH) {
|
|
4305
|
-
w = Math.max(1, limitDecimals(optW));
|
|
4306
|
-
h = Math.max(1, limitDecimals(optH));
|
|
4307
|
-
} else if (hasW) {
|
|
4308
|
-
w = Math.max(1, limitDecimals(optW));
|
|
4309
|
-
h = Math.max(1, limitDecimals(w / (aspect0 || 1)));
|
|
4310
|
-
} else if (hasH) {
|
|
4311
|
-
h = Math.max(1, limitDecimals(optH));
|
|
4312
|
-
w = Math.max(1, limitDecimals(h * (aspect0 || 1)));
|
|
4313
|
-
} else {
|
|
4314
|
-
w = w0;
|
|
4315
|
-
h = h0;
|
|
4316
|
-
}
|
|
4317
|
-
let minX = 0, minY = 0, maxX = w0, maxY = h0;
|
|
4318
|
-
if (!outerTransforms && rootTransform2D && Number.isFinite(rootTransform2D.a)) {
|
|
4319
|
-
const M2 = {
|
|
4320
|
-
a: rootTransform2D.a,
|
|
4321
|
-
b: rootTransform2D.b || 0,
|
|
4322
|
-
c: rootTransform2D.c || 0,
|
|
4323
|
-
d: rootTransform2D.d || 1,
|
|
4324
|
-
e: 0,
|
|
4325
|
-
f: 0
|
|
4326
|
-
};
|
|
4327
|
-
const bb2 = bboxWithOriginFull(w0, h0, M2, 0, 0);
|
|
4328
|
-
minX = limitDecimals(bb2.minX);
|
|
4329
|
-
minY = limitDecimals(bb2.minY);
|
|
4330
|
-
maxX = limitDecimals(bb2.maxX);
|
|
4331
|
-
maxY = limitDecimals(bb2.maxY);
|
|
4332
|
-
} else {
|
|
4333
|
-
const useTFBBox = outerTransforms && hasTFBBox(state.element);
|
|
4334
|
-
if (useTFBBox) {
|
|
4335
|
-
const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
|
|
4336
|
-
const ind2 = readIndividualTransforms(state.element);
|
|
4337
|
-
const TOTAL = readTotalTransformMatrix({
|
|
4338
|
-
baseTransform: baseTransform2,
|
|
4339
|
-
rotate: ind2.rotate || "0deg",
|
|
4340
|
-
scale: ind2.scale,
|
|
4341
|
-
translate: ind2.translate
|
|
4342
|
-
});
|
|
4343
|
-
const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
|
|
4344
|
-
const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
|
|
4345
|
-
const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
|
|
4346
|
-
minX = limitDecimals(bb.minX);
|
|
4347
|
-
minY = limitDecimals(bb.minY);
|
|
4348
|
-
maxX = limitDecimals(bb.maxX);
|
|
4349
|
-
maxY = limitDecimals(bb.maxY);
|
|
4350
|
-
}
|
|
4351
|
-
}
|
|
4352
|
-
const bleedShadow = parseBoxShadow(csEl);
|
|
4353
|
-
const bleedBlur = parseFilterBlur(csEl);
|
|
4354
|
-
const bleedOutline = parseOutline(csEl);
|
|
4355
|
-
const drop = parseFilterDropShadows(csEl);
|
|
4356
|
-
const bleed = !outerShadows ? { top: 0, right: 0, bottom: 0, left: 0 } : {
|
|
4357
|
-
top: limitDecimals(bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top),
|
|
4358
|
-
right: limitDecimals(bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right),
|
|
4359
|
-
bottom: limitDecimals(bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom),
|
|
4360
|
-
left: limitDecimals(bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left)
|
|
4361
|
-
};
|
|
4362
|
-
minX = limitDecimals(minX - bleed.left);
|
|
4363
|
-
minY = limitDecimals(minY - bleed.top);
|
|
4364
|
-
maxX = limitDecimals(maxX + bleed.right);
|
|
4365
|
-
maxY = limitDecimals(maxY + bleed.bottom);
|
|
4366
|
-
const vbW0 = Math.max(1, limitDecimals(maxX - minX));
|
|
4367
|
-
const vbH0 = Math.max(1, limitDecimals(maxY - minY));
|
|
4368
|
-
const scaleW = hasW || hasH ? limitDecimals(w / w0) : 1;
|
|
4369
|
-
const scaleH = hasH || hasW ? limitDecimals(h / h0) : 1;
|
|
4370
|
-
const outW = Math.max(1, limitDecimals(vbW0 * scaleW));
|
|
4371
|
-
const outH = Math.max(1, limitDecimals(vbH0 * scaleH));
|
|
4372
|
-
const svgNS = "http://www.w3.org/2000/svg";
|
|
4373
|
-
const basePad = isSafari() ? 1 : 0;
|
|
4374
|
-
const extraPad = !outerTransforms ? 1 : 0;
|
|
4375
|
-
const pad = limitDecimals(basePad + extraPad);
|
|
4376
|
-
const fo = document.createElementNS(svgNS, "foreignObject");
|
|
4377
|
-
const vbMinX = limitDecimals(minX);
|
|
4378
|
-
const vbMinY = limitDecimals(minY);
|
|
4379
|
-
fo.setAttribute("x", String(limitDecimals(-(vbMinX - pad))));
|
|
4380
|
-
fo.setAttribute("y", String(limitDecimals(-(vbMinY - pad))));
|
|
4381
|
-
fo.setAttribute("width", String(limitDecimals(w0 + pad * 2)));
|
|
4382
|
-
fo.setAttribute("height", String(limitDecimals(h0 + pad * 2)));
|
|
4383
|
-
fo.style.overflow = "visible";
|
|
4384
|
-
const styleTag = document.createElement("style");
|
|
4385
|
-
styleTag.textContent = state.baseCSS + state.fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + state.classCSS;
|
|
4386
|
-
fo.appendChild(styleTag);
|
|
4387
|
-
const container = document.createElement("div");
|
|
4388
|
-
container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
4389
|
-
container.style.width = `${limitDecimals(w0)}px`;
|
|
4390
|
-
container.style.height = `${limitDecimals(h0)}px`;
|
|
4391
|
-
container.style.overflow = "visible";
|
|
4392
|
-
state.clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
4393
|
-
container.appendChild(state.clone);
|
|
4394
|
-
fo.appendChild(container);
|
|
4395
|
-
const serializer = new XMLSerializer();
|
|
4396
|
-
const foString = serializer.serializeToString(fo);
|
|
4397
|
-
const vbW = limitDecimals(vbW0 + pad * 2);
|
|
4398
|
-
const vbH = limitDecimals(vbH0 + pad * 2);
|
|
4399
|
-
const wantsSize = hasW || hasH;
|
|
4400
|
-
options.meta = { w0, h0, vbW, vbH, targetW: w, targetH: h };
|
|
4401
|
-
const svgOutW = isSafari() && wantsSize ? vbW : limitDecimals(outW + pad * 2);
|
|
4402
|
-
const svgOutH = isSafari() && wantsSize ? vbH : limitDecimals(outH + pad * 2);
|
|
4403
|
-
const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
|
|
4404
|
-
const svgFooter = "</svg>";
|
|
4405
|
-
svgString = svgHeader + foString + svgFooter;
|
|
4406
|
-
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
4407
|
-
state = { svgString, dataURL, ...state };
|
|
4408
|
-
resolve();
|
|
4409
|
-
}, { fast });
|
|
4410
|
-
});
|
|
4411
|
-
await runHook("afterRender", state);
|
|
4412
|
-
const sandbox = document.getElementById("snapdom-sandbox");
|
|
4413
|
-
if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
|
|
4414
|
-
return state.dataURL;
|
|
4415
|
-
}
|
|
4416
|
-
function hasTFBBox(el) {
|
|
4417
|
-
return hasBBoxAffectingTransform(el);
|
|
4418
|
-
}
|
|
4419
|
-
|
|
4420
|
-
// src/core/context.js
|
|
4421
|
-
function normalizeCachePolicy(v) {
|
|
4422
|
-
if (typeof v === "string") {
|
|
4423
|
-
const s = v.toLowerCase().trim();
|
|
4424
|
-
if (s === "disabled" || s === "full" || s === "auto" || s === "soft") return (
|
|
4425
|
-
/** @type {CachePolicy} */
|
|
4426
|
-
s
|
|
4427
|
-
);
|
|
4428
|
-
}
|
|
4429
|
-
return "soft";
|
|
4430
|
-
}
|
|
4431
|
-
function createContext(options = {}) {
|
|
4432
|
-
let resolvedFormat = options.format ?? "png";
|
|
4433
|
-
if (resolvedFormat === "jpg") resolvedFormat = "jpeg";
|
|
4434
|
-
const cachePolicy = normalizeCachePolicy(options.cache);
|
|
4435
|
-
return {
|
|
4436
|
-
// Debug & perf
|
|
4437
|
-
debug: options.debug ?? false,
|
|
4438
|
-
fast: options.fast ?? true,
|
|
4439
|
-
scale: options.scale ?? 1,
|
|
4440
|
-
// DOM filters
|
|
4441
|
-
exclude: options.exclude ?? [],
|
|
4442
|
-
excludeMode: options.excludeMode ?? "hide",
|
|
4443
|
-
filter: options.filter ?? null,
|
|
4444
|
-
filterMode: options.filterMode ?? "hide",
|
|
4445
|
-
// Placeholders
|
|
4446
|
-
placeholders: options.placeholders !== false,
|
|
4447
|
-
// default true
|
|
4448
|
-
// Fonts
|
|
4449
|
-
embedFonts: options.embedFonts ?? false,
|
|
4450
|
-
iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
|
|
4451
|
-
localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
|
|
4452
|
-
excludeFonts: options.excludeFonts ?? void 0,
|
|
4453
|
-
fallbackURL: options.fallbackURL ?? void 0,
|
|
4454
|
-
/** @type {CachePolicy} */
|
|
4455
|
-
cache: cachePolicy,
|
|
4456
|
-
// Network
|
|
4457
|
-
useProxy: typeof options.useProxy === "string" ? options.useProxy : "",
|
|
4458
|
-
// Output
|
|
4459
|
-
width: options.width ?? null,
|
|
4460
|
-
height: options.height ?? null,
|
|
4461
|
-
format: resolvedFormat,
|
|
4462
|
-
type: options.type ?? "svg",
|
|
4463
|
-
quality: options.quality ?? 0.92,
|
|
4464
|
-
dpr: options.dpr ?? (window.devicePixelRatio || 1),
|
|
4465
|
-
backgroundColor: options.backgroundColor ?? (["jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null),
|
|
4466
|
-
filename: options.filename ?? "snapDOM",
|
|
4467
|
-
// NEW flags (user-friendly)
|
|
4468
|
-
outerTransforms: options.outerTransforms ?? true,
|
|
4469
|
-
outerShadows: options.outerShadows ?? false
|
|
4470
|
-
// Plugins (reservado)
|
|
4471
|
-
// plugins: normalizePlugins(...),
|
|
4472
|
-
};
|
|
4473
|
-
}
|
|
4474
|
-
|
|
4475
|
-
// src/exporters/toCanvas.js
|
|
4476
|
-
function isSvgDataURL(u) {
|
|
4477
|
-
return typeof u === "string" && /^data:image\/svg\+xml/i.test(u);
|
|
4478
|
-
}
|
|
4479
|
-
function decodeSvgFromDataURL(u) {
|
|
4480
|
-
const i = u.indexOf(",");
|
|
4481
|
-
return i >= 0 ? decodeURIComponent(u.slice(i + 1)) : "";
|
|
4482
|
-
}
|
|
4483
|
-
function encodeSvgToDataURL(svgText) {
|
|
4484
|
-
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
|
|
4485
|
-
}
|
|
4486
|
-
function splitDecls(s) {
|
|
4487
|
-
let parts = [], buf = "", depth = 0;
|
|
4488
|
-
for (let i = 0; i < s.length; i++) {
|
|
4489
|
-
const ch = s[i];
|
|
4490
|
-
if (ch === "(") depth++;
|
|
4491
|
-
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
4492
|
-
if (ch === ";" && depth === 0) {
|
|
4493
|
-
parts.push(buf);
|
|
4494
|
-
buf = "";
|
|
4495
|
-
} else buf += ch;
|
|
4496
|
-
}
|
|
4497
|
-
if (buf.trim()) parts.push(buf);
|
|
4498
|
-
return parts.map((x) => x.trim()).filter(Boolean);
|
|
4499
|
-
}
|
|
4500
|
-
function boxShadowToDropShadow(value) {
|
|
4501
|
-
const layers = [];
|
|
4502
|
-
let buf = "", depth = 0;
|
|
4503
|
-
for (let i = 0; i < value.length; i++) {
|
|
4504
|
-
const ch = value[i];
|
|
4505
|
-
if (ch === "(") depth++;
|
|
4506
|
-
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
4507
|
-
if (ch === "," && depth === 0) {
|
|
4508
|
-
layers.push(buf.trim());
|
|
4509
|
-
buf = "";
|
|
4510
|
-
} else buf += ch;
|
|
4511
|
-
}
|
|
4512
|
-
if (buf.trim()) layers.push(buf.trim());
|
|
4513
|
-
const fns = [];
|
|
4514
|
-
for (const layer of layers) {
|
|
4515
|
-
if (/\binset\b/i.test(layer)) continue;
|
|
4516
|
-
const nums = layer.match(/-?\d+(?:\.\d+)?px/gi) || [];
|
|
4517
|
-
const [ox = "0px", oy = "0px", blur = "0px"] = nums;
|
|
4518
|
-
let color = layer.replace(/-?\d+(?:\.\d+)?px/gi, "").replace(/\binset\b/ig, "").trim().replace(/\s{2,}/g, " ");
|
|
4519
|
-
const hasColor = !!color && color !== ",";
|
|
4520
|
-
fns.push(`drop-shadow(${ox} ${oy} ${blur}${hasColor ? ` ${color}` : ""})`);
|
|
4521
|
-
}
|
|
4522
|
-
return fns.join(" ");
|
|
4523
|
-
}
|
|
4524
|
-
function rewriteDeclList(list) {
|
|
4525
|
-
const decls = splitDecls(list);
|
|
4526
|
-
let filter = null, wfilter = null, box = null;
|
|
4527
|
-
const rest = [];
|
|
4528
|
-
for (const d of decls) {
|
|
4529
|
-
const idx = d.indexOf(":");
|
|
4530
|
-
if (idx < 0) continue;
|
|
4531
|
-
const prop = d.slice(0, idx).trim().toLowerCase();
|
|
4532
|
-
const val = d.slice(idx + 1).trim();
|
|
4533
|
-
if (prop === "box-shadow") box = val;
|
|
4534
|
-
else if (prop === "filter") filter = val;
|
|
4535
|
-
else if (prop === "-webkit-filter") wfilter = val;
|
|
4536
|
-
else rest.push([prop, val]);
|
|
4537
|
-
}
|
|
4538
|
-
if (box) {
|
|
4539
|
-
const ds = boxShadowToDropShadow(box);
|
|
4540
|
-
if (ds) {
|
|
4541
|
-
filter = filter ? `${filter} ${ds}` : ds;
|
|
4542
|
-
wfilter = wfilter ? `${wfilter} ${ds}` : ds;
|
|
4543
|
-
}
|
|
4544
|
-
}
|
|
4545
|
-
const out = [...rest];
|
|
4546
|
-
if (filter) out.push(["filter", filter]);
|
|
4547
|
-
if (wfilter) out.push(["-webkit-filter", wfilter]);
|
|
4548
|
-
return out.map(([k, v]) => `${k}:${v}`).join(";");
|
|
4549
|
-
}
|
|
4550
|
-
function rewriteCssBlock(css) {
|
|
4551
|
-
return css.replace(/([^{}]+)\{([^}]*)\}/g, (_m, sel, body) => `${sel}{${rewriteDeclList(body)}}`);
|
|
4552
|
-
}
|
|
4553
|
-
function rewriteSvgBoxShadowToDropShadow(svgText) {
|
|
4554
|
-
svgText = svgText.replace(
|
|
4555
|
-
/<style[^>]*>([\s\S]*?)<\/style>/gi,
|
|
4556
|
-
(m, css) => m.replace(css, rewriteCssBlock(css))
|
|
4557
|
-
);
|
|
4558
|
-
svgText = svgText.replace(
|
|
4559
|
-
/style=(['"])([\s\S]*?)\1/gi,
|
|
4560
|
-
(m, q, body) => `style=${q}${rewriteDeclList(body)}${q}`
|
|
4561
|
-
);
|
|
4562
|
-
return svgText;
|
|
4563
|
-
}
|
|
4564
|
-
function maybeConvertBoxShadowForSafari(url) {
|
|
4565
|
-
if (!isSafari() || !isSvgDataURL(url)) return url;
|
|
4566
|
-
try {
|
|
4567
|
-
const svg = decodeSvgFromDataURL(url);
|
|
4568
|
-
const fixed = rewriteSvgBoxShadowToDropShadow(svg);
|
|
4569
|
-
return encodeSvgToDataURL(fixed);
|
|
4570
|
-
} catch {
|
|
4571
|
-
return url;
|
|
4572
|
-
}
|
|
4573
|
-
}
|
|
4574
|
-
async function toCanvas(url, options) {
|
|
4575
|
-
let { width: optW, height: optH, scale = 1, dpr = 1, meta = {}, backgroundColor } = options;
|
|
4576
|
-
url = maybeConvertBoxShadowForSafari(url);
|
|
4577
|
-
const img = new Image();
|
|
4578
|
-
img.loading = "eager";
|
|
4579
|
-
img.decoding = "sync";
|
|
4580
|
-
img.crossOrigin = "anonymous";
|
|
4581
|
-
img.src = url;
|
|
4582
|
-
await img.decode();
|
|
4583
|
-
const natW = img.naturalWidth;
|
|
4584
|
-
const natH = img.naturalHeight;
|
|
4585
|
-
const refW = Number.isFinite(meta.w0) ? meta.w0 : natW;
|
|
4586
|
-
const refH = Number.isFinite(meta.h0) ? meta.h0 : natH;
|
|
4587
|
-
let outW, outH;
|
|
4588
|
-
const hasW = Number.isFinite(optW);
|
|
4589
|
-
const hasH = Number.isFinite(optH);
|
|
4590
|
-
if (hasW && hasH) {
|
|
4591
|
-
outW = Math.max(1, optW);
|
|
4592
|
-
outH = Math.max(1, optH);
|
|
4593
|
-
} else if (hasW) {
|
|
4594
|
-
const k = optW / Math.max(1, refW);
|
|
4595
|
-
outW = optW;
|
|
4596
|
-
outH = refH * k;
|
|
4597
|
-
} else if (hasH) {
|
|
4598
|
-
const k = optH / Math.max(1, refH);
|
|
4599
|
-
outH = optH;
|
|
4600
|
-
outW = refW * k;
|
|
4601
|
-
} else {
|
|
4602
|
-
outW = natW;
|
|
4603
|
-
outH = natH;
|
|
4604
|
-
}
|
|
4605
|
-
outW = outW * scale;
|
|
4606
|
-
outH = outH * scale;
|
|
4607
|
-
const canvas = document.createElement("canvas");
|
|
4608
|
-
canvas.width = outW * dpr;
|
|
4609
|
-
canvas.height = outH * dpr;
|
|
4610
|
-
canvas.style.width = `${outW}px`;
|
|
4611
|
-
canvas.style.height = `${outH}px`;
|
|
4612
|
-
const ctx = canvas.getContext("2d");
|
|
4613
|
-
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
4614
|
-
if (backgroundColor) {
|
|
4615
|
-
ctx.save();
|
|
4616
|
-
ctx.fillStyle = backgroundColor;
|
|
4617
|
-
ctx.fillRect(0, 0, outW, outH);
|
|
4618
|
-
ctx.restore();
|
|
4619
|
-
}
|
|
4620
|
-
ctx.drawImage(img, 0, 0, outW, outH);
|
|
4621
|
-
return canvas;
|
|
4622
|
-
}
|
|
4623
|
-
|
|
4624
|
-
// src/modules/rasterize.js
|
|
4625
|
-
async function rasterize(url, options) {
|
|
4626
|
-
const canvas = await toCanvas(url, options);
|
|
4627
|
-
const img = new Image();
|
|
4628
|
-
img.src = canvas.toDataURL(`image/${options.format}`, options.quality);
|
|
4629
|
-
await img.decode();
|
|
4630
|
-
img.style.width = `${canvas.width / options.dpr}px`;
|
|
4631
|
-
img.style.height = `${canvas.height / options.dpr}px`;
|
|
4632
|
-
return img;
|
|
4633
|
-
}
|
|
4634
|
-
|
|
4635
|
-
// src/exporters/toImg.js
|
|
4636
|
-
async function toImg(url, options) {
|
|
4637
|
-
const { scale = 1, width, height, meta = {} } = options;
|
|
4638
|
-
const hasW = Number.isFinite(width);
|
|
4639
|
-
const hasH = Number.isFinite(height);
|
|
4640
|
-
const wantsScale = Number.isFinite(scale) && scale !== 1 || hasW || hasH;
|
|
4641
|
-
if (isSafari() && wantsScale) {
|
|
4642
|
-
const pngUrl = await rasterize(url, { ...options, format: "png", quality: 1, meta });
|
|
4643
|
-
return pngUrl;
|
|
4644
|
-
}
|
|
4645
|
-
const img = new Image();
|
|
4646
|
-
img.decoding = "sync";
|
|
4647
|
-
img.loading = "eager";
|
|
4648
|
-
img.src = url;
|
|
4649
|
-
await img.decode();
|
|
4650
|
-
if (hasW && hasH) {
|
|
4651
|
-
img.style.width = `${width}px`;
|
|
4652
|
-
img.style.height = `${height}px`;
|
|
4653
|
-
} else if (hasW) {
|
|
4654
|
-
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
4655
|
-
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
4656
|
-
const k = width / Math.max(1, refW);
|
|
4657
|
-
img.style.width = `${width}px`;
|
|
4658
|
-
img.style.height = `${Math.round(refH * k)}px`;
|
|
4659
|
-
} else if (hasH) {
|
|
4660
|
-
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
4661
|
-
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
4662
|
-
const k = height / Math.max(1, refH);
|
|
4663
|
-
img.style.height = `${height}px`;
|
|
4664
|
-
img.style.width = `${Math.round(refW * k)}px`;
|
|
4665
|
-
} else {
|
|
4666
|
-
const cssW = Math.round(img.naturalWidth * scale);
|
|
4667
|
-
const cssH = Math.round(img.naturalHeight * scale);
|
|
4668
|
-
img.style.width = `${cssW}px`;
|
|
4669
|
-
img.style.height = `${cssH}px`;
|
|
4670
|
-
if (typeof url === "string" && url.startsWith("data:image/svg+xml")) {
|
|
4671
|
-
try {
|
|
4672
|
-
const decoded = decodeURIComponent(url.split(",")[1]);
|
|
4673
|
-
const patched = decoded.replace(/width="[^"]*"/, `width="${cssW}"`).replace(/height="[^"]*"/, `height="${cssH}"`);
|
|
4674
|
-
url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(patched)}`;
|
|
4675
|
-
img.src = url;
|
|
4676
|
-
} catch {
|
|
4677
|
-
}
|
|
4678
|
-
}
|
|
4698
|
+
}, { fast });
|
|
4699
|
+
});
|
|
4679
4700
|
}
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
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
|
+
});
|
|
4689
4713
|
}
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
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);
|
|
4694
4853
|
}
|
|
4695
4854
|
|
|
4696
|
-
// src/
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
a2.download = options.filename;
|
|
4705
|
-
a2.click();
|
|
4706
|
-
URL.revokeObjectURL(objectURL);
|
|
4707
|
-
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
|
+
);
|
|
4708
4863
|
}
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
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
|
+
};
|
|
4714
4908
|
}
|
|
4715
4909
|
|
|
4716
4910
|
// src/api/snapdom.js
|
|
4717
|
-
|
|
4911
|
+
init_browser();
|
|
4912
|
+
function plugins(...defs) {
|
|
4718
4913
|
registerPlugins(...defs);
|
|
4719
4914
|
return snapdom;
|
|
4720
|
-
}
|
|
4915
|
+
}
|
|
4916
|
+
var snapdom = Object.assign(main, { plugins });
|
|
4721
4917
|
var INTERNAL_TOKEN = Symbol("snapdom.internal");
|
|
4722
4918
|
var INTERNAL_EXPORT_TOKEN = Symbol("snapdom.internal.silent");
|
|
4723
4919
|
var _safariWarmup = false;
|
|
4724
|
-
async function
|
|
4920
|
+
async function main(element, userOptions) {
|
|
4725
4921
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
4726
4922
|
const context = createContext(userOptions);
|
|
4727
4923
|
attachSessionPlugins(context, userOptions && userOptions.plugins);
|
|
@@ -4729,7 +4925,6 @@ async function snapdom(element, userOptions) {
|
|
|
4729
4925
|
for (let i = 0; i < 3; i++) {
|
|
4730
4926
|
try {
|
|
4731
4927
|
await safariWarmup(element, userOptions);
|
|
4732
|
-
console.log("safariWarmup:", i);
|
|
4733
4928
|
_safariWarmup = false;
|
|
4734
4929
|
} catch {
|
|
4735
4930
|
}
|
|
@@ -4744,32 +4939,76 @@ async function snapdom(element, userOptions) {
|
|
|
4744
4939
|
}
|
|
4745
4940
|
return snapdom.capture(element, context, INTERNAL_TOKEN);
|
|
4746
4941
|
}
|
|
4747
|
-
snapdom.plugins = (...defs) => {
|
|
4748
|
-
registerPlugins(...defs);
|
|
4749
|
-
return snapdom;
|
|
4750
|
-
};
|
|
4751
4942
|
snapdom.capture = async (el, context, _token) => {
|
|
4752
4943
|
if (_token !== INTERNAL_TOKEN) throw new Error("[snapdom.capture] is internal. Use snapdom(...) instead.");
|
|
4753
4944
|
const url = await captureDOM(el, context);
|
|
4754
4945
|
const coreExports = {
|
|
4755
|
-
img: async (ctx, opts) =>
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
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
|
+
}
|
|
4763
4978
|
};
|
|
4764
4979
|
const _pluginExports = {
|
|
4765
|
-
svg: (opts) =>
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
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
|
+
}
|
|
4773
5012
|
};
|
|
4774
5013
|
const _defineCtx = { ...context, export: { url }, exports: _pluginExports };
|
|
4775
5014
|
const providedMaps = await runAll("defineExports", _defineCtx);
|
|
@@ -4816,10 +5055,9 @@ snapdom.capture = async (el, context, _token) => {
|
|
|
4816
5055
|
toBlob: (opts) => runExport("blob", opts),
|
|
4817
5056
|
toPng: (opts) => runExport("png", opts),
|
|
4818
5057
|
toJpg: (opts) => runExport("jpg", opts),
|
|
4819
|
-
//
|
|
5058
|
+
// alias requerido por tests
|
|
4820
5059
|
toWebp: (opts) => runExport("webp", opts),
|
|
4821
5060
|
download: (opts) => runExport("download", opts)
|
|
4822
|
-
// ← método directo (no toDownload)
|
|
4823
5061
|
};
|
|
4824
5062
|
for (const key of Object.keys(exportsMap)) {
|
|
4825
5063
|
const helper = "to" + key.charAt(0).toUpperCase() + key.slice(1);
|
|
@@ -4909,6 +5147,9 @@ function hasBackgroundOrMask(el) {
|
|
|
4909
5147
|
}
|
|
4910
5148
|
|
|
4911
5149
|
// src/api/preCache.js
|
|
5150
|
+
init_utils();
|
|
5151
|
+
init_snapFetch();
|
|
5152
|
+
init_cache();
|
|
4912
5153
|
async function preCache(root = document, options = {}) {
|
|
4913
5154
|
const {
|
|
4914
5155
|
embedFonts = true,
|