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