@zumer/snapdom 1.9.9 → 1.9.10-dev.0
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 +123 -84
- package/dist/snapdom.js +1511 -805
- package/dist/snapdom.min.js +5 -3
- package/dist/snapdom.min.mjs +5 -3
- package/dist/snapdom.mjs +1484 -759
- package/package.json +1 -1
- package/types/snapdom.d.ts +171 -60
package/dist/snapdom.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.9.
|
|
4
|
+
* v.1.9.10-dev.0
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -26,18 +26,6 @@
|
|
|
26
26
|
return a;
|
|
27
27
|
};
|
|
28
28
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
29
|
-
var __objRest = (source, exclude) => {
|
|
30
|
-
var target = {};
|
|
31
|
-
for (var prop in source)
|
|
32
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
33
|
-
target[prop] = source[prop];
|
|
34
|
-
if (source != null && __getOwnPropSymbols)
|
|
35
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
36
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
37
|
-
target[prop] = source[prop];
|
|
38
|
-
}
|
|
39
|
-
return target;
|
|
40
|
-
};
|
|
41
29
|
var __async = (__this, __arguments, generator) => {
|
|
42
30
|
return new Promise((resolve, reject) => {
|
|
43
31
|
var fulfilled = (value) => {
|
|
@@ -68,15 +56,314 @@
|
|
|
68
56
|
baseStyle: /* @__PURE__ */ new Map(),
|
|
69
57
|
computedStyle: /* @__PURE__ */ new WeakMap(),
|
|
70
58
|
font: /* @__PURE__ */ new Set(),
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
session: {
|
|
60
|
+
styleMap: /* @__PURE__ */ new Map(),
|
|
61
|
+
styleCache: /* @__PURE__ */ new WeakMap(),
|
|
62
|
+
nodeMap: /* @__PURE__ */ new Map()
|
|
63
|
+
}
|
|
74
64
|
};
|
|
75
|
-
function
|
|
76
|
-
|
|
65
|
+
function applyCachePolicy(policy = "soft") {
|
|
66
|
+
switch (policy) {
|
|
67
|
+
case "auto": {
|
|
68
|
+
cache.session.styleMap = /* @__PURE__ */ new Map();
|
|
69
|
+
cache.session.nodeMap = /* @__PURE__ */ new Map();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
case "soft": {
|
|
73
|
+
cache.session.styleMap = /* @__PURE__ */ new Map();
|
|
74
|
+
cache.session.nodeMap = /* @__PURE__ */ new Map();
|
|
75
|
+
cache.session.styleCache = /* @__PURE__ */ new WeakMap();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
case "full": {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
case "disabled": {
|
|
82
|
+
cache.session.styleMap = /* @__PURE__ */ new Map();
|
|
83
|
+
cache.session.nodeMap = /* @__PURE__ */ new Map();
|
|
84
|
+
cache.session.styleCache = /* @__PURE__ */ new WeakMap();
|
|
85
|
+
cache.image.clear();
|
|
86
|
+
cache.background.clear();
|
|
87
|
+
cache.resource.clear();
|
|
88
|
+
cache.font.clear();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
default: {
|
|
92
|
+
cache.session.styleMap = /* @__PURE__ */ new Map();
|
|
93
|
+
cache.session.nodeMap = /* @__PURE__ */ new Map();
|
|
94
|
+
cache.session.styleCache = /* @__PURE__ */ new WeakMap();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/utils/helpers.js
|
|
101
|
+
function extractURL(value) {
|
|
102
|
+
const match = value.match(/url\((['"]?)(.*?)(\1)\)/);
|
|
103
|
+
if (!match) return null;
|
|
104
|
+
const url = match[2].trim();
|
|
105
|
+
if (url.startsWith("#")) return null;
|
|
106
|
+
return url;
|
|
107
|
+
}
|
|
108
|
+
function stripTranslate(transform) {
|
|
109
|
+
if (!transform || transform === "none") return "";
|
|
110
|
+
let cleaned = transform.replace(/translate[XY]?\([^)]*\)/g, "");
|
|
111
|
+
cleaned = cleaned.replace(/matrix\(([^)]+)\)/g, (_, values) => {
|
|
112
|
+
const parts = values.split(",").map((s) => s.trim());
|
|
113
|
+
if (parts.length !== 6) return `matrix(${values})`;
|
|
114
|
+
parts[4] = "0";
|
|
115
|
+
parts[5] = "0";
|
|
116
|
+
return `matrix(${parts.join(", ")})`;
|
|
117
|
+
});
|
|
118
|
+
cleaned = cleaned.replace(/matrix3d\(([^)]+)\)/g, (_, values) => {
|
|
119
|
+
const parts = values.split(",").map((s) => s.trim());
|
|
120
|
+
if (parts.length !== 16) return `matrix3d(${values})`;
|
|
121
|
+
parts[12] = "0";
|
|
122
|
+
parts[13] = "0";
|
|
123
|
+
return `matrix3d(${parts.join(", ")})`;
|
|
124
|
+
});
|
|
125
|
+
return cleaned.trim().replace(/\s{2,}/g, " ");
|
|
126
|
+
}
|
|
127
|
+
function safeEncodeURI(uri) {
|
|
128
|
+
if (/%[0-9A-Fa-f]{2}/.test(uri)) return uri;
|
|
129
|
+
try {
|
|
130
|
+
return encodeURI(uri);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
return uri;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/modules/snapFetch.js
|
|
137
|
+
function createSnapLogger(prefix = "[snapDOM]", { ttlMs = 5 * 6e4, maxEntries = 12 } = {}) {
|
|
138
|
+
const seen = /* @__PURE__ */ new Map();
|
|
139
|
+
let emitted = 0;
|
|
140
|
+
function log(level, key, msg) {
|
|
141
|
+
if (emitted >= maxEntries) return;
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const until = seen.get(key) || 0;
|
|
144
|
+
if (until > now) return;
|
|
145
|
+
seen.set(key, now + ttlMs);
|
|
146
|
+
emitted++;
|
|
147
|
+
if (level === "warn" && console && console.warn) console.warn(`${prefix} ${msg}`);
|
|
148
|
+
else if (console && console.error) console.error(`${prefix} ${msg}`);
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
warnOnce(key, msg) {
|
|
152
|
+
log("warn", key, msg);
|
|
153
|
+
},
|
|
154
|
+
errorOnce(key, msg) {
|
|
155
|
+
log("error", key, msg);
|
|
156
|
+
},
|
|
157
|
+
reset() {
|
|
158
|
+
seen.clear();
|
|
159
|
+
emitted = 0;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
var snapLogger = createSnapLogger("[snapDOM]", { ttlMs: 3 * 6e4, maxEntries: 10 });
|
|
164
|
+
var _inflight = /* @__PURE__ */ new Map();
|
|
165
|
+
var _errorCache = /* @__PURE__ */ new Map();
|
|
166
|
+
function isSpecialURL(url) {
|
|
167
|
+
return /^data:|^blob:|^about:blank$/.test(url);
|
|
168
|
+
}
|
|
169
|
+
function shouldProxy(url, useProxy) {
|
|
170
|
+
if (!useProxy || isSpecialURL(url)) return false;
|
|
171
|
+
try {
|
|
172
|
+
const base = typeof location !== "undefined" && location.href ? location.href : "http://localhost/";
|
|
173
|
+
const u = new URL(url, base);
|
|
174
|
+
return typeof location !== "undefined" ? u.origin !== location.origin : true;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
return !!useProxy;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function applyProxy(url, useProxy) {
|
|
180
|
+
if (!useProxy) return url;
|
|
181
|
+
if (useProxy.includes("{url}")) return useProxy.replace("{url}", encodeURIComponent(url));
|
|
182
|
+
const sep = useProxy.endsWith("?") ? "" : useProxy.includes("?") ? "&" : "?";
|
|
183
|
+
return `${useProxy}${sep}url=${encodeURIComponent(url)}`;
|
|
184
|
+
}
|
|
185
|
+
function blobToDataURL(blob) {
|
|
186
|
+
return new Promise((res, rej) => {
|
|
187
|
+
const fr = new FileReader();
|
|
188
|
+
fr.onload = () => res(String(fr.result || ""));
|
|
189
|
+
fr.onerror = () => rej(new Error("read_failed"));
|
|
190
|
+
fr.readAsDataURL(blob);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function makeKey(url, o) {
|
|
194
|
+
var _a, _b;
|
|
195
|
+
return [
|
|
196
|
+
o.as || "blob",
|
|
197
|
+
(_a = o.timeout) != null ? _a : 3e3,
|
|
198
|
+
o.useProxy || "",
|
|
199
|
+
(_b = o.errorTTL) != null ? _b : 8e3,
|
|
200
|
+
url
|
|
201
|
+
].join("|");
|
|
202
|
+
}
|
|
203
|
+
function snapFetch(_0) {
|
|
204
|
+
return __async(this, arguments, function* (url, options = {}) {
|
|
205
|
+
var _a, _b, _c;
|
|
206
|
+
const as = (_a = options.as) != null ? _a : "blob";
|
|
207
|
+
const timeout = (_b = options.timeout) != null ? _b : 3e3;
|
|
208
|
+
const useProxy = options.useProxy || "";
|
|
209
|
+
const errorTTL = (_c = options.errorTTL) != null ? _c : 8e3;
|
|
210
|
+
const headers = options.headers || {};
|
|
211
|
+
const silent = !!options.silent;
|
|
212
|
+
const key = makeKey(url, { as, timeout, useProxy, errorTTL });
|
|
213
|
+
const e = _errorCache.get(key);
|
|
214
|
+
if (e && e.until > Date.now()) {
|
|
215
|
+
return __spreadProps(__spreadValues({}, e.result), { fromCache: true });
|
|
216
|
+
} else if (e) {
|
|
217
|
+
_errorCache.delete(key);
|
|
218
|
+
}
|
|
219
|
+
const inflight = _inflight.get(key);
|
|
220
|
+
if (inflight) return inflight;
|
|
221
|
+
const finalURL = shouldProxy(url, useProxy) ? applyProxy(url, useProxy) : url;
|
|
222
|
+
let cred = options.credentials;
|
|
223
|
+
if (!cred) {
|
|
224
|
+
try {
|
|
225
|
+
const base = typeof location !== "undefined" && location.href ? location.href : "http://localhost/";
|
|
226
|
+
const u = new URL(url, base);
|
|
227
|
+
const sameOrigin = typeof location !== "undefined" && u.origin === location.origin;
|
|
228
|
+
cred = sameOrigin ? "include" : "omit";
|
|
229
|
+
} catch (e2) {
|
|
230
|
+
cred = "omit";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const ctrl = new AbortController();
|
|
234
|
+
const timer = setTimeout(() => ctrl.abort("timeout"), timeout);
|
|
235
|
+
const p = (() => __async(null, null, function* () {
|
|
236
|
+
var _a2, _b2;
|
|
237
|
+
try {
|
|
238
|
+
const resp = yield fetch(finalURL, { signal: ctrl.signal, credentials: cred, headers });
|
|
239
|
+
if (!resp.ok) {
|
|
240
|
+
const result = { ok: false, data: null, status: resp.status, url: finalURL, fromCache: false, reason: "http_error" };
|
|
241
|
+
_errorCache.set(key, { until: Date.now() + errorTTL, result });
|
|
242
|
+
if (!silent) {
|
|
243
|
+
const short = `${resp.status} ${resp.statusText || ""}`.trim();
|
|
244
|
+
snapLogger.warnOnce(
|
|
245
|
+
`http:${resp.status}:${as}:${new URL(url, (_a2 = location == null ? void 0 : location.href) != null ? _a2 : "http://localhost/").origin}`,
|
|
246
|
+
`HTTP error ${short} while fetching ${as} ${url}`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
options.onError && options.onError(result);
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
if (as === "text") {
|
|
253
|
+
const text = yield resp.text();
|
|
254
|
+
return { ok: true, data: text, status: resp.status, url: finalURL, fromCache: false };
|
|
255
|
+
}
|
|
256
|
+
const blob = yield resp.blob();
|
|
257
|
+
const mime = blob.type || resp.headers.get("content-type") || "";
|
|
258
|
+
if (as === "dataURL") {
|
|
259
|
+
const dataURL = yield blobToDataURL(blob);
|
|
260
|
+
return { ok: true, data: dataURL, status: resp.status, url: finalURL, fromCache: false, mime };
|
|
261
|
+
}
|
|
262
|
+
return { ok: true, data: blob, status: resp.status, url: finalURL, fromCache: false, mime };
|
|
263
|
+
} catch (err) {
|
|
264
|
+
const reason = err && typeof err === "object" && "name" in err && err.name === "AbortError" ? String(err.message || "").includes("timeout") ? "timeout" : "abort" : "network";
|
|
265
|
+
const result = { ok: false, data: null, status: 0, url: finalURL, fromCache: false, reason };
|
|
266
|
+
_errorCache.set(key, { until: Date.now() + errorTTL, result });
|
|
267
|
+
if (!silent) {
|
|
268
|
+
const k = `${reason}:${as}:${new URL(url, (_b2 = location == null ? void 0 : location.href) != null ? _b2 : "http://localhost/").origin}`;
|
|
269
|
+
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`;
|
|
270
|
+
snapLogger.errorOnce(k, tips);
|
|
271
|
+
}
|
|
272
|
+
options.onError && options.onError(result);
|
|
273
|
+
return result;
|
|
274
|
+
} finally {
|
|
275
|
+
clearTimeout(timer);
|
|
276
|
+
_inflight.delete(key);
|
|
277
|
+
}
|
|
278
|
+
}))();
|
|
279
|
+
_inflight.set(key, p);
|
|
280
|
+
return p;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/utils/image.js
|
|
285
|
+
function createBackground(baseCanvas, backgroundColor) {
|
|
286
|
+
if (!backgroundColor || !baseCanvas.width || !baseCanvas.height) {
|
|
287
|
+
return baseCanvas;
|
|
288
|
+
}
|
|
289
|
+
const temp = document.createElement("canvas");
|
|
290
|
+
temp.width = baseCanvas.width;
|
|
291
|
+
temp.height = baseCanvas.height;
|
|
292
|
+
const ctx = temp.getContext("2d");
|
|
293
|
+
ctx.fillStyle = backgroundColor;
|
|
294
|
+
ctx.fillRect(0, 0, temp.width, temp.height);
|
|
295
|
+
ctx.drawImage(baseCanvas, 0, 0);
|
|
296
|
+
return temp;
|
|
297
|
+
}
|
|
298
|
+
function inlineSingleBackgroundEntry(_0) {
|
|
299
|
+
return __async(this, arguments, function* (entry, options = {}) {
|
|
300
|
+
const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry);
|
|
301
|
+
if (isGradient || entry.trim() === "none") {
|
|
302
|
+
return entry;
|
|
303
|
+
}
|
|
304
|
+
const rawUrl = extractURL(entry);
|
|
305
|
+
if (!rawUrl) {
|
|
306
|
+
return entry;
|
|
307
|
+
}
|
|
308
|
+
const encodedUrl = safeEncodeURI(rawUrl);
|
|
309
|
+
if (cache.background.has(encodedUrl)) {
|
|
310
|
+
const dataUrl = cache.background.get(encodedUrl);
|
|
311
|
+
return dataUrl ? `url("${dataUrl}")` : "none";
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const dataUrl = yield snapFetch(encodedUrl, { as: "dataURL", useProxy: options.useProxy });
|
|
315
|
+
if (dataUrl.ok) {
|
|
316
|
+
cache.background.set(encodedUrl, dataUrl.data);
|
|
317
|
+
return `url("${dataUrl.data}")`;
|
|
318
|
+
}
|
|
319
|
+
cache.background.set(encodedUrl, null);
|
|
320
|
+
return "none";
|
|
321
|
+
} catch (e) {
|
|
322
|
+
cache.background.set(encodedUrl, null);
|
|
323
|
+
return "none";
|
|
324
|
+
}
|
|
325
|
+
});
|
|
77
326
|
}
|
|
78
327
|
|
|
79
|
-
// src/utils/
|
|
328
|
+
// src/utils/css.js
|
|
329
|
+
var NO_CAPTURE_TAGS = /* @__PURE__ */ new Set([
|
|
330
|
+
"meta",
|
|
331
|
+
"script",
|
|
332
|
+
"noscript",
|
|
333
|
+
"title",
|
|
334
|
+
"link",
|
|
335
|
+
"template"
|
|
336
|
+
]);
|
|
337
|
+
var NO_DEFAULTS_TAGS = /* @__PURE__ */ new Set([
|
|
338
|
+
// non-painting / head stuff
|
|
339
|
+
"meta",
|
|
340
|
+
"link",
|
|
341
|
+
"style",
|
|
342
|
+
"title",
|
|
343
|
+
"noscript",
|
|
344
|
+
"script",
|
|
345
|
+
"template",
|
|
346
|
+
// SVG whole namespace (safe for LeaderLine/presentation attrs)
|
|
347
|
+
"svg",
|
|
348
|
+
"g",
|
|
349
|
+
"defs",
|
|
350
|
+
"use",
|
|
351
|
+
"marker",
|
|
352
|
+
"mask",
|
|
353
|
+
"clipPath",
|
|
354
|
+
"pattern",
|
|
355
|
+
"path",
|
|
356
|
+
"polygon",
|
|
357
|
+
"polyline",
|
|
358
|
+
"line",
|
|
359
|
+
"circle",
|
|
360
|
+
"ellipse",
|
|
361
|
+
"rect",
|
|
362
|
+
"filter",
|
|
363
|
+
"lineargradient",
|
|
364
|
+
"radialgradient",
|
|
365
|
+
"stop"
|
|
366
|
+
]);
|
|
80
367
|
var commonTags = [
|
|
81
368
|
"div",
|
|
82
369
|
"span",
|
|
@@ -103,12 +390,6 @@
|
|
|
103
390
|
"h4",
|
|
104
391
|
"h5",
|
|
105
392
|
"h6",
|
|
106
|
-
"svg",
|
|
107
|
-
"path",
|
|
108
|
-
"circle",
|
|
109
|
-
"rect",
|
|
110
|
-
"line",
|
|
111
|
-
"g",
|
|
112
393
|
"table",
|
|
113
394
|
"thead",
|
|
114
395
|
"tbody",
|
|
@@ -118,28 +399,33 @@
|
|
|
118
399
|
];
|
|
119
400
|
function precacheCommonTags() {
|
|
120
401
|
for (let tag of commonTags) {
|
|
121
|
-
|
|
402
|
+
const t = String(tag).toLowerCase();
|
|
403
|
+
if (NO_CAPTURE_TAGS.has(t)) continue;
|
|
404
|
+
if (NO_DEFAULTS_TAGS.has(t)) continue;
|
|
405
|
+
getDefaultStyleForTag(t);
|
|
122
406
|
}
|
|
123
407
|
}
|
|
124
408
|
function getDefaultStyleForTag(tagName) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template", "defs", "symbol", "title", "metadata", "desc"]);
|
|
129
|
-
if (skipTags.has(tagName)) {
|
|
409
|
+
tagName = String(tagName).toLowerCase();
|
|
410
|
+
if (NO_DEFAULTS_TAGS.has(tagName)) {
|
|
130
411
|
const empty = {};
|
|
131
412
|
cache.defaultStyle.set(tagName, empty);
|
|
132
413
|
return empty;
|
|
133
414
|
}
|
|
415
|
+
if (cache.defaultStyle.has(tagName)) {
|
|
416
|
+
return cache.defaultStyle.get(tagName);
|
|
417
|
+
}
|
|
134
418
|
let sandbox = document.getElementById("snapdom-sandbox");
|
|
135
419
|
if (!sandbox) {
|
|
136
420
|
sandbox = document.createElement("div");
|
|
137
421
|
sandbox.id = "snapdom-sandbox";
|
|
422
|
+
sandbox.setAttribute("data-snapdom-sandbox", "true");
|
|
423
|
+
sandbox.setAttribute("aria-hidden", "true");
|
|
138
424
|
sandbox.style.position = "absolute";
|
|
139
425
|
sandbox.style.left = "-9999px";
|
|
140
426
|
sandbox.style.top = "-9999px";
|
|
141
|
-
sandbox.style.width = "
|
|
142
|
-
sandbox.style.height = "
|
|
427
|
+
sandbox.style.width = "0px";
|
|
428
|
+
sandbox.style.height = "0px";
|
|
143
429
|
sandbox.style.overflow = "hidden";
|
|
144
430
|
document.body.appendChild(sandbox);
|
|
145
431
|
}
|
|
@@ -149,32 +435,55 @@
|
|
|
149
435
|
const styles = getComputedStyle(el);
|
|
150
436
|
const defaults = {};
|
|
151
437
|
for (let prop of styles) {
|
|
152
|
-
|
|
438
|
+
if (shouldIgnoreProp(prop)) continue;
|
|
439
|
+
const value = styles.getPropertyValue(prop);
|
|
440
|
+
defaults[prop] = value;
|
|
153
441
|
}
|
|
154
442
|
sandbox.removeChild(el);
|
|
155
443
|
cache.defaultStyle.set(tagName, defaults);
|
|
156
444
|
return defaults;
|
|
157
445
|
}
|
|
158
|
-
var
|
|
159
|
-
|
|
446
|
+
var NO_PAINT_TOKEN = /(?:^|-)(animation|transition)(?:-|$)/i;
|
|
447
|
+
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;
|
|
448
|
+
var NO_PAINT_EXACT = /* @__PURE__ */ new Set([
|
|
449
|
+
// Interaction hints
|
|
450
|
+
"cursor",
|
|
451
|
+
"pointer-events",
|
|
452
|
+
"touch-action",
|
|
453
|
+
"user-select",
|
|
454
|
+
// Printing/speech/reading-mode hints
|
|
455
|
+
"print-color-adjust",
|
|
456
|
+
"speak",
|
|
457
|
+
"reading-flow",
|
|
458
|
+
"reading-order",
|
|
459
|
+
// Anchoring/container/timeline scopes (metadata for layout queries)
|
|
460
|
+
"anchor-name",
|
|
461
|
+
"anchor-scope",
|
|
462
|
+
"container-name",
|
|
463
|
+
"container-type",
|
|
464
|
+
"timeline-scope"
|
|
160
465
|
]);
|
|
161
|
-
function
|
|
466
|
+
function shouldIgnoreProp(prop) {
|
|
467
|
+
const p = String(prop).toLowerCase();
|
|
468
|
+
if (NO_PAINT_EXACT.has(p)) return true;
|
|
469
|
+
if (NO_PAINT_PREFIX.test(p)) return true;
|
|
470
|
+
if (NO_PAINT_TOKEN.test(p)) return true;
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
function getStyleKey(snapshot, tagName) {
|
|
474
|
+
tagName = String(tagName || "").toLowerCase();
|
|
475
|
+
if (NO_DEFAULTS_TAGS.has(tagName)) {
|
|
476
|
+
return "";
|
|
477
|
+
}
|
|
162
478
|
const entries = [];
|
|
163
|
-
const
|
|
479
|
+
const defaults = getDefaultStyleForTag(tagName);
|
|
164
480
|
for (let [prop, value] of Object.entries(snapshot)) {
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
entries.push(`${prop}:${value}`);
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
171
|
-
const defaultValue = defaultStyles[prop];
|
|
172
|
-
if (value && value !== defaultValue) {
|
|
173
|
-
entries.push(`${prop}:${value}`);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
481
|
+
if (shouldIgnoreProp(prop)) continue;
|
|
482
|
+
const def = defaults[prop];
|
|
483
|
+
if (value && value !== def) entries.push(`${prop}:${value}`);
|
|
176
484
|
}
|
|
177
|
-
|
|
485
|
+
entries.sort();
|
|
486
|
+
return entries.join(";");
|
|
178
487
|
}
|
|
179
488
|
function collectUsedTagNames(root) {
|
|
180
489
|
const tagSet = /* @__PURE__ */ new Set();
|
|
@@ -195,6 +504,7 @@
|
|
|
195
504
|
const styles = cache.defaultStyle.get(tagName);
|
|
196
505
|
if (!styles) continue;
|
|
197
506
|
const key = Object.entries(styles).map(([k, v]) => `${k}:${v};`).sort().join("");
|
|
507
|
+
if (!key) continue;
|
|
198
508
|
if (!groups.has(key)) {
|
|
199
509
|
groups.set(key, []);
|
|
200
510
|
}
|
|
@@ -208,45 +518,12 @@
|
|
|
208
518
|
return css;
|
|
209
519
|
}
|
|
210
520
|
function generateCSSClasses(styleMap) {
|
|
211
|
-
const
|
|
521
|
+
const keys = Array.from(new Set(styleMap.values())).filter(Boolean).sort();
|
|
212
522
|
const classMap = /* @__PURE__ */ new Map();
|
|
213
|
-
let
|
|
214
|
-
for (const
|
|
215
|
-
if (!key.trim()) continue;
|
|
216
|
-
classMap.set(key, `c${counter++}`);
|
|
217
|
-
}
|
|
523
|
+
let i = 1;
|
|
524
|
+
for (const k of keys) classMap.set(k, `c${i++}`);
|
|
218
525
|
return classMap;
|
|
219
526
|
}
|
|
220
|
-
|
|
221
|
-
// src/utils/helpers.js
|
|
222
|
-
function inlineSingleBackgroundEntry(_0) {
|
|
223
|
-
return __async(this, arguments, function* (entry, options = {}) {
|
|
224
|
-
const rawUrl = extractURL(entry);
|
|
225
|
-
const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry);
|
|
226
|
-
if (rawUrl) {
|
|
227
|
-
const encodedUrl = safeEncodeURI(rawUrl);
|
|
228
|
-
if (cache.background.has(encodedUrl)) {
|
|
229
|
-
return options.skipInline ? void 0 : `url(${cache.background.get(encodedUrl)})`;
|
|
230
|
-
} else {
|
|
231
|
-
const dataUrl = yield fetchImage(encodedUrl, { useProxy: options.useProxy });
|
|
232
|
-
cache.background.set(encodedUrl, dataUrl);
|
|
233
|
-
return options.skipInline ? void 0 : `url("${dataUrl}")`;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
if (isGradient || entry === "none") {
|
|
237
|
-
return entry;
|
|
238
|
-
}
|
|
239
|
-
return entry;
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
function idle(fn, { fast = false } = {}) {
|
|
243
|
-
if (fast) return fn();
|
|
244
|
-
if ("requestIdleCallback" in window) {
|
|
245
|
-
requestIdleCallback(fn, { timeout: 50 });
|
|
246
|
-
} else {
|
|
247
|
-
setTimeout(fn, 1);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
527
|
function getStyle(el, pseudo = null) {
|
|
251
528
|
if (!(el instanceof Element)) {
|
|
252
529
|
return window.getComputedStyle(el, pseudo);
|
|
@@ -273,196 +550,6 @@
|
|
|
273
550
|
}
|
|
274
551
|
return clean;
|
|
275
552
|
}
|
|
276
|
-
function extractURL(value) {
|
|
277
|
-
const match = value.match(/url\((['"]?)(.*?)(\1)\)/);
|
|
278
|
-
if (!match) return null;
|
|
279
|
-
const url = match[2].trim();
|
|
280
|
-
if (url.startsWith("#")) return null;
|
|
281
|
-
return url;
|
|
282
|
-
}
|
|
283
|
-
function fetchResource(_0) {
|
|
284
|
-
return __async(this, arguments, function* (url, { useProxy = "" } = {}) {
|
|
285
|
-
function doFetch(u) {
|
|
286
|
-
return __async(this, null, function* () {
|
|
287
|
-
const res = yield fetch(u);
|
|
288
|
-
if (!res.ok) throw new Error(`[snapdom] Failed to fetch resource: ${u}`);
|
|
289
|
-
return res;
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
try {
|
|
293
|
-
return yield doFetch(url);
|
|
294
|
-
} catch (e) {
|
|
295
|
-
if (useProxy && typeof useProxy === "string") {
|
|
296
|
-
const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url);
|
|
297
|
-
return doFetch(proxied);
|
|
298
|
-
}
|
|
299
|
-
throw e;
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
var _inflight = /* @__PURE__ */ new Map();
|
|
304
|
-
var _errorCache = /* @__PURE__ */ new Map();
|
|
305
|
-
function fetchImage(src, { timeout = 3e3, useProxy = "", errorTTL = 8e3 } = {}) {
|
|
306
|
-
function getCrossOriginMode(url) {
|
|
307
|
-
try {
|
|
308
|
-
const parsed = new URL(url, window.location.href);
|
|
309
|
-
return parsed.origin === window.location.origin ? "use-credentials" : "anonymous";
|
|
310
|
-
} catch (e) {
|
|
311
|
-
return "anonymous";
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
const ok = (data) => ({ ok: true, data });
|
|
315
|
-
const fail = (e) => ({ ok: false, error: e instanceof Error ? e : new Error(String(e)) });
|
|
316
|
-
function fetchBlobAsDataURLSafe(fetchUrl) {
|
|
317
|
-
try {
|
|
318
|
-
return fetch(fetchUrl, {
|
|
319
|
-
mode: "cors",
|
|
320
|
-
credentials: getCrossOriginMode(fetchUrl) === "use-credentials" ? "include" : "omit"
|
|
321
|
-
}).then((r) => {
|
|
322
|
-
if (!r.ok) return fail(new Error("HTTP " + r.status));
|
|
323
|
-
return r.blob().then((blob) => new Promise((resolve) => {
|
|
324
|
-
const reader = new FileReader();
|
|
325
|
-
reader.onloadend = () => {
|
|
326
|
-
const base64 = reader.result;
|
|
327
|
-
if (typeof base64 !== "string" || !base64.startsWith("data:image/")) {
|
|
328
|
-
resolve(fail(new Error("Invalid image data URL")));
|
|
329
|
-
} else {
|
|
330
|
-
resolve(ok(base64));
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
reader.onerror = () => resolve(fail(new Error("FileReader error")));
|
|
334
|
-
reader.readAsDataURL(blob);
|
|
335
|
-
}));
|
|
336
|
-
}).catch((e) => fail(e));
|
|
337
|
-
} catch (e) {
|
|
338
|
-
return Promise.resolve(fail(e));
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function fetchWithFallbackOnceSafe(url) {
|
|
342
|
-
return fetchBlobAsDataURLSafe(url).then((r) => {
|
|
343
|
-
if (r.ok) return r;
|
|
344
|
-
if (useProxy && typeof useProxy === "string") {
|
|
345
|
-
const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url);
|
|
346
|
-
return fetchBlobAsDataURLSafe(proxied).then((r2) => {
|
|
347
|
-
if (r2.ok) return r2;
|
|
348
|
-
return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"));
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"));
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
const now = Date.now();
|
|
355
|
-
const until = _errorCache.get(src);
|
|
356
|
-
if (until && until > now) {
|
|
357
|
-
const pr = Promise.reject(new Error("[SnapDOM - fetchImage] Recently failed (cooldown)."));
|
|
358
|
-
pr.catch(() => {
|
|
359
|
-
});
|
|
360
|
-
return pr;
|
|
361
|
-
}
|
|
362
|
-
if (_inflight.has(src)) return _inflight.get(src);
|
|
363
|
-
const crossOriginValue = getCrossOriginMode(src);
|
|
364
|
-
if (cache.image.has(src)) return Promise.resolve(cache.image.get(src));
|
|
365
|
-
if (src.startsWith("data:image/")) {
|
|
366
|
-
cache.image.set(src, src);
|
|
367
|
-
return Promise.resolve(src);
|
|
368
|
-
}
|
|
369
|
-
if (/\.svg(\?.*)?$/i.test(src)) {
|
|
370
|
-
const p2 = (() => __async(null, null, function* () {
|
|
371
|
-
const direct = yield (() => __async(null, null, function* () {
|
|
372
|
-
try {
|
|
373
|
-
const res = yield fetch(src, {
|
|
374
|
-
mode: "cors",
|
|
375
|
-
credentials: crossOriginValue === "use-credentials" ? "include" : "omit"
|
|
376
|
-
});
|
|
377
|
-
if (!res.ok) return fail(new Error("HTTP " + res.status));
|
|
378
|
-
const svgText = yield res.text();
|
|
379
|
-
return ok(`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`);
|
|
380
|
-
} catch (e) {
|
|
381
|
-
return fail(e);
|
|
382
|
-
}
|
|
383
|
-
}))();
|
|
384
|
-
if (direct.ok) {
|
|
385
|
-
cache.image.set(src, direct.data);
|
|
386
|
-
return direct.data;
|
|
387
|
-
}
|
|
388
|
-
const via = yield fetchWithFallbackOnceSafe(src);
|
|
389
|
-
if (via.ok) {
|
|
390
|
-
cache.image.set(src, via.data);
|
|
391
|
-
return via.data;
|
|
392
|
-
}
|
|
393
|
-
_errorCache.set(src, now + errorTTL);
|
|
394
|
-
return Promise.reject(via.error);
|
|
395
|
-
}))();
|
|
396
|
-
_inflight.set(src, p2);
|
|
397
|
-
p2.finally(() => _inflight.delete(src));
|
|
398
|
-
p2.catch(() => {
|
|
399
|
-
});
|
|
400
|
-
return p2;
|
|
401
|
-
}
|
|
402
|
-
const p = new Promise((resolve, reject) => {
|
|
403
|
-
let finished = false;
|
|
404
|
-
const img = new Image();
|
|
405
|
-
const finish = (fn) => (arg) => {
|
|
406
|
-
if (finished) return;
|
|
407
|
-
finished = true;
|
|
408
|
-
clearTimeout(timeoutId);
|
|
409
|
-
img.onload = img.onerror = null;
|
|
410
|
-
fn(arg);
|
|
411
|
-
};
|
|
412
|
-
const onSuccess = (d) => {
|
|
413
|
-
cache.image.set(src, d);
|
|
414
|
-
resolve(d);
|
|
415
|
-
};
|
|
416
|
-
const onFinalError = (e) => {
|
|
417
|
-
_errorCache.set(src, Date.now() + errorTTL);
|
|
418
|
-
reject(e);
|
|
419
|
-
};
|
|
420
|
-
const timeoutId = setTimeout(
|
|
421
|
-
finish(() => {
|
|
422
|
-
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
423
|
-
if (r.ok) onSuccess(r.data);
|
|
424
|
-
else onFinalError(new Error("Image load timed out"));
|
|
425
|
-
});
|
|
426
|
-
}),
|
|
427
|
-
timeout
|
|
428
|
-
);
|
|
429
|
-
img.crossOrigin = crossOriginValue;
|
|
430
|
-
img.onload = finish(() => {
|
|
431
|
-
Promise.resolve(img.decode()).then(() => {
|
|
432
|
-
try {
|
|
433
|
-
const canvas = document.createElement("canvas");
|
|
434
|
-
canvas.width = img.naturalWidth || img.width;
|
|
435
|
-
canvas.height = img.naturalHeight || img.height;
|
|
436
|
-
const ctx = canvas.getContext("2d");
|
|
437
|
-
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
438
|
-
onSuccess(canvas.toDataURL("image/png"));
|
|
439
|
-
} catch (e) {
|
|
440
|
-
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
441
|
-
if (r.ok) onSuccess(r.data);
|
|
442
|
-
else onFinalError(r.error);
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
}).catch(() => {
|
|
446
|
-
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
447
|
-
if (r.ok) onSuccess(r.data);
|
|
448
|
-
else onFinalError(r.error);
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
});
|
|
452
|
-
img.onerror = finish(() => {
|
|
453
|
-
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
454
|
-
if (r.ok) onSuccess(r.data);
|
|
455
|
-
else onFinalError(r.error);
|
|
456
|
-
});
|
|
457
|
-
});
|
|
458
|
-
img.src = src;
|
|
459
|
-
});
|
|
460
|
-
_inflight.set(src, p);
|
|
461
|
-
p.finally(() => _inflight.delete(src));
|
|
462
|
-
p.catch(() => {
|
|
463
|
-
});
|
|
464
|
-
return p;
|
|
465
|
-
}
|
|
466
553
|
function snapshotComputedStyle(style) {
|
|
467
554
|
const snap = {};
|
|
468
555
|
for (let prop of style) {
|
|
@@ -470,36 +557,6 @@
|
|
|
470
557
|
}
|
|
471
558
|
return snap;
|
|
472
559
|
}
|
|
473
|
-
function isSafari() {
|
|
474
|
-
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
475
|
-
}
|
|
476
|
-
function stripTranslate(transform) {
|
|
477
|
-
if (!transform || transform === "none") return "";
|
|
478
|
-
let cleaned = transform.replace(/translate[XY]?\([^)]*\)/g, "");
|
|
479
|
-
cleaned = cleaned.replace(/matrix\(([^)]+)\)/g, (_, values) => {
|
|
480
|
-
const parts = values.split(",").map((s) => s.trim());
|
|
481
|
-
if (parts.length !== 6) return `matrix(${values})`;
|
|
482
|
-
parts[4] = "0";
|
|
483
|
-
parts[5] = "0";
|
|
484
|
-
return `matrix(${parts.join(", ")})`;
|
|
485
|
-
});
|
|
486
|
-
cleaned = cleaned.replace(/matrix3d\(([^)]+)\)/g, (_, values) => {
|
|
487
|
-
const parts = values.split(",").map((s) => s.trim());
|
|
488
|
-
if (parts.length !== 16) return `matrix3d(${values})`;
|
|
489
|
-
parts[12] = "0";
|
|
490
|
-
parts[13] = "0";
|
|
491
|
-
return `matrix3d(${parts.join(", ")})`;
|
|
492
|
-
});
|
|
493
|
-
return cleaned.trim().replace(/\s{2,}/g, " ");
|
|
494
|
-
}
|
|
495
|
-
function safeEncodeURI(uri) {
|
|
496
|
-
if (/%[0-9A-Fa-f]{2}/.test(uri)) return uri;
|
|
497
|
-
try {
|
|
498
|
-
return encodeURI(uri);
|
|
499
|
-
} catch (e) {
|
|
500
|
-
return uri;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
560
|
function splitBackgroundImage(bg) {
|
|
504
561
|
const parts = [];
|
|
505
562
|
let depth = 0;
|
|
@@ -517,49 +574,209 @@
|
|
|
517
574
|
return parts;
|
|
518
575
|
}
|
|
519
576
|
|
|
577
|
+
// src/utils/browser.js
|
|
578
|
+
function idle(fn, { fast = false } = {}) {
|
|
579
|
+
if (fast) return fn();
|
|
580
|
+
if ("requestIdleCallback" in window) {
|
|
581
|
+
requestIdleCallback(fn, { timeout: 50 });
|
|
582
|
+
} else {
|
|
583
|
+
setTimeout(fn, 1);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
function isSafari() {
|
|
587
|
+
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
588
|
+
}
|
|
589
|
+
|
|
520
590
|
// src/modules/styles.js
|
|
521
591
|
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
522
592
|
var snapshotKeyCache = /* @__PURE__ */ new Map();
|
|
593
|
+
var __epoch = 0;
|
|
594
|
+
function bumpEpoch() {
|
|
595
|
+
__epoch++;
|
|
596
|
+
}
|
|
597
|
+
var __wired = false;
|
|
598
|
+
function setupInvalidationOnce(root = document.documentElement) {
|
|
599
|
+
var _a, _b;
|
|
600
|
+
if (__wired) return;
|
|
601
|
+
__wired = true;
|
|
602
|
+
try {
|
|
603
|
+
const domObs = new MutationObserver(() => bumpEpoch());
|
|
604
|
+
domObs.observe(root, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
605
|
+
} catch (e) {
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
const headObs = new MutationObserver(() => bumpEpoch());
|
|
609
|
+
headObs.observe(document.head, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
610
|
+
} catch (e) {
|
|
611
|
+
}
|
|
612
|
+
try {
|
|
613
|
+
const f = document.fonts;
|
|
614
|
+
if (f) {
|
|
615
|
+
(_a = f.addEventListener) == null ? void 0 : _a.call(f, "loadingdone", bumpEpoch);
|
|
616
|
+
(_b = f.ready) == null ? void 0 : _b.then(() => bumpEpoch()).catch(() => {
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
} catch (e) {
|
|
620
|
+
}
|
|
621
|
+
}
|
|
523
622
|
function snapshotComputedStyleFull(style) {
|
|
524
|
-
const
|
|
525
|
-
const
|
|
623
|
+
const out = {};
|
|
624
|
+
const vis = style.getPropertyValue("visibility");
|
|
526
625
|
for (let i = 0; i < style.length; i++) {
|
|
527
626
|
const prop = style[i];
|
|
528
627
|
let val = style.getPropertyValue(prop);
|
|
529
628
|
if ((prop === "background-image" || prop === "content") && val.includes("url(") && !val.includes("data:")) {
|
|
530
629
|
val = "none";
|
|
531
630
|
}
|
|
532
|
-
|
|
631
|
+
out[prop] = val;
|
|
533
632
|
}
|
|
534
|
-
if (
|
|
535
|
-
|
|
633
|
+
if (vis === "hidden") out.opacity = "0";
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
636
|
+
var __snapshotSig = /* @__PURE__ */ new WeakMap();
|
|
637
|
+
function styleSignature(snap) {
|
|
638
|
+
let sig = __snapshotSig.get(snap);
|
|
639
|
+
if (sig) return sig;
|
|
640
|
+
const entries = Object.entries(snap).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
641
|
+
sig = entries.map(([k, v]) => `${k}:${v}`).join(";");
|
|
642
|
+
__snapshotSig.set(snap, sig);
|
|
643
|
+
return sig;
|
|
644
|
+
}
|
|
645
|
+
function getSnapshot(el, preStyle = null) {
|
|
646
|
+
const rec = snapshotCache.get(el);
|
|
647
|
+
if (rec && rec.epoch === __epoch) return rec.snapshot;
|
|
648
|
+
const style = preStyle || getComputedStyle(el);
|
|
649
|
+
const snap = snapshotComputedStyleFull(style);
|
|
650
|
+
snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
|
|
651
|
+
return snap;
|
|
652
|
+
}
|
|
653
|
+
function _resolveCtx(sessionOrCtx, opts) {
|
|
654
|
+
if (sessionOrCtx && sessionOrCtx.session && sessionOrCtx.persist) return sessionOrCtx;
|
|
655
|
+
if (sessionOrCtx && (sessionOrCtx.styleMap || sessionOrCtx.styleCache || sessionOrCtx.nodeMap)) {
|
|
656
|
+
return {
|
|
657
|
+
session: sessionOrCtx,
|
|
658
|
+
persist: {
|
|
659
|
+
snapshotKeyCache,
|
|
660
|
+
defaultStyle: cache.defaultStyle,
|
|
661
|
+
baseStyle: cache.baseStyle,
|
|
662
|
+
image: cache.image,
|
|
663
|
+
resource: cache.resource,
|
|
664
|
+
background: cache.background,
|
|
665
|
+
font: cache.font
|
|
666
|
+
},
|
|
667
|
+
options: opts || {}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
session: cache.session,
|
|
672
|
+
persist: {
|
|
673
|
+
snapshotKeyCache,
|
|
674
|
+
defaultStyle: cache.defaultStyle,
|
|
675
|
+
baseStyle: cache.baseStyle,
|
|
676
|
+
image: cache.image,
|
|
677
|
+
resource: cache.resource,
|
|
678
|
+
background: cache.background,
|
|
679
|
+
font: cache.font
|
|
680
|
+
},
|
|
681
|
+
options: sessionOrCtx || opts || {}
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function inlineAllStyles(source, clone, sessionOrCtx, opts) {
|
|
685
|
+
return __async(this, null, function* () {
|
|
686
|
+
var _a, _b, _c;
|
|
687
|
+
if (source.tagName === "STYLE") return;
|
|
688
|
+
const ctx = _resolveCtx(sessionOrCtx, opts);
|
|
689
|
+
const resetMode = ctx.options && ctx.options.cache || "auto";
|
|
690
|
+
if (resetMode !== "disabled") setupInvalidationOnce(document.documentElement);
|
|
691
|
+
if (NO_DEFAULTS_TAGS.has((_a = source.tagName) == null ? void 0 : _a.toLowerCase())) {
|
|
692
|
+
const author = (_b = source.getAttribute) == null ? void 0 : _b.call(source, "style");
|
|
693
|
+
if (author) clone.setAttribute("style", author);
|
|
694
|
+
}
|
|
695
|
+
const { session, persist } = ctx;
|
|
696
|
+
if (!session.styleCache.has(source)) {
|
|
697
|
+
session.styleCache.set(source, getComputedStyle(source));
|
|
698
|
+
}
|
|
699
|
+
const pre = session.styleCache.get(source);
|
|
700
|
+
const snap = getSnapshot(source, pre);
|
|
701
|
+
const sig = styleSignature(snap);
|
|
702
|
+
let key = persist.snapshotKeyCache.get(sig);
|
|
703
|
+
if (!key) {
|
|
704
|
+
const tag = ((_c = source.tagName) == null ? void 0 : _c.toLowerCase()) || "div";
|
|
705
|
+
key = getStyleKey(snap, tag);
|
|
706
|
+
persist.snapshotKeyCache.set(sig, key);
|
|
707
|
+
}
|
|
708
|
+
session.styleMap.set(clone, key);
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/core/clone.js
|
|
713
|
+
function addNotSlottedRightmost(sel) {
|
|
714
|
+
sel = sel.trim();
|
|
715
|
+
if (!sel) return sel;
|
|
716
|
+
if (/\:not\(\s*\[data-sd-slotted\]\s*\)\s*$/.test(sel)) return sel;
|
|
717
|
+
return `${sel}:not([data-sd-slotted])`;
|
|
718
|
+
}
|
|
719
|
+
function wrapWithScope(selectorList, scopeSelector, excludeSlotted = true) {
|
|
720
|
+
return selectorList.split(",").map((s) => s.trim()).filter(Boolean).map((s) => {
|
|
721
|
+
if (s.startsWith(":where(")) return s;
|
|
722
|
+
if (s.startsWith("@")) return s;
|
|
723
|
+
const body = excludeSlotted ? addNotSlottedRightmost(s) : s;
|
|
724
|
+
return `:where(${scopeSelector} ${body})`;
|
|
725
|
+
}).join(", ");
|
|
726
|
+
}
|
|
727
|
+
function rewriteShadowCSS(cssText, scopeSelector) {
|
|
728
|
+
if (!cssText) return "";
|
|
729
|
+
cssText = cssText.replace(/:host\(([^)]+)\)/g, (_, sel) => {
|
|
730
|
+
return `:where(${scopeSelector}:is(${sel.trim()}))`;
|
|
731
|
+
});
|
|
732
|
+
cssText = cssText.replace(/:host\b/g, `:where(${scopeSelector})`);
|
|
733
|
+
cssText = cssText.replace(/:host-context\(([^)]+)\)/g, (_, sel) => {
|
|
734
|
+
return `:where(:where(${sel.trim()}) ${scopeSelector})`;
|
|
735
|
+
});
|
|
736
|
+
cssText = cssText.replace(/::slotted\(([^)]+)\)/g, (_, sel) => {
|
|
737
|
+
return `:where(${scopeSelector} ${sel.trim()})`;
|
|
738
|
+
});
|
|
739
|
+
cssText = cssText.replace(/(^|})(\s*)([^@}{]+){/g, (_, brace, ws, selectorList) => {
|
|
740
|
+
const wrapped = wrapWithScope(
|
|
741
|
+
selectorList,
|
|
742
|
+
scopeSelector,
|
|
743
|
+
/*excludeSlotted*/
|
|
744
|
+
true
|
|
745
|
+
);
|
|
746
|
+
return `${brace}${ws}${wrapped}{`;
|
|
747
|
+
});
|
|
748
|
+
return cssText;
|
|
749
|
+
}
|
|
750
|
+
function nextShadowScopeId(sessionCache) {
|
|
751
|
+
sessionCache.shadowScopeSeq = (sessionCache.shadowScopeSeq || 0) + 1;
|
|
752
|
+
return `s${sessionCache.shadowScopeSeq}`;
|
|
753
|
+
}
|
|
754
|
+
function extractShadowCSS(sr) {
|
|
755
|
+
let css = "";
|
|
756
|
+
try {
|
|
757
|
+
sr.querySelectorAll("style").forEach((s) => {
|
|
758
|
+
css += (s.textContent || "") + "\n";
|
|
759
|
+
});
|
|
760
|
+
const sheets = sr.adoptedStyleSheets || [];
|
|
761
|
+
for (const sh of sheets) {
|
|
762
|
+
try {
|
|
763
|
+
if (sh && sh.cssRules) {
|
|
764
|
+
for (const rule of sh.cssRules) css += rule.cssText + "\n";
|
|
765
|
+
}
|
|
766
|
+
} catch (e) {
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
} catch (e) {
|
|
536
770
|
}
|
|
537
|
-
return
|
|
771
|
+
return css;
|
|
538
772
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
const style = cache2.get(source);
|
|
546
|
-
if (!snapshotCache.has(source)) {
|
|
547
|
-
const snapshot2 = snapshotComputedStyleFull(style);
|
|
548
|
-
snapshotCache.set(source, snapshot2);
|
|
549
|
-
}
|
|
550
|
-
const snapshot = snapshotCache.get(source);
|
|
551
|
-
const hash = Object.entries(snapshot).sort(([a], [b]) => a.localeCompare(b)).map(([prop, val]) => `${prop}:${val}`).join(";");
|
|
552
|
-
if (snapshotKeyCache.has(hash)) {
|
|
553
|
-
styleMap.set(clone, snapshotKeyCache.get(hash));
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
const tagName = ((_a = source.tagName) == null ? void 0 : _a.toLowerCase()) || "div";
|
|
557
|
-
const key = getStyleKey(snapshot, tagName, compress);
|
|
558
|
-
snapshotKeyCache.set(hash, key);
|
|
559
|
-
styleMap.set(clone, key);
|
|
773
|
+
function injectScopedStyle(hostClone, cssText, scopeId) {
|
|
774
|
+
if (!cssText) return;
|
|
775
|
+
const style = document.createElement("style");
|
|
776
|
+
style.setAttribute("data-sd", scopeId);
|
|
777
|
+
style.textContent = cssText;
|
|
778
|
+
hostClone.insertBefore(style, hostClone.firstChild || null);
|
|
560
779
|
}
|
|
561
|
-
|
|
562
|
-
// src/core/clone.js
|
|
563
780
|
function freezeImgSrcset(original, cloned) {
|
|
564
781
|
try {
|
|
565
782
|
const chosen = original.currentSrc || original.src || "";
|
|
@@ -572,11 +789,62 @@
|
|
|
572
789
|
} catch (e) {
|
|
573
790
|
}
|
|
574
791
|
}
|
|
575
|
-
function
|
|
576
|
-
|
|
792
|
+
function collectCustomPropsFromCSS(cssText) {
|
|
793
|
+
const out = /* @__PURE__ */ new Set();
|
|
794
|
+
if (!cssText) return out;
|
|
795
|
+
const re = /var\(\s*(--[A-Za-z0-9_-]+)\b/g;
|
|
796
|
+
let m;
|
|
797
|
+
while (m = re.exec(cssText)) out.add(m[1]);
|
|
798
|
+
return out;
|
|
799
|
+
}
|
|
800
|
+
function resolveCustomProp(el, name) {
|
|
801
|
+
try {
|
|
802
|
+
const cs = getComputedStyle(el);
|
|
803
|
+
let v = cs.getPropertyValue(name).trim();
|
|
804
|
+
if (v) return v;
|
|
805
|
+
} catch (e) {
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
const rootCS = getComputedStyle(document.documentElement);
|
|
809
|
+
let v = rootCS.getPropertyValue(name).trim();
|
|
810
|
+
if (v) return v;
|
|
811
|
+
} catch (e) {
|
|
812
|
+
}
|
|
813
|
+
return "";
|
|
814
|
+
}
|
|
815
|
+
function buildSeedCustomPropsRule(hostEl, names, scopeSelector) {
|
|
816
|
+
const decls = [];
|
|
817
|
+
for (const name of names) {
|
|
818
|
+
const val = resolveCustomProp(hostEl, name);
|
|
819
|
+
if (val) decls.push(`${name}: ${val};`);
|
|
820
|
+
}
|
|
821
|
+
if (!decls.length) return "";
|
|
822
|
+
return `${scopeSelector}{${decls.join("")}}
|
|
823
|
+
`;
|
|
824
|
+
}
|
|
825
|
+
function markSlottedSubtree(root) {
|
|
826
|
+
if (!root) return;
|
|
827
|
+
if (root.nodeType === Node.ELEMENT_NODE) {
|
|
828
|
+
root.setAttribute("data-sd-slotted", "");
|
|
829
|
+
}
|
|
830
|
+
if (root.querySelectorAll) {
|
|
831
|
+
root.querySelectorAll("*").forEach((el) => el.setAttribute("data-sd-slotted", ""));
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
function deepClone(node, sessionCache, options) {
|
|
835
|
+
var _a, _b, _c, _d, _e;
|
|
577
836
|
if (!node) throw new Error("Invalid node");
|
|
578
837
|
const clonedAssignedNodes = /* @__PURE__ */ new Set();
|
|
579
838
|
let pendingSelectValue = null;
|
|
839
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
840
|
+
const tag = (node.localName || node.tagName || "").toLowerCase();
|
|
841
|
+
if (node.id === "snapdom-sandbox" || node.hasAttribute("data-snapdom-sandbox")) {
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
if (NO_CAPTURE_TAGS.has(tag)) {
|
|
845
|
+
return null;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
580
848
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
581
849
|
return node.cloneNode(true);
|
|
582
850
|
}
|
|
@@ -605,7 +873,7 @@
|
|
|
605
873
|
}
|
|
606
874
|
if (typeof options.filter === "function") {
|
|
607
875
|
try {
|
|
608
|
-
if (!options.filter(node
|
|
876
|
+
if (!options.filter(node)) {
|
|
609
877
|
const spacer = document.createElement("div");
|
|
610
878
|
const rect = node.getBoundingClientRect();
|
|
611
879
|
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
@@ -622,8 +890,8 @@
|
|
|
622
890
|
}
|
|
623
891
|
if (node.getAttribute("data-capture") === "placeholder") {
|
|
624
892
|
const clone2 = node.cloneNode(false);
|
|
625
|
-
nodeMap.set(clone2, node);
|
|
626
|
-
inlineAllStyles(node, clone2,
|
|
893
|
+
sessionCache.nodeMap.set(clone2, node);
|
|
894
|
+
inlineAllStyles(node, clone2, sessionCache, options);
|
|
627
895
|
const placeholder = document.createElement("div");
|
|
628
896
|
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
629
897
|
placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`;
|
|
@@ -636,14 +904,14 @@
|
|
|
636
904
|
img.src = dataURL;
|
|
637
905
|
img.width = node.width;
|
|
638
906
|
img.height = node.height;
|
|
639
|
-
nodeMap.set(img, node);
|
|
640
|
-
inlineAllStyles(node, img,
|
|
907
|
+
sessionCache.nodeMap.set(img, node);
|
|
908
|
+
inlineAllStyles(node, img, sessionCache, options);
|
|
641
909
|
return img;
|
|
642
910
|
}
|
|
643
911
|
let clone;
|
|
644
912
|
try {
|
|
645
913
|
clone = node.cloneNode(false);
|
|
646
|
-
nodeMap.set(clone, node);
|
|
914
|
+
sessionCache.nodeMap.set(clone, node);
|
|
647
915
|
if (node.tagName === "IMG") {
|
|
648
916
|
freezeImgSrcset(node, clone);
|
|
649
917
|
}
|
|
@@ -671,47 +939,56 @@
|
|
|
671
939
|
if (node instanceof HTMLSelectElement) {
|
|
672
940
|
pendingSelectValue = node.value;
|
|
673
941
|
}
|
|
674
|
-
inlineAllStyles(node, clone,
|
|
942
|
+
inlineAllStyles(node, clone, sessionCache, options);
|
|
675
943
|
if (node.shadowRoot) {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
for (const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
} else {
|
|
687
|
-
const shadowFrag = document.createDocumentFragment();
|
|
688
|
-
for (const child of node.shadowRoot.childNodes) {
|
|
689
|
-
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
|
|
690
|
-
const cssText = child.textContent || "";
|
|
691
|
-
if (cssText.trim() && compress) {
|
|
692
|
-
styleCache.set(child, cssText);
|
|
693
|
-
}
|
|
694
|
-
continue;
|
|
944
|
+
try {
|
|
945
|
+
const slots = node.shadowRoot.querySelectorAll("slot");
|
|
946
|
+
for (const s of slots) {
|
|
947
|
+
let assigned = [];
|
|
948
|
+
try {
|
|
949
|
+
assigned = ((_b = s.assignedNodes) == null ? void 0 : _b.call(s, { flatten: true })) || ((_c = s.assignedNodes) == null ? void 0 : _c.call(s)) || [];
|
|
950
|
+
} catch (e) {
|
|
951
|
+
assigned = ((_d = s.assignedNodes) == null ? void 0 : _d.call(s)) || [];
|
|
695
952
|
}
|
|
696
|
-
|
|
697
|
-
if (clonedChild) shadowFrag.appendChild(clonedChild);
|
|
953
|
+
for (const an of assigned) clonedAssignedNodes.add(an);
|
|
698
954
|
}
|
|
699
|
-
|
|
955
|
+
} catch (e) {
|
|
956
|
+
}
|
|
957
|
+
const scopeId = nextShadowScopeId(sessionCache);
|
|
958
|
+
const scopeSelector = `[data-sd="${scopeId}"]`;
|
|
959
|
+
try {
|
|
960
|
+
clone.setAttribute("data-sd", scopeId);
|
|
961
|
+
} catch (e) {
|
|
700
962
|
}
|
|
963
|
+
const rawCSS = extractShadowCSS(node.shadowRoot);
|
|
964
|
+
const rewritten = rewriteShadowCSS(rawCSS, scopeSelector);
|
|
965
|
+
const neededVars = collectCustomPropsFromCSS(rawCSS);
|
|
966
|
+
const seed = buildSeedCustomPropsRule(node, neededVars, scopeSelector);
|
|
967
|
+
injectScopedStyle(clone, seed + rewritten, scopeId);
|
|
968
|
+
const shadowFrag = document.createDocumentFragment();
|
|
969
|
+
for (const child of node.shadowRoot.childNodes) {
|
|
970
|
+
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") continue;
|
|
971
|
+
const clonedChild = deepClone(child, sessionCache, options);
|
|
972
|
+
if (clonedChild) shadowFrag.appendChild(clonedChild);
|
|
973
|
+
}
|
|
974
|
+
clone.appendChild(shadowFrag);
|
|
701
975
|
}
|
|
702
976
|
if (node.tagName === "SLOT") {
|
|
703
|
-
const assigned = ((
|
|
977
|
+
const assigned = ((_e = node.assignedNodes) == null ? void 0 : _e.call(node, { flatten: true })) || [];
|
|
704
978
|
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
705
979
|
const fragment = document.createDocumentFragment();
|
|
706
980
|
for (const child of nodesToClone) {
|
|
707
|
-
const clonedChild = deepClone(child,
|
|
708
|
-
if (clonedChild)
|
|
981
|
+
const clonedChild = deepClone(child, sessionCache, options);
|
|
982
|
+
if (clonedChild) {
|
|
983
|
+
markSlottedSubtree(clonedChild);
|
|
984
|
+
fragment.appendChild(clonedChild);
|
|
985
|
+
}
|
|
709
986
|
}
|
|
710
987
|
return fragment;
|
|
711
988
|
}
|
|
712
989
|
for (const child of node.childNodes) {
|
|
713
990
|
if (clonedAssignedNodes.has(child)) continue;
|
|
714
|
-
const clonedChild = deepClone(child,
|
|
991
|
+
const clonedChild = deepClone(child, sessionCache, options);
|
|
715
992
|
if (clonedChild) clone.appendChild(clonedChild);
|
|
716
993
|
}
|
|
717
994
|
if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
|
|
@@ -754,7 +1031,7 @@
|
|
|
754
1031
|
}
|
|
755
1032
|
}
|
|
756
1033
|
}
|
|
757
|
-
function
|
|
1034
|
+
function isIconFont2(input) {
|
|
758
1035
|
const text = typeof input === "string" ? input : "";
|
|
759
1036
|
const candidates = [...defaultIconFonts, ...userIconFonts];
|
|
760
1037
|
for (const rx of candidates) {
|
|
@@ -803,249 +1080,630 @@
|
|
|
803
1080
|
};
|
|
804
1081
|
});
|
|
805
1082
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1083
|
+
var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
1084
|
+
"serif",
|
|
1085
|
+
"sans-serif",
|
|
1086
|
+
"monospace",
|
|
1087
|
+
"cursive",
|
|
1088
|
+
"fantasy",
|
|
1089
|
+
"system-ui",
|
|
1090
|
+
"emoji",
|
|
1091
|
+
"math",
|
|
1092
|
+
"fangsong",
|
|
1093
|
+
"ui-serif",
|
|
1094
|
+
"ui-sans-serif",
|
|
1095
|
+
"ui-monospace",
|
|
1096
|
+
"ui-rounded"
|
|
1097
|
+
]);
|
|
1098
|
+
function pickPrimaryFamily(familyList) {
|
|
1099
|
+
if (!familyList) return "";
|
|
1100
|
+
for (let raw of familyList.split(",")) {
|
|
1101
|
+
let f = raw.trim().replace(/^['"]+|['"]+$/g, "");
|
|
1102
|
+
if (!f) continue;
|
|
1103
|
+
if (!GENERIC_FAMILIES.has(f.toLowerCase())) return f;
|
|
1104
|
+
}
|
|
1105
|
+
return "";
|
|
1106
|
+
}
|
|
1107
|
+
function normWeight(w) {
|
|
1108
|
+
const t = String(w != null ? w : "400").trim().toLowerCase();
|
|
1109
|
+
if (t === "normal") return 400;
|
|
1110
|
+
if (t === "bold") return 700;
|
|
1111
|
+
const n = parseInt(t, 10);
|
|
1112
|
+
return Number.isFinite(n) ? Math.min(900, Math.max(100, n)) : 400;
|
|
1113
|
+
}
|
|
1114
|
+
function normStyle(s) {
|
|
1115
|
+
const t = String(s != null ? s : "normal").trim().toLowerCase();
|
|
1116
|
+
if (t.startsWith("italic")) return "italic";
|
|
1117
|
+
if (t.startsWith("oblique")) return "oblique";
|
|
1118
|
+
return "normal";
|
|
1119
|
+
}
|
|
1120
|
+
function normStretchPct(st) {
|
|
1121
|
+
const m = String(st != null ? st : "100%").match(/(\d+(?:\.\d+)?)\s*%/);
|
|
1122
|
+
return m ? Math.max(50, Math.min(200, parseFloat(m[1]))) : 100;
|
|
1123
|
+
}
|
|
1124
|
+
function parseWeightSpec(spec) {
|
|
1125
|
+
const s = String(spec || "400").trim();
|
|
1126
|
+
const m = s.match(/^(\d{2,3})\s+(\d{2,3})$/);
|
|
1127
|
+
if (m) {
|
|
1128
|
+
const a = normWeight(m[1]), b = normWeight(m[2]);
|
|
1129
|
+
return { min: Math.min(a, b), max: Math.max(a, b) };
|
|
1130
|
+
}
|
|
1131
|
+
const v = normWeight(s);
|
|
1132
|
+
return { min: v, max: v };
|
|
1133
|
+
}
|
|
1134
|
+
function parseStyleSpec(spec) {
|
|
1135
|
+
const t = String(spec || "normal").trim().toLowerCase();
|
|
1136
|
+
if (t === "italic") return { kind: "italic" };
|
|
1137
|
+
if (t.startsWith("oblique")) return { kind: "oblique" };
|
|
1138
|
+
return { kind: "normal" };
|
|
1139
|
+
}
|
|
1140
|
+
function parseStretchSpec(spec) {
|
|
1141
|
+
const s = String(spec || "100%").trim();
|
|
1142
|
+
const mm = s.match(/(\d+(?:\.\d+)?)\s*%\s+(\d+(?:\.\d+)?)\s*%/);
|
|
1143
|
+
if (mm) {
|
|
1144
|
+
const a = parseFloat(mm[1]), b = parseFloat(mm[2]);
|
|
1145
|
+
return { min: Math.min(a, b), max: Math.max(a, b) };
|
|
1146
|
+
}
|
|
1147
|
+
const m = s.match(/(\d+(?:\.\d+)?)\s*%/);
|
|
1148
|
+
const v = m ? parseFloat(m[1]) : 100;
|
|
1149
|
+
return { min: v, max: v };
|
|
1150
|
+
}
|
|
1151
|
+
function isLikelyFontStylesheet(href, requiredFamilies) {
|
|
1152
|
+
if (!href) return false;
|
|
1153
|
+
try {
|
|
1154
|
+
const u = new URL(href, location.href);
|
|
1155
|
+
const sameOrigin = u.origin === location.origin;
|
|
1156
|
+
if (sameOrigin) return true;
|
|
1157
|
+
const host = u.host.toLowerCase();
|
|
1158
|
+
const FONT_HOSTS = [
|
|
1159
|
+
"fonts.googleapis.com",
|
|
1160
|
+
"fonts.gstatic.com",
|
|
1161
|
+
"use.typekit.net",
|
|
1162
|
+
"p.typekit.net",
|
|
1163
|
+
"kit.fontawesome.com",
|
|
1164
|
+
"use.fontawesome.com"
|
|
1165
|
+
];
|
|
1166
|
+
if (FONT_HOSTS.some((h) => host.endsWith(h))) return true;
|
|
1167
|
+
const path = (u.pathname + u.search).toLowerCase();
|
|
1168
|
+
if (/\bfont(s)?\b/.test(path) || /\.woff2?(\b|$)/.test(path)) return true;
|
|
1169
|
+
for (const fam of requiredFamilies) {
|
|
1170
|
+
const tokenA = fam.toLowerCase().replace(/\s+/g, "+");
|
|
1171
|
+
const tokenB = fam.toLowerCase().replace(/\s+/g, "-");
|
|
1172
|
+
if (path.includes(tokenA) || path.includes(tokenB)) return true;
|
|
1173
|
+
}
|
|
1174
|
+
return false;
|
|
1175
|
+
} catch (e) {
|
|
1176
|
+
return false;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
function familiesFromRequired(required) {
|
|
1180
|
+
var _a;
|
|
1181
|
+
const out = /* @__PURE__ */ new Set();
|
|
1182
|
+
for (const k of required || []) {
|
|
1183
|
+
const fam = (_a = String(k).split("__")[0]) == null ? void 0 : _a.trim();
|
|
1184
|
+
if (fam) out.add(fam);
|
|
1185
|
+
}
|
|
1186
|
+
return out;
|
|
820
1187
|
}
|
|
821
1188
|
function embedCustomFonts() {
|
|
822
|
-
return __async(this, arguments, function* ({
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1189
|
+
return __async(this, arguments, function* ({
|
|
1190
|
+
required,
|
|
1191
|
+
usedCodepoints,
|
|
1192
|
+
exclude = void 0,
|
|
1193
|
+
localFonts = [],
|
|
1194
|
+
useProxy = ""
|
|
1195
|
+
} = {}) {
|
|
1196
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
|
|
1197
|
+
if (!(required instanceof Set)) required = /* @__PURE__ */ new Set();
|
|
1198
|
+
if (!(usedCodepoints instanceof Set)) usedCodepoints = /* @__PURE__ */ new Set();
|
|
1199
|
+
const requiredIndex = /* @__PURE__ */ new Map();
|
|
1200
|
+
for (const key of required) {
|
|
1201
|
+
const [fam, w, s, st] = String(key).split("__");
|
|
1202
|
+
if (!fam) continue;
|
|
1203
|
+
const arr = requiredIndex.get(fam) || [];
|
|
1204
|
+
arr.push({ w: parseInt(w, 10), s, st: parseInt(st, 10) });
|
|
1205
|
+
requiredIndex.set(fam, arr);
|
|
1206
|
+
}
|
|
1207
|
+
const IMPORT_RE = /@import\s+url\(["']?([^"')]+)["']?\)/g;
|
|
1208
|
+
const URL_RE = /url\((["']?)([^"')]+)\1\)/g;
|
|
1209
|
+
const FACE_RE = /@font-face[^{}]*\{[^}]*\}/g;
|
|
1210
|
+
function faceMatchesRequired(fam, styleSpec, weightSpec, stretchSpec) {
|
|
1211
|
+
if (!requiredIndex.has(fam)) return false;
|
|
1212
|
+
const need = requiredIndex.get(fam);
|
|
1213
|
+
const ws = parseWeightSpec(weightSpec);
|
|
1214
|
+
const ss = parseStyleSpec(styleSpec);
|
|
1215
|
+
const ts = parseStretchSpec(stretchSpec);
|
|
1216
|
+
const faceIsRange = ws.min !== ws.max;
|
|
1217
|
+
const faceSingleW = ws.min;
|
|
1218
|
+
const styleOK = (reqKind) => ss.kind === "normal" && reqKind === "normal" || ss.kind !== "normal" && (reqKind === "italic" || reqKind === "oblique");
|
|
1219
|
+
let exactMatched = false;
|
|
1220
|
+
for (const r of need) {
|
|
1221
|
+
const wOk = faceIsRange ? r.w >= ws.min && r.w <= ws.max : r.w === faceSingleW;
|
|
1222
|
+
const sOk = styleOK(normStyle(r.s));
|
|
1223
|
+
const tOk = r.st >= ts.min && r.st <= ts.max;
|
|
1224
|
+
if (wOk && sOk && tOk) {
|
|
1225
|
+
exactMatched = true;
|
|
1226
|
+
break;
|
|
1227
|
+
}
|
|
829
1228
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1229
|
+
if (exactMatched) return true;
|
|
1230
|
+
if (!faceIsRange) {
|
|
1231
|
+
for (const r of need) {
|
|
1232
|
+
const sOk = styleOK(normStyle(r.s));
|
|
1233
|
+
const tOk = r.st >= ts.min && r.st <= ts.max;
|
|
1234
|
+
const nearWeight = Math.abs(faceSingleW - r.w) <= 300;
|
|
1235
|
+
if (nearWeight && sOk && tOk) {
|
|
1236
|
+
return true;
|
|
1237
|
+
}
|
|
837
1238
|
}
|
|
838
1239
|
}
|
|
839
|
-
|
|
1240
|
+
return false;
|
|
1241
|
+
}
|
|
1242
|
+
function parseUnicodeRange(ur) {
|
|
1243
|
+
if (!ur) return [];
|
|
1244
|
+
const ranges = [];
|
|
1245
|
+
const parts = ur.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1246
|
+
for (const p of parts) {
|
|
1247
|
+
const m = p.match(/^U\+([0-9A-Fa-f?]+)(?:-([0-9A-Fa-f?]+))?$/);
|
|
1248
|
+
if (!m) continue;
|
|
1249
|
+
const a = m[1], b = m[2];
|
|
1250
|
+
const expand = (hex) => {
|
|
1251
|
+
if (!hex.includes("?")) return parseInt(hex, 16);
|
|
1252
|
+
const min = parseInt(hex.replace(/\?/g, "0"), 16);
|
|
1253
|
+
const max = parseInt(hex.replace(/\?/g, "F"), 16);
|
|
1254
|
+
return [min, max];
|
|
1255
|
+
};
|
|
1256
|
+
if (b) {
|
|
1257
|
+
const A = expand(a), B = expand(b);
|
|
1258
|
+
const min = Array.isArray(A) ? A[0] : A;
|
|
1259
|
+
const max = Array.isArray(B) ? B[1] : B;
|
|
1260
|
+
ranges.push([Math.min(min, max), Math.max(min, max)]);
|
|
1261
|
+
} else {
|
|
1262
|
+
const X = expand(a);
|
|
1263
|
+
if (Array.isArray(X)) ranges.push([X[0], X[1]]);
|
|
1264
|
+
else ranges.push([X, X]);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return ranges;
|
|
840
1268
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1269
|
+
function unicodeIntersects(used, ranges) {
|
|
1270
|
+
if (!ranges.length) return true;
|
|
1271
|
+
if (!used || used.size === 0) return true;
|
|
1272
|
+
for (const cp of used) {
|
|
1273
|
+
for (const [a, b] of ranges) if (cp >= a && cp <= b) return true;
|
|
1274
|
+
}
|
|
1275
|
+
return false;
|
|
1276
|
+
}
|
|
1277
|
+
function extractSrcUrls(srcValue, baseHref) {
|
|
1278
|
+
const urls = [];
|
|
1279
|
+
if (!srcValue) return urls;
|
|
1280
|
+
for (const m of srcValue.matchAll(URL_RE)) {
|
|
1281
|
+
let u = (m[2] || "").trim();
|
|
1282
|
+
if (!u || u.startsWith("data:")) continue;
|
|
1283
|
+
if (!/^https?:/i.test(u)) {
|
|
1284
|
+
try {
|
|
1285
|
+
u = new URL(u, baseHref || location.href).href;
|
|
1286
|
+
} catch (e) {
|
|
1287
|
+
}
|
|
851
1288
|
}
|
|
1289
|
+
urls.push(u);
|
|
852
1290
|
}
|
|
1291
|
+
return urls;
|
|
853
1292
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
const family = famMatch[1].replace(/['"]/g, "").trim();
|
|
868
|
-
const weightMatch = face.match(/font-weight:\s*([^;]+);/i);
|
|
869
|
-
const styleMatch = face.match(/font-style:\s*([^;]+);/i);
|
|
870
|
-
const weight = weightMatch ? weightMatch[1].trim() : "normal";
|
|
871
|
-
const style = styleMatch ? styleMatch[1].trim() : "normal";
|
|
872
|
-
const key = `${family}__${weight}__${style}`;
|
|
873
|
-
const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
|
|
874
|
-
const hasURL = /url\(/i.test(face);
|
|
875
|
-
const hasLocal = /local\(/i.test(face);
|
|
876
|
-
if (!hasURL && hasLocal) {
|
|
877
|
-
continue;
|
|
1293
|
+
function inlineUrlsInCssBlock(cssBlock, baseHref) {
|
|
1294
|
+
return __async(this, null, function* () {
|
|
1295
|
+
var _a2, _b2, _c2, _d2, _e2;
|
|
1296
|
+
let out = cssBlock;
|
|
1297
|
+
for (const m of cssBlock.matchAll(URL_RE)) {
|
|
1298
|
+
const raw = extractURL(m[0]);
|
|
1299
|
+
if (!raw) continue;
|
|
1300
|
+
let abs = raw;
|
|
1301
|
+
if (!abs.startsWith("http") && !abs.startsWith("data:")) {
|
|
1302
|
+
try {
|
|
1303
|
+
abs = new URL(abs, baseHref || location.href).href;
|
|
1304
|
+
} catch (e) {
|
|
1305
|
+
}
|
|
878
1306
|
}
|
|
879
|
-
if (
|
|
880
|
-
|
|
1307
|
+
if (isIconFont2(abs)) continue;
|
|
1308
|
+
if ((_a2 = cache.resource) == null ? void 0 : _a2.has(abs)) {
|
|
1309
|
+
(_b2 = cache.font) == null ? void 0 : _b2.add(abs);
|
|
1310
|
+
out = out.replace(m[0], `url(${cache.resource.get(abs)})`);
|
|
881
1311
|
continue;
|
|
882
1312
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1313
|
+
if ((_c2 = cache.font) == null ? void 0 : _c2.has(abs)) continue;
|
|
1314
|
+
try {
|
|
1315
|
+
const r = yield snapFetch(abs, { as: "dataURL", useProxy, silent: true });
|
|
1316
|
+
if (r.ok && typeof r.data === "string") {
|
|
1317
|
+
const b64 = r.data;
|
|
1318
|
+
(_d2 = cache.resource) == null ? void 0 : _d2.set(abs, b64);
|
|
1319
|
+
(_e2 = cache.font) == null ? void 0 : _e2.add(abs);
|
|
1320
|
+
out = out.replace(m[0], `url(${b64})`);
|
|
891
1321
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1322
|
+
} catch (e) {
|
|
1323
|
+
console.warn("[snapDOM] Failed to fetch font resource:", abs);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return out;
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
function subsetFromRanges(ranges) {
|
|
1330
|
+
if (!ranges.length) return null;
|
|
1331
|
+
const hit = (a, b) => ranges.some(([x, y]) => !(y < a || x > b));
|
|
1332
|
+
const latin = hit(0, 255) || hit(305, 305);
|
|
1333
|
+
const latinExt = hit(256, 591) || hit(7680, 7935);
|
|
1334
|
+
const greek = hit(880, 1023);
|
|
1335
|
+
const cyr = hit(1024, 1279);
|
|
1336
|
+
const viet = hit(7840, 7929) || hit(258, 259) || hit(416, 417) || hit(431, 432);
|
|
1337
|
+
if (viet) return "vietnamese";
|
|
1338
|
+
if (cyr) return "cyrillic";
|
|
1339
|
+
if (greek) return "greek";
|
|
1340
|
+
if (latinExt) return "latin-ext";
|
|
1341
|
+
if (latin) return "latin";
|
|
1342
|
+
return null;
|
|
1343
|
+
}
|
|
1344
|
+
function buildSimpleExcluder(ex = {}) {
|
|
1345
|
+
const famSet = new Set((ex.families || []).map((s) => String(s).toLowerCase()));
|
|
1346
|
+
const domSet = new Set((ex.domains || []).map((s) => String(s).toLowerCase()));
|
|
1347
|
+
const subSet = new Set((ex.subsets || []).map((s) => String(s).toLowerCase()));
|
|
1348
|
+
return (meta, parsedRanges) => {
|
|
1349
|
+
if (famSet.size && famSet.has(meta.family.toLowerCase())) return true;
|
|
1350
|
+
if (domSet.size) {
|
|
1351
|
+
for (const u of meta.srcUrls) {
|
|
1352
|
+
try {
|
|
1353
|
+
if (domSet.has(new URL(u).host.toLowerCase())) return true;
|
|
1354
|
+
} catch (e) {
|
|
897
1355
|
}
|
|
898
|
-
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
if (subSet.size) {
|
|
1359
|
+
const label = subsetFromRanges(parsedRanges);
|
|
1360
|
+
if (label && subSet.has(label)) return true;
|
|
1361
|
+
}
|
|
1362
|
+
return false;
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
const simpleExcluder = buildSimpleExcluder(exclude);
|
|
1366
|
+
function dedupeFontFaces(cssText) {
|
|
1367
|
+
var _a2, _b2, _c2, _d2, _e2, _f2;
|
|
1368
|
+
if (!cssText) return cssText;
|
|
1369
|
+
const FACE_RE2 = /@font-face[^{}]*\{[^}]*\}/gi;
|
|
1370
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1371
|
+
const out = [];
|
|
1372
|
+
for (const block of cssText.match(FACE_RE2) || []) {
|
|
1373
|
+
const familyRaw = ((_a2 = block.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _a2[1]) || "";
|
|
1374
|
+
const family = pickPrimaryFamily(familyRaw);
|
|
1375
|
+
const weightSpec = (((_b2 = block.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _b2[1]) || "400").trim();
|
|
1376
|
+
const styleSpec = (((_c2 = block.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _c2[1]) || "normal").trim();
|
|
1377
|
+
const stretchSpec = (((_d2 = block.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _d2[1]) || "100%").trim();
|
|
1378
|
+
const urange = (((_e2 = block.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _e2[1]) || "").trim();
|
|
1379
|
+
const srcRaw = (((_f2 = block.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _f2[1]) || "").trim();
|
|
1380
|
+
const urls = extractSrcUrls(srcRaw, location.href);
|
|
1381
|
+
const srcPart = urls.length ? urls.map((u) => String(u).toLowerCase()).sort().join("|") : srcRaw.toLowerCase();
|
|
1382
|
+
const key = [
|
|
1383
|
+
String(family || "").toLowerCase(),
|
|
1384
|
+
weightSpec,
|
|
1385
|
+
styleSpec,
|
|
1386
|
+
stretchSpec,
|
|
1387
|
+
urange.toLowerCase(),
|
|
1388
|
+
srcPart
|
|
1389
|
+
].join("|");
|
|
1390
|
+
if (!seen.has(key)) {
|
|
1391
|
+
seen.add(key);
|
|
1392
|
+
out.push(block);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (out.length === 0) return cssText;
|
|
1396
|
+
let i = 0;
|
|
1397
|
+
return cssText.replace(FACE_RE2, () => out[i++] || "");
|
|
1398
|
+
}
|
|
1399
|
+
function buildFontsCacheKey() {
|
|
1400
|
+
const req = Array.from(required || []).sort().join("|");
|
|
1401
|
+
const ex = exclude ? JSON.stringify({
|
|
1402
|
+
families: (exclude.families || []).map((s) => String(s).toLowerCase()).sort(),
|
|
1403
|
+
domains: (exclude.domains || []).map((s) => String(s).toLowerCase()).sort(),
|
|
1404
|
+
subsets: (exclude.subsets || []).map((s) => String(s).toLowerCase()).sort()
|
|
1405
|
+
}) : "";
|
|
1406
|
+
const lf = (localFonts || []).map((f) => `${(f.family || "").toLowerCase()}::${f.weight || "normal"}::${f.style || "normal"}::${f.src || ""}`).sort().join("|");
|
|
1407
|
+
const px = useProxy || "";
|
|
1408
|
+
return `fonts-embed-css::req=${req}::ex=${ex}::lf=${lf}::px=${px}`;
|
|
1409
|
+
}
|
|
1410
|
+
const cacheKey = buildFontsCacheKey();
|
|
1411
|
+
if ((_a = cache.resource) == null ? void 0 : _a.has(cacheKey)) {
|
|
1412
|
+
const css = cache.resource.get(cacheKey);
|
|
1413
|
+
return css;
|
|
1414
|
+
}
|
|
1415
|
+
const requiredFamilies = familiesFromRequired(required);
|
|
1416
|
+
const importUrls = [];
|
|
1417
|
+
for (const styleTag of document.querySelectorAll("style")) {
|
|
1418
|
+
const cssText = styleTag.textContent || "";
|
|
1419
|
+
for (const m of cssText.matchAll(IMPORT_RE)) {
|
|
1420
|
+
const u = m[1];
|
|
1421
|
+
if (!u || isIconFont2(u)) continue;
|
|
1422
|
+
const hasLink = !!document.querySelector(`link[rel="stylesheet"][href="${u}"]`);
|
|
1423
|
+
if (!hasLink) importUrls.push(u);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (importUrls.length) {
|
|
1427
|
+
yield Promise.all(importUrls.map((u) => new Promise((resolve) => {
|
|
1428
|
+
if (document.querySelector(`link[rel="stylesheet"][href="${u}"]`)) return resolve(null);
|
|
1429
|
+
const link = document.createElement("link");
|
|
1430
|
+
link.rel = "stylesheet";
|
|
1431
|
+
link.href = u;
|
|
1432
|
+
link.setAttribute("data-snapdom", "injected-import");
|
|
1433
|
+
link.onload = () => resolve(link);
|
|
1434
|
+
link.onerror = () => resolve(null);
|
|
1435
|
+
document.head.appendChild(link);
|
|
1436
|
+
})));
|
|
1437
|
+
}
|
|
1438
|
+
let finalCSS = "";
|
|
1439
|
+
const linkNodes = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((l) => !!l.href);
|
|
1440
|
+
for (const link of linkNodes) {
|
|
1441
|
+
try {
|
|
1442
|
+
if (isIconFont2(link.href)) continue;
|
|
1443
|
+
let cssText = "";
|
|
1444
|
+
let sameOrigin = false;
|
|
1445
|
+
try {
|
|
1446
|
+
sameOrigin = new URL(link.href, location.href).origin === location.origin;
|
|
1447
|
+
} catch (e) {
|
|
1448
|
+
}
|
|
1449
|
+
if (!sameOrigin) {
|
|
1450
|
+
if (!isLikelyFontStylesheet(link.href, requiredFamilies)) continue;
|
|
1451
|
+
}
|
|
1452
|
+
if (sameOrigin) {
|
|
1453
|
+
const sheet = Array.from(document.styleSheets).find((s) => s.href === link.href);
|
|
1454
|
+
if (sheet) {
|
|
899
1455
|
try {
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
const b64 = yield new Promise((resolve) => {
|
|
903
|
-
const reader = new FileReader();
|
|
904
|
-
reader.onload = () => resolve(reader.result);
|
|
905
|
-
reader.readAsDataURL(blob);
|
|
906
|
-
});
|
|
907
|
-
cache.resource.set(url, b64);
|
|
908
|
-
cache.font.add(url);
|
|
909
|
-
inlined = inlined.replace(match[0], `url(${b64})`);
|
|
1456
|
+
const rules = sheet.cssRules || [];
|
|
1457
|
+
cssText = Array.from(rules).map((r) => r.cssText).join("");
|
|
910
1458
|
} catch (e) {
|
|
911
|
-
console.warn("[snapdom] Failed to fetch font resource:", url);
|
|
912
1459
|
}
|
|
913
1460
|
}
|
|
914
|
-
cssFinal = cssFinal.replace(face, inlined);
|
|
915
1461
|
}
|
|
916
|
-
|
|
1462
|
+
if (!cssText) {
|
|
1463
|
+
const res = yield snapFetch(link.href, { as: "text", useProxy });
|
|
1464
|
+
cssText = res.data;
|
|
1465
|
+
if (isIconFont2(link.href)) continue;
|
|
1466
|
+
}
|
|
1467
|
+
let facesOut = "";
|
|
1468
|
+
for (const face of cssText.match(FACE_RE) || []) {
|
|
1469
|
+
const famRaw = (((_b = face.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _b[1]) || "").trim();
|
|
1470
|
+
const family = pickPrimaryFamily(famRaw);
|
|
1471
|
+
if (!family || isIconFont2(family)) {
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
const weightSpec = (((_c = face.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _c[1]) || "400").trim();
|
|
1475
|
+
const styleSpec = (((_d = face.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _d[1]) || "normal").trim();
|
|
1476
|
+
const stretchSpec = (((_e = face.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _e[1]) || "100%").trim();
|
|
1477
|
+
const urange = (((_f = face.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _f[1]) || "").trim();
|
|
1478
|
+
const srcRaw = (((_g = face.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _g[1]) || "").trim();
|
|
1479
|
+
const srcUrls = extractSrcUrls(srcRaw, link.href);
|
|
1480
|
+
if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) {
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
const ranges = parseUnicodeRange(urange);
|
|
1484
|
+
if (!unicodeIntersects(usedCodepoints, ranges)) {
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
const meta = { family, weightSpec, styleSpec, stretchSpec, unicodeRange: urange, srcRaw, srcUrls, href: link.href };
|
|
1488
|
+
if (exclude && simpleExcluder(meta, ranges)) {
|
|
1489
|
+
continue;
|
|
1490
|
+
}
|
|
1491
|
+
const newFace = /url\(/i.test(srcRaw) ? yield inlineUrlsInCssBlock(face, link.href) : face;
|
|
1492
|
+
facesOut += newFace + "";
|
|
1493
|
+
}
|
|
1494
|
+
if (facesOut.trim()) finalCSS += facesOut + "";
|
|
917
1495
|
} catch (e) {
|
|
918
|
-
console.warn("[snapdom] Failed to
|
|
1496
|
+
console.warn("[snapdom] Failed to process stylesheet:", link.href);
|
|
919
1497
|
}
|
|
920
1498
|
}
|
|
921
1499
|
for (const sheet of document.styleSheets) {
|
|
922
1500
|
try {
|
|
923
|
-
if (
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
let url = rawUrl;
|
|
947
|
-
if (!url.startsWith("http") && !url.startsWith("data:")) {
|
|
948
|
-
url = new URL(url, sheet.href || location.href).href;
|
|
949
|
-
}
|
|
950
|
-
if (isIconFont(url)) continue;
|
|
951
|
-
if (cache.resource.has(url)) {
|
|
952
|
-
cache.font.add(url);
|
|
953
|
-
inlinedSrc = inlinedSrc.replace(match[0], `url(${cache.resource.get(url)})`);
|
|
954
|
-
continue;
|
|
955
|
-
}
|
|
956
|
-
if (cache.font.has(url)) continue;
|
|
957
|
-
try {
|
|
958
|
-
const res = yield fetchResource(url, { useProxy });
|
|
959
|
-
const blob = yield res.blob();
|
|
960
|
-
const b64 = yield new Promise((resolve) => {
|
|
961
|
-
const reader = new FileReader();
|
|
962
|
-
reader.onload = () => resolve(reader.result);
|
|
963
|
-
reader.readAsDataURL(blob);
|
|
964
|
-
});
|
|
965
|
-
cache.resource.set(url, b64);
|
|
966
|
-
cache.font.add(url);
|
|
967
|
-
inlinedSrc = inlinedSrc.replace(match[0], `url(${b64})`);
|
|
968
|
-
} catch (e) {
|
|
969
|
-
console.warn("[snapdom] Failed to fetch font URL:", url);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleVal};font-weight:${weightVal};}`;
|
|
973
|
-
}
|
|
1501
|
+
if (sheet.href && linkNodes.some((l) => l.href === sheet.href)) continue;
|
|
1502
|
+
const rules = sheet.cssRules || [];
|
|
1503
|
+
for (const rule of rules) {
|
|
1504
|
+
if (rule.type !== CSSRule.FONT_FACE_RULE) continue;
|
|
1505
|
+
const famRaw = (rule.style.getPropertyValue("font-family") || "").trim();
|
|
1506
|
+
const family = pickPrimaryFamily(famRaw);
|
|
1507
|
+
if (!family || isIconFont2(family)) continue;
|
|
1508
|
+
const weightSpec = (rule.style.getPropertyValue("font-weight") || "400").trim();
|
|
1509
|
+
const styleSpec = (rule.style.getPropertyValue("font-style") || "normal").trim();
|
|
1510
|
+
const stretchSpec = (rule.style.getPropertyValue("font-stretch") || "100%").trim();
|
|
1511
|
+
const srcRaw = (rule.style.getPropertyValue("src") || "").trim();
|
|
1512
|
+
const urange = (rule.style.getPropertyValue("unicode-range") || "").trim();
|
|
1513
|
+
const srcUrls = extractSrcUrls(srcRaw, sheet.href || location.href);
|
|
1514
|
+
if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) continue;
|
|
1515
|
+
const ranges = parseUnicodeRange(urange);
|
|
1516
|
+
if (!unicodeIntersects(usedCodepoints, ranges)) continue;
|
|
1517
|
+
const meta = { family, weightSpec, styleSpec, stretchSpec, unicodeRange: urange, srcRaw, srcUrls, href: sheet.href || location.href };
|
|
1518
|
+
if (exclude && simpleExcluder(meta, ranges)) continue;
|
|
1519
|
+
if (/url\(/i.test(srcRaw)) {
|
|
1520
|
+
const inlinedSrc = yield inlineUrlsInCssBlock(srcRaw, sheet.href || location.href);
|
|
1521
|
+
finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`;
|
|
1522
|
+
} else {
|
|
1523
|
+
finalCSS += `@font-face{font-family:${family};src:${srcRaw};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`;
|
|
974
1524
|
}
|
|
975
1525
|
}
|
|
976
1526
|
} catch (e) {
|
|
977
|
-
console.warn("[snapdom] Cannot access stylesheet", sheet.href, e);
|
|
978
1527
|
}
|
|
979
1528
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
if (
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1529
|
+
try {
|
|
1530
|
+
for (const f of document.fonts || []) {
|
|
1531
|
+
if (!f || !f.family || f.status !== "loaded" || !f._snapdomSrc) continue;
|
|
1532
|
+
const fam = String(f.family).replace(/^['"]+|['"]+$/g, "");
|
|
1533
|
+
if (isIconFont2(fam)) continue;
|
|
1534
|
+
if (!requiredIndex.has(fam)) continue;
|
|
1535
|
+
if ((exclude == null ? void 0 : exclude.families) && exclude.families.some((n) => String(n).toLowerCase() === fam.toLowerCase())) {
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
let b64 = f._snapdomSrc;
|
|
1539
|
+
if (!String(b64).startsWith("data:")) {
|
|
1540
|
+
if ((_h = cache.resource) == null ? void 0 : _h.has(f._snapdomSrc)) {
|
|
1541
|
+
b64 = cache.resource.get(f._snapdomSrc);
|
|
1542
|
+
(_i = cache.font) == null ? void 0 : _i.add(f._snapdomSrc);
|
|
1543
|
+
} else if (!((_j = cache.font) == null ? void 0 : _j.has(f._snapdomSrc))) {
|
|
989
1544
|
try {
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
cache.font.add(font._snapdomSrc);
|
|
1545
|
+
const r = yield snapFetch(f._snapdomSrc, { as: "dataURL", useProxy, silent: true });
|
|
1546
|
+
if (r.ok && typeof r.data === "string") {
|
|
1547
|
+
b64 = r.data;
|
|
1548
|
+
(_k = cache.resource) == null ? void 0 : _k.set(f._snapdomSrc, b64);
|
|
1549
|
+
(_l = cache.font) == null ? void 0 : _l.add(f._snapdomSrc);
|
|
1550
|
+
} else {
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
999
1553
|
} catch (e) {
|
|
1000
|
-
console.warn("[snapdom] Failed to fetch dynamic font src:",
|
|
1554
|
+
console.warn("[snapdom] Failed to fetch dynamic font src:", f._snapdomSrc);
|
|
1001
1555
|
continue;
|
|
1002
1556
|
}
|
|
1003
1557
|
}
|
|
1004
1558
|
}
|
|
1005
|
-
finalCSS += `@font-face{font-family:'${
|
|
1559
|
+
finalCSS += `@font-face{font-family:'${fam}';src:url(${b64});font-style:${f.style || "normal"};font-weight:${f.weight || "normal"};}`;
|
|
1006
1560
|
}
|
|
1561
|
+
} catch (e) {
|
|
1007
1562
|
}
|
|
1008
1563
|
for (const font of localFonts) {
|
|
1009
1564
|
if (!font || typeof font !== "object") continue;
|
|
1010
|
-
const
|
|
1011
|
-
if (!family ||
|
|
1565
|
+
const family = String(font.family || "").replace(/^['"]+|['"]+$/g, "");
|
|
1566
|
+
if (!family || isIconFont2(family)) continue;
|
|
1567
|
+
if (!requiredIndex.has(family)) continue;
|
|
1568
|
+
if ((exclude == null ? void 0 : exclude.families) && exclude.families.some((n) => String(n).toLowerCase() === family.toLowerCase())) continue;
|
|
1569
|
+
const weight = font.weight != null ? String(font.weight) : "normal";
|
|
1570
|
+
const style = font.style != null ? String(font.style) : "normal";
|
|
1571
|
+
const stretch = font.stretchPct != null ? `${font.stretchPct}%` : "100%";
|
|
1572
|
+
const src = String(font.src || "");
|
|
1012
1573
|
let b64 = src;
|
|
1013
1574
|
if (!b64.startsWith("data:")) {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1575
|
+
if ((_m = cache.resource) == null ? void 0 : _m.has(src)) {
|
|
1576
|
+
b64 = cache.resource.get(src);
|
|
1577
|
+
(_n = cache.font) == null ? void 0 : _n.add(src);
|
|
1578
|
+
} else if (!((_o = cache.font) == null ? void 0 : _o.has(src))) {
|
|
1579
|
+
try {
|
|
1580
|
+
const r = yield snapFetch(src, { as: "dataURL", useProxy, silent: true });
|
|
1581
|
+
if (r.ok && typeof r.data === "string") {
|
|
1582
|
+
b64 = r.data;
|
|
1583
|
+
(_p = cache.resource) == null ? void 0 : _p.set(src, b64);
|
|
1584
|
+
(_q = cache.font) == null ? void 0 : _q.add(src);
|
|
1585
|
+
} else {
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1588
|
+
} catch (e) {
|
|
1589
|
+
console.warn("[snapdom] Failed to fetch localFonts src:", src);
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1027
1592
|
}
|
|
1028
|
-
} else {
|
|
1029
|
-
cache.resource.set(src, b64);
|
|
1030
|
-
cache.font.add(src);
|
|
1031
1593
|
}
|
|
1032
|
-
finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};}`;
|
|
1594
|
+
finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};font-stretch:${stretch};}`;
|
|
1033
1595
|
}
|
|
1034
1596
|
if (finalCSS) {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
const style = document.createElement("style");
|
|
1038
|
-
style.setAttribute("data-snapdom", "embedFonts");
|
|
1039
|
-
style.textContent = finalCSS;
|
|
1040
|
-
document.head.appendChild(style);
|
|
1041
|
-
}
|
|
1597
|
+
finalCSS = dedupeFontFaces(finalCSS);
|
|
1598
|
+
(_r = cache.resource) == null ? void 0 : _r.set(cacheKey, finalCSS);
|
|
1042
1599
|
}
|
|
1043
1600
|
return finalCSS;
|
|
1044
1601
|
});
|
|
1045
1602
|
}
|
|
1603
|
+
function collectUsedFontVariants(root) {
|
|
1604
|
+
const req = /* @__PURE__ */ new Set();
|
|
1605
|
+
if (!root) return req;
|
|
1606
|
+
const tw = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);
|
|
1607
|
+
const addFromStyle = (cs) => {
|
|
1608
|
+
const family = pickPrimaryFamily(cs.fontFamily);
|
|
1609
|
+
if (!family) return;
|
|
1610
|
+
const key = (w, s, st) => `${family}__${normWeight(w)}__${normStyle(s)}__${normStretchPct(st)}`;
|
|
1611
|
+
req.add(key(cs.fontWeight, cs.fontStyle, cs.fontStretch));
|
|
1612
|
+
};
|
|
1613
|
+
addFromStyle(getComputedStyle(root));
|
|
1614
|
+
const csBeforeRoot = getComputedStyle(root, "::before");
|
|
1615
|
+
if (csBeforeRoot && csBeforeRoot.content && csBeforeRoot.content !== "none") addFromStyle(csBeforeRoot);
|
|
1616
|
+
const csAfterRoot = getComputedStyle(root, "::after");
|
|
1617
|
+
if (csAfterRoot && csAfterRoot.content && csAfterRoot.content !== "none") addFromStyle(csAfterRoot);
|
|
1618
|
+
while (tw.nextNode()) {
|
|
1619
|
+
const el = (
|
|
1620
|
+
/** @type {Element} */
|
|
1621
|
+
tw.currentNode
|
|
1622
|
+
);
|
|
1623
|
+
const cs = getComputedStyle(el);
|
|
1624
|
+
addFromStyle(cs);
|
|
1625
|
+
const b = getComputedStyle(el, "::before");
|
|
1626
|
+
if (b && b.content && b.content !== "none") addFromStyle(b);
|
|
1627
|
+
const a = getComputedStyle(el, "::after");
|
|
1628
|
+
if (a && a.content && a.content !== "none") addFromStyle(a);
|
|
1629
|
+
}
|
|
1630
|
+
return req;
|
|
1631
|
+
}
|
|
1632
|
+
function collectUsedCodepoints(root) {
|
|
1633
|
+
const used = /* @__PURE__ */ new Set();
|
|
1634
|
+
const pushText = (txt) => {
|
|
1635
|
+
if (!txt) return;
|
|
1636
|
+
for (const ch of txt) used.add(ch.codePointAt(0));
|
|
1637
|
+
};
|
|
1638
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null);
|
|
1639
|
+
while (walker.nextNode()) {
|
|
1640
|
+
const n = walker.currentNode;
|
|
1641
|
+
if (n.nodeType === Node.TEXT_NODE) {
|
|
1642
|
+
pushText(n.nodeValue || "");
|
|
1643
|
+
} else if (n.nodeType === Node.ELEMENT_NODE) {
|
|
1644
|
+
const el = (
|
|
1645
|
+
/** @type {Element} */
|
|
1646
|
+
n
|
|
1647
|
+
);
|
|
1648
|
+
for (const pseudo of ["::before", "::after"]) {
|
|
1649
|
+
const cs = getComputedStyle(el, pseudo);
|
|
1650
|
+
const c = cs == null ? void 0 : cs.getPropertyValue("content");
|
|
1651
|
+
if (!c || c === "none") continue;
|
|
1652
|
+
if (/^"/.test(c) || /^'/.test(c)) {
|
|
1653
|
+
pushText(c.slice(1, -1));
|
|
1654
|
+
} else {
|
|
1655
|
+
const matches = c.match(/\\[0-9A-Fa-f]{1,6}/g);
|
|
1656
|
+
if (matches) {
|
|
1657
|
+
for (const m of matches) {
|
|
1658
|
+
try {
|
|
1659
|
+
used.add(parseInt(m.slice(1), 16));
|
|
1660
|
+
} catch (e) {
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
return used;
|
|
1669
|
+
}
|
|
1670
|
+
function ensureFontsReady(families, warmupRepetitions = 1) {
|
|
1671
|
+
return __async(this, null, function* () {
|
|
1672
|
+
try {
|
|
1673
|
+
yield document.fonts.ready;
|
|
1674
|
+
} catch (e) {
|
|
1675
|
+
}
|
|
1676
|
+
const fams = Array.from(families || []).filter(Boolean);
|
|
1677
|
+
if (fams.length === 0) return;
|
|
1678
|
+
const warmupOnce = () => {
|
|
1679
|
+
const container = document.createElement("div");
|
|
1680
|
+
container.style.cssText = `position: absolute !important;left: -9999px !important;top: 0 !important;opacity: 0 !important;pointer-events: none !important;contain: layout size style;`;
|
|
1681
|
+
for (const fam of fams) {
|
|
1682
|
+
const span = document.createElement("span");
|
|
1683
|
+
span.textContent = "AaBbGg1234\xC1\xC9\xCD\xD3\xDA\xE7\xF1\u2014\u221E";
|
|
1684
|
+
span.style.fontFamily = `"${fam}"`;
|
|
1685
|
+
span.style.fontWeight = "700";
|
|
1686
|
+
span.style.fontStyle = "italic";
|
|
1687
|
+
span.style.fontSize = "32px";
|
|
1688
|
+
span.style.lineHeight = "1";
|
|
1689
|
+
span.style.whiteSpace = "nowrap";
|
|
1690
|
+
span.style.margin = "0";
|
|
1691
|
+
span.style.padding = "0";
|
|
1692
|
+
container.appendChild(span);
|
|
1693
|
+
}
|
|
1694
|
+
document.body.appendChild(container);
|
|
1695
|
+
container.offsetWidth;
|
|
1696
|
+
document.body.removeChild(container);
|
|
1697
|
+
};
|
|
1698
|
+
for (let i = 0; i < Math.max(1, warmupRepetitions); i++) {
|
|
1699
|
+
warmupOnce();
|
|
1700
|
+
yield new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1046
1704
|
|
|
1047
1705
|
// src/modules/pseudo.js
|
|
1048
|
-
function inlinePseudoElements(source, clone,
|
|
1706
|
+
function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
1049
1707
|
return __async(this, null, function* () {
|
|
1050
1708
|
var _a;
|
|
1051
1709
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
@@ -1075,8 +1733,8 @@
|
|
|
1075
1733
|
span.textContent = first;
|
|
1076
1734
|
span.dataset.snapdomPseudo = "::first-letter";
|
|
1077
1735
|
const snapshot2 = snapshotComputedStyle(style);
|
|
1078
|
-
const key2 = getStyleKey(snapshot2, "span"
|
|
1079
|
-
styleMap.set(span, key2);
|
|
1736
|
+
const key2 = getStyleKey(snapshot2, "span");
|
|
1737
|
+
sessionCache.styleMap.set(span, key2);
|
|
1080
1738
|
const restNode = document.createTextNode(rest);
|
|
1081
1739
|
clone.replaceChild(restNode, textNode);
|
|
1082
1740
|
clone.insertBefore(span, restNode);
|
|
@@ -1096,7 +1754,7 @@
|
|
|
1096
1754
|
const borderStyle = style.borderStyle;
|
|
1097
1755
|
const borderWidth = parseFloat(style.borderWidth);
|
|
1098
1756
|
const transform = style.transform;
|
|
1099
|
-
const
|
|
1757
|
+
const isIconFont22 = isIconFont2(fontFamily);
|
|
1100
1758
|
const hasExplicitContent = content !== "none" && cleanContent !== "";
|
|
1101
1759
|
const hasBg = bg && bg !== "none";
|
|
1102
1760
|
const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
|
|
@@ -1109,9 +1767,9 @@
|
|
|
1109
1767
|
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
1110
1768
|
pseudoEl.style.verticalAlign = "middle";
|
|
1111
1769
|
const snapshot = snapshotComputedStyle(style);
|
|
1112
|
-
const key = getStyleKey(snapshot, "span"
|
|
1113
|
-
styleMap.set(pseudoEl, key);
|
|
1114
|
-
if (
|
|
1770
|
+
const key = getStyleKey(snapshot, "span");
|
|
1771
|
+
sessionCache.styleMap.set(pseudoEl, key);
|
|
1772
|
+
if (isIconFont22 && cleanContent.length === 1) {
|
|
1115
1773
|
const { dataUrl, width: width2, height: height2 } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
1116
1774
|
const imgEl = document.createElement("img");
|
|
1117
1775
|
imgEl.src = dataUrl;
|
|
@@ -1123,15 +1781,15 @@
|
|
|
1123
1781
|
if (rawUrl == null ? void 0 : rawUrl.trim()) {
|
|
1124
1782
|
try {
|
|
1125
1783
|
const imgEl = document.createElement("img");
|
|
1126
|
-
const dataUrl = yield
|
|
1127
|
-
imgEl.src = dataUrl;
|
|
1784
|
+
const dataUrl = yield snapFetch(safeEncodeURI(rawUrl), { as: "dataURL", useProxy: options.useProxy });
|
|
1785
|
+
imgEl.src = dataUrl.data;
|
|
1128
1786
|
imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
|
|
1129
1787
|
pseudoEl.appendChild(imgEl);
|
|
1130
1788
|
} catch (e) {
|
|
1131
1789
|
console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
|
|
1132
1790
|
}
|
|
1133
1791
|
}
|
|
1134
|
-
} else if (!
|
|
1792
|
+
} else if (!isIconFont22 && hasExplicitContent) {
|
|
1135
1793
|
pseudoEl.textContent = cleanContent;
|
|
1136
1794
|
}
|
|
1137
1795
|
if (hasBg) {
|
|
@@ -1159,7 +1817,7 @@
|
|
|
1159
1817
|
const sChildren = Array.from(source.children);
|
|
1160
1818
|
const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
|
|
1161
1819
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
1162
|
-
yield inlinePseudoElements(sChildren[i], cChildren[i],
|
|
1820
|
+
yield inlinePseudoElements(sChildren[i], cChildren[i], sessionCache, options);
|
|
1163
1821
|
}
|
|
1164
1822
|
});
|
|
1165
1823
|
}
|
|
@@ -1216,13 +1874,16 @@
|
|
|
1216
1874
|
|
|
1217
1875
|
// src/core/prepare.js
|
|
1218
1876
|
function prepareClone(_0) {
|
|
1219
|
-
return __async(this, arguments, function* (element,
|
|
1877
|
+
return __async(this, arguments, function* (element, options = {}) {
|
|
1220
1878
|
var _a, _b;
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
|
|
1879
|
+
const sessionCache = {
|
|
1880
|
+
styleMap: cache.session.styleMap,
|
|
1881
|
+
styleCache: cache.session.styleCache,
|
|
1882
|
+
nodeMap: cache.session.nodeMap
|
|
1883
|
+
};
|
|
1224
1884
|
let clone;
|
|
1225
1885
|
let classCSS = "";
|
|
1886
|
+
let shadowScopedCSS = "";
|
|
1226
1887
|
stabilizeLayout(element);
|
|
1227
1888
|
try {
|
|
1228
1889
|
inlineExternalDefsAndSymbols(element);
|
|
@@ -1230,43 +1891,45 @@
|
|
|
1230
1891
|
console.warn("inlineExternal defs or symbol failed:", e);
|
|
1231
1892
|
}
|
|
1232
1893
|
try {
|
|
1233
|
-
clone = deepClone(element,
|
|
1894
|
+
clone = deepClone(element, sessionCache, options, element);
|
|
1234
1895
|
} catch (e) {
|
|
1235
1896
|
console.warn("deepClone failed:", e);
|
|
1236
1897
|
throw e;
|
|
1237
1898
|
}
|
|
1238
1899
|
try {
|
|
1239
|
-
yield inlinePseudoElements(element, clone,
|
|
1900
|
+
yield inlinePseudoElements(element, clone, sessionCache, options);
|
|
1240
1901
|
} catch (e) {
|
|
1241
1902
|
console.warn("inlinePseudoElements failed:", e);
|
|
1242
1903
|
}
|
|
1243
1904
|
yield resolveBlobUrlsInTree(clone);
|
|
1244
|
-
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
|
|
1250
|
-
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
1251
|
-
continue;
|
|
1252
|
-
}
|
|
1253
|
-
const className = keyToClass.get(key);
|
|
1254
|
-
if (className) node.classList.add(className);
|
|
1255
|
-
const bgImage = (_a = node.style) == null ? void 0 : _a.backgroundImage;
|
|
1256
|
-
const hasIcon = (_b = node.dataset) == null ? void 0 : _b.snapdomHasIcon;
|
|
1257
|
-
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
1258
|
-
if (hasIcon) {
|
|
1259
|
-
node.style.verticalAlign = "middle";
|
|
1260
|
-
node.style.display = "inline";
|
|
1261
|
-
}
|
|
1905
|
+
try {
|
|
1906
|
+
const styleNodes = clone.querySelectorAll("style[data-sd]");
|
|
1907
|
+
for (const s of styleNodes) {
|
|
1908
|
+
shadowScopedCSS += s.textContent || "";
|
|
1909
|
+
s.remove();
|
|
1262
1910
|
}
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1911
|
+
} catch (e) {
|
|
1912
|
+
}
|
|
1913
|
+
const keyToClass = generateCSSClasses(sessionCache.styleMap);
|
|
1914
|
+
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
1915
|
+
classCSS = shadowScopedCSS + classCSS;
|
|
1916
|
+
for (const [node, key] of sessionCache.styleMap.entries()) {
|
|
1917
|
+
if (node.tagName === "STYLE") continue;
|
|
1918
|
+
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
|
|
1266
1919
|
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1922
|
+
const className = keyToClass.get(key);
|
|
1923
|
+
if (className) node.classList.add(className);
|
|
1924
|
+
const bgImage = (_a = node.style) == null ? void 0 : _a.backgroundImage;
|
|
1925
|
+
const hasIcon = (_b = node.dataset) == null ? void 0 : _b.snapdomHasIcon;
|
|
1926
|
+
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
1927
|
+
if (hasIcon) {
|
|
1928
|
+
node.style.verticalAlign = "middle";
|
|
1929
|
+
node.style.display = "inline";
|
|
1267
1930
|
}
|
|
1268
1931
|
}
|
|
1269
|
-
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1932
|
+
for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) {
|
|
1270
1933
|
const scrollX = originalNode.scrollLeft;
|
|
1271
1934
|
const scrollY = originalNode.scrollTop;
|
|
1272
1935
|
const hasScroll = scrollX || scrollY;
|
|
@@ -1285,9 +1948,9 @@
|
|
|
1285
1948
|
cloneNode.appendChild(inner);
|
|
1286
1949
|
}
|
|
1287
1950
|
}
|
|
1288
|
-
if (element === nodeMap.get(clone)) {
|
|
1289
|
-
const computed = styleCache.get(element) || window.getComputedStyle(element);
|
|
1290
|
-
styleCache.set(element, computed);
|
|
1951
|
+
if (element === sessionCache.nodeMap.get(clone)) {
|
|
1952
|
+
const computed = sessionCache.styleCache.get(element) || window.getComputedStyle(element);
|
|
1953
|
+
sessionCache.styleCache.set(element, computed);
|
|
1291
1954
|
const transform = stripTranslate(computed.transform);
|
|
1292
1955
|
clone.style.margin = "0";
|
|
1293
1956
|
clone.style.position = "static";
|
|
@@ -1300,13 +1963,13 @@
|
|
|
1300
1963
|
clone.style.clear = "none";
|
|
1301
1964
|
clone.style.transform = transform || "";
|
|
1302
1965
|
}
|
|
1303
|
-
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1966
|
+
for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) {
|
|
1304
1967
|
if (originalNode.tagName === "PRE") {
|
|
1305
1968
|
cloneNode.style.marginTop = "0";
|
|
1306
1969
|
cloneNode.style.marginBlockStart = "0";
|
|
1307
1970
|
}
|
|
1308
1971
|
}
|
|
1309
|
-
return { clone, classCSS, styleCache };
|
|
1972
|
+
return { clone, classCSS, styleCache: sessionCache.styleCache };
|
|
1310
1973
|
});
|
|
1311
1974
|
}
|
|
1312
1975
|
function stabilizeLayout(element) {
|
|
@@ -1324,22 +1987,27 @@
|
|
|
1324
1987
|
var _blobToDataUrlCache = /* @__PURE__ */ new Map();
|
|
1325
1988
|
function blobUrlToDataUrl(blobUrl) {
|
|
1326
1989
|
return __async(this, null, function* () {
|
|
1990
|
+
var _a;
|
|
1991
|
+
if ((_a = cache.resource) == null ? void 0 : _a.has(blobUrl)) return cache.resource.get(blobUrl);
|
|
1327
1992
|
if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl);
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1993
|
+
const p = (() => __async(null, null, function* () {
|
|
1994
|
+
var _a2;
|
|
1995
|
+
const r = yield snapFetch(blobUrl, { as: "dataURL", silent: true });
|
|
1996
|
+
if (!r.ok || typeof r.data !== "string") {
|
|
1997
|
+
throw new Error(`[snapDOM] Failed to read blob URL: ${blobUrl}`);
|
|
1998
|
+
}
|
|
1999
|
+
(_a2 = cache.resource) == null ? void 0 : _a2.set(blobUrl, r.data);
|
|
2000
|
+
return r.data;
|
|
2001
|
+
}))();
|
|
2002
|
+
_blobToDataUrlCache.set(blobUrl, p);
|
|
2003
|
+
try {
|
|
2004
|
+
const data = yield p;
|
|
2005
|
+
_blobToDataUrlCache.set(blobUrl, data);
|
|
2006
|
+
return data;
|
|
2007
|
+
} catch (e) {
|
|
2008
|
+
_blobToDataUrlCache.delete(blobUrl);
|
|
2009
|
+
throw e;
|
|
2010
|
+
}
|
|
1343
2011
|
});
|
|
1344
2012
|
}
|
|
1345
2013
|
var BLOB_URL_RE = /\bblob:[^)"'\s]+/g;
|
|
@@ -1463,18 +2131,31 @@
|
|
|
1463
2131
|
}
|
|
1464
2132
|
img.removeAttribute("srcset");
|
|
1465
2133
|
img.removeAttribute("sizes");
|
|
1466
|
-
const src = img.src;
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
2134
|
+
const src = img.src || "";
|
|
2135
|
+
if (!src) return;
|
|
2136
|
+
const r = yield snapFetch(src, { as: "dataURL", useProxy: options.useProxy });
|
|
2137
|
+
if (r.ok && typeof r.data === "string" && r.data.startsWith("data:")) {
|
|
2138
|
+
img.src = r.data;
|
|
1470
2139
|
if (!img.width) img.width = img.naturalWidth || 100;
|
|
1471
2140
|
if (!img.height) img.height = img.naturalHeight || 100;
|
|
1472
|
-
|
|
1473
|
-
const fallback = document.createElement("div");
|
|
1474
|
-
fallback.style = `width: ${img.width || 100}px; height: ${img.height || 100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${img.height || 100}px; color: #666; font-size: 12px;`;
|
|
1475
|
-
fallback.innerText = "img";
|
|
1476
|
-
img.replaceWith(fallback);
|
|
2141
|
+
return;
|
|
1477
2142
|
}
|
|
2143
|
+
const w = img.width || img.naturalWidth || 100;
|
|
2144
|
+
const h = img.height || img.naturalHeight || 100;
|
|
2145
|
+
const fallback = document.createElement("div");
|
|
2146
|
+
fallback.style.cssText = [
|
|
2147
|
+
`width:${w}px`,
|
|
2148
|
+
`height:${h}px`,
|
|
2149
|
+
"background:#ccc",
|
|
2150
|
+
"display:inline-block",
|
|
2151
|
+
"text-align:center",
|
|
2152
|
+
`line-height:${h}px`,
|
|
2153
|
+
"color:#666",
|
|
2154
|
+
"font-size:12px",
|
|
2155
|
+
"overflow:hidden"
|
|
2156
|
+
].join(";");
|
|
2157
|
+
fallback.textContent = "img";
|
|
2158
|
+
img.replaceWith(fallback);
|
|
1478
2159
|
});
|
|
1479
2160
|
for (let i = 0; i < imgs.length; i += 4) {
|
|
1480
2161
|
const group = imgs.slice(i, i + 4).map(processImg);
|
|
@@ -1526,10 +2207,6 @@
|
|
|
1526
2207
|
cloneNode.style.setProperty(prop, inlined.join(", "));
|
|
1527
2208
|
}
|
|
1528
2209
|
}
|
|
1529
|
-
const bgColor = style.getPropertyValue("background-color");
|
|
1530
|
-
if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") {
|
|
1531
|
-
cloneNode.style.backgroundColor = bgColor;
|
|
1532
|
-
}
|
|
1533
2210
|
const sChildren = Array.from(srcNode.children);
|
|
1534
2211
|
const cChildren = Array.from(cloneNode.children);
|
|
1535
2212
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
@@ -1540,17 +2217,17 @@
|
|
|
1540
2217
|
}
|
|
1541
2218
|
|
|
1542
2219
|
// src/core/capture.js
|
|
1543
|
-
function captureDOM(
|
|
1544
|
-
return __async(this,
|
|
2220
|
+
function captureDOM(element, options) {
|
|
2221
|
+
return __async(this, null, function* () {
|
|
1545
2222
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1546
|
-
cache
|
|
1547
|
-
const
|
|
2223
|
+
applyCachePolicy(options.cache);
|
|
2224
|
+
const fast = options.fast;
|
|
1548
2225
|
let clone, classCSS, styleCache;
|
|
1549
2226
|
let fontsCSS = "";
|
|
1550
2227
|
let baseCSS = "";
|
|
1551
2228
|
let dataURL;
|
|
1552
2229
|
let svgString;
|
|
1553
|
-
({ clone, classCSS, styleCache } = yield prepareClone(element,
|
|
2230
|
+
({ clone, classCSS, styleCache } = yield prepareClone(element, options));
|
|
1554
2231
|
yield new Promise((resolve) => {
|
|
1555
2232
|
idle(() => __async(null, null, function* () {
|
|
1556
2233
|
yield inlineImages(clone, options);
|
|
@@ -1563,28 +2240,34 @@
|
|
|
1563
2240
|
resolve();
|
|
1564
2241
|
}), { fast });
|
|
1565
2242
|
});
|
|
1566
|
-
if (embedFonts) {
|
|
2243
|
+
if (options.embedFonts) {
|
|
1567
2244
|
yield new Promise((resolve) => {
|
|
1568
2245
|
idle(() => __async(null, null, function* () {
|
|
1569
|
-
|
|
2246
|
+
const required = collectUsedFontVariants(element);
|
|
2247
|
+
const usedCodepoints = collectUsedCodepoints(element);
|
|
2248
|
+
if (isSafari) {
|
|
2249
|
+
const families = new Set(
|
|
2250
|
+
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2251
|
+
);
|
|
2252
|
+
yield ensureFontsReady(families, 2);
|
|
2253
|
+
}
|
|
2254
|
+
fontsCSS = yield embedCustomFonts({ required, usedCodepoints, preCached: false, exclude: options.excludeFonts, useProxy: options.useProxy });
|
|
1570
2255
|
resolve();
|
|
1571
2256
|
}), { fast });
|
|
1572
2257
|
});
|
|
1573
2258
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
});
|
|
1587
|
-
}
|
|
2259
|
+
const usedTags = collectUsedTagNames(clone).sort();
|
|
2260
|
+
const tagKey = usedTags.join(",");
|
|
2261
|
+
if (cache.baseStyle.has(tagKey)) {
|
|
2262
|
+
baseCSS = cache.baseStyle.get(tagKey);
|
|
2263
|
+
} else {
|
|
2264
|
+
yield new Promise((resolve) => {
|
|
2265
|
+
idle(() => {
|
|
2266
|
+
baseCSS = generateDedupedBaseCSS(usedTags);
|
|
2267
|
+
cache.baseStyle.set(tagKey, baseCSS);
|
|
2268
|
+
resolve();
|
|
2269
|
+
}, { fast });
|
|
2270
|
+
});
|
|
1588
2271
|
}
|
|
1589
2272
|
yield new Promise((resolve) => {
|
|
1590
2273
|
idle(() => {
|
|
@@ -1643,26 +2326,77 @@
|
|
|
1643
2326
|
});
|
|
1644
2327
|
}
|
|
1645
2328
|
|
|
1646
|
-
// src/
|
|
1647
|
-
function
|
|
1648
|
-
|
|
2329
|
+
// src/core/context.js
|
|
2330
|
+
function normalizeCachePolicy(v) {
|
|
2331
|
+
if (v === true) return "soft";
|
|
2332
|
+
if (v === false) return "disabled";
|
|
2333
|
+
if (typeof v === "string") {
|
|
2334
|
+
const s = v.toLowerCase().trim();
|
|
2335
|
+
if (s === "disabled" || s === "full" || s === "auto" || s === "soft") return (
|
|
2336
|
+
/** @type {CachePolicy} */
|
|
2337
|
+
s
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2340
|
+
return "soft";
|
|
2341
|
+
}
|
|
2342
|
+
function createContext(options = {}) {
|
|
2343
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
2344
|
+
const resolvedFormat = (_a = options.format) != null ? _a : "png";
|
|
2345
|
+
const cachePolicy = normalizeCachePolicy(options.cache);
|
|
2346
|
+
return {
|
|
2347
|
+
// Debug & perf
|
|
2348
|
+
debug: (_b = options.debug) != null ? _b : false,
|
|
2349
|
+
fast: (_c = options.fast) != null ? _c : true,
|
|
2350
|
+
scale: (_d = options.scale) != null ? _d : 1,
|
|
2351
|
+
// DOM filters
|
|
2352
|
+
exclude: (_e = options.exclude) != null ? _e : [],
|
|
2353
|
+
filter: (_f = options.filter) != null ? _f : null,
|
|
2354
|
+
// Fuentes
|
|
2355
|
+
embedFonts: (_g = options.embedFonts) != null ? _g : false,
|
|
2356
|
+
iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
|
|
2357
|
+
localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
|
|
2358
|
+
excludeFonts: (_h = options.excludeFonts) != null ? _h : void 0,
|
|
2359
|
+
/** Cache policy (disabled|full|auto|soft). Default: "soft" */
|
|
2360
|
+
/** @type {CachePolicy} */
|
|
2361
|
+
cache: cachePolicy,
|
|
2362
|
+
// Red
|
|
2363
|
+
useProxy: typeof options.useProxy === "string" ? options.useProxy : "",
|
|
2364
|
+
// Salida
|
|
2365
|
+
width: (_i = options.width) != null ? _i : null,
|
|
2366
|
+
height: (_j = options.height) != null ? _j : null,
|
|
2367
|
+
format: resolvedFormat,
|
|
2368
|
+
type: (_k = options.type) != null ? _k : "svg",
|
|
2369
|
+
quality: (_l = options.quality) != null ? _l : 0.92,
|
|
2370
|
+
dpr: (_m = options.dpr) != null ? _m : window.devicePixelRatio || 1,
|
|
2371
|
+
backgroundColor: (_n = options.backgroundColor) != null ? _n : ["jpg", "jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null,
|
|
2372
|
+
filename: (_o = options.filename) != null ? _o : "snapDOM"
|
|
2373
|
+
// Plugins (reservado para futura activación)
|
|
2374
|
+
// plugins: normalizePlugins(...),
|
|
2375
|
+
};
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
// src/exporters/toImg.js
|
|
2379
|
+
function toImg(url, options) {
|
|
2380
|
+
return __async(this, null, function* () {
|
|
1649
2381
|
const img = new Image();
|
|
1650
2382
|
img.src = url;
|
|
1651
2383
|
yield img.decode();
|
|
1652
|
-
if (scale !== 1) {
|
|
1653
|
-
img.style.width = `${img.naturalWidth * scale}px`;
|
|
1654
|
-
img.style.height = `${img.naturalHeight * scale}px`;
|
|
2384
|
+
if (options.scale !== 1) {
|
|
2385
|
+
img.style.width = `${img.naturalWidth * options.scale}px`;
|
|
2386
|
+
img.style.height = `${img.naturalHeight * options.scale}px`;
|
|
1655
2387
|
}
|
|
1656
2388
|
return img;
|
|
1657
2389
|
});
|
|
1658
2390
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
2391
|
+
|
|
2392
|
+
// src/exporters/toCanvas.js
|
|
2393
|
+
function toCanvas(url, options) {
|
|
2394
|
+
return __async(this, null, function* () {
|
|
1661
2395
|
const img = new Image();
|
|
1662
|
-
img.src = url;
|
|
1663
2396
|
img.crossOrigin = "anonymous";
|
|
1664
2397
|
img.loading = "eager";
|
|
1665
2398
|
img.decoding = "sync";
|
|
2399
|
+
img.src = url;
|
|
1666
2400
|
const isSafariBrowser = isSafari();
|
|
1667
2401
|
let appended = false;
|
|
1668
2402
|
if (isSafariBrowser) {
|
|
@@ -1670,16 +2404,11 @@
|
|
|
1670
2404
|
appended = true;
|
|
1671
2405
|
}
|
|
1672
2406
|
yield img.decode();
|
|
1673
|
-
if (isSafariBrowser)
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
if (img.width === 0 || img.height === 0) {
|
|
1677
|
-
if (appended) img.remove();
|
|
1678
|
-
throw new Error("Image failed to load or has no dimensions");
|
|
1679
|
-
}
|
|
1680
|
-
const width = img.naturalWidth * scale;
|
|
1681
|
-
const height = img.naturalHeight * scale;
|
|
2407
|
+
if (isSafariBrowser) yield new Promise((resolve) => setTimeout(resolve, 100));
|
|
2408
|
+
const width = img.naturalWidth * options.scale;
|
|
2409
|
+
const height = img.naturalHeight * options.scale;
|
|
1682
2410
|
const canvas = document.createElement("canvas");
|
|
2411
|
+
const dpr = options.dpr;
|
|
1683
2412
|
canvas.width = Math.ceil(width * dpr);
|
|
1684
2413
|
canvas.height = Math.ceil(height * dpr);
|
|
1685
2414
|
canvas.style.width = `${width}px`;
|
|
@@ -1691,180 +2420,144 @@
|
|
|
1691
2420
|
return canvas;
|
|
1692
2421
|
});
|
|
1693
2422
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
quality
|
|
1700
|
-
} = {}) {
|
|
1701
|
-
const mime = {
|
|
1702
|
-
jpg: "image/jpeg",
|
|
1703
|
-
jpeg: "image/jpeg",
|
|
1704
|
-
png: "image/png",
|
|
1705
|
-
webp: "image/webp"
|
|
1706
|
-
}[type] || "image/png";
|
|
2423
|
+
|
|
2424
|
+
// src/exporters/toBlob.js
|
|
2425
|
+
function toBlob(url, options) {
|
|
2426
|
+
return __async(this, null, function* () {
|
|
2427
|
+
const type = options.type;
|
|
1707
2428
|
if (type === "svg") {
|
|
1708
2429
|
const svgText = decodeURIComponent(url.split(",")[1]);
|
|
1709
2430
|
return new Blob([svgText], { type: "image/svg+xml" });
|
|
1710
2431
|
}
|
|
1711
|
-
const canvas = yield
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
if (!backgroundColor) return baseCanvas;
|
|
1721
|
-
const temp = document.createElement("canvas");
|
|
1722
|
-
temp.width = baseCanvas.width;
|
|
1723
|
-
temp.height = baseCanvas.height;
|
|
1724
|
-
const ctx = temp.getContext("2d");
|
|
1725
|
-
ctx.fillStyle = backgroundColor;
|
|
1726
|
-
ctx.fillRect(0, 0, temp.width, temp.height);
|
|
1727
|
-
ctx.drawImage(baseCanvas, 0, 0);
|
|
1728
|
-
return temp;
|
|
2432
|
+
const canvas = yield toCanvas(url, options);
|
|
2433
|
+
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
2434
|
+
return new Promise(
|
|
2435
|
+
(resolve) => finalCanvas.toBlob(
|
|
2436
|
+
(blob) => resolve(blob),
|
|
2437
|
+
`image/${type}`,
|
|
2438
|
+
options.quality
|
|
2439
|
+
)
|
|
2440
|
+
);
|
|
1729
2441
|
});
|
|
1730
2442
|
}
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
const canvas = yield
|
|
2443
|
+
|
|
2444
|
+
// src/modules/rasterize.js
|
|
2445
|
+
function rasterize(url, options) {
|
|
2446
|
+
return __async(this, null, function* () {
|
|
2447
|
+
const canvas = yield toCanvas(url, options);
|
|
2448
|
+
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
1736
2449
|
const img = new Image();
|
|
1737
|
-
img.src =
|
|
2450
|
+
img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
1738
2451
|
yield img.decode();
|
|
1739
|
-
img.style.width = `${
|
|
1740
|
-
img.style.height = `${
|
|
2452
|
+
img.style.width = `${finalCanvas.width / options.dpr}px`;
|
|
2453
|
+
img.style.height = `${finalCanvas.height / options.dpr}px`;
|
|
1741
2454
|
return img;
|
|
1742
2455
|
});
|
|
1743
2456
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
2457
|
+
|
|
2458
|
+
// src/exporters/download.js
|
|
2459
|
+
function download(url, options) {
|
|
2460
|
+
return __async(this, null, function* () {
|
|
2461
|
+
if (options.format === "svg") {
|
|
2462
|
+
const blob = yield toBlob(url, __spreadProps(__spreadValues({}, options), { type: "svg" }));
|
|
1748
2463
|
const objectURL = URL.createObjectURL(blob);
|
|
1749
2464
|
const a2 = document.createElement("a");
|
|
1750
2465
|
a2.href = objectURL;
|
|
1751
|
-
a2.download =
|
|
2466
|
+
a2.download = options.filename;
|
|
1752
2467
|
a2.click();
|
|
1753
2468
|
URL.revokeObjectURL(objectURL);
|
|
1754
2469
|
return;
|
|
1755
2470
|
}
|
|
1756
|
-
const
|
|
1757
|
-
const
|
|
1758
|
-
const canvas = yield createBackground(url, { dpr, scale }, finalBg);
|
|
1759
|
-
const mime = {
|
|
1760
|
-
jpg: "image/jpeg",
|
|
1761
|
-
jpeg: "image/jpeg",
|
|
1762
|
-
png: "image/png",
|
|
1763
|
-
webp: "image/webp"
|
|
1764
|
-
}[format] || "image/png";
|
|
1765
|
-
const dataURL = canvas.toDataURL(mime);
|
|
2471
|
+
const canvas = yield toCanvas(url, options);
|
|
2472
|
+
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
1766
2473
|
const a = document.createElement("a");
|
|
1767
|
-
a.href =
|
|
1768
|
-
a.download =
|
|
2474
|
+
a.href = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
2475
|
+
a.download = options.filename;
|
|
1769
2476
|
a.click();
|
|
1770
2477
|
});
|
|
1771
2478
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
2479
|
+
|
|
2480
|
+
// src/api/snapdom.js
|
|
2481
|
+
var INTERNAL_TOKEN = Symbol("snapdom.internal");
|
|
2482
|
+
function snapdom(element, userOptions) {
|
|
2483
|
+
return __async(this, null, function* () {
|
|
1775
2484
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
return yield snapdom.capture(element, options);
|
|
2485
|
+
const context = createContext(userOptions);
|
|
2486
|
+
if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
|
|
2487
|
+
return snapdom.capture(element, context, INTERNAL_TOKEN);
|
|
1780
2488
|
});
|
|
1781
2489
|
}
|
|
1782
|
-
snapdom.capture = (
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
const
|
|
2490
|
+
snapdom.capture = (el, context, _token) => __async(null, null, function* () {
|
|
2491
|
+
if (_token !== INTERNAL_TOKEN) {
|
|
2492
|
+
throw new Error("[snapdom.capture] is internal. Use snapdom(...) instead.");
|
|
2493
|
+
}
|
|
2494
|
+
const url = yield captureDOM(el, context);
|
|
2495
|
+
const ensureContext = (opts) => __spreadValues(__spreadValues({}, context), opts || {});
|
|
2496
|
+
const withFormat = (format) => (opts) => rasterize(url, ensureContext(__spreadProps(__spreadValues({}, opts || {}), { format })));
|
|
1787
2497
|
return {
|
|
1788
2498
|
url,
|
|
1789
|
-
options,
|
|
1790
2499
|
toRaw: () => url,
|
|
1791
|
-
toImg: (opts
|
|
1792
|
-
toCanvas: (opts
|
|
1793
|
-
toBlob: (opts
|
|
1794
|
-
toPng: (
|
|
1795
|
-
toJpg: (
|
|
1796
|
-
toWebp: (
|
|
1797
|
-
download: (
|
|
1798
|
-
var _c = _b, { format = "png", filename = "snapDOM", backgroundColor } = _c, opts = __objRest(_c, ["format", "filename", "backgroundColor"]);
|
|
1799
|
-
return download(url, __spreadValues({ dpr, scale, format, filename, backgroundColor }, opts));
|
|
1800
|
-
}
|
|
2500
|
+
toImg: (opts) => toImg(url, ensureContext(opts)),
|
|
2501
|
+
toCanvas: (opts) => toCanvas(url, ensureContext(opts)),
|
|
2502
|
+
toBlob: (opts) => toBlob(url, ensureContext(opts)),
|
|
2503
|
+
toPng: withFormat("png"),
|
|
2504
|
+
toJpg: withFormat("jpeg"),
|
|
2505
|
+
toWebp: withFormat("webp"),
|
|
2506
|
+
download: (opts) => download(url, ensureContext(opts))
|
|
1801
2507
|
};
|
|
1802
2508
|
});
|
|
1803
|
-
snapdom.toRaw = (el, options) =>
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
snapdom.
|
|
1807
|
-
|
|
1808
|
-
});
|
|
1809
|
-
snapdom.
|
|
1810
|
-
|
|
1811
|
-
});
|
|
1812
|
-
snapdom.toBlob = (el, options) => __async(null, null, function* () {
|
|
1813
|
-
return (yield snapdom.capture(el, options)).toBlob(options);
|
|
1814
|
-
});
|
|
1815
|
-
snapdom.toPng = (el, options) => __async(null, null, function* () {
|
|
1816
|
-
return (yield snapdom.capture(el, options)).toPng(options);
|
|
1817
|
-
});
|
|
1818
|
-
snapdom.toJpg = (el, options) => __async(null, null, function* () {
|
|
1819
|
-
return (yield snapdom.capture(el, options)).toJpg(options);
|
|
1820
|
-
});
|
|
1821
|
-
snapdom.toWebp = (el, options) => __async(null, null, function* () {
|
|
1822
|
-
return (yield snapdom.capture(el, options)).toWebp(options);
|
|
1823
|
-
});
|
|
1824
|
-
snapdom.download = (_0, ..._1) => __async(null, [_0, ..._1], function* (el, options = {}) {
|
|
1825
|
-
const _a = options, {
|
|
1826
|
-
format = "png",
|
|
1827
|
-
filename = "capture",
|
|
1828
|
-
backgroundColor
|
|
1829
|
-
} = _a, rest = __objRest(_a, [
|
|
1830
|
-
"format",
|
|
1831
|
-
"filename",
|
|
1832
|
-
"backgroundColor"
|
|
1833
|
-
]);
|
|
1834
|
-
const capture = yield snapdom.capture(el, rest);
|
|
1835
|
-
return yield capture.download({ format, filename, backgroundColor });
|
|
1836
|
-
});
|
|
2509
|
+
snapdom.toRaw = (el, options) => snapdom(el, options).then((result) => result.toRaw());
|
|
2510
|
+
snapdom.toImg = (el, options) => snapdom(el, options).then((result) => result.toImg());
|
|
2511
|
+
snapdom.toCanvas = (el, options) => snapdom(el, options).then((result) => result.toCanvas());
|
|
2512
|
+
snapdom.toBlob = (el, options) => snapdom(el, options).then((result) => result.toBlob());
|
|
2513
|
+
snapdom.toPng = (el, options) => snapdom(el, __spreadProps(__spreadValues({}, options), { format: "png" })).then((result) => result.toPng());
|
|
2514
|
+
snapdom.toJpg = (el, options) => snapdom(el, __spreadProps(__spreadValues({}, options), { format: "jpeg" })).then((result) => result.toJpg());
|
|
2515
|
+
snapdom.toWebp = (el, options) => snapdom(el, __spreadProps(__spreadValues({}, options), { format: "webp" })).then((result) => result.toWebp());
|
|
2516
|
+
snapdom.download = (el, options) => snapdom(el, options).then((result) => result.download());
|
|
1837
2517
|
|
|
1838
2518
|
// src/api/preCache.js
|
|
1839
2519
|
function preCache() {
|
|
1840
2520
|
return __async(this, arguments, function* (root = document, options = {}) {
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
cache.font.clear();
|
|
1849
|
-
cache.computedStyle = /* @__PURE__ */ new WeakMap();
|
|
1850
|
-
return;
|
|
1851
|
-
}
|
|
2521
|
+
var _a;
|
|
2522
|
+
const {
|
|
2523
|
+
embedFonts = true,
|
|
2524
|
+
cacheOpt = "full",
|
|
2525
|
+
useProxy = ""
|
|
2526
|
+
} = options;
|
|
2527
|
+
applyCachePolicy(cacheOpt);
|
|
1852
2528
|
try {
|
|
1853
2529
|
yield document.fonts.ready;
|
|
1854
2530
|
} catch (e) {
|
|
1855
2531
|
}
|
|
1856
2532
|
precacheCommonTags();
|
|
2533
|
+
cache.session = cache.session || {};
|
|
2534
|
+
if (!cache.session.styleCache) {
|
|
2535
|
+
cache.session.styleCache = /* @__PURE__ */ new WeakMap();
|
|
2536
|
+
}
|
|
2537
|
+
try {
|
|
2538
|
+
yield inlineBackgroundImages(
|
|
2539
|
+
root,
|
|
2540
|
+
/* mirror */
|
|
2541
|
+
void 0,
|
|
2542
|
+
cache.session.styleCache,
|
|
2543
|
+
{ useProxy }
|
|
2544
|
+
);
|
|
2545
|
+
} catch (e) {
|
|
2546
|
+
}
|
|
1857
2547
|
let imgEls = [], allEls = [];
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
2548
|
+
try {
|
|
2549
|
+
if (root == null ? void 0 : root.querySelectorAll) {
|
|
2550
|
+
imgEls = Array.from(root.querySelectorAll("img[src]"));
|
|
2551
|
+
allEls = Array.from(root.querySelectorAll("*"));
|
|
2552
|
+
}
|
|
2553
|
+
} catch (e) {
|
|
1861
2554
|
}
|
|
1862
2555
|
const promises = [];
|
|
1863
2556
|
for (const img of imgEls) {
|
|
1864
2557
|
const src = img == null ? void 0 : img.src;
|
|
1865
2558
|
if (!src) continue;
|
|
1866
2559
|
if (!cache.image.has(src)) {
|
|
1867
|
-
const p = Promise.resolve().then(() =>
|
|
2560
|
+
const p = Promise.resolve().then(() => snapFetch(src, { as: "dataURL", useProxy })).then((dataURL) => {
|
|
1868
2561
|
cache.image.set(src, dataURL);
|
|
1869
2562
|
}).catch(() => {
|
|
1870
2563
|
});
|
|
@@ -1878,8 +2571,8 @@
|
|
|
1878
2571
|
} catch (e) {
|
|
1879
2572
|
}
|
|
1880
2573
|
if (bg && bg !== "none") {
|
|
1881
|
-
const
|
|
1882
|
-
for (const entry of
|
|
2574
|
+
const parts = splitBackgroundImage(bg);
|
|
2575
|
+
for (const entry of parts) {
|
|
1883
2576
|
if (entry.startsWith("url(")) {
|
|
1884
2577
|
const p = Promise.resolve().then(() => inlineSingleBackgroundEntry(entry, __spreadProps(__spreadValues({}, options), { useProxy }))).catch(() => {
|
|
1885
2578
|
});
|
|
@@ -1890,10 +2583,23 @@
|
|
|
1890
2583
|
}
|
|
1891
2584
|
if (embedFonts) {
|
|
1892
2585
|
try {
|
|
1893
|
-
|
|
2586
|
+
const required = collectUsedFontVariants(root);
|
|
2587
|
+
const usedCodepoints = collectUsedCodepoints(root);
|
|
2588
|
+
if (typeof isSafari === "function" ? isSafari() : !!isSafari) {
|
|
2589
|
+
const families = new Set(
|
|
2590
|
+
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2591
|
+
);
|
|
2592
|
+
yield ensureFontsReady(families, 2);
|
|
2593
|
+
}
|
|
2594
|
+
yield embedCustomFonts({
|
|
2595
|
+
required,
|
|
2596
|
+
usedCodepoints,
|
|
2597
|
+
exclude: options.excludeFonts,
|
|
2598
|
+
localFonts: options.localFonts,
|
|
2599
|
+
useProxy: (_a = options.useProxy) != null ? _a : useProxy
|
|
2600
|
+
});
|
|
1894
2601
|
} catch (e) {
|
|
1895
2602
|
}
|
|
1896
|
-
;
|
|
1897
2603
|
}
|
|
1898
2604
|
yield Promise.allSettled(promises);
|
|
1899
2605
|
});
|