@zumer/snapdom 1.9.9 → 1.9.10-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -84
- package/dist/snapdom.js +1542 -811
- package/dist/snapdom.min.js +5 -3
- package/dist/snapdom.min.mjs +5 -3
- package/dist/snapdom.mjs +1517 -767
- 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.1
|
|
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,313 @@
|
|
|
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
|
+
}
|
|
77
134
|
}
|
|
78
135
|
|
|
79
|
-
// src/
|
|
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
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
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
|
+
"g",
|
|
348
|
+
"defs",
|
|
349
|
+
"use",
|
|
350
|
+
"marker",
|
|
351
|
+
"mask",
|
|
352
|
+
"clipPath",
|
|
353
|
+
"pattern",
|
|
354
|
+
"path",
|
|
355
|
+
"polygon",
|
|
356
|
+
"polyline",
|
|
357
|
+
"line",
|
|
358
|
+
"circle",
|
|
359
|
+
"ellipse",
|
|
360
|
+
"rect",
|
|
361
|
+
"filter",
|
|
362
|
+
"lineargradient",
|
|
363
|
+
"radialgradient",
|
|
364
|
+
"stop"
|
|
365
|
+
]);
|
|
80
366
|
var commonTags = [
|
|
81
367
|
"div",
|
|
82
368
|
"span",
|
|
@@ -103,12 +389,6 @@
|
|
|
103
389
|
"h4",
|
|
104
390
|
"h5",
|
|
105
391
|
"h6",
|
|
106
|
-
"svg",
|
|
107
|
-
"path",
|
|
108
|
-
"circle",
|
|
109
|
-
"rect",
|
|
110
|
-
"line",
|
|
111
|
-
"g",
|
|
112
392
|
"table",
|
|
113
393
|
"thead",
|
|
114
394
|
"tbody",
|
|
@@ -118,28 +398,33 @@
|
|
|
118
398
|
];
|
|
119
399
|
function precacheCommonTags() {
|
|
120
400
|
for (let tag of commonTags) {
|
|
121
|
-
|
|
401
|
+
const t = String(tag).toLowerCase();
|
|
402
|
+
if (NO_CAPTURE_TAGS.has(t)) continue;
|
|
403
|
+
if (NO_DEFAULTS_TAGS.has(t)) continue;
|
|
404
|
+
getDefaultStyleForTag(t);
|
|
122
405
|
}
|
|
123
406
|
}
|
|
124
407
|
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)) {
|
|
408
|
+
tagName = String(tagName).toLowerCase();
|
|
409
|
+
if (NO_DEFAULTS_TAGS.has(tagName)) {
|
|
130
410
|
const empty = {};
|
|
131
411
|
cache.defaultStyle.set(tagName, empty);
|
|
132
412
|
return empty;
|
|
133
413
|
}
|
|
414
|
+
if (cache.defaultStyle.has(tagName)) {
|
|
415
|
+
return cache.defaultStyle.get(tagName);
|
|
416
|
+
}
|
|
134
417
|
let sandbox = document.getElementById("snapdom-sandbox");
|
|
135
418
|
if (!sandbox) {
|
|
136
419
|
sandbox = document.createElement("div");
|
|
137
420
|
sandbox.id = "snapdom-sandbox";
|
|
421
|
+
sandbox.setAttribute("data-snapdom-sandbox", "true");
|
|
422
|
+
sandbox.setAttribute("aria-hidden", "true");
|
|
138
423
|
sandbox.style.position = "absolute";
|
|
139
424
|
sandbox.style.left = "-9999px";
|
|
140
425
|
sandbox.style.top = "-9999px";
|
|
141
|
-
sandbox.style.width = "
|
|
142
|
-
sandbox.style.height = "
|
|
426
|
+
sandbox.style.width = "0px";
|
|
427
|
+
sandbox.style.height = "0px";
|
|
143
428
|
sandbox.style.overflow = "hidden";
|
|
144
429
|
document.body.appendChild(sandbox);
|
|
145
430
|
}
|
|
@@ -149,32 +434,55 @@
|
|
|
149
434
|
const styles = getComputedStyle(el);
|
|
150
435
|
const defaults = {};
|
|
151
436
|
for (let prop of styles) {
|
|
152
|
-
|
|
437
|
+
if (shouldIgnoreProp(prop)) continue;
|
|
438
|
+
const value = styles.getPropertyValue(prop);
|
|
439
|
+
defaults[prop] = value;
|
|
153
440
|
}
|
|
154
441
|
sandbox.removeChild(el);
|
|
155
442
|
cache.defaultStyle.set(tagName, defaults);
|
|
156
443
|
return defaults;
|
|
157
444
|
}
|
|
158
|
-
var
|
|
159
|
-
|
|
445
|
+
var NO_PAINT_TOKEN = /(?:^|-)(animation|transition)(?:-|$)/i;
|
|
446
|
+
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;
|
|
447
|
+
var NO_PAINT_EXACT = /* @__PURE__ */ new Set([
|
|
448
|
+
// Interaction hints
|
|
449
|
+
"cursor",
|
|
450
|
+
"pointer-events",
|
|
451
|
+
"touch-action",
|
|
452
|
+
"user-select",
|
|
453
|
+
// Printing/speech/reading-mode hints
|
|
454
|
+
"print-color-adjust",
|
|
455
|
+
"speak",
|
|
456
|
+
"reading-flow",
|
|
457
|
+
"reading-order",
|
|
458
|
+
// Anchoring/container/timeline scopes (metadata for layout queries)
|
|
459
|
+
"anchor-name",
|
|
460
|
+
"anchor-scope",
|
|
461
|
+
"container-name",
|
|
462
|
+
"container-type",
|
|
463
|
+
"timeline-scope"
|
|
160
464
|
]);
|
|
161
|
-
function
|
|
465
|
+
function shouldIgnoreProp(prop) {
|
|
466
|
+
const p = String(prop).toLowerCase();
|
|
467
|
+
if (NO_PAINT_EXACT.has(p)) return true;
|
|
468
|
+
if (NO_PAINT_PREFIX.test(p)) return true;
|
|
469
|
+
if (NO_PAINT_TOKEN.test(p)) return true;
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
function getStyleKey(snapshot, tagName) {
|
|
473
|
+
tagName = String(tagName || "").toLowerCase();
|
|
474
|
+
if (NO_DEFAULTS_TAGS.has(tagName)) {
|
|
475
|
+
return "";
|
|
476
|
+
}
|
|
162
477
|
const entries = [];
|
|
163
|
-
const
|
|
478
|
+
const defaults = getDefaultStyleForTag(tagName);
|
|
164
479
|
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
|
-
}
|
|
480
|
+
if (shouldIgnoreProp(prop)) continue;
|
|
481
|
+
const def = defaults[prop];
|
|
482
|
+
if (value && value !== def) entries.push(`${prop}:${value}`);
|
|
176
483
|
}
|
|
177
|
-
|
|
484
|
+
entries.sort();
|
|
485
|
+
return entries.join(";");
|
|
178
486
|
}
|
|
179
487
|
function collectUsedTagNames(root) {
|
|
180
488
|
const tagSet = /* @__PURE__ */ new Set();
|
|
@@ -195,6 +503,7 @@
|
|
|
195
503
|
const styles = cache.defaultStyle.get(tagName);
|
|
196
504
|
if (!styles) continue;
|
|
197
505
|
const key = Object.entries(styles).map(([k, v]) => `${k}:${v};`).sort().join("");
|
|
506
|
+
if (!key) continue;
|
|
198
507
|
if (!groups.has(key)) {
|
|
199
508
|
groups.set(key, []);
|
|
200
509
|
}
|
|
@@ -208,45 +517,12 @@
|
|
|
208
517
|
return css;
|
|
209
518
|
}
|
|
210
519
|
function generateCSSClasses(styleMap) {
|
|
211
|
-
const
|
|
520
|
+
const keys = Array.from(new Set(styleMap.values())).filter(Boolean).sort();
|
|
212
521
|
const classMap = /* @__PURE__ */ new Map();
|
|
213
|
-
let
|
|
214
|
-
for (const
|
|
215
|
-
if (!key.trim()) continue;
|
|
216
|
-
classMap.set(key, `c${counter++}`);
|
|
217
|
-
}
|
|
522
|
+
let i = 1;
|
|
523
|
+
for (const k of keys) classMap.set(k, `c${i++}`);
|
|
218
524
|
return classMap;
|
|
219
525
|
}
|
|
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
526
|
function getStyle(el, pseudo = null) {
|
|
251
527
|
if (!(el instanceof Element)) {
|
|
252
528
|
return window.getComputedStyle(el, pseudo);
|
|
@@ -273,196 +549,6 @@
|
|
|
273
549
|
}
|
|
274
550
|
return clean;
|
|
275
551
|
}
|
|
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
552
|
function snapshotComputedStyle(style) {
|
|
467
553
|
const snap = {};
|
|
468
554
|
for (let prop of style) {
|
|
@@ -470,36 +556,6 @@
|
|
|
470
556
|
}
|
|
471
557
|
return snap;
|
|
472
558
|
}
|
|
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
559
|
function splitBackgroundImage(bg) {
|
|
504
560
|
const parts = [];
|
|
505
561
|
let depth = 0;
|
|
@@ -517,49 +573,209 @@
|
|
|
517
573
|
return parts;
|
|
518
574
|
}
|
|
519
575
|
|
|
576
|
+
// src/utils/browser.js
|
|
577
|
+
function idle(fn, { fast = false } = {}) {
|
|
578
|
+
if (fast) return fn();
|
|
579
|
+
if ("requestIdleCallback" in window) {
|
|
580
|
+
requestIdleCallback(fn, { timeout: 50 });
|
|
581
|
+
} else {
|
|
582
|
+
setTimeout(fn, 1);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function isSafari() {
|
|
586
|
+
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
587
|
+
}
|
|
588
|
+
|
|
520
589
|
// src/modules/styles.js
|
|
521
590
|
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
522
591
|
var snapshotKeyCache = /* @__PURE__ */ new Map();
|
|
592
|
+
var __epoch = 0;
|
|
593
|
+
function bumpEpoch() {
|
|
594
|
+
__epoch++;
|
|
595
|
+
}
|
|
596
|
+
var __wired = false;
|
|
597
|
+
function setupInvalidationOnce(root = document.documentElement) {
|
|
598
|
+
var _a, _b;
|
|
599
|
+
if (__wired) return;
|
|
600
|
+
__wired = true;
|
|
601
|
+
try {
|
|
602
|
+
const domObs = new MutationObserver(() => bumpEpoch());
|
|
603
|
+
domObs.observe(root, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
604
|
+
} catch (e) {
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
const headObs = new MutationObserver(() => bumpEpoch());
|
|
608
|
+
headObs.observe(document.head, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
609
|
+
} catch (e) {
|
|
610
|
+
}
|
|
611
|
+
try {
|
|
612
|
+
const f = document.fonts;
|
|
613
|
+
if (f) {
|
|
614
|
+
(_a = f.addEventListener) == null ? void 0 : _a.call(f, "loadingdone", bumpEpoch);
|
|
615
|
+
(_b = f.ready) == null ? void 0 : _b.then(() => bumpEpoch()).catch(() => {
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
} catch (e) {
|
|
619
|
+
}
|
|
620
|
+
}
|
|
523
621
|
function snapshotComputedStyleFull(style) {
|
|
524
|
-
const
|
|
525
|
-
const
|
|
622
|
+
const out = {};
|
|
623
|
+
const vis = style.getPropertyValue("visibility");
|
|
526
624
|
for (let i = 0; i < style.length; i++) {
|
|
527
625
|
const prop = style[i];
|
|
528
626
|
let val = style.getPropertyValue(prop);
|
|
529
627
|
if ((prop === "background-image" || prop === "content") && val.includes("url(") && !val.includes("data:")) {
|
|
530
628
|
val = "none";
|
|
531
629
|
}
|
|
532
|
-
|
|
630
|
+
out[prop] = val;
|
|
533
631
|
}
|
|
534
|
-
if (
|
|
535
|
-
|
|
632
|
+
if (vis === "hidden") out.opacity = "0";
|
|
633
|
+
return out;
|
|
634
|
+
}
|
|
635
|
+
var __snapshotSig = /* @__PURE__ */ new WeakMap();
|
|
636
|
+
function styleSignature(snap) {
|
|
637
|
+
let sig = __snapshotSig.get(snap);
|
|
638
|
+
if (sig) return sig;
|
|
639
|
+
const entries = Object.entries(snap).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
640
|
+
sig = entries.map(([k, v]) => `${k}:${v}`).join(";");
|
|
641
|
+
__snapshotSig.set(snap, sig);
|
|
642
|
+
return sig;
|
|
643
|
+
}
|
|
644
|
+
function getSnapshot(el, preStyle = null) {
|
|
645
|
+
const rec = snapshotCache.get(el);
|
|
646
|
+
if (rec && rec.epoch === __epoch) return rec.snapshot;
|
|
647
|
+
const style = preStyle || getComputedStyle(el);
|
|
648
|
+
const snap = snapshotComputedStyleFull(style);
|
|
649
|
+
snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
|
|
650
|
+
return snap;
|
|
651
|
+
}
|
|
652
|
+
function _resolveCtx(sessionOrCtx, opts) {
|
|
653
|
+
if (sessionOrCtx && sessionOrCtx.session && sessionOrCtx.persist) return sessionOrCtx;
|
|
654
|
+
if (sessionOrCtx && (sessionOrCtx.styleMap || sessionOrCtx.styleCache || sessionOrCtx.nodeMap)) {
|
|
655
|
+
return {
|
|
656
|
+
session: sessionOrCtx,
|
|
657
|
+
persist: {
|
|
658
|
+
snapshotKeyCache,
|
|
659
|
+
defaultStyle: cache.defaultStyle,
|
|
660
|
+
baseStyle: cache.baseStyle,
|
|
661
|
+
image: cache.image,
|
|
662
|
+
resource: cache.resource,
|
|
663
|
+
background: cache.background,
|
|
664
|
+
font: cache.font
|
|
665
|
+
},
|
|
666
|
+
options: opts || {}
|
|
667
|
+
};
|
|
536
668
|
}
|
|
537
|
-
return
|
|
669
|
+
return {
|
|
670
|
+
session: cache.session,
|
|
671
|
+
persist: {
|
|
672
|
+
snapshotKeyCache,
|
|
673
|
+
defaultStyle: cache.defaultStyle,
|
|
674
|
+
baseStyle: cache.baseStyle,
|
|
675
|
+
image: cache.image,
|
|
676
|
+
resource: cache.resource,
|
|
677
|
+
background: cache.background,
|
|
678
|
+
font: cache.font
|
|
679
|
+
},
|
|
680
|
+
options: sessionOrCtx || opts || {}
|
|
681
|
+
};
|
|
538
682
|
}
|
|
539
|
-
function inlineAllStyles(source, clone,
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
683
|
+
function inlineAllStyles(source, clone, sessionOrCtx, opts) {
|
|
684
|
+
return __async(this, null, function* () {
|
|
685
|
+
var _a, _b, _c;
|
|
686
|
+
if (source.tagName === "STYLE") return;
|
|
687
|
+
const ctx = _resolveCtx(sessionOrCtx, opts);
|
|
688
|
+
const resetMode = ctx.options && ctx.options.cache || "auto";
|
|
689
|
+
if (resetMode !== "disabled") setupInvalidationOnce(document.documentElement);
|
|
690
|
+
if (NO_DEFAULTS_TAGS.has((_a = source.tagName) == null ? void 0 : _a.toLowerCase())) {
|
|
691
|
+
const author = (_b = source.getAttribute) == null ? void 0 : _b.call(source, "style");
|
|
692
|
+
if (author) clone.setAttribute("style", author);
|
|
693
|
+
}
|
|
694
|
+
const { session, persist } = ctx;
|
|
695
|
+
if (!session.styleCache.has(source)) {
|
|
696
|
+
session.styleCache.set(source, getComputedStyle(source));
|
|
697
|
+
}
|
|
698
|
+
const pre = session.styleCache.get(source);
|
|
699
|
+
const snap = getSnapshot(source, pre);
|
|
700
|
+
const sig = styleSignature(snap);
|
|
701
|
+
let key = persist.snapshotKeyCache.get(sig);
|
|
702
|
+
if (!key) {
|
|
703
|
+
const tag = ((_c = source.tagName) == null ? void 0 : _c.toLowerCase()) || "div";
|
|
704
|
+
key = getStyleKey(snap, tag);
|
|
705
|
+
persist.snapshotKeyCache.set(sig, key);
|
|
706
|
+
}
|
|
707
|
+
session.styleMap.set(clone, key);
|
|
708
|
+
});
|
|
560
709
|
}
|
|
561
710
|
|
|
562
711
|
// src/core/clone.js
|
|
712
|
+
function addNotSlottedRightmost(sel) {
|
|
713
|
+
sel = sel.trim();
|
|
714
|
+
if (!sel) return sel;
|
|
715
|
+
if (/\:not\(\s*\[data-sd-slotted\]\s*\)\s*$/.test(sel)) return sel;
|
|
716
|
+
return `${sel}:not([data-sd-slotted])`;
|
|
717
|
+
}
|
|
718
|
+
function wrapWithScope(selectorList, scopeSelector, excludeSlotted = true) {
|
|
719
|
+
return selectorList.split(",").map((s) => s.trim()).filter(Boolean).map((s) => {
|
|
720
|
+
if (s.startsWith(":where(")) return s;
|
|
721
|
+
if (s.startsWith("@")) return s;
|
|
722
|
+
const body = excludeSlotted ? addNotSlottedRightmost(s) : s;
|
|
723
|
+
return `:where(${scopeSelector} ${body})`;
|
|
724
|
+
}).join(", ");
|
|
725
|
+
}
|
|
726
|
+
function rewriteShadowCSS(cssText, scopeSelector) {
|
|
727
|
+
if (!cssText) return "";
|
|
728
|
+
cssText = cssText.replace(/:host\(([^)]+)\)/g, (_, sel) => {
|
|
729
|
+
return `:where(${scopeSelector}:is(${sel.trim()}))`;
|
|
730
|
+
});
|
|
731
|
+
cssText = cssText.replace(/:host\b/g, `:where(${scopeSelector})`);
|
|
732
|
+
cssText = cssText.replace(/:host-context\(([^)]+)\)/g, (_, sel) => {
|
|
733
|
+
return `:where(:where(${sel.trim()}) ${scopeSelector})`;
|
|
734
|
+
});
|
|
735
|
+
cssText = cssText.replace(/::slotted\(([^)]+)\)/g, (_, sel) => {
|
|
736
|
+
return `:where(${scopeSelector} ${sel.trim()})`;
|
|
737
|
+
});
|
|
738
|
+
cssText = cssText.replace(/(^|})(\s*)([^@}{]+){/g, (_, brace, ws, selectorList) => {
|
|
739
|
+
const wrapped = wrapWithScope(
|
|
740
|
+
selectorList,
|
|
741
|
+
scopeSelector,
|
|
742
|
+
/*excludeSlotted*/
|
|
743
|
+
true
|
|
744
|
+
);
|
|
745
|
+
return `${brace}${ws}${wrapped}{`;
|
|
746
|
+
});
|
|
747
|
+
return cssText;
|
|
748
|
+
}
|
|
749
|
+
function nextShadowScopeId(sessionCache) {
|
|
750
|
+
sessionCache.shadowScopeSeq = (sessionCache.shadowScopeSeq || 0) + 1;
|
|
751
|
+
return `s${sessionCache.shadowScopeSeq}`;
|
|
752
|
+
}
|
|
753
|
+
function extractShadowCSS(sr) {
|
|
754
|
+
let css = "";
|
|
755
|
+
try {
|
|
756
|
+
sr.querySelectorAll("style").forEach((s) => {
|
|
757
|
+
css += (s.textContent || "") + "\n";
|
|
758
|
+
});
|
|
759
|
+
const sheets = sr.adoptedStyleSheets || [];
|
|
760
|
+
for (const sh of sheets) {
|
|
761
|
+
try {
|
|
762
|
+
if (sh && sh.cssRules) {
|
|
763
|
+
for (const rule of sh.cssRules) css += rule.cssText + "\n";
|
|
764
|
+
}
|
|
765
|
+
} catch (e) {
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
} catch (e) {
|
|
769
|
+
}
|
|
770
|
+
return css;
|
|
771
|
+
}
|
|
772
|
+
function injectScopedStyle(hostClone, cssText, scopeId) {
|
|
773
|
+
if (!cssText) return;
|
|
774
|
+
const style = document.createElement("style");
|
|
775
|
+
style.setAttribute("data-sd", scopeId);
|
|
776
|
+
style.textContent = cssText;
|
|
777
|
+
hostClone.insertBefore(style, hostClone.firstChild || null);
|
|
778
|
+
}
|
|
563
779
|
function freezeImgSrcset(original, cloned) {
|
|
564
780
|
try {
|
|
565
781
|
const chosen = original.currentSrc || original.src || "";
|
|
@@ -572,11 +788,62 @@
|
|
|
572
788
|
} catch (e) {
|
|
573
789
|
}
|
|
574
790
|
}
|
|
575
|
-
function
|
|
576
|
-
|
|
791
|
+
function collectCustomPropsFromCSS(cssText) {
|
|
792
|
+
const out = /* @__PURE__ */ new Set();
|
|
793
|
+
if (!cssText) return out;
|
|
794
|
+
const re = /var\(\s*(--[A-Za-z0-9_-]+)\b/g;
|
|
795
|
+
let m;
|
|
796
|
+
while (m = re.exec(cssText)) out.add(m[1]);
|
|
797
|
+
return out;
|
|
798
|
+
}
|
|
799
|
+
function resolveCustomProp(el, name) {
|
|
800
|
+
try {
|
|
801
|
+
const cs = getComputedStyle(el);
|
|
802
|
+
let v = cs.getPropertyValue(name).trim();
|
|
803
|
+
if (v) return v;
|
|
804
|
+
} catch (e) {
|
|
805
|
+
}
|
|
806
|
+
try {
|
|
807
|
+
const rootCS = getComputedStyle(document.documentElement);
|
|
808
|
+
let v = rootCS.getPropertyValue(name).trim();
|
|
809
|
+
if (v) return v;
|
|
810
|
+
} catch (e) {
|
|
811
|
+
}
|
|
812
|
+
return "";
|
|
813
|
+
}
|
|
814
|
+
function buildSeedCustomPropsRule(hostEl, names, scopeSelector) {
|
|
815
|
+
const decls = [];
|
|
816
|
+
for (const name of names) {
|
|
817
|
+
const val = resolveCustomProp(hostEl, name);
|
|
818
|
+
if (val) decls.push(`${name}: ${val};`);
|
|
819
|
+
}
|
|
820
|
+
if (!decls.length) return "";
|
|
821
|
+
return `${scopeSelector}{${decls.join("")}}
|
|
822
|
+
`;
|
|
823
|
+
}
|
|
824
|
+
function markSlottedSubtree(root) {
|
|
825
|
+
if (!root) return;
|
|
826
|
+
if (root.nodeType === Node.ELEMENT_NODE) {
|
|
827
|
+
root.setAttribute("data-sd-slotted", "");
|
|
828
|
+
}
|
|
829
|
+
if (root.querySelectorAll) {
|
|
830
|
+
root.querySelectorAll("*").forEach((el) => el.setAttribute("data-sd-slotted", ""));
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
function deepClone(node, sessionCache, options) {
|
|
834
|
+
var _a, _b, _c, _d, _e;
|
|
577
835
|
if (!node) throw new Error("Invalid node");
|
|
578
836
|
const clonedAssignedNodes = /* @__PURE__ */ new Set();
|
|
579
837
|
let pendingSelectValue = null;
|
|
838
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
839
|
+
const tag = (node.localName || node.tagName || "").toLowerCase();
|
|
840
|
+
if (node.id === "snapdom-sandbox" || node.hasAttribute("data-snapdom-sandbox")) {
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
if (NO_CAPTURE_TAGS.has(tag)) {
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
580
847
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
581
848
|
return node.cloneNode(true);
|
|
582
849
|
}
|
|
@@ -605,7 +872,7 @@
|
|
|
605
872
|
}
|
|
606
873
|
if (typeof options.filter === "function") {
|
|
607
874
|
try {
|
|
608
|
-
if (!options.filter(node
|
|
875
|
+
if (!options.filter(node)) {
|
|
609
876
|
const spacer = document.createElement("div");
|
|
610
877
|
const rect = node.getBoundingClientRect();
|
|
611
878
|
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
@@ -616,14 +883,23 @@
|
|
|
616
883
|
}
|
|
617
884
|
}
|
|
618
885
|
if (node.tagName === "IFRAME") {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
886
|
+
if (options.placeholders) {
|
|
887
|
+
const fallback = document.createElement("div");
|
|
888
|
+
fallback.style.cssText = `width:${node.offsetWidth}px;height:${node.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`;
|
|
889
|
+
inlineAllStyles(node, fallback, sessionCache, options);
|
|
890
|
+
return fallback;
|
|
891
|
+
} else {
|
|
892
|
+
const rect = node.getBoundingClientRect();
|
|
893
|
+
const spacer = document.createElement("div");
|
|
894
|
+
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
895
|
+
inlineAllStyles(node, spacer, sessionCache, options);
|
|
896
|
+
return spacer;
|
|
897
|
+
}
|
|
622
898
|
}
|
|
623
899
|
if (node.getAttribute("data-capture") === "placeholder") {
|
|
624
900
|
const clone2 = node.cloneNode(false);
|
|
625
|
-
nodeMap.set(clone2, node);
|
|
626
|
-
inlineAllStyles(node, clone2,
|
|
901
|
+
sessionCache.nodeMap.set(clone2, node);
|
|
902
|
+
inlineAllStyles(node, clone2, sessionCache, options);
|
|
627
903
|
const placeholder = document.createElement("div");
|
|
628
904
|
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
629
905
|
placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`;
|
|
@@ -636,14 +912,14 @@
|
|
|
636
912
|
img.src = dataURL;
|
|
637
913
|
img.width = node.width;
|
|
638
914
|
img.height = node.height;
|
|
639
|
-
nodeMap.set(img, node);
|
|
640
|
-
inlineAllStyles(node, img,
|
|
915
|
+
sessionCache.nodeMap.set(img, node);
|
|
916
|
+
inlineAllStyles(node, img, sessionCache, options);
|
|
641
917
|
return img;
|
|
642
918
|
}
|
|
643
919
|
let clone;
|
|
644
920
|
try {
|
|
645
921
|
clone = node.cloneNode(false);
|
|
646
|
-
nodeMap.set(clone, node);
|
|
922
|
+
sessionCache.nodeMap.set(clone, node);
|
|
647
923
|
if (node.tagName === "IMG") {
|
|
648
924
|
freezeImgSrcset(node, clone);
|
|
649
925
|
}
|
|
@@ -657,7 +933,6 @@
|
|
|
657
933
|
const rect = node.getBoundingClientRect();
|
|
658
934
|
clone.style.width = `${rect.width}px`;
|
|
659
935
|
clone.style.height = `${rect.height}px`;
|
|
660
|
-
return clone;
|
|
661
936
|
}
|
|
662
937
|
if (node instanceof HTMLInputElement) {
|
|
663
938
|
clone.value = node.value;
|
|
@@ -671,47 +946,56 @@
|
|
|
671
946
|
if (node instanceof HTMLSelectElement) {
|
|
672
947
|
pendingSelectValue = node.value;
|
|
673
948
|
}
|
|
674
|
-
inlineAllStyles(node, clone,
|
|
949
|
+
inlineAllStyles(node, clone, sessionCache, options);
|
|
675
950
|
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;
|
|
951
|
+
try {
|
|
952
|
+
const slots = node.shadowRoot.querySelectorAll("slot");
|
|
953
|
+
for (const s of slots) {
|
|
954
|
+
let assigned = [];
|
|
955
|
+
try {
|
|
956
|
+
assigned = ((_b = s.assignedNodes) == null ? void 0 : _b.call(s, { flatten: true })) || ((_c = s.assignedNodes) == null ? void 0 : _c.call(s)) || [];
|
|
957
|
+
} catch (e) {
|
|
958
|
+
assigned = ((_d = s.assignedNodes) == null ? void 0 : _d.call(s)) || [];
|
|
695
959
|
}
|
|
696
|
-
|
|
697
|
-
if (clonedChild) shadowFrag.appendChild(clonedChild);
|
|
960
|
+
for (const an of assigned) clonedAssignedNodes.add(an);
|
|
698
961
|
}
|
|
699
|
-
|
|
962
|
+
} catch (e) {
|
|
963
|
+
}
|
|
964
|
+
const scopeId = nextShadowScopeId(sessionCache);
|
|
965
|
+
const scopeSelector = `[data-sd="${scopeId}"]`;
|
|
966
|
+
try {
|
|
967
|
+
clone.setAttribute("data-sd", scopeId);
|
|
968
|
+
} catch (e) {
|
|
700
969
|
}
|
|
970
|
+
const rawCSS = extractShadowCSS(node.shadowRoot);
|
|
971
|
+
const rewritten = rewriteShadowCSS(rawCSS, scopeSelector);
|
|
972
|
+
const neededVars = collectCustomPropsFromCSS(rawCSS);
|
|
973
|
+
const seed = buildSeedCustomPropsRule(node, neededVars, scopeSelector);
|
|
974
|
+
injectScopedStyle(clone, seed + rewritten, scopeId);
|
|
975
|
+
const shadowFrag = document.createDocumentFragment();
|
|
976
|
+
for (const child of node.shadowRoot.childNodes) {
|
|
977
|
+
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") continue;
|
|
978
|
+
const clonedChild = deepClone(child, sessionCache, options);
|
|
979
|
+
if (clonedChild) shadowFrag.appendChild(clonedChild);
|
|
980
|
+
}
|
|
981
|
+
clone.appendChild(shadowFrag);
|
|
701
982
|
}
|
|
702
983
|
if (node.tagName === "SLOT") {
|
|
703
|
-
const assigned = ((
|
|
984
|
+
const assigned = ((_e = node.assignedNodes) == null ? void 0 : _e.call(node, { flatten: true })) || [];
|
|
704
985
|
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
705
986
|
const fragment = document.createDocumentFragment();
|
|
706
987
|
for (const child of nodesToClone) {
|
|
707
|
-
const clonedChild = deepClone(child,
|
|
708
|
-
if (clonedChild)
|
|
988
|
+
const clonedChild = deepClone(child, sessionCache, options);
|
|
989
|
+
if (clonedChild) {
|
|
990
|
+
markSlottedSubtree(clonedChild);
|
|
991
|
+
fragment.appendChild(clonedChild);
|
|
992
|
+
}
|
|
709
993
|
}
|
|
710
994
|
return fragment;
|
|
711
995
|
}
|
|
712
996
|
for (const child of node.childNodes) {
|
|
713
997
|
if (clonedAssignedNodes.has(child)) continue;
|
|
714
|
-
const clonedChild = deepClone(child,
|
|
998
|
+
const clonedChild = deepClone(child, sessionCache, options);
|
|
715
999
|
if (clonedChild) clone.appendChild(clonedChild);
|
|
716
1000
|
}
|
|
717
1001
|
if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
|
|
@@ -754,7 +1038,7 @@
|
|
|
754
1038
|
}
|
|
755
1039
|
}
|
|
756
1040
|
}
|
|
757
|
-
function
|
|
1041
|
+
function isIconFont2(input) {
|
|
758
1042
|
const text = typeof input === "string" ? input : "";
|
|
759
1043
|
const candidates = [...defaultIconFonts, ...userIconFonts];
|
|
760
1044
|
for (const rx of candidates) {
|
|
@@ -803,249 +1087,630 @@
|
|
|
803
1087
|
};
|
|
804
1088
|
});
|
|
805
1089
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1090
|
+
var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
1091
|
+
"serif",
|
|
1092
|
+
"sans-serif",
|
|
1093
|
+
"monospace",
|
|
1094
|
+
"cursive",
|
|
1095
|
+
"fantasy",
|
|
1096
|
+
"system-ui",
|
|
1097
|
+
"emoji",
|
|
1098
|
+
"math",
|
|
1099
|
+
"fangsong",
|
|
1100
|
+
"ui-serif",
|
|
1101
|
+
"ui-sans-serif",
|
|
1102
|
+
"ui-monospace",
|
|
1103
|
+
"ui-rounded"
|
|
1104
|
+
]);
|
|
1105
|
+
function pickPrimaryFamily(familyList) {
|
|
1106
|
+
if (!familyList) return "";
|
|
1107
|
+
for (let raw of familyList.split(",")) {
|
|
1108
|
+
let f = raw.trim().replace(/^['"]+|['"]+$/g, "");
|
|
1109
|
+
if (!f) continue;
|
|
1110
|
+
if (!GENERIC_FAMILIES.has(f.toLowerCase())) return f;
|
|
1111
|
+
}
|
|
1112
|
+
return "";
|
|
1113
|
+
}
|
|
1114
|
+
function normWeight(w) {
|
|
1115
|
+
const t = String(w != null ? w : "400").trim().toLowerCase();
|
|
1116
|
+
if (t === "normal") return 400;
|
|
1117
|
+
if (t === "bold") return 700;
|
|
1118
|
+
const n = parseInt(t, 10);
|
|
1119
|
+
return Number.isFinite(n) ? Math.min(900, Math.max(100, n)) : 400;
|
|
1120
|
+
}
|
|
1121
|
+
function normStyle(s) {
|
|
1122
|
+
const t = String(s != null ? s : "normal").trim().toLowerCase();
|
|
1123
|
+
if (t.startsWith("italic")) return "italic";
|
|
1124
|
+
if (t.startsWith("oblique")) return "oblique";
|
|
1125
|
+
return "normal";
|
|
1126
|
+
}
|
|
1127
|
+
function normStretchPct(st) {
|
|
1128
|
+
const m = String(st != null ? st : "100%").match(/(\d+(?:\.\d+)?)\s*%/);
|
|
1129
|
+
return m ? Math.max(50, Math.min(200, parseFloat(m[1]))) : 100;
|
|
1130
|
+
}
|
|
1131
|
+
function parseWeightSpec(spec) {
|
|
1132
|
+
const s = String(spec || "400").trim();
|
|
1133
|
+
const m = s.match(/^(\d{2,3})\s+(\d{2,3})$/);
|
|
1134
|
+
if (m) {
|
|
1135
|
+
const a = normWeight(m[1]), b = normWeight(m[2]);
|
|
1136
|
+
return { min: Math.min(a, b), max: Math.max(a, b) };
|
|
1137
|
+
}
|
|
1138
|
+
const v = normWeight(s);
|
|
1139
|
+
return { min: v, max: v };
|
|
1140
|
+
}
|
|
1141
|
+
function parseStyleSpec(spec) {
|
|
1142
|
+
const t = String(spec || "normal").trim().toLowerCase();
|
|
1143
|
+
if (t === "italic") return { kind: "italic" };
|
|
1144
|
+
if (t.startsWith("oblique")) return { kind: "oblique" };
|
|
1145
|
+
return { kind: "normal" };
|
|
1146
|
+
}
|
|
1147
|
+
function parseStretchSpec(spec) {
|
|
1148
|
+
const s = String(spec || "100%").trim();
|
|
1149
|
+
const mm = s.match(/(\d+(?:\.\d+)?)\s*%\s+(\d+(?:\.\d+)?)\s*%/);
|
|
1150
|
+
if (mm) {
|
|
1151
|
+
const a = parseFloat(mm[1]), b = parseFloat(mm[2]);
|
|
1152
|
+
return { min: Math.min(a, b), max: Math.max(a, b) };
|
|
1153
|
+
}
|
|
1154
|
+
const m = s.match(/(\d+(?:\.\d+)?)\s*%/);
|
|
1155
|
+
const v = m ? parseFloat(m[1]) : 100;
|
|
1156
|
+
return { min: v, max: v };
|
|
1157
|
+
}
|
|
1158
|
+
function isLikelyFontStylesheet(href, requiredFamilies) {
|
|
1159
|
+
if (!href) return false;
|
|
1160
|
+
try {
|
|
1161
|
+
const u = new URL(href, location.href);
|
|
1162
|
+
const sameOrigin = u.origin === location.origin;
|
|
1163
|
+
if (sameOrigin) return true;
|
|
1164
|
+
const host = u.host.toLowerCase();
|
|
1165
|
+
const FONT_HOSTS = [
|
|
1166
|
+
"fonts.googleapis.com",
|
|
1167
|
+
"fonts.gstatic.com",
|
|
1168
|
+
"use.typekit.net",
|
|
1169
|
+
"p.typekit.net",
|
|
1170
|
+
"kit.fontawesome.com",
|
|
1171
|
+
"use.fontawesome.com"
|
|
1172
|
+
];
|
|
1173
|
+
if (FONT_HOSTS.some((h) => host.endsWith(h))) return true;
|
|
1174
|
+
const path = (u.pathname + u.search).toLowerCase();
|
|
1175
|
+
if (/\bfont(s)?\b/.test(path) || /\.woff2?(\b|$)/.test(path)) return true;
|
|
1176
|
+
for (const fam of requiredFamilies) {
|
|
1177
|
+
const tokenA = fam.toLowerCase().replace(/\s+/g, "+");
|
|
1178
|
+
const tokenB = fam.toLowerCase().replace(/\s+/g, "-");
|
|
1179
|
+
if (path.includes(tokenA) || path.includes(tokenB)) return true;
|
|
1180
|
+
}
|
|
1181
|
+
return false;
|
|
1182
|
+
} catch (e) {
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
function familiesFromRequired(required) {
|
|
1187
|
+
var _a;
|
|
1188
|
+
const out = /* @__PURE__ */ new Set();
|
|
1189
|
+
for (const k of required || []) {
|
|
1190
|
+
const fam = (_a = String(k).split("__")[0]) == null ? void 0 : _a.trim();
|
|
1191
|
+
if (fam) out.add(fam);
|
|
1192
|
+
}
|
|
1193
|
+
return out;
|
|
820
1194
|
}
|
|
821
1195
|
function embedCustomFonts() {
|
|
822
|
-
return __async(this, arguments, function* ({
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1196
|
+
return __async(this, arguments, function* ({
|
|
1197
|
+
required,
|
|
1198
|
+
usedCodepoints,
|
|
1199
|
+
exclude = void 0,
|
|
1200
|
+
localFonts = [],
|
|
1201
|
+
useProxy = ""
|
|
1202
|
+
} = {}) {
|
|
1203
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
|
|
1204
|
+
if (!(required instanceof Set)) required = /* @__PURE__ */ new Set();
|
|
1205
|
+
if (!(usedCodepoints instanceof Set)) usedCodepoints = /* @__PURE__ */ new Set();
|
|
1206
|
+
const requiredIndex = /* @__PURE__ */ new Map();
|
|
1207
|
+
for (const key of required) {
|
|
1208
|
+
const [fam, w, s, st] = String(key).split("__");
|
|
1209
|
+
if (!fam) continue;
|
|
1210
|
+
const arr = requiredIndex.get(fam) || [];
|
|
1211
|
+
arr.push({ w: parseInt(w, 10), s, st: parseInt(st, 10) });
|
|
1212
|
+
requiredIndex.set(fam, arr);
|
|
1213
|
+
}
|
|
1214
|
+
const IMPORT_RE = /@import\s+url\(["']?([^"')]+)["']?\)/g;
|
|
1215
|
+
const URL_RE = /url\((["']?)([^"')]+)\1\)/g;
|
|
1216
|
+
const FACE_RE = /@font-face[^{}]*\{[^}]*\}/g;
|
|
1217
|
+
function faceMatchesRequired(fam, styleSpec, weightSpec, stretchSpec) {
|
|
1218
|
+
if (!requiredIndex.has(fam)) return false;
|
|
1219
|
+
const need = requiredIndex.get(fam);
|
|
1220
|
+
const ws = parseWeightSpec(weightSpec);
|
|
1221
|
+
const ss = parseStyleSpec(styleSpec);
|
|
1222
|
+
const ts = parseStretchSpec(stretchSpec);
|
|
1223
|
+
const faceIsRange = ws.min !== ws.max;
|
|
1224
|
+
const faceSingleW = ws.min;
|
|
1225
|
+
const styleOK = (reqKind) => ss.kind === "normal" && reqKind === "normal" || ss.kind !== "normal" && (reqKind === "italic" || reqKind === "oblique");
|
|
1226
|
+
let exactMatched = false;
|
|
1227
|
+
for (const r of need) {
|
|
1228
|
+
const wOk = faceIsRange ? r.w >= ws.min && r.w <= ws.max : r.w === faceSingleW;
|
|
1229
|
+
const sOk = styleOK(normStyle(r.s));
|
|
1230
|
+
const tOk = r.st >= ts.min && r.st <= ts.max;
|
|
1231
|
+
if (wOk && sOk && tOk) {
|
|
1232
|
+
exactMatched = true;
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
829
1235
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1236
|
+
if (exactMatched) return true;
|
|
1237
|
+
if (!faceIsRange) {
|
|
1238
|
+
for (const r of need) {
|
|
1239
|
+
const sOk = styleOK(normStyle(r.s));
|
|
1240
|
+
const tOk = r.st >= ts.min && r.st <= ts.max;
|
|
1241
|
+
const nearWeight = Math.abs(faceSingleW - r.w) <= 300;
|
|
1242
|
+
if (nearWeight && sOk && tOk) {
|
|
1243
|
+
return true;
|
|
1244
|
+
}
|
|
837
1245
|
}
|
|
838
1246
|
}
|
|
839
|
-
|
|
1247
|
+
return false;
|
|
1248
|
+
}
|
|
1249
|
+
function parseUnicodeRange(ur) {
|
|
1250
|
+
if (!ur) return [];
|
|
1251
|
+
const ranges = [];
|
|
1252
|
+
const parts = ur.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1253
|
+
for (const p of parts) {
|
|
1254
|
+
const m = p.match(/^U\+([0-9A-Fa-f?]+)(?:-([0-9A-Fa-f?]+))?$/);
|
|
1255
|
+
if (!m) continue;
|
|
1256
|
+
const a = m[1], b = m[2];
|
|
1257
|
+
const expand = (hex) => {
|
|
1258
|
+
if (!hex.includes("?")) return parseInt(hex, 16);
|
|
1259
|
+
const min = parseInt(hex.replace(/\?/g, "0"), 16);
|
|
1260
|
+
const max = parseInt(hex.replace(/\?/g, "F"), 16);
|
|
1261
|
+
return [min, max];
|
|
1262
|
+
};
|
|
1263
|
+
if (b) {
|
|
1264
|
+
const A = expand(a), B = expand(b);
|
|
1265
|
+
const min = Array.isArray(A) ? A[0] : A;
|
|
1266
|
+
const max = Array.isArray(B) ? B[1] : B;
|
|
1267
|
+
ranges.push([Math.min(min, max), Math.max(min, max)]);
|
|
1268
|
+
} else {
|
|
1269
|
+
const X = expand(a);
|
|
1270
|
+
if (Array.isArray(X)) ranges.push([X[0], X[1]]);
|
|
1271
|
+
else ranges.push([X, X]);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return ranges;
|
|
840
1275
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1276
|
+
function unicodeIntersects(used, ranges) {
|
|
1277
|
+
if (!ranges.length) return true;
|
|
1278
|
+
if (!used || used.size === 0) return true;
|
|
1279
|
+
for (const cp of used) {
|
|
1280
|
+
for (const [a, b] of ranges) if (cp >= a && cp <= b) return true;
|
|
1281
|
+
}
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
function extractSrcUrls(srcValue, baseHref) {
|
|
1285
|
+
const urls = [];
|
|
1286
|
+
if (!srcValue) return urls;
|
|
1287
|
+
for (const m of srcValue.matchAll(URL_RE)) {
|
|
1288
|
+
let u = (m[2] || "").trim();
|
|
1289
|
+
if (!u || u.startsWith("data:")) continue;
|
|
1290
|
+
if (!/^https?:/i.test(u)) {
|
|
1291
|
+
try {
|
|
1292
|
+
u = new URL(u, baseHref || location.href).href;
|
|
1293
|
+
} catch (e) {
|
|
1294
|
+
}
|
|
851
1295
|
}
|
|
1296
|
+
urls.push(u);
|
|
852
1297
|
}
|
|
1298
|
+
return urls;
|
|
853
1299
|
}
|
|
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;
|
|
1300
|
+
function inlineUrlsInCssBlock(cssBlock, baseHref) {
|
|
1301
|
+
return __async(this, null, function* () {
|
|
1302
|
+
var _a2, _b2, _c2, _d2, _e2;
|
|
1303
|
+
let out = cssBlock;
|
|
1304
|
+
for (const m of cssBlock.matchAll(URL_RE)) {
|
|
1305
|
+
const raw = extractURL(m[0]);
|
|
1306
|
+
if (!raw) continue;
|
|
1307
|
+
let abs = raw;
|
|
1308
|
+
if (!abs.startsWith("http") && !abs.startsWith("data:")) {
|
|
1309
|
+
try {
|
|
1310
|
+
abs = new URL(abs, baseHref || location.href).href;
|
|
1311
|
+
} catch (e) {
|
|
1312
|
+
}
|
|
878
1313
|
}
|
|
879
|
-
if (
|
|
880
|
-
|
|
1314
|
+
if (isIconFont2(abs)) continue;
|
|
1315
|
+
if ((_a2 = cache.resource) == null ? void 0 : _a2.has(abs)) {
|
|
1316
|
+
(_b2 = cache.font) == null ? void 0 : _b2.add(abs);
|
|
1317
|
+
out = out.replace(m[0], `url(${cache.resource.get(abs)})`);
|
|
881
1318
|
continue;
|
|
882
1319
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1320
|
+
if ((_c2 = cache.font) == null ? void 0 : _c2.has(abs)) continue;
|
|
1321
|
+
try {
|
|
1322
|
+
const r = yield snapFetch(abs, { as: "dataURL", useProxy, silent: true });
|
|
1323
|
+
if (r.ok && typeof r.data === "string") {
|
|
1324
|
+
const b64 = r.data;
|
|
1325
|
+
(_d2 = cache.resource) == null ? void 0 : _d2.set(abs, b64);
|
|
1326
|
+
(_e2 = cache.font) == null ? void 0 : _e2.add(abs);
|
|
1327
|
+
out = out.replace(m[0], `url(${b64})`);
|
|
891
1328
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1329
|
+
} catch (e) {
|
|
1330
|
+
console.warn("[snapDOM] Failed to fetch font resource:", abs);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return out;
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
function subsetFromRanges(ranges) {
|
|
1337
|
+
if (!ranges.length) return null;
|
|
1338
|
+
const hit = (a, b) => ranges.some(([x, y]) => !(y < a || x > b));
|
|
1339
|
+
const latin = hit(0, 255) || hit(305, 305);
|
|
1340
|
+
const latinExt = hit(256, 591) || hit(7680, 7935);
|
|
1341
|
+
const greek = hit(880, 1023);
|
|
1342
|
+
const cyr = hit(1024, 1279);
|
|
1343
|
+
const viet = hit(7840, 7929) || hit(258, 259) || hit(416, 417) || hit(431, 432);
|
|
1344
|
+
if (viet) return "vietnamese";
|
|
1345
|
+
if (cyr) return "cyrillic";
|
|
1346
|
+
if (greek) return "greek";
|
|
1347
|
+
if (latinExt) return "latin-ext";
|
|
1348
|
+
if (latin) return "latin";
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
function buildSimpleExcluder(ex = {}) {
|
|
1352
|
+
const famSet = new Set((ex.families || []).map((s) => String(s).toLowerCase()));
|
|
1353
|
+
const domSet = new Set((ex.domains || []).map((s) => String(s).toLowerCase()));
|
|
1354
|
+
const subSet = new Set((ex.subsets || []).map((s) => String(s).toLowerCase()));
|
|
1355
|
+
return (meta, parsedRanges) => {
|
|
1356
|
+
if (famSet.size && famSet.has(meta.family.toLowerCase())) return true;
|
|
1357
|
+
if (domSet.size) {
|
|
1358
|
+
for (const u of meta.srcUrls) {
|
|
1359
|
+
try {
|
|
1360
|
+
if (domSet.has(new URL(u).host.toLowerCase())) return true;
|
|
1361
|
+
} catch (e) {
|
|
897
1362
|
}
|
|
898
|
-
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
if (subSet.size) {
|
|
1366
|
+
const label = subsetFromRanges(parsedRanges);
|
|
1367
|
+
if (label && subSet.has(label)) return true;
|
|
1368
|
+
}
|
|
1369
|
+
return false;
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
const simpleExcluder = buildSimpleExcluder(exclude);
|
|
1373
|
+
function dedupeFontFaces(cssText) {
|
|
1374
|
+
var _a2, _b2, _c2, _d2, _e2, _f2;
|
|
1375
|
+
if (!cssText) return cssText;
|
|
1376
|
+
const FACE_RE2 = /@font-face[^{}]*\{[^}]*\}/gi;
|
|
1377
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1378
|
+
const out = [];
|
|
1379
|
+
for (const block of cssText.match(FACE_RE2) || []) {
|
|
1380
|
+
const familyRaw = ((_a2 = block.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _a2[1]) || "";
|
|
1381
|
+
const family = pickPrimaryFamily(familyRaw);
|
|
1382
|
+
const weightSpec = (((_b2 = block.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _b2[1]) || "400").trim();
|
|
1383
|
+
const styleSpec = (((_c2 = block.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _c2[1]) || "normal").trim();
|
|
1384
|
+
const stretchSpec = (((_d2 = block.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _d2[1]) || "100%").trim();
|
|
1385
|
+
const urange = (((_e2 = block.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _e2[1]) || "").trim();
|
|
1386
|
+
const srcRaw = (((_f2 = block.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _f2[1]) || "").trim();
|
|
1387
|
+
const urls = extractSrcUrls(srcRaw, location.href);
|
|
1388
|
+
const srcPart = urls.length ? urls.map((u) => String(u).toLowerCase()).sort().join("|") : srcRaw.toLowerCase();
|
|
1389
|
+
const key = [
|
|
1390
|
+
String(family || "").toLowerCase(),
|
|
1391
|
+
weightSpec,
|
|
1392
|
+
styleSpec,
|
|
1393
|
+
stretchSpec,
|
|
1394
|
+
urange.toLowerCase(),
|
|
1395
|
+
srcPart
|
|
1396
|
+
].join("|");
|
|
1397
|
+
if (!seen.has(key)) {
|
|
1398
|
+
seen.add(key);
|
|
1399
|
+
out.push(block);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
if (out.length === 0) return cssText;
|
|
1403
|
+
let i = 0;
|
|
1404
|
+
return cssText.replace(FACE_RE2, () => out[i++] || "");
|
|
1405
|
+
}
|
|
1406
|
+
function buildFontsCacheKey() {
|
|
1407
|
+
const req = Array.from(required || []).sort().join("|");
|
|
1408
|
+
const ex = exclude ? JSON.stringify({
|
|
1409
|
+
families: (exclude.families || []).map((s) => String(s).toLowerCase()).sort(),
|
|
1410
|
+
domains: (exclude.domains || []).map((s) => String(s).toLowerCase()).sort(),
|
|
1411
|
+
subsets: (exclude.subsets || []).map((s) => String(s).toLowerCase()).sort()
|
|
1412
|
+
}) : "";
|
|
1413
|
+
const lf = (localFonts || []).map((f) => `${(f.family || "").toLowerCase()}::${f.weight || "normal"}::${f.style || "normal"}::${f.src || ""}`).sort().join("|");
|
|
1414
|
+
const px = useProxy || "";
|
|
1415
|
+
return `fonts-embed-css::req=${req}::ex=${ex}::lf=${lf}::px=${px}`;
|
|
1416
|
+
}
|
|
1417
|
+
const cacheKey = buildFontsCacheKey();
|
|
1418
|
+
if ((_a = cache.resource) == null ? void 0 : _a.has(cacheKey)) {
|
|
1419
|
+
const css = cache.resource.get(cacheKey);
|
|
1420
|
+
return css;
|
|
1421
|
+
}
|
|
1422
|
+
const requiredFamilies = familiesFromRequired(required);
|
|
1423
|
+
const importUrls = [];
|
|
1424
|
+
for (const styleTag of document.querySelectorAll("style")) {
|
|
1425
|
+
const cssText = styleTag.textContent || "";
|
|
1426
|
+
for (const m of cssText.matchAll(IMPORT_RE)) {
|
|
1427
|
+
const u = m[1];
|
|
1428
|
+
if (!u || isIconFont2(u)) continue;
|
|
1429
|
+
const hasLink = !!document.querySelector(`link[rel="stylesheet"][href="${u}"]`);
|
|
1430
|
+
if (!hasLink) importUrls.push(u);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
if (importUrls.length) {
|
|
1434
|
+
yield Promise.all(importUrls.map((u) => new Promise((resolve) => {
|
|
1435
|
+
if (document.querySelector(`link[rel="stylesheet"][href="${u}"]`)) return resolve(null);
|
|
1436
|
+
const link = document.createElement("link");
|
|
1437
|
+
link.rel = "stylesheet";
|
|
1438
|
+
link.href = u;
|
|
1439
|
+
link.setAttribute("data-snapdom", "injected-import");
|
|
1440
|
+
link.onload = () => resolve(link);
|
|
1441
|
+
link.onerror = () => resolve(null);
|
|
1442
|
+
document.head.appendChild(link);
|
|
1443
|
+
})));
|
|
1444
|
+
}
|
|
1445
|
+
let finalCSS = "";
|
|
1446
|
+
const linkNodes = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((l) => !!l.href);
|
|
1447
|
+
for (const link of linkNodes) {
|
|
1448
|
+
try {
|
|
1449
|
+
if (isIconFont2(link.href)) continue;
|
|
1450
|
+
let cssText = "";
|
|
1451
|
+
let sameOrigin = false;
|
|
1452
|
+
try {
|
|
1453
|
+
sameOrigin = new URL(link.href, location.href).origin === location.origin;
|
|
1454
|
+
} catch (e) {
|
|
1455
|
+
}
|
|
1456
|
+
if (!sameOrigin) {
|
|
1457
|
+
if (!isLikelyFontStylesheet(link.href, requiredFamilies)) continue;
|
|
1458
|
+
}
|
|
1459
|
+
if (sameOrigin) {
|
|
1460
|
+
const sheet = Array.from(document.styleSheets).find((s) => s.href === link.href);
|
|
1461
|
+
if (sheet) {
|
|
899
1462
|
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})`);
|
|
1463
|
+
const rules = sheet.cssRules || [];
|
|
1464
|
+
cssText = Array.from(rules).map((r) => r.cssText).join("");
|
|
910
1465
|
} catch (e) {
|
|
911
|
-
console.warn("[snapdom] Failed to fetch font resource:", url);
|
|
912
1466
|
}
|
|
913
1467
|
}
|
|
914
|
-
cssFinal = cssFinal.replace(face, inlined);
|
|
915
1468
|
}
|
|
916
|
-
|
|
1469
|
+
if (!cssText) {
|
|
1470
|
+
const res = yield snapFetch(link.href, { as: "text", useProxy });
|
|
1471
|
+
cssText = res.data;
|
|
1472
|
+
if (isIconFont2(link.href)) continue;
|
|
1473
|
+
}
|
|
1474
|
+
let facesOut = "";
|
|
1475
|
+
for (const face of cssText.match(FACE_RE) || []) {
|
|
1476
|
+
const famRaw = (((_b = face.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _b[1]) || "").trim();
|
|
1477
|
+
const family = pickPrimaryFamily(famRaw);
|
|
1478
|
+
if (!family || isIconFont2(family)) {
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
const weightSpec = (((_c = face.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _c[1]) || "400").trim();
|
|
1482
|
+
const styleSpec = (((_d = face.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _d[1]) || "normal").trim();
|
|
1483
|
+
const stretchSpec = (((_e = face.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _e[1]) || "100%").trim();
|
|
1484
|
+
const urange = (((_f = face.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _f[1]) || "").trim();
|
|
1485
|
+
const srcRaw = (((_g = face.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _g[1]) || "").trim();
|
|
1486
|
+
const srcUrls = extractSrcUrls(srcRaw, link.href);
|
|
1487
|
+
if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) {
|
|
1488
|
+
continue;
|
|
1489
|
+
}
|
|
1490
|
+
const ranges = parseUnicodeRange(urange);
|
|
1491
|
+
if (!unicodeIntersects(usedCodepoints, ranges)) {
|
|
1492
|
+
continue;
|
|
1493
|
+
}
|
|
1494
|
+
const meta = { family, weightSpec, styleSpec, stretchSpec, unicodeRange: urange, srcRaw, srcUrls, href: link.href };
|
|
1495
|
+
if (exclude && simpleExcluder(meta, ranges)) {
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
const newFace = /url\(/i.test(srcRaw) ? yield inlineUrlsInCssBlock(face, link.href) : face;
|
|
1499
|
+
facesOut += newFace + "";
|
|
1500
|
+
}
|
|
1501
|
+
if (facesOut.trim()) finalCSS += facesOut + "";
|
|
917
1502
|
} catch (e) {
|
|
918
|
-
console.warn("[snapdom] Failed to
|
|
1503
|
+
console.warn("[snapdom] Failed to process stylesheet:", link.href);
|
|
919
1504
|
}
|
|
920
1505
|
}
|
|
921
1506
|
for (const sheet of document.styleSheets) {
|
|
922
1507
|
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
|
-
}
|
|
1508
|
+
if (sheet.href && linkNodes.some((l) => l.href === sheet.href)) continue;
|
|
1509
|
+
const rules = sheet.cssRules || [];
|
|
1510
|
+
for (const rule of rules) {
|
|
1511
|
+
if (rule.type !== CSSRule.FONT_FACE_RULE) continue;
|
|
1512
|
+
const famRaw = (rule.style.getPropertyValue("font-family") || "").trim();
|
|
1513
|
+
const family = pickPrimaryFamily(famRaw);
|
|
1514
|
+
if (!family || isIconFont2(family)) continue;
|
|
1515
|
+
const weightSpec = (rule.style.getPropertyValue("font-weight") || "400").trim();
|
|
1516
|
+
const styleSpec = (rule.style.getPropertyValue("font-style") || "normal").trim();
|
|
1517
|
+
const stretchSpec = (rule.style.getPropertyValue("font-stretch") || "100%").trim();
|
|
1518
|
+
const srcRaw = (rule.style.getPropertyValue("src") || "").trim();
|
|
1519
|
+
const urange = (rule.style.getPropertyValue("unicode-range") || "").trim();
|
|
1520
|
+
const srcUrls = extractSrcUrls(srcRaw, sheet.href || location.href);
|
|
1521
|
+
if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) continue;
|
|
1522
|
+
const ranges = parseUnicodeRange(urange);
|
|
1523
|
+
if (!unicodeIntersects(usedCodepoints, ranges)) continue;
|
|
1524
|
+
const meta = { family, weightSpec, styleSpec, stretchSpec, unicodeRange: urange, srcRaw, srcUrls, href: sheet.href || location.href };
|
|
1525
|
+
if (exclude && simpleExcluder(meta, ranges)) continue;
|
|
1526
|
+
if (/url\(/i.test(srcRaw)) {
|
|
1527
|
+
const inlinedSrc = yield inlineUrlsInCssBlock(srcRaw, sheet.href || location.href);
|
|
1528
|
+
finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`;
|
|
1529
|
+
} else {
|
|
1530
|
+
finalCSS += `@font-face{font-family:${family};src:${srcRaw};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`;
|
|
974
1531
|
}
|
|
975
1532
|
}
|
|
976
1533
|
} catch (e) {
|
|
977
|
-
console.warn("[snapdom] Cannot access stylesheet", sheet.href, e);
|
|
978
1534
|
}
|
|
979
1535
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
if (
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1536
|
+
try {
|
|
1537
|
+
for (const f of document.fonts || []) {
|
|
1538
|
+
if (!f || !f.family || f.status !== "loaded" || !f._snapdomSrc) continue;
|
|
1539
|
+
const fam = String(f.family).replace(/^['"]+|['"]+$/g, "");
|
|
1540
|
+
if (isIconFont2(fam)) continue;
|
|
1541
|
+
if (!requiredIndex.has(fam)) continue;
|
|
1542
|
+
if ((exclude == null ? void 0 : exclude.families) && exclude.families.some((n) => String(n).toLowerCase() === fam.toLowerCase())) {
|
|
1543
|
+
continue;
|
|
1544
|
+
}
|
|
1545
|
+
let b64 = f._snapdomSrc;
|
|
1546
|
+
if (!String(b64).startsWith("data:")) {
|
|
1547
|
+
if ((_h = cache.resource) == null ? void 0 : _h.has(f._snapdomSrc)) {
|
|
1548
|
+
b64 = cache.resource.get(f._snapdomSrc);
|
|
1549
|
+
(_i = cache.font) == null ? void 0 : _i.add(f._snapdomSrc);
|
|
1550
|
+
} else if (!((_j = cache.font) == null ? void 0 : _j.has(f._snapdomSrc))) {
|
|
989
1551
|
try {
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
cache.font.add(font._snapdomSrc);
|
|
1552
|
+
const r = yield snapFetch(f._snapdomSrc, { as: "dataURL", useProxy, silent: true });
|
|
1553
|
+
if (r.ok && typeof r.data === "string") {
|
|
1554
|
+
b64 = r.data;
|
|
1555
|
+
(_k = cache.resource) == null ? void 0 : _k.set(f._snapdomSrc, b64);
|
|
1556
|
+
(_l = cache.font) == null ? void 0 : _l.add(f._snapdomSrc);
|
|
1557
|
+
} else {
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
999
1560
|
} catch (e) {
|
|
1000
|
-
console.warn("[snapdom] Failed to fetch dynamic font src:",
|
|
1561
|
+
console.warn("[snapdom] Failed to fetch dynamic font src:", f._snapdomSrc);
|
|
1001
1562
|
continue;
|
|
1002
1563
|
}
|
|
1003
1564
|
}
|
|
1004
1565
|
}
|
|
1005
|
-
finalCSS += `@font-face{font-family:'${
|
|
1566
|
+
finalCSS += `@font-face{font-family:'${fam}';src:url(${b64});font-style:${f.style || "normal"};font-weight:${f.weight || "normal"};}`;
|
|
1006
1567
|
}
|
|
1568
|
+
} catch (e) {
|
|
1007
1569
|
}
|
|
1008
1570
|
for (const font of localFonts) {
|
|
1009
1571
|
if (!font || typeof font !== "object") continue;
|
|
1010
|
-
const
|
|
1011
|
-
if (!family ||
|
|
1572
|
+
const family = String(font.family || "").replace(/^['"]+|['"]+$/g, "");
|
|
1573
|
+
if (!family || isIconFont2(family)) continue;
|
|
1574
|
+
if (!requiredIndex.has(family)) continue;
|
|
1575
|
+
if ((exclude == null ? void 0 : exclude.families) && exclude.families.some((n) => String(n).toLowerCase() === family.toLowerCase())) continue;
|
|
1576
|
+
const weight = font.weight != null ? String(font.weight) : "normal";
|
|
1577
|
+
const style = font.style != null ? String(font.style) : "normal";
|
|
1578
|
+
const stretch = font.stretchPct != null ? `${font.stretchPct}%` : "100%";
|
|
1579
|
+
const src = String(font.src || "");
|
|
1012
1580
|
let b64 = src;
|
|
1013
1581
|
if (!b64.startsWith("data:")) {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1582
|
+
if ((_m = cache.resource) == null ? void 0 : _m.has(src)) {
|
|
1583
|
+
b64 = cache.resource.get(src);
|
|
1584
|
+
(_n = cache.font) == null ? void 0 : _n.add(src);
|
|
1585
|
+
} else if (!((_o = cache.font) == null ? void 0 : _o.has(src))) {
|
|
1586
|
+
try {
|
|
1587
|
+
const r = yield snapFetch(src, { as: "dataURL", useProxy, silent: true });
|
|
1588
|
+
if (r.ok && typeof r.data === "string") {
|
|
1589
|
+
b64 = r.data;
|
|
1590
|
+
(_p = cache.resource) == null ? void 0 : _p.set(src, b64);
|
|
1591
|
+
(_q = cache.font) == null ? void 0 : _q.add(src);
|
|
1592
|
+
} else {
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
} catch (e) {
|
|
1596
|
+
console.warn("[snapdom] Failed to fetch localFonts src:", src);
|
|
1597
|
+
continue;
|
|
1598
|
+
}
|
|
1027
1599
|
}
|
|
1028
|
-
} else {
|
|
1029
|
-
cache.resource.set(src, b64);
|
|
1030
|
-
cache.font.add(src);
|
|
1031
1600
|
}
|
|
1032
|
-
finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};}`;
|
|
1601
|
+
finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};font-stretch:${stretch};}`;
|
|
1033
1602
|
}
|
|
1034
1603
|
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
|
-
}
|
|
1604
|
+
finalCSS = dedupeFontFaces(finalCSS);
|
|
1605
|
+
(_r = cache.resource) == null ? void 0 : _r.set(cacheKey, finalCSS);
|
|
1042
1606
|
}
|
|
1043
1607
|
return finalCSS;
|
|
1044
1608
|
});
|
|
1045
1609
|
}
|
|
1610
|
+
function collectUsedFontVariants(root) {
|
|
1611
|
+
const req = /* @__PURE__ */ new Set();
|
|
1612
|
+
if (!root) return req;
|
|
1613
|
+
const tw = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);
|
|
1614
|
+
const addFromStyle = (cs) => {
|
|
1615
|
+
const family = pickPrimaryFamily(cs.fontFamily);
|
|
1616
|
+
if (!family) return;
|
|
1617
|
+
const key = (w, s, st) => `${family}__${normWeight(w)}__${normStyle(s)}__${normStretchPct(st)}`;
|
|
1618
|
+
req.add(key(cs.fontWeight, cs.fontStyle, cs.fontStretch));
|
|
1619
|
+
};
|
|
1620
|
+
addFromStyle(getComputedStyle(root));
|
|
1621
|
+
const csBeforeRoot = getComputedStyle(root, "::before");
|
|
1622
|
+
if (csBeforeRoot && csBeforeRoot.content && csBeforeRoot.content !== "none") addFromStyle(csBeforeRoot);
|
|
1623
|
+
const csAfterRoot = getComputedStyle(root, "::after");
|
|
1624
|
+
if (csAfterRoot && csAfterRoot.content && csAfterRoot.content !== "none") addFromStyle(csAfterRoot);
|
|
1625
|
+
while (tw.nextNode()) {
|
|
1626
|
+
const el = (
|
|
1627
|
+
/** @type {Element} */
|
|
1628
|
+
tw.currentNode
|
|
1629
|
+
);
|
|
1630
|
+
const cs = getComputedStyle(el);
|
|
1631
|
+
addFromStyle(cs);
|
|
1632
|
+
const b = getComputedStyle(el, "::before");
|
|
1633
|
+
if (b && b.content && b.content !== "none") addFromStyle(b);
|
|
1634
|
+
const a = getComputedStyle(el, "::after");
|
|
1635
|
+
if (a && a.content && a.content !== "none") addFromStyle(a);
|
|
1636
|
+
}
|
|
1637
|
+
return req;
|
|
1638
|
+
}
|
|
1639
|
+
function collectUsedCodepoints(root) {
|
|
1640
|
+
const used = /* @__PURE__ */ new Set();
|
|
1641
|
+
const pushText = (txt) => {
|
|
1642
|
+
if (!txt) return;
|
|
1643
|
+
for (const ch of txt) used.add(ch.codePointAt(0));
|
|
1644
|
+
};
|
|
1645
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null);
|
|
1646
|
+
while (walker.nextNode()) {
|
|
1647
|
+
const n = walker.currentNode;
|
|
1648
|
+
if (n.nodeType === Node.TEXT_NODE) {
|
|
1649
|
+
pushText(n.nodeValue || "");
|
|
1650
|
+
} else if (n.nodeType === Node.ELEMENT_NODE) {
|
|
1651
|
+
const el = (
|
|
1652
|
+
/** @type {Element} */
|
|
1653
|
+
n
|
|
1654
|
+
);
|
|
1655
|
+
for (const pseudo of ["::before", "::after"]) {
|
|
1656
|
+
const cs = getComputedStyle(el, pseudo);
|
|
1657
|
+
const c = cs == null ? void 0 : cs.getPropertyValue("content");
|
|
1658
|
+
if (!c || c === "none") continue;
|
|
1659
|
+
if (/^"/.test(c) || /^'/.test(c)) {
|
|
1660
|
+
pushText(c.slice(1, -1));
|
|
1661
|
+
} else {
|
|
1662
|
+
const matches = c.match(/\\[0-9A-Fa-f]{1,6}/g);
|
|
1663
|
+
if (matches) {
|
|
1664
|
+
for (const m of matches) {
|
|
1665
|
+
try {
|
|
1666
|
+
used.add(parseInt(m.slice(1), 16));
|
|
1667
|
+
} catch (e) {
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
return used;
|
|
1676
|
+
}
|
|
1677
|
+
function ensureFontsReady(families, warmupRepetitions = 1) {
|
|
1678
|
+
return __async(this, null, function* () {
|
|
1679
|
+
try {
|
|
1680
|
+
yield document.fonts.ready;
|
|
1681
|
+
} catch (e) {
|
|
1682
|
+
}
|
|
1683
|
+
const fams = Array.from(families || []).filter(Boolean);
|
|
1684
|
+
if (fams.length === 0) return;
|
|
1685
|
+
const warmupOnce = () => {
|
|
1686
|
+
const container = document.createElement("div");
|
|
1687
|
+
container.style.cssText = `position: absolute !important;left: -9999px !important;top: 0 !important;opacity: 0 !important;pointer-events: none !important;contain: layout size style;`;
|
|
1688
|
+
for (const fam of fams) {
|
|
1689
|
+
const span = document.createElement("span");
|
|
1690
|
+
span.textContent = "AaBbGg1234\xC1\xC9\xCD\xD3\xDA\xE7\xF1\u2014\u221E";
|
|
1691
|
+
span.style.fontFamily = `"${fam}"`;
|
|
1692
|
+
span.style.fontWeight = "700";
|
|
1693
|
+
span.style.fontStyle = "italic";
|
|
1694
|
+
span.style.fontSize = "32px";
|
|
1695
|
+
span.style.lineHeight = "1";
|
|
1696
|
+
span.style.whiteSpace = "nowrap";
|
|
1697
|
+
span.style.margin = "0";
|
|
1698
|
+
span.style.padding = "0";
|
|
1699
|
+
container.appendChild(span);
|
|
1700
|
+
}
|
|
1701
|
+
document.body.appendChild(container);
|
|
1702
|
+
container.offsetWidth;
|
|
1703
|
+
document.body.removeChild(container);
|
|
1704
|
+
};
|
|
1705
|
+
for (let i = 0; i < Math.max(1, warmupRepetitions); i++) {
|
|
1706
|
+
warmupOnce();
|
|
1707
|
+
yield new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1046
1711
|
|
|
1047
1712
|
// src/modules/pseudo.js
|
|
1048
|
-
function inlinePseudoElements(source, clone,
|
|
1713
|
+
function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
1049
1714
|
return __async(this, null, function* () {
|
|
1050
1715
|
var _a;
|
|
1051
1716
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
@@ -1075,8 +1740,8 @@
|
|
|
1075
1740
|
span.textContent = first;
|
|
1076
1741
|
span.dataset.snapdomPseudo = "::first-letter";
|
|
1077
1742
|
const snapshot2 = snapshotComputedStyle(style);
|
|
1078
|
-
const key2 = getStyleKey(snapshot2, "span"
|
|
1079
|
-
styleMap.set(span, key2);
|
|
1743
|
+
const key2 = getStyleKey(snapshot2, "span");
|
|
1744
|
+
sessionCache.styleMap.set(span, key2);
|
|
1080
1745
|
const restNode = document.createTextNode(rest);
|
|
1081
1746
|
clone.replaceChild(restNode, textNode);
|
|
1082
1747
|
clone.insertBefore(span, restNode);
|
|
@@ -1096,7 +1761,7 @@
|
|
|
1096
1761
|
const borderStyle = style.borderStyle;
|
|
1097
1762
|
const borderWidth = parseFloat(style.borderWidth);
|
|
1098
1763
|
const transform = style.transform;
|
|
1099
|
-
const
|
|
1764
|
+
const isIconFont22 = isIconFont2(fontFamily);
|
|
1100
1765
|
const hasExplicitContent = content !== "none" && cleanContent !== "";
|
|
1101
1766
|
const hasBg = bg && bg !== "none";
|
|
1102
1767
|
const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
|
|
@@ -1109,9 +1774,9 @@
|
|
|
1109
1774
|
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
1110
1775
|
pseudoEl.style.verticalAlign = "middle";
|
|
1111
1776
|
const snapshot = snapshotComputedStyle(style);
|
|
1112
|
-
const key = getStyleKey(snapshot, "span"
|
|
1113
|
-
styleMap.set(pseudoEl, key);
|
|
1114
|
-
if (
|
|
1777
|
+
const key = getStyleKey(snapshot, "span");
|
|
1778
|
+
sessionCache.styleMap.set(pseudoEl, key);
|
|
1779
|
+
if (isIconFont22 && cleanContent.length === 1) {
|
|
1115
1780
|
const { dataUrl, width: width2, height: height2 } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
1116
1781
|
const imgEl = document.createElement("img");
|
|
1117
1782
|
imgEl.src = dataUrl;
|
|
@@ -1123,15 +1788,15 @@
|
|
|
1123
1788
|
if (rawUrl == null ? void 0 : rawUrl.trim()) {
|
|
1124
1789
|
try {
|
|
1125
1790
|
const imgEl = document.createElement("img");
|
|
1126
|
-
const dataUrl = yield
|
|
1127
|
-
imgEl.src = dataUrl;
|
|
1791
|
+
const dataUrl = yield snapFetch(safeEncodeURI(rawUrl), { as: "dataURL", useProxy: options.useProxy });
|
|
1792
|
+
imgEl.src = dataUrl.data;
|
|
1128
1793
|
imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
|
|
1129
1794
|
pseudoEl.appendChild(imgEl);
|
|
1130
1795
|
} catch (e) {
|
|
1131
1796
|
console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
|
|
1132
1797
|
}
|
|
1133
1798
|
}
|
|
1134
|
-
} else if (!
|
|
1799
|
+
} else if (!isIconFont22 && hasExplicitContent) {
|
|
1135
1800
|
pseudoEl.textContent = cleanContent;
|
|
1136
1801
|
}
|
|
1137
1802
|
if (hasBg) {
|
|
@@ -1159,7 +1824,7 @@
|
|
|
1159
1824
|
const sChildren = Array.from(source.children);
|
|
1160
1825
|
const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
|
|
1161
1826
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
1162
|
-
yield inlinePseudoElements(sChildren[i], cChildren[i],
|
|
1827
|
+
yield inlinePseudoElements(sChildren[i], cChildren[i], sessionCache, options);
|
|
1163
1828
|
}
|
|
1164
1829
|
});
|
|
1165
1830
|
}
|
|
@@ -1216,13 +1881,16 @@
|
|
|
1216
1881
|
|
|
1217
1882
|
// src/core/prepare.js
|
|
1218
1883
|
function prepareClone(_0) {
|
|
1219
|
-
return __async(this, arguments, function* (element,
|
|
1884
|
+
return __async(this, arguments, function* (element, options = {}) {
|
|
1220
1885
|
var _a, _b;
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
|
|
1886
|
+
const sessionCache = {
|
|
1887
|
+
styleMap: cache.session.styleMap,
|
|
1888
|
+
styleCache: cache.session.styleCache,
|
|
1889
|
+
nodeMap: cache.session.nodeMap
|
|
1890
|
+
};
|
|
1224
1891
|
let clone;
|
|
1225
1892
|
let classCSS = "";
|
|
1893
|
+
let shadowScopedCSS = "";
|
|
1226
1894
|
stabilizeLayout(element);
|
|
1227
1895
|
try {
|
|
1228
1896
|
inlineExternalDefsAndSymbols(element);
|
|
@@ -1230,43 +1898,45 @@
|
|
|
1230
1898
|
console.warn("inlineExternal defs or symbol failed:", e);
|
|
1231
1899
|
}
|
|
1232
1900
|
try {
|
|
1233
|
-
clone = deepClone(element,
|
|
1901
|
+
clone = deepClone(element, sessionCache, options, element);
|
|
1234
1902
|
} catch (e) {
|
|
1235
1903
|
console.warn("deepClone failed:", e);
|
|
1236
1904
|
throw e;
|
|
1237
1905
|
}
|
|
1238
1906
|
try {
|
|
1239
|
-
yield inlinePseudoElements(element, clone,
|
|
1907
|
+
yield inlinePseudoElements(element, clone, sessionCache, options);
|
|
1240
1908
|
} catch (e) {
|
|
1241
1909
|
console.warn("inlinePseudoElements failed:", e);
|
|
1242
1910
|
}
|
|
1243
1911
|
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
|
-
}
|
|
1912
|
+
try {
|
|
1913
|
+
const styleNodes = clone.querySelectorAll("style[data-sd]");
|
|
1914
|
+
for (const s of styleNodes) {
|
|
1915
|
+
shadowScopedCSS += s.textContent || "";
|
|
1916
|
+
s.remove();
|
|
1262
1917
|
}
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1918
|
+
} catch (e) {
|
|
1919
|
+
}
|
|
1920
|
+
const keyToClass = generateCSSClasses(sessionCache.styleMap);
|
|
1921
|
+
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
1922
|
+
classCSS = shadowScopedCSS + classCSS;
|
|
1923
|
+
for (const [node, key] of sessionCache.styleMap.entries()) {
|
|
1924
|
+
if (node.tagName === "STYLE") continue;
|
|
1925
|
+
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
|
|
1266
1926
|
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
const className = keyToClass.get(key);
|
|
1930
|
+
if (className) node.classList.add(className);
|
|
1931
|
+
const bgImage = (_a = node.style) == null ? void 0 : _a.backgroundImage;
|
|
1932
|
+
const hasIcon = (_b = node.dataset) == null ? void 0 : _b.snapdomHasIcon;
|
|
1933
|
+
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
1934
|
+
if (hasIcon) {
|
|
1935
|
+
node.style.verticalAlign = "middle";
|
|
1936
|
+
node.style.display = "inline";
|
|
1267
1937
|
}
|
|
1268
1938
|
}
|
|
1269
|
-
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1939
|
+
for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) {
|
|
1270
1940
|
const scrollX = originalNode.scrollLeft;
|
|
1271
1941
|
const scrollY = originalNode.scrollTop;
|
|
1272
1942
|
const hasScroll = scrollX || scrollY;
|
|
@@ -1285,9 +1955,9 @@
|
|
|
1285
1955
|
cloneNode.appendChild(inner);
|
|
1286
1956
|
}
|
|
1287
1957
|
}
|
|
1288
|
-
if (element === nodeMap.get(clone)) {
|
|
1289
|
-
const computed = styleCache.get(element) || window.getComputedStyle(element);
|
|
1290
|
-
styleCache.set(element, computed);
|
|
1958
|
+
if (element === sessionCache.nodeMap.get(clone)) {
|
|
1959
|
+
const computed = sessionCache.styleCache.get(element) || window.getComputedStyle(element);
|
|
1960
|
+
sessionCache.styleCache.set(element, computed);
|
|
1291
1961
|
const transform = stripTranslate(computed.transform);
|
|
1292
1962
|
clone.style.margin = "0";
|
|
1293
1963
|
clone.style.position = "static";
|
|
@@ -1300,13 +1970,13 @@
|
|
|
1300
1970
|
clone.style.clear = "none";
|
|
1301
1971
|
clone.style.transform = transform || "";
|
|
1302
1972
|
}
|
|
1303
|
-
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1973
|
+
for (const [cloneNode, originalNode] of sessionCache.nodeMap.entries()) {
|
|
1304
1974
|
if (originalNode.tagName === "PRE") {
|
|
1305
1975
|
cloneNode.style.marginTop = "0";
|
|
1306
1976
|
cloneNode.style.marginBlockStart = "0";
|
|
1307
1977
|
}
|
|
1308
1978
|
}
|
|
1309
|
-
return { clone, classCSS, styleCache };
|
|
1979
|
+
return { clone, classCSS, styleCache: sessionCache.styleCache };
|
|
1310
1980
|
});
|
|
1311
1981
|
}
|
|
1312
1982
|
function stabilizeLayout(element) {
|
|
@@ -1324,22 +1994,27 @@
|
|
|
1324
1994
|
var _blobToDataUrlCache = /* @__PURE__ */ new Map();
|
|
1325
1995
|
function blobUrlToDataUrl(blobUrl) {
|
|
1326
1996
|
return __async(this, null, function* () {
|
|
1997
|
+
var _a;
|
|
1998
|
+
if ((_a = cache.resource) == null ? void 0 : _a.has(blobUrl)) return cache.resource.get(blobUrl);
|
|
1327
1999
|
if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl);
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
2000
|
+
const p = (() => __async(null, null, function* () {
|
|
2001
|
+
var _a2;
|
|
2002
|
+
const r = yield snapFetch(blobUrl, { as: "dataURL", silent: true });
|
|
2003
|
+
if (!r.ok || typeof r.data !== "string") {
|
|
2004
|
+
throw new Error(`[snapDOM] Failed to read blob URL: ${blobUrl}`);
|
|
2005
|
+
}
|
|
2006
|
+
(_a2 = cache.resource) == null ? void 0 : _a2.set(blobUrl, r.data);
|
|
2007
|
+
return r.data;
|
|
2008
|
+
}))();
|
|
2009
|
+
_blobToDataUrlCache.set(blobUrl, p);
|
|
2010
|
+
try {
|
|
2011
|
+
const data = yield p;
|
|
2012
|
+
_blobToDataUrlCache.set(blobUrl, data);
|
|
2013
|
+
return data;
|
|
2014
|
+
} catch (e) {
|
|
2015
|
+
_blobToDataUrlCache.delete(blobUrl);
|
|
2016
|
+
throw e;
|
|
2017
|
+
}
|
|
1343
2018
|
});
|
|
1344
2019
|
}
|
|
1345
2020
|
var BLOB_URL_RE = /\bblob:[^)"'\s]+/g;
|
|
@@ -1463,17 +2138,36 @@
|
|
|
1463
2138
|
}
|
|
1464
2139
|
img.removeAttribute("srcset");
|
|
1465
2140
|
img.removeAttribute("sizes");
|
|
1466
|
-
const src = img.src;
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
2141
|
+
const src = img.src || "";
|
|
2142
|
+
if (!src) return;
|
|
2143
|
+
const r = yield snapFetch(src, { as: "dataURL", useProxy: options.useProxy });
|
|
2144
|
+
if (r.ok && typeof r.data === "string" && r.data.startsWith("data:")) {
|
|
2145
|
+
img.src = r.data;
|
|
1470
2146
|
if (!img.width) img.width = img.naturalWidth || 100;
|
|
1471
2147
|
if (!img.height) img.height = img.naturalHeight || 100;
|
|
1472
|
-
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
const w = img.width || img.naturalWidth || 100;
|
|
2151
|
+
const h = img.height || img.naturalHeight || 100;
|
|
2152
|
+
if (options.placeholders !== false) {
|
|
1473
2153
|
const fallback = document.createElement("div");
|
|
1474
|
-
fallback.style =
|
|
1475
|
-
|
|
2154
|
+
fallback.style.cssText = [
|
|
2155
|
+
`width:${w}px`,
|
|
2156
|
+
`height:${h}px`,
|
|
2157
|
+
"background:#ccc",
|
|
2158
|
+
"display:inline-block",
|
|
2159
|
+
"text-align:center",
|
|
2160
|
+
`line-height:${h}px`,
|
|
2161
|
+
"color:#666",
|
|
2162
|
+
"font-size:12px",
|
|
2163
|
+
"overflow:hidden"
|
|
2164
|
+
].join(";");
|
|
2165
|
+
fallback.textContent = "img";
|
|
1476
2166
|
img.replaceWith(fallback);
|
|
2167
|
+
} else {
|
|
2168
|
+
const spacer = document.createElement("div");
|
|
2169
|
+
spacer.style.cssText = `display:inline-block;width:${w}px;height:${h}px;visibility:hidden;`;
|
|
2170
|
+
img.replaceWith(spacer);
|
|
1477
2171
|
}
|
|
1478
2172
|
});
|
|
1479
2173
|
for (let i = 0; i < imgs.length; i += 4) {
|
|
@@ -1526,10 +2220,6 @@
|
|
|
1526
2220
|
cloneNode.style.setProperty(prop, inlined.join(", "));
|
|
1527
2221
|
}
|
|
1528
2222
|
}
|
|
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
2223
|
const sChildren = Array.from(srcNode.children);
|
|
1534
2224
|
const cChildren = Array.from(cloneNode.children);
|
|
1535
2225
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
@@ -1540,17 +2230,17 @@
|
|
|
1540
2230
|
}
|
|
1541
2231
|
|
|
1542
2232
|
// src/core/capture.js
|
|
1543
|
-
function captureDOM(
|
|
1544
|
-
return __async(this,
|
|
2233
|
+
function captureDOM(element, options) {
|
|
2234
|
+
return __async(this, null, function* () {
|
|
1545
2235
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1546
|
-
cache
|
|
1547
|
-
const
|
|
2236
|
+
applyCachePolicy(options.cache);
|
|
2237
|
+
const fast = options.fast;
|
|
1548
2238
|
let clone, classCSS, styleCache;
|
|
1549
2239
|
let fontsCSS = "";
|
|
1550
2240
|
let baseCSS = "";
|
|
1551
2241
|
let dataURL;
|
|
1552
2242
|
let svgString;
|
|
1553
|
-
({ clone, classCSS, styleCache } = yield prepareClone(element,
|
|
2243
|
+
({ clone, classCSS, styleCache } = yield prepareClone(element, options));
|
|
1554
2244
|
yield new Promise((resolve) => {
|
|
1555
2245
|
idle(() => __async(null, null, function* () {
|
|
1556
2246
|
yield inlineImages(clone, options);
|
|
@@ -1563,28 +2253,34 @@
|
|
|
1563
2253
|
resolve();
|
|
1564
2254
|
}), { fast });
|
|
1565
2255
|
});
|
|
1566
|
-
if (embedFonts) {
|
|
2256
|
+
if (options.embedFonts) {
|
|
1567
2257
|
yield new Promise((resolve) => {
|
|
1568
2258
|
idle(() => __async(null, null, function* () {
|
|
1569
|
-
|
|
2259
|
+
const required = collectUsedFontVariants(element);
|
|
2260
|
+
const usedCodepoints = collectUsedCodepoints(element);
|
|
2261
|
+
if (isSafari) {
|
|
2262
|
+
const families = new Set(
|
|
2263
|
+
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2264
|
+
);
|
|
2265
|
+
yield ensureFontsReady(families, 2);
|
|
2266
|
+
}
|
|
2267
|
+
fontsCSS = yield embedCustomFonts({ required, usedCodepoints, preCached: false, exclude: options.excludeFonts, useProxy: options.useProxy });
|
|
1570
2268
|
resolve();
|
|
1571
2269
|
}), { fast });
|
|
1572
2270
|
});
|
|
1573
2271
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
});
|
|
1587
|
-
}
|
|
2272
|
+
const usedTags = collectUsedTagNames(clone).sort();
|
|
2273
|
+
const tagKey = usedTags.join(",");
|
|
2274
|
+
if (cache.baseStyle.has(tagKey)) {
|
|
2275
|
+
baseCSS = cache.baseStyle.get(tagKey);
|
|
2276
|
+
} else {
|
|
2277
|
+
yield new Promise((resolve) => {
|
|
2278
|
+
idle(() => {
|
|
2279
|
+
baseCSS = generateDedupedBaseCSS(usedTags);
|
|
2280
|
+
cache.baseStyle.set(tagKey, baseCSS);
|
|
2281
|
+
resolve();
|
|
2282
|
+
}, { fast });
|
|
2283
|
+
});
|
|
1588
2284
|
}
|
|
1589
2285
|
yield new Promise((resolve) => {
|
|
1590
2286
|
idle(() => {
|
|
@@ -1643,228 +2339,250 @@
|
|
|
1643
2339
|
});
|
|
1644
2340
|
}
|
|
1645
2341
|
|
|
1646
|
-
// src/
|
|
1647
|
-
function
|
|
1648
|
-
|
|
2342
|
+
// src/core/context.js
|
|
2343
|
+
function normalizeCachePolicy(v) {
|
|
2344
|
+
if (v === true) return "soft";
|
|
2345
|
+
if (v === false) return "disabled";
|
|
2346
|
+
if (typeof v === "string") {
|
|
2347
|
+
const s = v.toLowerCase().trim();
|
|
2348
|
+
if (s === "disabled" || s === "full" || s === "auto" || s === "soft") return (
|
|
2349
|
+
/** @type {CachePolicy} */
|
|
2350
|
+
s
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
return "soft";
|
|
2354
|
+
}
|
|
2355
|
+
function createContext(options = {}) {
|
|
2356
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
2357
|
+
const resolvedFormat = (_a = options.format) != null ? _a : "png";
|
|
2358
|
+
const cachePolicy = normalizeCachePolicy(options.cache);
|
|
2359
|
+
return {
|
|
2360
|
+
// Debug & perf
|
|
2361
|
+
debug: (_b = options.debug) != null ? _b : false,
|
|
2362
|
+
fast: (_c = options.fast) != null ? _c : true,
|
|
2363
|
+
scale: (_d = options.scale) != null ? _d : 1,
|
|
2364
|
+
// DOM filters
|
|
2365
|
+
exclude: (_e = options.exclude) != null ? _e : [],
|
|
2366
|
+
filter: (_f = options.filter) != null ? _f : null,
|
|
2367
|
+
placeholders: options.placeholders !== false,
|
|
2368
|
+
// default true
|
|
2369
|
+
// Fuentes
|
|
2370
|
+
embedFonts: (_g = options.embedFonts) != null ? _g : false,
|
|
2371
|
+
iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
|
|
2372
|
+
localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
|
|
2373
|
+
excludeFonts: (_h = options.excludeFonts) != null ? _h : void 0,
|
|
2374
|
+
/** Cache policy (disabled|full|auto|soft). Default: "soft" */
|
|
2375
|
+
/** @type {CachePolicy} */
|
|
2376
|
+
cache: cachePolicy,
|
|
2377
|
+
// Red
|
|
2378
|
+
useProxy: typeof options.useProxy === "string" ? options.useProxy : "",
|
|
2379
|
+
// Salida
|
|
2380
|
+
width: (_i = options.width) != null ? _i : null,
|
|
2381
|
+
height: (_j = options.height) != null ? _j : null,
|
|
2382
|
+
format: resolvedFormat,
|
|
2383
|
+
type: (_k = options.type) != null ? _k : "svg",
|
|
2384
|
+
quality: (_l = options.quality) != null ? _l : 0.92,
|
|
2385
|
+
dpr: (_m = options.dpr) != null ? _m : window.devicePixelRatio || 1,
|
|
2386
|
+
backgroundColor: (_n = options.backgroundColor) != null ? _n : ["jpg", "jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null,
|
|
2387
|
+
filename: (_o = options.filename) != null ? _o : "snapDOM"
|
|
2388
|
+
// Plugins (reservado para futura activación)
|
|
2389
|
+
// plugins: normalizePlugins(...),
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
// src/exporters/toImg.js
|
|
2394
|
+
function toImg(url, options) {
|
|
2395
|
+
return __async(this, null, function* () {
|
|
1649
2396
|
const img = new Image();
|
|
1650
2397
|
img.src = url;
|
|
1651
2398
|
yield img.decode();
|
|
1652
|
-
if (scale !== 1) {
|
|
1653
|
-
img.style.width = `${img.naturalWidth * scale}px`;
|
|
1654
|
-
img.style.height = `${img.naturalHeight * scale}px`;
|
|
2399
|
+
if (options.scale !== 1) {
|
|
2400
|
+
img.style.width = `${img.naturalWidth * options.scale}px`;
|
|
2401
|
+
img.style.height = `${img.naturalHeight * options.scale}px`;
|
|
1655
2402
|
}
|
|
1656
2403
|
return img;
|
|
1657
2404
|
});
|
|
1658
2405
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
2406
|
+
|
|
2407
|
+
// src/exporters/toCanvas.js
|
|
2408
|
+
function toCanvas(url, options) {
|
|
2409
|
+
return __async(this, null, function* () {
|
|
2410
|
+
const { scale: scale2, dpr } = options;
|
|
2411
|
+
const safari = isSafari();
|
|
1661
2412
|
const img = new Image();
|
|
1662
|
-
img.src = url;
|
|
1663
2413
|
img.crossOrigin = "anonymous";
|
|
1664
2414
|
img.loading = "eager";
|
|
1665
2415
|
img.decoding = "sync";
|
|
1666
|
-
|
|
1667
|
-
let
|
|
1668
|
-
if (
|
|
1669
|
-
document.
|
|
1670
|
-
|
|
2416
|
+
img.src = url;
|
|
2417
|
+
let tempSlot = null;
|
|
2418
|
+
if (safari) {
|
|
2419
|
+
tempSlot = document.createElement("div");
|
|
2420
|
+
tempSlot.setAttribute("aria-hidden", "true");
|
|
2421
|
+
tempSlot.style.cssText = "position:absolute;left:-99999px;top:0;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;contain:size layout style;";
|
|
2422
|
+
document.body.appendChild(tempSlot);
|
|
2423
|
+
tempSlot.appendChild(img);
|
|
1671
2424
|
}
|
|
1672
2425
|
yield img.decode();
|
|
1673
|
-
if (
|
|
1674
|
-
yield new Promise((
|
|
2426
|
+
if (safari) {
|
|
2427
|
+
yield new Promise((r) => setTimeout(r, 100));
|
|
1675
2428
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
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;
|
|
2429
|
+
const width = img.naturalWidth * scale2;
|
|
2430
|
+
const height = img.naturalHeight * scale2;
|
|
1682
2431
|
const canvas = document.createElement("canvas");
|
|
1683
2432
|
canvas.width = Math.ceil(width * dpr);
|
|
1684
2433
|
canvas.height = Math.ceil(height * dpr);
|
|
1685
2434
|
canvas.style.width = `${width}px`;
|
|
1686
2435
|
canvas.style.height = `${height}px`;
|
|
1687
2436
|
const ctx = canvas.getContext("2d");
|
|
1688
|
-
ctx.scale(dpr, dpr);
|
|
2437
|
+
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
1689
2438
|
ctx.drawImage(img, 0, 0, width, height);
|
|
1690
|
-
if (
|
|
2439
|
+
if (tempSlot && tempSlot.parentNode) {
|
|
2440
|
+
try {
|
|
2441
|
+
tempSlot.parentNode.removeChild(tempSlot);
|
|
2442
|
+
} catch (e) {
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
1691
2445
|
return canvas;
|
|
1692
2446
|
});
|
|
1693
2447
|
}
|
|
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";
|
|
2448
|
+
|
|
2449
|
+
// src/exporters/toBlob.js
|
|
2450
|
+
function toBlob(url, options) {
|
|
2451
|
+
return __async(this, null, function* () {
|
|
2452
|
+
const type = options.type;
|
|
1707
2453
|
if (type === "svg") {
|
|
1708
2454
|
const svgText = decodeURIComponent(url.split(",")[1]);
|
|
1709
2455
|
return new Blob([svgText], { type: "image/svg+xml" });
|
|
1710
2456
|
}
|
|
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;
|
|
2457
|
+
const canvas = yield toCanvas(url, options);
|
|
2458
|
+
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
2459
|
+
return new Promise(
|
|
2460
|
+
(resolve) => finalCanvas.toBlob(
|
|
2461
|
+
(blob) => resolve(blob),
|
|
2462
|
+
`image/${type}`,
|
|
2463
|
+
options.quality
|
|
2464
|
+
)
|
|
2465
|
+
);
|
|
1729
2466
|
});
|
|
1730
2467
|
}
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
const canvas = yield
|
|
2468
|
+
|
|
2469
|
+
// src/modules/rasterize.js
|
|
2470
|
+
function rasterize(url, options) {
|
|
2471
|
+
return __async(this, null, function* () {
|
|
2472
|
+
const canvas = yield toCanvas(url, options);
|
|
2473
|
+
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
1736
2474
|
const img = new Image();
|
|
1737
|
-
img.src =
|
|
2475
|
+
img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
1738
2476
|
yield img.decode();
|
|
1739
|
-
img.style.width = `${
|
|
1740
|
-
img.style.height = `${
|
|
2477
|
+
img.style.width = `${finalCanvas.width / options.dpr}px`;
|
|
2478
|
+
img.style.height = `${finalCanvas.height / options.dpr}px`;
|
|
1741
2479
|
return img;
|
|
1742
2480
|
});
|
|
1743
2481
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
2482
|
+
|
|
2483
|
+
// src/exporters/download.js
|
|
2484
|
+
function download(url, options) {
|
|
2485
|
+
return __async(this, null, function* () {
|
|
2486
|
+
if (options.format === "svg") {
|
|
2487
|
+
const blob = yield toBlob(url, __spreadProps(__spreadValues({}, options), { type: "svg" }));
|
|
1748
2488
|
const objectURL = URL.createObjectURL(blob);
|
|
1749
2489
|
const a2 = document.createElement("a");
|
|
1750
2490
|
a2.href = objectURL;
|
|
1751
|
-
a2.download =
|
|
2491
|
+
a2.download = options.filename;
|
|
1752
2492
|
a2.click();
|
|
1753
2493
|
URL.revokeObjectURL(objectURL);
|
|
1754
2494
|
return;
|
|
1755
2495
|
}
|
|
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);
|
|
2496
|
+
const canvas = yield toCanvas(url, options);
|
|
2497
|
+
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
1766
2498
|
const a = document.createElement("a");
|
|
1767
|
-
a.href =
|
|
1768
|
-
a.download =
|
|
2499
|
+
a.href = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
2500
|
+
a.download = options.filename;
|
|
1769
2501
|
a.click();
|
|
1770
2502
|
});
|
|
1771
2503
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
2504
|
+
|
|
2505
|
+
// src/api/snapdom.js
|
|
2506
|
+
var INTERNAL_TOKEN = Symbol("snapdom.internal");
|
|
2507
|
+
function snapdom(element, userOptions) {
|
|
2508
|
+
return __async(this, null, function* () {
|
|
1775
2509
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
return yield snapdom.capture(element, options);
|
|
2510
|
+
const context = createContext(userOptions);
|
|
2511
|
+
if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
|
|
2512
|
+
return snapdom.capture(element, context, INTERNAL_TOKEN);
|
|
1780
2513
|
});
|
|
1781
2514
|
}
|
|
1782
|
-
snapdom.capture = (
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
const
|
|
2515
|
+
snapdom.capture = (el, context, _token) => __async(null, null, function* () {
|
|
2516
|
+
if (_token !== INTERNAL_TOKEN) {
|
|
2517
|
+
throw new Error("[snapdom.capture] is internal. Use snapdom(...) instead.");
|
|
2518
|
+
}
|
|
2519
|
+
const url = yield captureDOM(el, context);
|
|
2520
|
+
const ensureContext = (opts) => __spreadValues(__spreadValues({}, context), opts || {});
|
|
2521
|
+
const withFormat = (format) => (opts) => rasterize(url, ensureContext(__spreadProps(__spreadValues({}, opts || {}), { format })));
|
|
1787
2522
|
return {
|
|
1788
2523
|
url,
|
|
1789
|
-
options,
|
|
1790
2524
|
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
|
-
}
|
|
2525
|
+
toImg: (opts) => toImg(url, ensureContext(opts)),
|
|
2526
|
+
toCanvas: (opts) => toCanvas(url, ensureContext(opts)),
|
|
2527
|
+
toBlob: (opts) => toBlob(url, ensureContext(opts)),
|
|
2528
|
+
toPng: withFormat("png"),
|
|
2529
|
+
toJpg: withFormat("jpeg"),
|
|
2530
|
+
toWebp: withFormat("webp"),
|
|
2531
|
+
download: (opts) => download(url, ensureContext(opts))
|
|
1801
2532
|
};
|
|
1802
2533
|
});
|
|
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
|
-
});
|
|
2534
|
+
snapdom.toRaw = (el, options) => snapdom(el, options).then((result) => result.toRaw());
|
|
2535
|
+
snapdom.toImg = (el, options) => snapdom(el, options).then((result) => result.toImg());
|
|
2536
|
+
snapdom.toCanvas = (el, options) => snapdom(el, options).then((result) => result.toCanvas());
|
|
2537
|
+
snapdom.toBlob = (el, options) => snapdom(el, options).then((result) => result.toBlob());
|
|
2538
|
+
snapdom.toPng = (el, options) => snapdom(el, __spreadProps(__spreadValues({}, options), { format: "png" })).then((result) => result.toPng());
|
|
2539
|
+
snapdom.toJpg = (el, options) => snapdom(el, __spreadProps(__spreadValues({}, options), { format: "jpeg" })).then((result) => result.toJpg());
|
|
2540
|
+
snapdom.toWebp = (el, options) => snapdom(el, __spreadProps(__spreadValues({}, options), { format: "webp" })).then((result) => result.toWebp());
|
|
2541
|
+
snapdom.download = (el, options) => snapdom(el, options).then((result) => result.download());
|
|
1837
2542
|
|
|
1838
2543
|
// src/api/preCache.js
|
|
1839
2544
|
function preCache() {
|
|
1840
2545
|
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
|
-
}
|
|
2546
|
+
var _a;
|
|
2547
|
+
const {
|
|
2548
|
+
embedFonts = true,
|
|
2549
|
+
cacheOpt = "full",
|
|
2550
|
+
useProxy = ""
|
|
2551
|
+
} = options;
|
|
2552
|
+
applyCachePolicy(cacheOpt);
|
|
1852
2553
|
try {
|
|
1853
2554
|
yield document.fonts.ready;
|
|
1854
2555
|
} catch (e) {
|
|
1855
2556
|
}
|
|
1856
2557
|
precacheCommonTags();
|
|
2558
|
+
cache.session = cache.session || {};
|
|
2559
|
+
if (!cache.session.styleCache) {
|
|
2560
|
+
cache.session.styleCache = /* @__PURE__ */ new WeakMap();
|
|
2561
|
+
}
|
|
2562
|
+
try {
|
|
2563
|
+
yield inlineBackgroundImages(
|
|
2564
|
+
root,
|
|
2565
|
+
/* mirror */
|
|
2566
|
+
void 0,
|
|
2567
|
+
cache.session.styleCache,
|
|
2568
|
+
{ useProxy }
|
|
2569
|
+
);
|
|
2570
|
+
} catch (e) {
|
|
2571
|
+
}
|
|
1857
2572
|
let imgEls = [], allEls = [];
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
2573
|
+
try {
|
|
2574
|
+
if (root == null ? void 0 : root.querySelectorAll) {
|
|
2575
|
+
imgEls = Array.from(root.querySelectorAll("img[src]"));
|
|
2576
|
+
allEls = Array.from(root.querySelectorAll("*"));
|
|
2577
|
+
}
|
|
2578
|
+
} catch (e) {
|
|
1861
2579
|
}
|
|
1862
2580
|
const promises = [];
|
|
1863
2581
|
for (const img of imgEls) {
|
|
1864
2582
|
const src = img == null ? void 0 : img.src;
|
|
1865
2583
|
if (!src) continue;
|
|
1866
2584
|
if (!cache.image.has(src)) {
|
|
1867
|
-
const p = Promise.resolve().then(() =>
|
|
2585
|
+
const p = Promise.resolve().then(() => snapFetch(src, { as: "dataURL", useProxy })).then((dataURL) => {
|
|
1868
2586
|
cache.image.set(src, dataURL);
|
|
1869
2587
|
}).catch(() => {
|
|
1870
2588
|
});
|
|
@@ -1878,8 +2596,8 @@
|
|
|
1878
2596
|
} catch (e) {
|
|
1879
2597
|
}
|
|
1880
2598
|
if (bg && bg !== "none") {
|
|
1881
|
-
const
|
|
1882
|
-
for (const entry of
|
|
2599
|
+
const parts = splitBackgroundImage(bg);
|
|
2600
|
+
for (const entry of parts) {
|
|
1883
2601
|
if (entry.startsWith("url(")) {
|
|
1884
2602
|
const p = Promise.resolve().then(() => inlineSingleBackgroundEntry(entry, __spreadProps(__spreadValues({}, options), { useProxy }))).catch(() => {
|
|
1885
2603
|
});
|
|
@@ -1890,10 +2608,23 @@
|
|
|
1890
2608
|
}
|
|
1891
2609
|
if (embedFonts) {
|
|
1892
2610
|
try {
|
|
1893
|
-
|
|
2611
|
+
const required = collectUsedFontVariants(root);
|
|
2612
|
+
const usedCodepoints = collectUsedCodepoints(root);
|
|
2613
|
+
if (typeof isSafari === "function" ? isSafari() : !!isSafari) {
|
|
2614
|
+
const families = new Set(
|
|
2615
|
+
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2616
|
+
);
|
|
2617
|
+
yield ensureFontsReady(families, 2);
|
|
2618
|
+
}
|
|
2619
|
+
yield embedCustomFonts({
|
|
2620
|
+
required,
|
|
2621
|
+
usedCodepoints,
|
|
2622
|
+
exclude: options.excludeFonts,
|
|
2623
|
+
localFonts: options.localFonts,
|
|
2624
|
+
useProxy: (_a = options.useProxy) != null ? _a : useProxy
|
|
2625
|
+
});
|
|
1894
2626
|
} catch (e) {
|
|
1895
2627
|
}
|
|
1896
|
-
;
|
|
1897
2628
|
}
|
|
1898
2629
|
yield Promise.allSettled(promises);
|
|
1899
2630
|
});
|