@zumer/snapdom 1.9.11 → 1.9.12-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -40
- package/dist/snapdom.js +520 -112
- package/dist/snapdom.min.js +5 -5
- package/dist/snapdom.min.mjs +4 -4
- package/dist/snapdom.mjs +492 -90
- package/package.json +1 -1
- package/types/snapdom.d.ts +16 -1
package/dist/snapdom.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.9.
|
|
4
|
+
* v.1.9.12-dev.1
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -127,10 +127,25 @@ var snapLogger = createSnapLogger("[snapDOM]", { ttlMs: 3 * 6e4, maxEntries: 10
|
|
|
127
127
|
var _inflight = /* @__PURE__ */ new Map();
|
|
128
128
|
var _errorCache = /* @__PURE__ */ new Map();
|
|
129
129
|
function isSpecialURL(url) {
|
|
130
|
-
return /^data:|^blob:|^about:blank
|
|
130
|
+
return /^data:|^blob:|^about:blank$/i.test(url);
|
|
131
|
+
}
|
|
132
|
+
function isAlreadyProxied(url, useProxy) {
|
|
133
|
+
try {
|
|
134
|
+
const baseHref = typeof location !== "undefined" && location.href ? location.href : "http://localhost/";
|
|
135
|
+
const proxyBaseRaw = useProxy.includes("{url}") ? useProxy.split("{url}")[0] : useProxy;
|
|
136
|
+
const proxyBase = new URL(proxyBaseRaw || ".", baseHref);
|
|
137
|
+
const u = new URL(url, baseHref);
|
|
138
|
+
if (u.origin === proxyBase.origin) return true;
|
|
139
|
+
const sp = u.searchParams;
|
|
140
|
+
if (sp && (sp.has("url") || sp.has("target"))) return true;
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
131
144
|
}
|
|
132
145
|
function shouldProxy(url, useProxy) {
|
|
133
|
-
if (!useProxy
|
|
146
|
+
if (!useProxy) return false;
|
|
147
|
+
if (isSpecialURL(url)) return false;
|
|
148
|
+
if (isAlreadyProxied(url, useProxy)) return false;
|
|
134
149
|
try {
|
|
135
150
|
const base = typeof location !== "undefined" && location.href ? location.href : "http://localhost/";
|
|
136
151
|
const u = new URL(url, base);
|
|
@@ -141,8 +156,19 @@ function shouldProxy(url, useProxy) {
|
|
|
141
156
|
}
|
|
142
157
|
function applyProxy(url, useProxy) {
|
|
143
158
|
if (!useProxy) return url;
|
|
144
|
-
if (useProxy.includes("{url}"))
|
|
145
|
-
|
|
159
|
+
if (useProxy.includes("{url}")) {
|
|
160
|
+
return useProxy.replace("{urlRaw}", safeEncodeURI(url)).replace("{url}", encodeURIComponent(url));
|
|
161
|
+
}
|
|
162
|
+
if (/[?&]url=?$/.test(useProxy)) {
|
|
163
|
+
return `${useProxy}${encodeURIComponent(url)}`;
|
|
164
|
+
}
|
|
165
|
+
if (useProxy.endsWith("?")) {
|
|
166
|
+
return `${useProxy}url=${encodeURIComponent(url)}`;
|
|
167
|
+
}
|
|
168
|
+
if (useProxy.endsWith("/")) {
|
|
169
|
+
return `${useProxy}${safeEncodeURI(url)}`;
|
|
170
|
+
}
|
|
171
|
+
const sep = useProxy.includes("?") ? "&" : "?";
|
|
146
172
|
return `${useProxy}${sep}url=${encodeURIComponent(url)}`;
|
|
147
173
|
}
|
|
148
174
|
function blobToDataURL(blob) {
|
|
@@ -169,6 +195,65 @@ async function snapFetch(url, options = {}) {
|
|
|
169
195
|
const errorTTL = options.errorTTL ?? 8e3;
|
|
170
196
|
const headers = options.headers || {};
|
|
171
197
|
const silent = !!options.silent;
|
|
198
|
+
if (/^data:/i.test(url)) {
|
|
199
|
+
try {
|
|
200
|
+
if (as === "text") {
|
|
201
|
+
return { ok: true, data: String(url), status: 200, url, fromCache: false };
|
|
202
|
+
}
|
|
203
|
+
if (as === "dataURL") {
|
|
204
|
+
return {
|
|
205
|
+
ok: true,
|
|
206
|
+
data: String(url),
|
|
207
|
+
status: 200,
|
|
208
|
+
url,
|
|
209
|
+
fromCache: false,
|
|
210
|
+
mime: String(url).slice(5).split(";")[0] || ""
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const [, meta = "", data = ""] = String(url).match(/^data:([^,]*),(.*)$/) || [];
|
|
214
|
+
const isBase64 = /;base64/i.test(meta);
|
|
215
|
+
const bin = isBase64 ? atob(data) : decodeURIComponent(data);
|
|
216
|
+
const bytes = new Uint8Array([...bin].map((c) => c.charCodeAt(0)));
|
|
217
|
+
const b = new Blob([bytes], { type: (meta || "").split(";")[0] || "" });
|
|
218
|
+
return { ok: true, data: b, status: 200, url, fromCache: false, mime: b.type || "" };
|
|
219
|
+
} catch {
|
|
220
|
+
return { ok: false, data: null, status: 0, url, fromCache: false, reason: "special_url_error" };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (/^blob:/i.test(url)) {
|
|
224
|
+
try {
|
|
225
|
+
const resp = await fetch(url);
|
|
226
|
+
if (!resp.ok) {
|
|
227
|
+
return { ok: false, data: null, status: resp.status, url, fromCache: false, reason: "http_error" };
|
|
228
|
+
}
|
|
229
|
+
const blob = await resp.blob();
|
|
230
|
+
const mime = blob.type || resp.headers.get("content-type") || "";
|
|
231
|
+
if (as === "dataURL") {
|
|
232
|
+
const dataURL = await blobToDataURL(blob);
|
|
233
|
+
return { ok: true, data: dataURL, status: resp.status, url, fromCache: false, mime };
|
|
234
|
+
}
|
|
235
|
+
if (as === "text") {
|
|
236
|
+
const text = await blob.text();
|
|
237
|
+
return { ok: true, data: text, status: resp.status, url, fromCache: false, mime };
|
|
238
|
+
}
|
|
239
|
+
return { ok: true, data: blob, status: resp.status, url, fromCache: false, mime };
|
|
240
|
+
} catch {
|
|
241
|
+
return { ok: false, data: null, status: 0, url, fromCache: false, reason: "network" };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (/^about:blank$/i.test(url)) {
|
|
245
|
+
if (as === "dataURL") {
|
|
246
|
+
return {
|
|
247
|
+
ok: true,
|
|
248
|
+
data: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
|
|
249
|
+
status: 200,
|
|
250
|
+
url,
|
|
251
|
+
fromCache: false,
|
|
252
|
+
mime: "image/png"
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return { ok: true, data: as === "text" ? "" : new Blob([]), status: 200, url, fromCache: false };
|
|
256
|
+
}
|
|
172
257
|
const key = makeKey(url, { as, timeout, useProxy, errorTTL });
|
|
173
258
|
const e = _errorCache.get(key);
|
|
174
259
|
if (e && e.until > Date.now()) {
|
|
@@ -197,7 +282,7 @@ async function snapFetch(url, options = {}) {
|
|
|
197
282
|
const resp = await fetch(finalURL, { signal: ctrl.signal, credentials: cred, headers });
|
|
198
283
|
if (!resp.ok) {
|
|
199
284
|
const result = { ok: false, data: null, status: resp.status, url: finalURL, fromCache: false, reason: "http_error" };
|
|
200
|
-
_errorCache.set(key, { until: Date.now() + errorTTL, result });
|
|
285
|
+
if (errorTTL > 0) _errorCache.set(key, { until: Date.now() + errorTTL, result });
|
|
201
286
|
if (!silent) {
|
|
202
287
|
const short = `${resp.status} ${resp.statusText || ""}`.trim();
|
|
203
288
|
snapLogger.warnOnce(
|
|
@@ -222,7 +307,9 @@ async function snapFetch(url, options = {}) {
|
|
|
222
307
|
} catch (err) {
|
|
223
308
|
const reason = err && typeof err === "object" && "name" in err && err.name === "AbortError" ? String(err.message || "").includes("timeout") ? "timeout" : "abort" : "network";
|
|
224
309
|
const result = { ok: false, data: null, status: 0, url: finalURL, fromCache: false, reason };
|
|
225
|
-
|
|
310
|
+
if (!/^blob:/i.test(url) && errorTTL > 0) {
|
|
311
|
+
_errorCache.set(key, { until: Date.now() + errorTTL, result });
|
|
312
|
+
}
|
|
226
313
|
if (!silent) {
|
|
227
314
|
const k = `${reason}:${as}:${new URL(url, location?.href ?? "http://localhost/").origin}`;
|
|
228
315
|
const tips = reason === "timeout" ? `Timeout after ${timeout}ms. Consider increasing timeout or using a proxy for ${url}` : reason === "abort" ? `Request aborted while fetching ${as} ${url}` : `Network/CORS issue while fetching ${as} ${url}. A proxy may be required`;
|
|
@@ -539,7 +626,10 @@ function idle(fn, { fast = false } = {}) {
|
|
|
539
626
|
}
|
|
540
627
|
}
|
|
541
628
|
function isSafari() {
|
|
542
|
-
|
|
629
|
+
const ua = typeof navigator !== "undefined" && navigator.userAgent ? navigator.userAgent : "";
|
|
630
|
+
const isSafariUA = /^((?!chrome|android).)*safari/i.test(ua);
|
|
631
|
+
const isWeChatUA = /(MicroMessenger|wxwork|WeCom|WindowsWechat|MacWechat)/i.test(ua);
|
|
632
|
+
return isSafariUA || isWeChatUA;
|
|
543
633
|
}
|
|
544
634
|
|
|
545
635
|
// src/modules/styles.js
|
|
@@ -573,7 +663,7 @@ function setupInvalidationOnce(root = document.documentElement) {
|
|
|
573
663
|
} catch {
|
|
574
664
|
}
|
|
575
665
|
}
|
|
576
|
-
function snapshotComputedStyleFull(style) {
|
|
666
|
+
function snapshotComputedStyleFull(style, options = {}) {
|
|
577
667
|
const out = {};
|
|
578
668
|
const vis = style.getPropertyValue("visibility");
|
|
579
669
|
for (let i = 0; i < style.length; i++) {
|
|
@@ -584,6 +674,24 @@ function snapshotComputedStyleFull(style) {
|
|
|
584
674
|
}
|
|
585
675
|
out[prop] = val;
|
|
586
676
|
}
|
|
677
|
+
if (options.embedFonts) {
|
|
678
|
+
const EXTRA_FONT_PROPS = [
|
|
679
|
+
"font-feature-settings",
|
|
680
|
+
"font-variation-settings",
|
|
681
|
+
"font-kerning",
|
|
682
|
+
"font-variant",
|
|
683
|
+
"font-variant-ligatures",
|
|
684
|
+
"font-optical-sizing"
|
|
685
|
+
];
|
|
686
|
+
for (const prop of EXTRA_FONT_PROPS) {
|
|
687
|
+
if (out[prop]) continue;
|
|
688
|
+
try {
|
|
689
|
+
const v = style.getPropertyValue(prop);
|
|
690
|
+
if (v) out[prop] = v;
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
587
695
|
if (vis === "hidden") out.opacity = "0";
|
|
588
696
|
return out;
|
|
589
697
|
}
|
|
@@ -596,11 +704,11 @@ function styleSignature(snap) {
|
|
|
596
704
|
__snapshotSig.set(snap, sig);
|
|
597
705
|
return sig;
|
|
598
706
|
}
|
|
599
|
-
function getSnapshot(el, preStyle = null) {
|
|
707
|
+
function getSnapshot(el, preStyle = null, options = {}) {
|
|
600
708
|
const rec = snapshotCache.get(el);
|
|
601
709
|
if (rec && rec.epoch === __epoch) return rec.snapshot;
|
|
602
710
|
const style = preStyle || getComputedStyle(el);
|
|
603
|
-
const snap = snapshotComputedStyleFull(style);
|
|
711
|
+
const snap = snapshotComputedStyleFull(style, options);
|
|
604
712
|
snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
|
|
605
713
|
return snap;
|
|
606
714
|
}
|
|
@@ -654,7 +762,7 @@ async function inlineAllStyles(source, clone, sessionOrCtx, opts) {
|
|
|
654
762
|
session.styleCache.set(source, getComputedStyle(source));
|
|
655
763
|
}
|
|
656
764
|
const pre = session.styleCache.get(source);
|
|
657
|
-
const snap = getSnapshot(source, pre);
|
|
765
|
+
const snap = getSnapshot(source, pre, ctx.options);
|
|
658
766
|
const sig = styleSignature(snap);
|
|
659
767
|
let key = persist.snapshotKeyCache.get(sig);
|
|
660
768
|
if (!key) {
|
|
@@ -898,19 +1006,27 @@ async function deepClone(node, sessionCache, options) {
|
|
|
898
1006
|
return node.cloneNode(true);
|
|
899
1007
|
}
|
|
900
1008
|
if (node.getAttribute("data-capture") === "exclude") {
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1009
|
+
if (options.excludeMode === "hide") {
|
|
1010
|
+
const spacer = document.createElement("div");
|
|
1011
|
+
const rect = node.getBoundingClientRect();
|
|
1012
|
+
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1013
|
+
return spacer;
|
|
1014
|
+
} else if (options.excludeMode === "remove") {
|
|
1015
|
+
return null;
|
|
1016
|
+
}
|
|
905
1017
|
}
|
|
906
1018
|
if (options.exclude && Array.isArray(options.exclude)) {
|
|
907
1019
|
for (const selector of options.exclude) {
|
|
908
1020
|
try {
|
|
909
1021
|
if (node.matches?.(selector)) {
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1022
|
+
if (options.excludeMode === "hide") {
|
|
1023
|
+
const spacer = document.createElement("div");
|
|
1024
|
+
const rect = node.getBoundingClientRect();
|
|
1025
|
+
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1026
|
+
return spacer;
|
|
1027
|
+
} else if (options.excludeMode === "remove") {
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
914
1030
|
}
|
|
915
1031
|
} catch (err) {
|
|
916
1032
|
console.warn(`Invalid selector in exclude option: ${selector}`, err);
|
|
@@ -920,10 +1036,14 @@ async function deepClone(node, sessionCache, options) {
|
|
|
920
1036
|
if (typeof options.filter === "function") {
|
|
921
1037
|
try {
|
|
922
1038
|
if (!options.filter(node)) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1039
|
+
if (options.filterMode === "hide") {
|
|
1040
|
+
const spacer = document.createElement("div");
|
|
1041
|
+
const rect = node.getBoundingClientRect();
|
|
1042
|
+
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
1043
|
+
return spacer;
|
|
1044
|
+
} else if (options.filterMode === "remove") {
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
927
1047
|
}
|
|
928
1048
|
} catch (err) {
|
|
929
1049
|
console.warn("Error in filter function:", err);
|
|
@@ -1799,7 +1919,182 @@ async function ensureFontsReady(families, warmupRepetitions = 2) {
|
|
|
1799
1919
|
}
|
|
1800
1920
|
}
|
|
1801
1921
|
|
|
1922
|
+
// src/modules/counter.js
|
|
1923
|
+
function hasCounters(input) {
|
|
1924
|
+
return /\bcounter\s*\(|\bcounters\s*\(/.test(input || "");
|
|
1925
|
+
}
|
|
1926
|
+
function unquoteDoubleStrings(s) {
|
|
1927
|
+
return (s || "").replace(/"([^"]*)"/g, "$1");
|
|
1928
|
+
}
|
|
1929
|
+
function alpha(n, upper = false) {
|
|
1930
|
+
let s = "", x = Math.max(1, n);
|
|
1931
|
+
while (x > 0) {
|
|
1932
|
+
x--;
|
|
1933
|
+
s = String.fromCharCode(97 + x % 26) + s;
|
|
1934
|
+
x = Math.floor(x / 26);
|
|
1935
|
+
}
|
|
1936
|
+
return upper ? s.toUpperCase() : s;
|
|
1937
|
+
}
|
|
1938
|
+
function roman(n, upper = true) {
|
|
1939
|
+
const map = [[1e3, "M"], [900, "CM"], [500, "D"], [400, "CD"], [100, "C"], [90, "XC"], [50, "L"], [40, "XL"], [10, "X"], [9, "IX"], [5, "V"], [4, "IV"], [1, "I"]];
|
|
1940
|
+
let num = Math.max(1, Math.min(3999, n)), out = "";
|
|
1941
|
+
for (const [v, sym] of map) while (num >= v) {
|
|
1942
|
+
out += sym;
|
|
1943
|
+
num -= v;
|
|
1944
|
+
}
|
|
1945
|
+
return upper ? out : out.toLowerCase();
|
|
1946
|
+
}
|
|
1947
|
+
function formatCounter(value, style) {
|
|
1948
|
+
switch ((style || "decimal").toLowerCase()) {
|
|
1949
|
+
case "decimal":
|
|
1950
|
+
return String(Math.max(0, value));
|
|
1951
|
+
case "decimal-leading-zero":
|
|
1952
|
+
return (value < 10 ? "0" : "") + String(Math.max(0, value));
|
|
1953
|
+
case "lower-alpha":
|
|
1954
|
+
return alpha(value, false);
|
|
1955
|
+
case "upper-alpha":
|
|
1956
|
+
return alpha(value, true);
|
|
1957
|
+
case "lower-roman":
|
|
1958
|
+
return roman(value, false);
|
|
1959
|
+
case "upper-roman":
|
|
1960
|
+
return roman(value, true);
|
|
1961
|
+
default:
|
|
1962
|
+
return String(Math.max(0, value));
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
function buildCounterContext(root) {
|
|
1966
|
+
const nodeCounters = /* @__PURE__ */ new WeakMap();
|
|
1967
|
+
const rootEl = root instanceof Document ? root.documentElement : root;
|
|
1968
|
+
const isLi = (el) => el && el.tagName === "LI";
|
|
1969
|
+
const countPrevLi = (li) => {
|
|
1970
|
+
let c = 0, p = li?.parentElement;
|
|
1971
|
+
if (!p) return 0;
|
|
1972
|
+
for (const sib of p.children) {
|
|
1973
|
+
if (sib === li) break;
|
|
1974
|
+
if (sib.tagName === "LI") c++;
|
|
1975
|
+
}
|
|
1976
|
+
return c;
|
|
1977
|
+
};
|
|
1978
|
+
const cloneMap = (m) => {
|
|
1979
|
+
const out = /* @__PURE__ */ new Map();
|
|
1980
|
+
for (const [k, arr] of m) out.set(k, arr.slice());
|
|
1981
|
+
return out;
|
|
1982
|
+
};
|
|
1983
|
+
const applyTo = (baseMap, parentMap, el) => {
|
|
1984
|
+
const map = cloneMap(baseMap);
|
|
1985
|
+
let reset;
|
|
1986
|
+
try {
|
|
1987
|
+
reset = el.style?.counterReset || getComputedStyle(el).counterReset;
|
|
1988
|
+
} catch {
|
|
1989
|
+
}
|
|
1990
|
+
if (reset && reset !== "none") {
|
|
1991
|
+
for (const part of reset.split(",")) {
|
|
1992
|
+
const toks = part.trim().split(/\s+/);
|
|
1993
|
+
const name = toks[0];
|
|
1994
|
+
const val = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : 0;
|
|
1995
|
+
if (!name) continue;
|
|
1996
|
+
const parentStack = parentMap.get(name);
|
|
1997
|
+
if (parentStack && parentStack.length) {
|
|
1998
|
+
const s = parentStack.slice();
|
|
1999
|
+
s.push(val);
|
|
2000
|
+
map.set(name, s);
|
|
2001
|
+
} else {
|
|
2002
|
+
map.set(name, [val]);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
let inc;
|
|
2007
|
+
try {
|
|
2008
|
+
inc = el.style?.counterIncrement || getComputedStyle(el).counterIncrement;
|
|
2009
|
+
} catch {
|
|
2010
|
+
}
|
|
2011
|
+
if (inc && inc !== "none") {
|
|
2012
|
+
for (const part of inc.split(",")) {
|
|
2013
|
+
const toks = part.trim().split(/\s+/);
|
|
2014
|
+
const name = toks[0];
|
|
2015
|
+
const by = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : 1;
|
|
2016
|
+
if (!name) continue;
|
|
2017
|
+
const stack = map.get(name) || [];
|
|
2018
|
+
if (stack.length === 0) stack.push(0);
|
|
2019
|
+
stack[stack.length - 1] += by;
|
|
2020
|
+
map.set(name, stack);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
try {
|
|
2024
|
+
const cs = getComputedStyle(el);
|
|
2025
|
+
if (cs.display === "list-item" && isLi(el)) {
|
|
2026
|
+
const p = el.parentElement;
|
|
2027
|
+
let idx = 1;
|
|
2028
|
+
if (p && p.tagName === "OL") {
|
|
2029
|
+
const startAttr = p.getAttribute("start");
|
|
2030
|
+
const start = Number.isFinite(Number(startAttr)) ? Number(startAttr) : 1;
|
|
2031
|
+
const prev = countPrevLi(el);
|
|
2032
|
+
const ownAttr = el.getAttribute("value");
|
|
2033
|
+
idx = Number.isFinite(Number(ownAttr)) ? Number(ownAttr) : start + prev;
|
|
2034
|
+
} else {
|
|
2035
|
+
idx = 1 + countPrevLi(el);
|
|
2036
|
+
}
|
|
2037
|
+
const s = map.get("list-item") || [];
|
|
2038
|
+
if (s.length === 0) s.push(0);
|
|
2039
|
+
s[s.length - 1] = idx;
|
|
2040
|
+
map.set("list-item", s);
|
|
2041
|
+
}
|
|
2042
|
+
} catch {
|
|
2043
|
+
}
|
|
2044
|
+
return map;
|
|
2045
|
+
};
|
|
2046
|
+
const build = (el, parentMap, carryMap) => {
|
|
2047
|
+
const curr = applyTo(carryMap, parentMap, el);
|
|
2048
|
+
nodeCounters.set(el, curr);
|
|
2049
|
+
let nextCarry = curr;
|
|
2050
|
+
for (const child of el.children) {
|
|
2051
|
+
const childCarry = build(child, curr, nextCarry);
|
|
2052
|
+
nextCarry = childCarry;
|
|
2053
|
+
}
|
|
2054
|
+
return curr;
|
|
2055
|
+
};
|
|
2056
|
+
const empty = /* @__PURE__ */ new Map();
|
|
2057
|
+
build(rootEl, empty, empty);
|
|
2058
|
+
return {
|
|
2059
|
+
get(node, name) {
|
|
2060
|
+
const s = nodeCounters.get(node)?.get(name);
|
|
2061
|
+
return s && s.length ? s[s.length - 1] : 0;
|
|
2062
|
+
},
|
|
2063
|
+
getStack(node, name) {
|
|
2064
|
+
const s = nodeCounters.get(node)?.get(name);
|
|
2065
|
+
return s ? s.slice() : [];
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
function resolveCountersInContent(raw, node, ctx) {
|
|
2070
|
+
if (!raw || raw === "none") return raw;
|
|
2071
|
+
try {
|
|
2072
|
+
const RX = /\b(counter|counters)\s*\(([^)]+)\)/g;
|
|
2073
|
+
let out = raw.replace(RX, (_, fn, args) => {
|
|
2074
|
+
const parts = String(args).split(",").map((s) => s.trim());
|
|
2075
|
+
if (fn === "counter") {
|
|
2076
|
+
const name = parts[0]?.replace(/^["']|["']$/g, "");
|
|
2077
|
+
const style = (parts[1] || "decimal").toLowerCase();
|
|
2078
|
+
const v = ctx.get(node, name);
|
|
2079
|
+
return formatCounter(v, style);
|
|
2080
|
+
} else {
|
|
2081
|
+
const name = parts[0]?.replace(/^["']|["']$/g, "");
|
|
2082
|
+
const sep = parts[1]?.replace(/^["']|["']$/g, "") ?? "";
|
|
2083
|
+
const style = (parts[2] || "decimal").toLowerCase();
|
|
2084
|
+
const stack = ctx.getStack(node, name);
|
|
2085
|
+
if (!stack.length) return "";
|
|
2086
|
+
const pieces = stack.map((v) => formatCounter(v, style));
|
|
2087
|
+
return pieces.join(sep);
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
return unquoteDoubleStrings(out);
|
|
2091
|
+
} catch {
|
|
2092
|
+
return "- ";
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
|
|
1802
2096
|
// src/modules/pseudo.js
|
|
2097
|
+
var counterCtx = null;
|
|
1803
2098
|
async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
1804
2099
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
1805
2100
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
@@ -1833,7 +2128,11 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
1833
2128
|
continue;
|
|
1834
2129
|
}
|
|
1835
2130
|
const content = style.content;
|
|
1836
|
-
|
|
2131
|
+
let cleanContent = parseContent(content);
|
|
2132
|
+
if (hasCounters(content)) {
|
|
2133
|
+
counterCtx = buildCounterContext(source.ownerDocument || document);
|
|
2134
|
+
cleanContent = resolveCountersInContent(cleanContent, source, counterCtx);
|
|
2135
|
+
}
|
|
1837
2136
|
const bg = style.backgroundImage;
|
|
1838
2137
|
const bgColor = style.backgroundColor;
|
|
1839
2138
|
const fontFamily = style.fontFamily;
|
|
@@ -2218,8 +2517,8 @@ async function inlineImages(clone, options = {}) {
|
|
|
2218
2517
|
if (!img.height) img.height = img.naturalHeight || 100;
|
|
2219
2518
|
return;
|
|
2220
2519
|
}
|
|
2221
|
-
const {
|
|
2222
|
-
if (
|
|
2520
|
+
const { fallbackURL } = options || {};
|
|
2521
|
+
if (fallbackURL) {
|
|
2223
2522
|
try {
|
|
2224
2523
|
const dsW = parseInt(img.dataset?.snapdomWidth || "", 10) || 0;
|
|
2225
2524
|
const dsH = parseInt(img.dataset?.snapdomHeight || "", 10) || 0;
|
|
@@ -2227,14 +2526,14 @@ async function inlineImages(clone, options = {}) {
|
|
|
2227
2526
|
const attrH = parseInt(img.getAttribute("height") || "", 10) || 0;
|
|
2228
2527
|
const styleW = parseFloat(img.style?.width || "") || 0;
|
|
2229
2528
|
const styleH = parseFloat(img.style?.height || "") || 0;
|
|
2230
|
-
const
|
|
2231
|
-
const
|
|
2232
|
-
const fallbackUrl = typeof
|
|
2529
|
+
const fbW = dsW || styleW || attrW || img.width || void 0;
|
|
2530
|
+
const fbH = dsH || styleH || attrH || img.height || void 0;
|
|
2531
|
+
const fallbackUrl = typeof fallbackURL === "function" ? await fallbackURL({ width: fbW, height: fbH, src, element: img }) : fallbackURL;
|
|
2233
2532
|
if (fallbackUrl) {
|
|
2234
2533
|
const fallbackData = await snapFetch(fallbackUrl, { as: "dataURL", useProxy: options.useProxy });
|
|
2235
2534
|
img.src = fallbackData.data;
|
|
2236
|
-
if (!img.width &&
|
|
2237
|
-
if (!img.height &&
|
|
2535
|
+
if (!img.width && fbW) img.width = fbW;
|
|
2536
|
+
if (!img.height && fbH) img.height = fbH;
|
|
2238
2537
|
if (!img.width) img.width = img.naturalWidth || 100;
|
|
2239
2538
|
if (!img.height) img.height = img.naturalHeight || 100;
|
|
2240
2539
|
return;
|
|
@@ -2353,6 +2652,63 @@ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
|
|
|
2353
2652
|
}
|
|
2354
2653
|
}
|
|
2355
2654
|
|
|
2655
|
+
// src/modules/lineClamp.js
|
|
2656
|
+
function lineClamp(el) {
|
|
2657
|
+
if (!el) return () => {
|
|
2658
|
+
};
|
|
2659
|
+
const lines = getClamp(el);
|
|
2660
|
+
if (lines <= 0) return () => {
|
|
2661
|
+
};
|
|
2662
|
+
if (!isPlainTextContainer(el)) return () => {
|
|
2663
|
+
};
|
|
2664
|
+
const cs = getComputedStyle(el);
|
|
2665
|
+
const targetH = Math.round(usedLineHeightPx(cs) * lines + vpad(cs));
|
|
2666
|
+
const original = el.textContent ?? "";
|
|
2667
|
+
const prevText = original;
|
|
2668
|
+
if (el.scrollHeight <= targetH + 0.5) {
|
|
2669
|
+
return () => {
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
let lo = 0, hi = original.length, best = -1;
|
|
2673
|
+
while (lo <= hi) {
|
|
2674
|
+
const mid = lo + hi >> 1;
|
|
2675
|
+
el.textContent = original.slice(0, mid) + "\u2026";
|
|
2676
|
+
if (el.scrollHeight <= targetH + 0.5) {
|
|
2677
|
+
best = mid;
|
|
2678
|
+
lo = mid + 1;
|
|
2679
|
+
} else {
|
|
2680
|
+
hi = mid - 1;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
el.textContent = (best >= 0 ? original.slice(0, best) : "") + "\u2026";
|
|
2684
|
+
return () => {
|
|
2685
|
+
el.textContent = prevText;
|
|
2686
|
+
};
|
|
2687
|
+
}
|
|
2688
|
+
function getClamp(el) {
|
|
2689
|
+
const cs = getComputedStyle(el);
|
|
2690
|
+
let v = cs.getPropertyValue("-webkit-line-clamp") || cs.getPropertyValue("line-clamp");
|
|
2691
|
+
v = (v || "").trim();
|
|
2692
|
+
const n = parseInt(v, 10);
|
|
2693
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
2694
|
+
}
|
|
2695
|
+
function usedLineHeightPx(cs) {
|
|
2696
|
+
const lh = (cs.lineHeight || "").trim();
|
|
2697
|
+
const fs = parseFloat(cs.fontSize) || 16;
|
|
2698
|
+
if (!lh || lh === "normal") return Math.round(fs * 1.2);
|
|
2699
|
+
if (lh.endsWith("px")) return parseFloat(lh);
|
|
2700
|
+
if (/^\d+(\.\d+)?$/.test(lh)) return Math.round(parseFloat(lh) * fs);
|
|
2701
|
+
if (lh.endsWith("%")) return Math.round(parseFloat(lh) / 100 * fs);
|
|
2702
|
+
return Math.round(fs * 1.2);
|
|
2703
|
+
}
|
|
2704
|
+
function vpad(cs) {
|
|
2705
|
+
return (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
|
|
2706
|
+
}
|
|
2707
|
+
function isPlainTextContainer(el) {
|
|
2708
|
+
if (el.childElementCount > 0) return false;
|
|
2709
|
+
return Array.from(el.childNodes).some((n) => n.nodeType === Node.TEXT_NODE);
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2356
2712
|
// src/core/capture.js
|
|
2357
2713
|
async function captureDOM(element, options) {
|
|
2358
2714
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
@@ -2363,7 +2719,12 @@ async function captureDOM(element, options) {
|
|
|
2363
2719
|
let baseCSS = "";
|
|
2364
2720
|
let dataURL;
|
|
2365
2721
|
let svgString;
|
|
2366
|
-
|
|
2722
|
+
const undoClamp = lineClamp(element);
|
|
2723
|
+
try {
|
|
2724
|
+
({ clone, classCSS, styleCache } = await prepareClone(element, options));
|
|
2725
|
+
} finally {
|
|
2726
|
+
undoClamp();
|
|
2727
|
+
}
|
|
2367
2728
|
await new Promise((resolve) => {
|
|
2368
2729
|
idle(async () => {
|
|
2369
2730
|
await inlineImages(clone, options);
|
|
@@ -2385,7 +2746,7 @@ async function captureDOM(element, options) {
|
|
|
2385
2746
|
const families = new Set(
|
|
2386
2747
|
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2387
2748
|
);
|
|
2388
|
-
await ensureFontsReady(families,
|
|
2749
|
+
await ensureFontsReady(families, 2);
|
|
2389
2750
|
}
|
|
2390
2751
|
fontsCSS = await embedCustomFonts({
|
|
2391
2752
|
required,
|
|
@@ -2413,28 +2774,28 @@ async function captureDOM(element, options) {
|
|
|
2413
2774
|
}
|
|
2414
2775
|
await new Promise((resolve) => {
|
|
2415
2776
|
idle(() => {
|
|
2416
|
-
const rect =
|
|
2417
|
-
let w = rect.width;
|
|
2418
|
-
let h = rect.height;
|
|
2777
|
+
const rect = element.getBoundingClientRect();
|
|
2778
|
+
let w = Number(rect.width) || 0;
|
|
2779
|
+
let h = Number(rect.height) || 0;
|
|
2419
2780
|
const hasW = Number.isFinite(options.width);
|
|
2420
2781
|
const hasH = Number.isFinite(options.height);
|
|
2421
2782
|
const scaleOpt = options.scale ?? 1;
|
|
2422
2783
|
const hasScale = typeof scaleOpt === "number" && scaleOpt !== 1;
|
|
2423
2784
|
if (!hasScale) {
|
|
2424
|
-
const aspect =
|
|
2785
|
+
const aspect = h > 0 ? w / h : 1;
|
|
2425
2786
|
if (hasW && hasH) {
|
|
2426
|
-
w = options.width;
|
|
2427
|
-
h = options.height;
|
|
2787
|
+
w = Number(options.width);
|
|
2788
|
+
h = Number(options.height);
|
|
2428
2789
|
} else if (hasW) {
|
|
2429
|
-
w = options.width;
|
|
2430
|
-
h = w / aspect;
|
|
2790
|
+
w = Number(options.width);
|
|
2791
|
+
h = w / (aspect || 1);
|
|
2431
2792
|
} else if (hasH) {
|
|
2432
|
-
h = options.height;
|
|
2433
|
-
w = h * aspect;
|
|
2793
|
+
h = Number(options.height);
|
|
2794
|
+
w = h * (aspect || 1);
|
|
2434
2795
|
}
|
|
2435
2796
|
}
|
|
2436
|
-
w = Math.max(1, Math.ceil(w));
|
|
2437
|
-
h = Math.max(1, Math.ceil(h));
|
|
2797
|
+
w = Math.max(1, Math.ceil(Number.isFinite(w) ? w : 1));
|
|
2798
|
+
h = Math.max(1, Math.ceil(Number.isFinite(h) ? h : 1));
|
|
2438
2799
|
const csEl = getComputedStyle(element);
|
|
2439
2800
|
const baseTransform = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
|
|
2440
2801
|
const ind = readIndividualTransforms(element);
|
|
@@ -2505,7 +2866,9 @@ async function captureDOM(element, options) {
|
|
|
2505
2866
|
fo.appendChild(container);
|
|
2506
2867
|
const serializer = new XMLSerializer();
|
|
2507
2868
|
const foString = serializer.serializeToString(fo);
|
|
2508
|
-
const
|
|
2869
|
+
const finalW = !hasScale && (hasW || hasH) ? w : outW;
|
|
2870
|
+
const finalH = !hasScale && (hasH || hasW) ? h : outH;
|
|
2871
|
+
const svgHeader = `<svg xmlns="${svgNS}" width="${finalW}" height="${finalH}" viewBox="0 0 ${outW} ${outH}">`;
|
|
2509
2872
|
const svgFooter = "</svg>";
|
|
2510
2873
|
svgString = svgHeader + foString + svgFooter;
|
|
2511
2874
|
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
@@ -2516,17 +2879,6 @@ async function captureDOM(element, options) {
|
|
|
2516
2879
|
if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
|
|
2517
2880
|
return dataURL;
|
|
2518
2881
|
}
|
|
2519
|
-
function getNaturalBorderBoxSize(el) {
|
|
2520
|
-
const cs = getComputedStyle(el);
|
|
2521
|
-
const px = (v) => parseFloat(v) || 0;
|
|
2522
|
-
let w = px(cs.width);
|
|
2523
|
-
let h = px(cs.height);
|
|
2524
|
-
if (cs.boxSizing === "content-box") {
|
|
2525
|
-
w += px(cs.paddingLeft) + px(cs.paddingRight) + px(cs.borderLeftWidth) + px(cs.borderRightWidth);
|
|
2526
|
-
h += px(cs.paddingTop) + px(cs.paddingBottom) + px(cs.borderTopWidth) + px(cs.borderBottomWidth);
|
|
2527
|
-
}
|
|
2528
|
-
return { width: w, height: h };
|
|
2529
|
-
}
|
|
2530
2882
|
function matrixFromComputed(el) {
|
|
2531
2883
|
const tr = getComputedStyle(el).transform;
|
|
2532
2884
|
if (!tr || tr === "none") return new DOMMatrix();
|
|
@@ -2558,24 +2910,33 @@ function bboxFromMatrix(w, h, M2D) {
|
|
|
2558
2910
|
}
|
|
2559
2911
|
function readIndividualTransforms(el) {
|
|
2560
2912
|
const out = { rotate: "0deg", scale: null, translate: null };
|
|
2561
|
-
const map = el.computedStyleMap
|
|
2913
|
+
const map = typeof el.computedStyleMap === "function" ? el.computedStyleMap() : null;
|
|
2562
2914
|
if (map) {
|
|
2563
|
-
const
|
|
2915
|
+
const safeGet = (prop) => {
|
|
2916
|
+
try {
|
|
2917
|
+
if (typeof map.has === "function" && !map.has(prop)) return null;
|
|
2918
|
+
if (typeof map.get !== "function") return null;
|
|
2919
|
+
return map.get(prop);
|
|
2920
|
+
} catch {
|
|
2921
|
+
return null;
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2924
|
+
const rot = safeGet("rotate");
|
|
2564
2925
|
if (rot) {
|
|
2565
2926
|
const ang = rot.angle ?? rot;
|
|
2566
|
-
if (ang && "unit" in ang) {
|
|
2927
|
+
if (ang && typeof ang === "object" && "unit" in ang) {
|
|
2567
2928
|
out.rotate = ang.unit === "rad" ? ang.value * 180 / Math.PI + "deg" : ang.value + ang.unit;
|
|
2568
2929
|
} else {
|
|
2569
2930
|
out.rotate = String(rot);
|
|
2570
2931
|
}
|
|
2571
2932
|
}
|
|
2572
|
-
const sc =
|
|
2933
|
+
const sc = safeGet("scale");
|
|
2573
2934
|
if (sc && sc.length) {
|
|
2574
2935
|
const sx = sc[0]?.value ?? 1;
|
|
2575
2936
|
const sy = sc[1]?.value ?? sx;
|
|
2576
2937
|
out.scale = `${sx} ${sy}`;
|
|
2577
2938
|
}
|
|
2578
|
-
const tr =
|
|
2939
|
+
const tr = safeGet("translate");
|
|
2579
2940
|
if (tr && tr.length) {
|
|
2580
2941
|
const tx = tr[0]?.value ?? 0;
|
|
2581
2942
|
const ty = tr[1]?.value ?? 0;
|
|
@@ -2660,7 +3021,9 @@ function createContext(options = {}) {
|
|
|
2660
3021
|
scale: options.scale ?? 1,
|
|
2661
3022
|
// DOM filters
|
|
2662
3023
|
exclude: options.exclude ?? [],
|
|
3024
|
+
excludeMode: options.excludeMode ?? "hide",
|
|
2663
3025
|
filter: options.filter ?? null,
|
|
3026
|
+
filterMode: options.filterMode ?? "hide",
|
|
2664
3027
|
placeholders: options.placeholders !== false,
|
|
2665
3028
|
// default true
|
|
2666
3029
|
// Fuentes
|
|
@@ -2668,7 +3031,7 @@ function createContext(options = {}) {
|
|
|
2668
3031
|
iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
|
|
2669
3032
|
localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
|
|
2670
3033
|
excludeFonts: options.excludeFonts ?? void 0,
|
|
2671
|
-
|
|
3034
|
+
fallbackURL: options.fallbackURL ?? void 0,
|
|
2672
3035
|
/** Cache policy (disabled|full|auto|soft). Default: "soft" */
|
|
2673
3036
|
/** @type {CachePolicy} */
|
|
2674
3037
|
cache: cachePolicy,
|
|
@@ -2703,24 +3066,12 @@ async function toImg(url, options) {
|
|
|
2703
3066
|
// src/exporters/toCanvas.js
|
|
2704
3067
|
async function toCanvas(url, options) {
|
|
2705
3068
|
const { scale, dpr } = options;
|
|
2706
|
-
const safari = isSafari();
|
|
2707
3069
|
const img = new Image();
|
|
2708
3070
|
img.crossOrigin = "anonymous";
|
|
2709
3071
|
img.loading = "eager";
|
|
2710
3072
|
img.decoding = "sync";
|
|
2711
3073
|
img.src = url;
|
|
2712
|
-
let tempSlot = null;
|
|
2713
|
-
if (safari) {
|
|
2714
|
-
tempSlot = document.createElement("div");
|
|
2715
|
-
tempSlot.setAttribute("aria-hidden", "true");
|
|
2716
|
-
tempSlot.style.cssText = "position:absolute;left:-99999px;top:0;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;contain:size layout style;";
|
|
2717
|
-
document.body.appendChild(tempSlot);
|
|
2718
|
-
tempSlot.appendChild(img);
|
|
2719
|
-
}
|
|
2720
3074
|
await img.decode();
|
|
2721
|
-
if (safari) {
|
|
2722
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
2723
|
-
}
|
|
2724
3075
|
const width = img.naturalWidth * scale;
|
|
2725
3076
|
const height = img.naturalHeight * scale;
|
|
2726
3077
|
const canvas = document.createElement("canvas");
|
|
@@ -2731,12 +3082,6 @@ async function toCanvas(url, options) {
|
|
|
2731
3082
|
const ctx = canvas.getContext("2d");
|
|
2732
3083
|
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
2733
3084
|
ctx.drawImage(img, 0, 0, width, height);
|
|
2734
|
-
if (tempSlot && tempSlot.parentNode) {
|
|
2735
|
-
try {
|
|
2736
|
-
tempSlot.parentNode.removeChild(tempSlot);
|
|
2737
|
-
} catch {
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
3085
|
return canvas;
|
|
2741
3086
|
}
|
|
2742
3087
|
|
|
@@ -2772,6 +3117,7 @@ async function rasterize(url, options) {
|
|
|
2772
3117
|
|
|
2773
3118
|
// src/exporters/download.js
|
|
2774
3119
|
async function download(url, options) {
|
|
3120
|
+
options.dpr = 1;
|
|
2775
3121
|
if (options.format === "svg") {
|
|
2776
3122
|
const blob = await toBlob(url, { ...options, type: "svg" });
|
|
2777
3123
|
const objectURL = URL.createObjectURL(blob);
|
|
@@ -2792,8 +3138,19 @@ async function download(url, options) {
|
|
|
2792
3138
|
|
|
2793
3139
|
// src/api/snapdom.js
|
|
2794
3140
|
var INTERNAL_TOKEN = Symbol("snapdom.internal");
|
|
3141
|
+
var _safariWarmup = false;
|
|
2795
3142
|
async function snapdom(element, userOptions) {
|
|
2796
3143
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
3144
|
+
if (isSafari()) {
|
|
3145
|
+
for (let i = 0; i < 3; i++) {
|
|
3146
|
+
try {
|
|
3147
|
+
await safariWarmup(element, userOptions);
|
|
3148
|
+
console.log("Iteraci\xF3n n\xFAmero:", i);
|
|
3149
|
+
_safariWarmup = false;
|
|
3150
|
+
} catch (error) {
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
2797
3154
|
const context = createContext(userOptions);
|
|
2798
3155
|
if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
|
|
2799
3156
|
if (!context.snap) {
|
|
@@ -2837,24 +3194,65 @@ snapdom.toPng = (el, options) => snapdom(el, { ...options, format: "png" }).then
|
|
|
2837
3194
|
snapdom.toJpg = (el, options) => snapdom(el, { ...options, format: "jpeg" }).then((result) => result.toJpg());
|
|
2838
3195
|
snapdom.toWebp = (el, options) => snapdom(el, { ...options, format: "webp" }).then((result) => result.toWebp());
|
|
2839
3196
|
snapdom.download = (el, options) => snapdom(el, options).then((result) => result.download());
|
|
3197
|
+
async function safariWarmup(element, baseOptions) {
|
|
3198
|
+
if (_safariWarmup) return;
|
|
3199
|
+
const preflight = {
|
|
3200
|
+
...baseOptions,
|
|
3201
|
+
fast: true,
|
|
3202
|
+
embedFonts: true,
|
|
3203
|
+
scale: 0.2
|
|
3204
|
+
};
|
|
3205
|
+
let url;
|
|
3206
|
+
try {
|
|
3207
|
+
url = await captureDOM(element, preflight);
|
|
3208
|
+
} catch {
|
|
3209
|
+
return;
|
|
3210
|
+
}
|
|
3211
|
+
await new Promise((resolve) => {
|
|
3212
|
+
const img = new Image();
|
|
3213
|
+
img.decoding = "sync";
|
|
3214
|
+
img.loading = "eager";
|
|
3215
|
+
img.style.position = "fixed";
|
|
3216
|
+
img.style.left = 0;
|
|
3217
|
+
img.style.top = 0;
|
|
3218
|
+
img.style.width = "10px";
|
|
3219
|
+
img.style.height = "10px";
|
|
3220
|
+
img.style.opacity = "0.01";
|
|
3221
|
+
img.style.transform = "translateZ(10px)";
|
|
3222
|
+
img.style.willChange = "transform,opacity;";
|
|
3223
|
+
img.src = url;
|
|
3224
|
+
const cleanup = async () => {
|
|
3225
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
3226
|
+
if (img && img.parentNode) img.parentNode.removeChild(img);
|
|
3227
|
+
_safariWarmup = true;
|
|
3228
|
+
resolve();
|
|
3229
|
+
};
|
|
3230
|
+
document.body.appendChild(img);
|
|
3231
|
+
cleanup();
|
|
3232
|
+
});
|
|
3233
|
+
}
|
|
2840
3234
|
|
|
2841
3235
|
// src/api/preCache.js
|
|
2842
3236
|
async function preCache(root = document, options = {}) {
|
|
2843
3237
|
const {
|
|
2844
3238
|
embedFonts = true,
|
|
2845
|
-
cacheOpt = "full",
|
|
2846
3239
|
useProxy = ""
|
|
2847
3240
|
} = options;
|
|
2848
|
-
|
|
3241
|
+
const cacheMode = options.cache ?? options.cacheOpt ?? "full";
|
|
3242
|
+
applyCachePolicy(cacheMode);
|
|
2849
3243
|
try {
|
|
2850
|
-
await document.fonts
|
|
3244
|
+
await document.fonts?.ready;
|
|
3245
|
+
} catch {
|
|
3246
|
+
}
|
|
3247
|
+
try {
|
|
3248
|
+
precacheCommonTags();
|
|
2851
3249
|
} catch {
|
|
2852
3250
|
}
|
|
2853
|
-
precacheCommonTags();
|
|
2854
3251
|
cache.session = cache.session || {};
|
|
2855
3252
|
if (!cache.session.styleCache) {
|
|
2856
3253
|
cache.session.styleCache = /* @__PURE__ */ new WeakMap();
|
|
2857
3254
|
}
|
|
3255
|
+
cache.image = cache.image || /* @__PURE__ */ new Map();
|
|
2858
3256
|
try {
|
|
2859
3257
|
await inlineBackgroundImages(
|
|
2860
3258
|
root,
|
|
@@ -2875,11 +3273,14 @@ async function preCache(root = document, options = {}) {
|
|
|
2875
3273
|
}
|
|
2876
3274
|
const promises = [];
|
|
2877
3275
|
for (const img of imgEls) {
|
|
2878
|
-
const src = img?.src;
|
|
3276
|
+
const src = img?.currentSrc || img?.src;
|
|
2879
3277
|
if (!src) continue;
|
|
2880
3278
|
if (!cache.image.has(src)) {
|
|
2881
|
-
const p = Promise.resolve().then(
|
|
2882
|
-
|
|
3279
|
+
const p = Promise.resolve().then(async () => {
|
|
3280
|
+
const res = await snapFetch(src, { as: "dataURL", useProxy });
|
|
3281
|
+
if (res?.ok && typeof res.data === "string") {
|
|
3282
|
+
cache.image.set(src, res.data);
|
|
3283
|
+
}
|
|
2883
3284
|
}).catch(() => {
|
|
2884
3285
|
});
|
|
2885
3286
|
promises.push(p);
|
|
@@ -2906,7 +3307,8 @@ async function preCache(root = document, options = {}) {
|
|
|
2906
3307
|
try {
|
|
2907
3308
|
const required = collectUsedFontVariants(root);
|
|
2908
3309
|
const usedCodepoints = collectUsedCodepoints(root);
|
|
2909
|
-
|
|
3310
|
+
const safari = typeof isSafari === "function" ? isSafari() : !!isSafari;
|
|
3311
|
+
if (safari) {
|
|
2910
3312
|
const families = new Set(
|
|
2911
3313
|
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2912
3314
|
);
|