@zumer/snapdom 1.9.7 → 1.9.9
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 +58 -8
- package/dist/snapdom.js +660 -281
- package/dist/snapdom.min.js +3 -3
- package/dist/snapdom.min.mjs +3 -3
- package/dist/snapdom.mjs +636 -277
- package/package.json +1 -1
- package/types/snapdom.d.ts +4 -0
package/dist/snapdom.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.9.
|
|
4
|
+
* v.1.9.9
|
|
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,6 +25,7 @@
|
|
|
23
25
|
}
|
|
24
26
|
return a;
|
|
25
27
|
};
|
|
28
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
26
29
|
var __objRest = (source, exclude) => {
|
|
27
30
|
var target = {};
|
|
28
31
|
for (var prop in source)
|
|
@@ -67,18 +70,10 @@
|
|
|
67
70
|
font: /* @__PURE__ */ new Set(),
|
|
68
71
|
snapshot: /* @__PURE__ */ new WeakMap(),
|
|
69
72
|
snapshotKey: /* @__PURE__ */ new Map(),
|
|
70
|
-
preStyleMap: /* @__PURE__ */ new Map(),
|
|
71
|
-
preStyle: /* @__PURE__ */ new WeakMap(),
|
|
72
|
-
preNodeMap: /* @__PURE__ */ new Map(),
|
|
73
73
|
reset: resetCache
|
|
74
74
|
};
|
|
75
75
|
function resetCache() {
|
|
76
76
|
cache.computedStyle = /* @__PURE__ */ new WeakMap();
|
|
77
|
-
cache.snapshot = /* @__PURE__ */ new WeakMap();
|
|
78
|
-
cache.snapshotKey.clear();
|
|
79
|
-
cache.preStyleMap.clear();
|
|
80
|
-
cache.preStyle = /* @__PURE__ */ new WeakMap();
|
|
81
|
-
cache.preNodeMap.clear();
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
// src/utils/cssTools.js
|
|
@@ -130,7 +125,7 @@
|
|
|
130
125
|
if (cache.defaultStyle.has(tagName)) {
|
|
131
126
|
return cache.defaultStyle.get(tagName);
|
|
132
127
|
}
|
|
133
|
-
const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template"]);
|
|
128
|
+
const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template", "defs", "symbol", "title", "metadata", "desc"]);
|
|
134
129
|
if (skipTags.has(tagName)) {
|
|
135
130
|
const empty = {};
|
|
136
131
|
cache.defaultStyle.set(tagName, empty);
|
|
@@ -160,10 +155,14 @@
|
|
|
160
155
|
cache.defaultStyle.set(tagName, defaults);
|
|
161
156
|
return defaults;
|
|
162
157
|
}
|
|
158
|
+
var IGNORED_PROPS = /* @__PURE__ */ new Set([
|
|
159
|
+
"-webkit-locale"
|
|
160
|
+
]);
|
|
163
161
|
function getStyleKey(snapshot, tagName, compress = false) {
|
|
164
162
|
const entries = [];
|
|
165
163
|
const defaultStyles = getDefaultStyleForTag(tagName);
|
|
166
164
|
for (let [prop, value] of Object.entries(snapshot)) {
|
|
165
|
+
if (IGNORED_PROPS.has(prop)) continue;
|
|
167
166
|
if (!compress) {
|
|
168
167
|
if (value) {
|
|
169
168
|
entries.push(`${prop}:${value}`);
|
|
@@ -208,11 +207,12 @@
|
|
|
208
207
|
}
|
|
209
208
|
return css;
|
|
210
209
|
}
|
|
211
|
-
function generateCSSClasses() {
|
|
212
|
-
const keySet = new Set(
|
|
210
|
+
function generateCSSClasses(styleMap) {
|
|
211
|
+
const keySet = new Set(styleMap.values());
|
|
213
212
|
const classMap = /* @__PURE__ */ new Map();
|
|
214
213
|
let counter = 1;
|
|
215
214
|
for (const key of keySet) {
|
|
215
|
+
if (!key.trim()) continue;
|
|
216
216
|
classMap.set(key, `c${counter++}`);
|
|
217
217
|
}
|
|
218
218
|
return classMap;
|
|
@@ -280,7 +280,29 @@
|
|
|
280
280
|
if (url.startsWith("#")) return null;
|
|
281
281
|
return url;
|
|
282
282
|
}
|
|
283
|
-
function
|
|
283
|
+
function fetchResource(_0) {
|
|
284
|
+
return __async(this, arguments, function* (url, { useProxy = "" } = {}) {
|
|
285
|
+
function doFetch(u) {
|
|
286
|
+
return __async(this, null, function* () {
|
|
287
|
+
const res = yield fetch(u);
|
|
288
|
+
if (!res.ok) throw new Error(`[snapdom] Failed to fetch resource: ${u}`);
|
|
289
|
+
return res;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
return yield doFetch(url);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
if (useProxy && typeof useProxy === "string") {
|
|
296
|
+
const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url);
|
|
297
|
+
return doFetch(proxied);
|
|
298
|
+
}
|
|
299
|
+
throw e;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
var _inflight = /* @__PURE__ */ new Map();
|
|
304
|
+
var _errorCache = /* @__PURE__ */ new Map();
|
|
305
|
+
function fetchImage(src, { timeout = 3e3, useProxy = "", errorTTL = 8e3 } = {}) {
|
|
284
306
|
function getCrossOriginMode(url) {
|
|
285
307
|
try {
|
|
286
308
|
const parsed = new URL(url, window.location.href);
|
|
@@ -289,107 +311,157 @@
|
|
|
289
311
|
return "anonymous";
|
|
290
312
|
}
|
|
291
313
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
314
|
+
const ok = (data) => ({ ok: true, data });
|
|
315
|
+
const fail = (e) => ({ ok: false, error: e instanceof Error ? e : new Error(String(e)) });
|
|
316
|
+
function fetchBlobAsDataURLSafe(fetchUrl) {
|
|
317
|
+
try {
|
|
318
|
+
return fetch(fetchUrl, {
|
|
295
319
|
mode: "cors",
|
|
296
320
|
credentials: getCrossOriginMode(fetchUrl) === "use-credentials" ? "include" : "omit"
|
|
297
|
-
}).then((r) =>
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
321
|
+
}).then((r) => {
|
|
322
|
+
if (!r.ok) return fail(new Error("HTTP " + r.status));
|
|
323
|
+
return r.blob().then((blob) => new Promise((resolve) => {
|
|
324
|
+
const reader = new FileReader();
|
|
325
|
+
reader.onloadend = () => {
|
|
326
|
+
const base64 = reader.result;
|
|
327
|
+
if (typeof base64 !== "string" || !base64.startsWith("data:image/")) {
|
|
328
|
+
resolve(fail(new Error("Invalid image data URL")));
|
|
329
|
+
} else {
|
|
330
|
+
resolve(ok(base64));
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
reader.onerror = () => resolve(fail(new Error("FileReader error")));
|
|
334
|
+
reader.readAsDataURL(blob);
|
|
335
|
+
}));
|
|
336
|
+
}).catch((e) => fail(e));
|
|
337
|
+
} catch (e) {
|
|
338
|
+
return Promise.resolve(fail(e));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function fetchWithFallbackOnceSafe(url) {
|
|
342
|
+
return fetchBlobAsDataURLSafe(url).then((r) => {
|
|
343
|
+
if (r.ok) return r;
|
|
344
|
+
if (useProxy && typeof useProxy === "string") {
|
|
345
|
+
const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url);
|
|
346
|
+
return fetchBlobAsDataURLSafe(proxied).then((r2) => {
|
|
347
|
+
if (r2.ok) return r2;
|
|
348
|
+
return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"));
|
|
349
|
+
});
|
|
323
350
|
}
|
|
351
|
+
return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"));
|
|
324
352
|
});
|
|
325
353
|
}
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
354
|
+
const now = Date.now();
|
|
355
|
+
const until = _errorCache.get(src);
|
|
356
|
+
if (until && until > now) {
|
|
357
|
+
const pr = Promise.reject(new Error("[SnapDOM - fetchImage] Recently failed (cooldown)."));
|
|
358
|
+
pr.catch(() => {
|
|
359
|
+
});
|
|
360
|
+
return pr;
|
|
329
361
|
}
|
|
330
|
-
|
|
331
|
-
|
|
362
|
+
if (_inflight.has(src)) return _inflight.get(src);
|
|
363
|
+
const crossOriginValue = getCrossOriginMode(src);
|
|
364
|
+
if (cache.image.has(src)) return Promise.resolve(cache.image.get(src));
|
|
365
|
+
if (src.startsWith("data:image/")) {
|
|
332
366
|
cache.image.set(src, src);
|
|
333
367
|
return Promise.resolve(src);
|
|
334
368
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
369
|
+
if (/\.svg(\?.*)?$/i.test(src)) {
|
|
370
|
+
const p2 = (() => __async(null, null, function* () {
|
|
371
|
+
const direct = yield (() => __async(null, null, function* () {
|
|
372
|
+
try {
|
|
373
|
+
const res = yield fetch(src, {
|
|
374
|
+
mode: "cors",
|
|
375
|
+
credentials: crossOriginValue === "use-credentials" ? "include" : "omit"
|
|
376
|
+
});
|
|
377
|
+
if (!res.ok) return fail(new Error("HTTP " + res.status));
|
|
378
|
+
const svgText = yield res.text();
|
|
379
|
+
return ok(`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`);
|
|
380
|
+
} catch (e) {
|
|
381
|
+
return fail(e);
|
|
382
|
+
}
|
|
383
|
+
}))();
|
|
384
|
+
if (direct.ok) {
|
|
385
|
+
cache.image.set(src, direct.data);
|
|
386
|
+
return direct.data;
|
|
387
|
+
}
|
|
388
|
+
const via = yield fetchWithFallbackOnceSafe(src);
|
|
389
|
+
if (via.ok) {
|
|
390
|
+
cache.image.set(src, via.data);
|
|
391
|
+
return via.data;
|
|
349
392
|
}
|
|
393
|
+
_errorCache.set(src, now + errorTTL);
|
|
394
|
+
return Promise.reject(via.error);
|
|
350
395
|
}))();
|
|
396
|
+
_inflight.set(src, p2);
|
|
397
|
+
p2.finally(() => _inflight.delete(src));
|
|
398
|
+
p2.catch(() => {
|
|
399
|
+
});
|
|
400
|
+
return p2;
|
|
351
401
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
image.onload = () => __async(null, null, function* () {
|
|
402
|
+
const p = new Promise((resolve, reject) => {
|
|
403
|
+
let finished = false;
|
|
404
|
+
const img = new Image();
|
|
405
|
+
const finish = (fn) => (arg) => {
|
|
406
|
+
if (finished) return;
|
|
407
|
+
finished = true;
|
|
359
408
|
clearTimeout(timeoutId);
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
409
|
+
img.onload = img.onerror = null;
|
|
410
|
+
fn(arg);
|
|
411
|
+
};
|
|
412
|
+
const onSuccess = (d) => {
|
|
413
|
+
cache.image.set(src, d);
|
|
414
|
+
resolve(d);
|
|
415
|
+
};
|
|
416
|
+
const onFinalError = (e) => {
|
|
417
|
+
_errorCache.set(src, Date.now() + errorTTL);
|
|
418
|
+
reject(e);
|
|
419
|
+
};
|
|
420
|
+
const timeoutId = setTimeout(
|
|
421
|
+
finish(() => {
|
|
422
|
+
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
423
|
+
if (r.ok) onSuccess(r.data);
|
|
424
|
+
else onFinalError(new Error("Image load timed out"));
|
|
425
|
+
});
|
|
426
|
+
}),
|
|
427
|
+
timeout
|
|
428
|
+
);
|
|
429
|
+
img.crossOrigin = crossOriginValue;
|
|
430
|
+
img.onload = finish(() => {
|
|
431
|
+
Promise.resolve(img.decode()).then(() => {
|
|
371
432
|
try {
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
433
|
+
const canvas = document.createElement("canvas");
|
|
434
|
+
canvas.width = img.naturalWidth || img.width;
|
|
435
|
+
canvas.height = img.naturalHeight || img.height;
|
|
436
|
+
const ctx = canvas.getContext("2d");
|
|
437
|
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
438
|
+
onSuccess(canvas.toDataURL("image/png"));
|
|
439
|
+
} catch (e) {
|
|
440
|
+
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
441
|
+
if (r.ok) onSuccess(r.data);
|
|
442
|
+
else onFinalError(r.error);
|
|
443
|
+
});
|
|
377
444
|
}
|
|
378
|
-
}
|
|
445
|
+
}).catch(() => {
|
|
446
|
+
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
447
|
+
if (r.ok) onSuccess(r.data);
|
|
448
|
+
else onFinalError(r.error);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
379
451
|
});
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
cache.image.set(src, fallbackDataURL);
|
|
386
|
-
resolve(fallbackDataURL);
|
|
387
|
-
} catch (e) {
|
|
388
|
-
reject(e);
|
|
389
|
-
}
|
|
452
|
+
img.onerror = finish(() => {
|
|
453
|
+
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
454
|
+
if (r.ok) onSuccess(r.data);
|
|
455
|
+
else onFinalError(r.error);
|
|
456
|
+
});
|
|
390
457
|
});
|
|
391
|
-
|
|
458
|
+
img.src = src;
|
|
392
459
|
});
|
|
460
|
+
_inflight.set(src, p);
|
|
461
|
+
p.finally(() => _inflight.delete(src));
|
|
462
|
+
p.catch(() => {
|
|
463
|
+
});
|
|
464
|
+
return p;
|
|
393
465
|
}
|
|
394
466
|
function snapshotComputedStyle(style) {
|
|
395
467
|
const snap = {};
|
|
@@ -446,8 +518,11 @@
|
|
|
446
518
|
}
|
|
447
519
|
|
|
448
520
|
// src/modules/styles.js
|
|
521
|
+
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
522
|
+
var snapshotKeyCache = /* @__PURE__ */ new Map();
|
|
449
523
|
function snapshotComputedStyleFull(style) {
|
|
450
524
|
const result = {};
|
|
525
|
+
const computedVisibility = style.getPropertyValue("visibility");
|
|
451
526
|
for (let i = 0; i < style.length; i++) {
|
|
452
527
|
const prop = style[i];
|
|
453
528
|
let val = style.getPropertyValue(prop);
|
|
@@ -456,33 +531,48 @@
|
|
|
456
531
|
}
|
|
457
532
|
result[prop] = val;
|
|
458
533
|
}
|
|
534
|
+
if (computedVisibility === "hidden") {
|
|
535
|
+
result.opacity = "0";
|
|
536
|
+
}
|
|
459
537
|
return result;
|
|
460
538
|
}
|
|
461
|
-
function inlineAllStyles(source, clone, compress) {
|
|
539
|
+
function inlineAllStyles(source, clone, styleMap, cache2, compress) {
|
|
462
540
|
var _a;
|
|
463
541
|
if (source.tagName === "STYLE") return;
|
|
464
|
-
if (!
|
|
465
|
-
|
|
542
|
+
if (!cache2.has(source)) {
|
|
543
|
+
cache2.set(source, getStyle(source));
|
|
466
544
|
}
|
|
467
|
-
const style =
|
|
468
|
-
if (!
|
|
545
|
+
const style = cache2.get(source);
|
|
546
|
+
if (!snapshotCache.has(source)) {
|
|
469
547
|
const snapshot2 = snapshotComputedStyleFull(style);
|
|
470
|
-
|
|
548
|
+
snapshotCache.set(source, snapshot2);
|
|
471
549
|
}
|
|
472
|
-
const snapshot =
|
|
550
|
+
const snapshot = snapshotCache.get(source);
|
|
473
551
|
const hash = Object.entries(snapshot).sort(([a], [b]) => a.localeCompare(b)).map(([prop, val]) => `${prop}:${val}`).join(";");
|
|
474
|
-
if (
|
|
475
|
-
|
|
552
|
+
if (snapshotKeyCache.has(hash)) {
|
|
553
|
+
styleMap.set(clone, snapshotKeyCache.get(hash));
|
|
476
554
|
return;
|
|
477
555
|
}
|
|
478
556
|
const tagName = ((_a = source.tagName) == null ? void 0 : _a.toLowerCase()) || "div";
|
|
479
557
|
const key = getStyleKey(snapshot, tagName, compress);
|
|
480
|
-
|
|
481
|
-
|
|
558
|
+
snapshotKeyCache.set(hash, key);
|
|
559
|
+
styleMap.set(clone, key);
|
|
482
560
|
}
|
|
483
561
|
|
|
484
562
|
// src/core/clone.js
|
|
485
|
-
function
|
|
563
|
+
function freezeImgSrcset(original, cloned) {
|
|
564
|
+
try {
|
|
565
|
+
const chosen = original.currentSrc || original.src || "";
|
|
566
|
+
if (!chosen) return;
|
|
567
|
+
cloned.setAttribute("src", chosen);
|
|
568
|
+
cloned.removeAttribute("srcset");
|
|
569
|
+
cloned.removeAttribute("sizes");
|
|
570
|
+
cloned.loading = "eager";
|
|
571
|
+
cloned.decoding = "sync";
|
|
572
|
+
} catch (e) {
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function deepClone(node, styleMap, styleCache, nodeMap, compress, options = {}, originalRoot) {
|
|
486
576
|
var _a, _b;
|
|
487
577
|
if (!node) throw new Error("Invalid node");
|
|
488
578
|
const clonedAssignedNodes = /* @__PURE__ */ new Set();
|
|
@@ -532,8 +622,8 @@
|
|
|
532
622
|
}
|
|
533
623
|
if (node.getAttribute("data-capture") === "placeholder") {
|
|
534
624
|
const clone2 = node.cloneNode(false);
|
|
535
|
-
|
|
536
|
-
inlineAllStyles(node, clone2, compress);
|
|
625
|
+
nodeMap.set(clone2, node);
|
|
626
|
+
inlineAllStyles(node, clone2, styleMap, styleCache, compress);
|
|
537
627
|
const placeholder = document.createElement("div");
|
|
538
628
|
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
539
629
|
placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`;
|
|
@@ -546,14 +636,17 @@
|
|
|
546
636
|
img.src = dataURL;
|
|
547
637
|
img.width = node.width;
|
|
548
638
|
img.height = node.height;
|
|
549
|
-
|
|
550
|
-
inlineAllStyles(node, img, compress);
|
|
639
|
+
nodeMap.set(img, node);
|
|
640
|
+
inlineAllStyles(node, img, styleMap, styleCache, compress);
|
|
551
641
|
return img;
|
|
552
642
|
}
|
|
553
643
|
let clone;
|
|
554
644
|
try {
|
|
555
645
|
clone = node.cloneNode(false);
|
|
556
|
-
|
|
646
|
+
nodeMap.set(clone, node);
|
|
647
|
+
if (node.tagName === "IMG") {
|
|
648
|
+
freezeImgSrcset(node, clone);
|
|
649
|
+
}
|
|
557
650
|
} catch (err) {
|
|
558
651
|
console.error("[Snapdom] Failed to clone node:", node, err);
|
|
559
652
|
throw err;
|
|
@@ -578,7 +671,7 @@
|
|
|
578
671
|
if (node instanceof HTMLSelectElement) {
|
|
579
672
|
pendingSelectValue = node.value;
|
|
580
673
|
}
|
|
581
|
-
inlineAllStyles(node, clone, compress);
|
|
674
|
+
inlineAllStyles(node, clone, styleMap, styleCache, compress);
|
|
582
675
|
if (node.shadowRoot) {
|
|
583
676
|
const hasSlot = Array.from(node.shadowRoot.querySelectorAll("slot")).length > 0;
|
|
584
677
|
if (hasSlot) {
|
|
@@ -586,8 +679,7 @@
|
|
|
586
679
|
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
|
|
587
680
|
const cssText = child.textContent || "";
|
|
588
681
|
if (cssText.trim() && compress) {
|
|
589
|
-
|
|
590
|
-
cache.preStyle.set(child, cssText);
|
|
682
|
+
styleCache.set(child, cssText);
|
|
591
683
|
}
|
|
592
684
|
}
|
|
593
685
|
}
|
|
@@ -597,12 +689,11 @@
|
|
|
597
689
|
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
|
|
598
690
|
const cssText = child.textContent || "";
|
|
599
691
|
if (cssText.trim() && compress) {
|
|
600
|
-
|
|
601
|
-
cache.preStyle.set(child, cssText);
|
|
692
|
+
styleCache.set(child, cssText);
|
|
602
693
|
}
|
|
603
694
|
continue;
|
|
604
695
|
}
|
|
605
|
-
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
696
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
606
697
|
if (clonedChild) shadowFrag.appendChild(clonedChild);
|
|
607
698
|
}
|
|
608
699
|
clone.appendChild(shadowFrag);
|
|
@@ -613,14 +704,14 @@
|
|
|
613
704
|
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
614
705
|
const fragment = document.createDocumentFragment();
|
|
615
706
|
for (const child of nodesToClone) {
|
|
616
|
-
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
707
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
617
708
|
if (clonedChild) fragment.appendChild(clonedChild);
|
|
618
709
|
}
|
|
619
710
|
return fragment;
|
|
620
711
|
}
|
|
621
712
|
for (const child of node.childNodes) {
|
|
622
713
|
if (clonedAssignedNodes.has(child)) continue;
|
|
623
|
-
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
714
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
624
715
|
if (clonedChild) clone.appendChild(clonedChild);
|
|
625
716
|
}
|
|
626
717
|
if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
|
|
@@ -678,25 +769,38 @@
|
|
|
678
769
|
return __async(this, null, function* () {
|
|
679
770
|
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
680
771
|
const dpr = window.devicePixelRatio || 1;
|
|
681
|
-
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
772
|
+
yield document.fonts.ready;
|
|
773
|
+
const span = document.createElement("span");
|
|
774
|
+
span.textContent = unicodeChar;
|
|
775
|
+
span.style.position = "absolute";
|
|
776
|
+
span.style.visibility = "hidden";
|
|
777
|
+
span.style.fontFamily = `"${fontFamily}"`;
|
|
778
|
+
span.style.fontWeight = fontWeight || "normal";
|
|
779
|
+
span.style.fontSize = `${fontSize}px`;
|
|
780
|
+
span.style.lineHeight = "1";
|
|
781
|
+
span.style.whiteSpace = "nowrap";
|
|
782
|
+
span.style.padding = "0";
|
|
783
|
+
span.style.margin = "0";
|
|
784
|
+
document.body.appendChild(span);
|
|
785
|
+
const rect = span.getBoundingClientRect();
|
|
786
|
+
const width = Math.ceil(rect.width);
|
|
787
|
+
const height = Math.ceil(rect.height);
|
|
788
|
+
document.body.removeChild(span);
|
|
689
789
|
const canvas = document.createElement("canvas");
|
|
690
|
-
canvas.width =
|
|
691
|
-
canvas.height =
|
|
790
|
+
canvas.width = width * dpr;
|
|
791
|
+
canvas.height = height * dpr;
|
|
692
792
|
const ctx = canvas.getContext("2d");
|
|
693
793
|
ctx.scale(dpr, dpr);
|
|
694
|
-
ctx.font =
|
|
794
|
+
ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
|
|
695
795
|
ctx.textAlign = "left";
|
|
696
|
-
ctx.textBaseline = "
|
|
796
|
+
ctx.textBaseline = "top";
|
|
697
797
|
ctx.fillStyle = color;
|
|
698
|
-
ctx.fillText(unicodeChar, 0,
|
|
699
|
-
return
|
|
798
|
+
ctx.fillText(unicodeChar, 0, 0);
|
|
799
|
+
return {
|
|
800
|
+
dataUrl: canvas.toDataURL(),
|
|
801
|
+
width,
|
|
802
|
+
height
|
|
803
|
+
};
|
|
700
804
|
});
|
|
701
805
|
}
|
|
702
806
|
function isStylesheetLoaded(href) {
|
|
@@ -715,7 +819,7 @@
|
|
|
715
819
|
});
|
|
716
820
|
}
|
|
717
821
|
function embedCustomFonts() {
|
|
718
|
-
return __async(this, arguments, function* ({ preCached = false } = {}) {
|
|
822
|
+
return __async(this, arguments, function* ({ preCached = false, localFonts = [], useProxy = "" } = {}) {
|
|
719
823
|
if (cache.resource.has("fonts-embed-css")) {
|
|
720
824
|
if (preCached) {
|
|
721
825
|
const style = document.createElement("style");
|
|
@@ -725,6 +829,15 @@
|
|
|
725
829
|
}
|
|
726
830
|
return cache.resource.get("fonts-embed-css");
|
|
727
831
|
}
|
|
832
|
+
const loadedFonts = /* @__PURE__ */ new Set();
|
|
833
|
+
try {
|
|
834
|
+
for (const f of document.fonts) {
|
|
835
|
+
if (f.status === "loaded") {
|
|
836
|
+
loadedFonts.add(`${f.family}__${f.weight || "normal"}__${f.style || "normal"}`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
} catch (e) {
|
|
840
|
+
}
|
|
728
841
|
const importRegex = /@import\s+url\(["']?([^"')]+)["']?\)/g;
|
|
729
842
|
const styleImports = [];
|
|
730
843
|
for (const styleTag of document.querySelectorAll("style")) {
|
|
@@ -743,26 +856,48 @@
|
|
|
743
856
|
let finalCSS = "";
|
|
744
857
|
for (const link of links) {
|
|
745
858
|
try {
|
|
746
|
-
const res = yield
|
|
859
|
+
const res = yield fetchResource(link.href, { useProxy });
|
|
747
860
|
const cssText = yield res.text();
|
|
748
861
|
if (isIconFont(link.href) || isIconFont(cssText)) continue;
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
862
|
+
const faceRegex = /@font-face[^{}]*{[^}]*}/g;
|
|
863
|
+
let cssFinal = cssText;
|
|
864
|
+
for (const face of cssText.match(faceRegex) || []) {
|
|
865
|
+
const famMatch = face.match(/font-family:\s*([^;]+);/i);
|
|
866
|
+
if (!famMatch) continue;
|
|
867
|
+
const family = famMatch[1].replace(/['"]/g, "").trim();
|
|
868
|
+
const weightMatch = face.match(/font-weight:\s*([^;]+);/i);
|
|
869
|
+
const styleMatch = face.match(/font-style:\s*([^;]+);/i);
|
|
870
|
+
const weight = weightMatch ? weightMatch[1].trim() : "normal";
|
|
871
|
+
const style = styleMatch ? styleMatch[1].trim() : "normal";
|
|
872
|
+
const key = `${family}__${weight}__${style}`;
|
|
873
|
+
const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
|
|
874
|
+
const hasURL = /url\(/i.test(face);
|
|
875
|
+
const hasLocal = /local\(/i.test(face);
|
|
876
|
+
if (!hasURL && hasLocal) {
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
if (!loadedFonts.has(key)) {
|
|
880
|
+
cssFinal = cssFinal.replace(face, "");
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
let inlined = face;
|
|
884
|
+
const matches = Array.from(face.matchAll(urlRegex));
|
|
885
|
+
for (const match of matches) {
|
|
752
886
|
let rawUrl = extractURL(match[0]);
|
|
753
|
-
if (!rawUrl)
|
|
887
|
+
if (!rawUrl) continue;
|
|
754
888
|
let url = rawUrl;
|
|
755
889
|
if (!url.startsWith("http") && !url.startsWith("data:")) {
|
|
756
890
|
url = new URL(url, link.href).href;
|
|
757
891
|
}
|
|
758
|
-
if (isIconFont(url))
|
|
892
|
+
if (isIconFont(url)) continue;
|
|
759
893
|
if (cache.resource.has(url)) {
|
|
760
894
|
cache.font.add(url);
|
|
761
|
-
|
|
895
|
+
inlined = inlined.replace(match[0], `url(${cache.resource.get(url)})`);
|
|
896
|
+
continue;
|
|
762
897
|
}
|
|
763
|
-
if (cache.font.has(url))
|
|
898
|
+
if (cache.font.has(url)) continue;
|
|
764
899
|
try {
|
|
765
|
-
const fontRes = yield
|
|
900
|
+
const fontRes = yield fetchResource(url, { useProxy });
|
|
766
901
|
const blob = yield fontRes.blob();
|
|
767
902
|
const b64 = yield new Promise((resolve) => {
|
|
768
903
|
const reader = new FileReader();
|
|
@@ -771,16 +906,12 @@
|
|
|
771
906
|
});
|
|
772
907
|
cache.resource.set(url, b64);
|
|
773
908
|
cache.font.add(url);
|
|
774
|
-
|
|
909
|
+
inlined = inlined.replace(match[0], `url(${b64})`);
|
|
775
910
|
} catch (e) {
|
|
776
911
|
console.warn("[snapdom] Failed to fetch font resource:", url);
|
|
777
|
-
return null;
|
|
778
912
|
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
let cssFinal = cssText;
|
|
782
|
-
for (const r of inlinedCSS) {
|
|
783
|
-
if (r) cssFinal = cssFinal.replace(r.original, r.inlined);
|
|
913
|
+
}
|
|
914
|
+
cssFinal = cssFinal.replace(face, inlined);
|
|
784
915
|
}
|
|
785
916
|
finalCSS += cssFinal + "\n";
|
|
786
917
|
} catch (e) {
|
|
@@ -795,14 +926,18 @@
|
|
|
795
926
|
const src = rule.style.getPropertyValue("src");
|
|
796
927
|
const family = rule.style.getPropertyValue("font-family");
|
|
797
928
|
if (!src || isIconFont(family)) continue;
|
|
929
|
+
const weightVal = rule.style.getPropertyValue("font-weight") || "normal";
|
|
930
|
+
const styleVal = rule.style.getPropertyValue("font-style") || "normal";
|
|
931
|
+
const key = `${family}__${weightVal}__${styleVal}`;
|
|
798
932
|
const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
|
|
799
933
|
const localRegex = /local\((["']?)[^)]+?\1\)/g;
|
|
800
934
|
const hasURL = !!src.match(urlRegex);
|
|
801
935
|
const hasLocal = !!src.match(localRegex);
|
|
802
936
|
if (!hasURL && hasLocal) {
|
|
803
|
-
finalCSS += `@font-face{font-family:${family};src:${src};font-style:${
|
|
937
|
+
finalCSS += `@font-face{font-family:${family};src:${src};font-style:${styleVal};font-weight:${weightVal};}`;
|
|
804
938
|
continue;
|
|
805
939
|
}
|
|
940
|
+
if (!loadedFonts.has(key)) continue;
|
|
806
941
|
let inlinedSrc = src;
|
|
807
942
|
const matches = Array.from(src.matchAll(urlRegex));
|
|
808
943
|
for (const match of matches) {
|
|
@@ -820,7 +955,7 @@
|
|
|
820
955
|
}
|
|
821
956
|
if (cache.font.has(url)) continue;
|
|
822
957
|
try {
|
|
823
|
-
const res = yield
|
|
958
|
+
const res = yield fetchResource(url, { useProxy });
|
|
824
959
|
const blob = yield res.blob();
|
|
825
960
|
const b64 = yield new Promise((resolve) => {
|
|
826
961
|
const reader = new FileReader();
|
|
@@ -834,7 +969,7 @@
|
|
|
834
969
|
console.warn("[snapdom] Failed to fetch font URL:", url);
|
|
835
970
|
}
|
|
836
971
|
}
|
|
837
|
-
finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${
|
|
972
|
+
finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleVal};font-weight:${weightVal};}`;
|
|
838
973
|
}
|
|
839
974
|
}
|
|
840
975
|
}
|
|
@@ -852,7 +987,7 @@
|
|
|
852
987
|
cache.font.add(font._snapdomSrc);
|
|
853
988
|
} else if (!cache.font.has(font._snapdomSrc)) {
|
|
854
989
|
try {
|
|
855
|
-
const res = yield
|
|
990
|
+
const res = yield fetchResource(font._snapdomSrc, { useProxy });
|
|
856
991
|
const blob = yield res.blob();
|
|
857
992
|
b64 = yield new Promise((resolve) => {
|
|
858
993
|
const reader = new FileReader();
|
|
@@ -870,6 +1005,32 @@
|
|
|
870
1005
|
finalCSS += `@font-face{font-family:'${font.family}';src:url(${b64});font-style:${font.style || "normal"};font-weight:${font.weight || "normal"};}`;
|
|
871
1006
|
}
|
|
872
1007
|
}
|
|
1008
|
+
for (const font of localFonts) {
|
|
1009
|
+
if (!font || typeof font !== "object") continue;
|
|
1010
|
+
const { family, src, weight = "normal", style = "normal" } = font;
|
|
1011
|
+
if (!family || !src) continue;
|
|
1012
|
+
let b64 = src;
|
|
1013
|
+
if (!b64.startsWith("data:")) {
|
|
1014
|
+
try {
|
|
1015
|
+
const res = yield fetchResource(src, { useProxy });
|
|
1016
|
+
const blob = yield res.blob();
|
|
1017
|
+
b64 = yield new Promise((resolve) => {
|
|
1018
|
+
const reader = new FileReader();
|
|
1019
|
+
reader.onload = () => resolve(reader.result);
|
|
1020
|
+
reader.readAsDataURL(blob);
|
|
1021
|
+
});
|
|
1022
|
+
cache.resource.set(src, b64);
|
|
1023
|
+
cache.font.add(src);
|
|
1024
|
+
} catch (e) {
|
|
1025
|
+
console.warn("[snapdom] Failed to load local font:", src);
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
} else {
|
|
1029
|
+
cache.resource.set(src, b64);
|
|
1030
|
+
cache.font.add(src);
|
|
1031
|
+
}
|
|
1032
|
+
finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};}`;
|
|
1033
|
+
}
|
|
873
1034
|
if (finalCSS) {
|
|
874
1035
|
cache.resource.set("fonts-embed-css", finalCSS);
|
|
875
1036
|
if (preCached) {
|
|
@@ -884,19 +1045,25 @@
|
|
|
884
1045
|
}
|
|
885
1046
|
|
|
886
1047
|
// src/modules/pseudo.js
|
|
887
|
-
function inlinePseudoElements(source, clone,
|
|
1048
|
+
function inlinePseudoElements(source, clone, styleMap, styleCache, options) {
|
|
888
1049
|
return __async(this, null, function* () {
|
|
1050
|
+
var _a;
|
|
889
1051
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
890
1052
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
891
1053
|
try {
|
|
892
1054
|
const style = getStyle(source, pseudo);
|
|
893
1055
|
if (!style || typeof style[Symbol.iterator] !== "function") continue;
|
|
1056
|
+
const isEmptyPseudo = style.content === "none" && style.backgroundImage === "none" && style.backgroundColor === "transparent" && (style.borderStyle === "none" || parseFloat(style.borderWidth) === 0) && (!style.transform || style.transform === "none") && style.display === "inline";
|
|
1057
|
+
if (isEmptyPseudo) continue;
|
|
894
1058
|
if (pseudo === "::first-letter") {
|
|
895
1059
|
const normal = getComputedStyle(source);
|
|
896
1060
|
const isMeaningful = style.color !== normal.color || style.fontSize !== normal.fontSize || style.fontWeight !== normal.fontWeight;
|
|
897
1061
|
if (!isMeaningful) continue;
|
|
898
1062
|
const textNode = Array.from(clone.childNodes).find(
|
|
899
|
-
(n) =>
|
|
1063
|
+
(n) => {
|
|
1064
|
+
var _a2;
|
|
1065
|
+
return n.nodeType === Node.TEXT_NODE && ((_a2 = n.textContent) == null ? void 0 : _a2.trim().length) > 0;
|
|
1066
|
+
}
|
|
900
1067
|
);
|
|
901
1068
|
if (!textNode) continue;
|
|
902
1069
|
const text = textNode.textContent;
|
|
@@ -908,57 +1075,55 @@
|
|
|
908
1075
|
span.textContent = first;
|
|
909
1076
|
span.dataset.snapdomPseudo = "::first-letter";
|
|
910
1077
|
const snapshot2 = snapshotComputedStyle(style);
|
|
911
|
-
const key2 = getStyleKey(snapshot2, "span",
|
|
912
|
-
|
|
1078
|
+
const key2 = getStyleKey(snapshot2, "span", options);
|
|
1079
|
+
styleMap.set(span, key2);
|
|
913
1080
|
const restNode = document.createTextNode(rest);
|
|
914
1081
|
clone.replaceChild(restNode, textNode);
|
|
915
1082
|
clone.insertBefore(span, restNode);
|
|
916
1083
|
continue;
|
|
917
1084
|
}
|
|
918
|
-
const content = style.
|
|
919
|
-
const
|
|
920
|
-
const
|
|
921
|
-
const
|
|
922
|
-
const
|
|
923
|
-
const
|
|
924
|
-
const
|
|
925
|
-
const
|
|
926
|
-
const
|
|
927
|
-
const
|
|
928
|
-
const
|
|
929
|
-
const
|
|
1085
|
+
const content = style.content;
|
|
1086
|
+
const cleanContent = /counter\s*\(|counters\s*\(/.test(content) ? "- " : parseContent(content);
|
|
1087
|
+
const bg = style.backgroundImage;
|
|
1088
|
+
const bgColor = style.backgroundColor;
|
|
1089
|
+
const fontFamily = style.fontFamily;
|
|
1090
|
+
const fontSize = parseInt(style.fontSize) || 32;
|
|
1091
|
+
const fontWeight = parseInt(style.fontWeight) || false;
|
|
1092
|
+
const color = style.color || "#000";
|
|
1093
|
+
const display = style.display;
|
|
1094
|
+
const width = parseFloat(style.width);
|
|
1095
|
+
const height = parseFloat(style.height);
|
|
1096
|
+
const borderStyle = style.borderStyle;
|
|
1097
|
+
const borderWidth = parseFloat(style.borderWidth);
|
|
1098
|
+
const transform = style.transform;
|
|
930
1099
|
const isIconFont2 = isIconFont(fontFamily);
|
|
931
|
-
|
|
932
|
-
if (/counter\s*\(|counters\s*\(/.test(content)) {
|
|
933
|
-
cleanContent = "- ";
|
|
934
|
-
} else {
|
|
935
|
-
cleanContent = parseContent(content);
|
|
936
|
-
}
|
|
937
|
-
const hasContent = content !== "none";
|
|
938
|
-
const hasExplicitContent = hasContent && cleanContent !== "";
|
|
1100
|
+
const hasExplicitContent = content !== "none" && cleanContent !== "";
|
|
939
1101
|
const hasBg = bg && bg !== "none";
|
|
940
1102
|
const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
|
|
941
1103
|
const hasBox = display !== "inline" && (width > 0 || height > 0);
|
|
942
|
-
const hasBorder = borderStyle && borderStyle !== "none";
|
|
1104
|
+
const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
|
|
943
1105
|
const hasTransform = transform && transform !== "none";
|
|
944
1106
|
const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox || hasBorder || hasTransform;
|
|
945
1107
|
if (!shouldRender) continue;
|
|
946
1108
|
const pseudoEl = document.createElement("span");
|
|
947
1109
|
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
1110
|
+
pseudoEl.style.verticalAlign = "middle";
|
|
948
1111
|
const snapshot = snapshotComputedStyle(style);
|
|
949
|
-
const key = getStyleKey(snapshot, "span",
|
|
950
|
-
|
|
1112
|
+
const key = getStyleKey(snapshot, "span", options);
|
|
1113
|
+
styleMap.set(pseudoEl, key);
|
|
951
1114
|
if (isIconFont2 && cleanContent.length === 1) {
|
|
1115
|
+
const { dataUrl, width: width2, height: height2 } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
952
1116
|
const imgEl = document.createElement("img");
|
|
953
|
-
imgEl.src =
|
|
954
|
-
imgEl.style = `width:${fontSize}px;
|
|
1117
|
+
imgEl.src = dataUrl;
|
|
1118
|
+
imgEl.style = `height:${fontSize}px;width:${width2 / height2 * fontSize}px;object-fit:contain;`;
|
|
955
1119
|
pseudoEl.appendChild(imgEl);
|
|
1120
|
+
clone.dataset.snapdomHasIcon = "true";
|
|
956
1121
|
} else if (cleanContent.startsWith("url(")) {
|
|
957
1122
|
const rawUrl = extractURL(cleanContent);
|
|
958
|
-
if (rawUrl
|
|
1123
|
+
if (rawUrl == null ? void 0 : rawUrl.trim()) {
|
|
959
1124
|
try {
|
|
960
1125
|
const imgEl = document.createElement("img");
|
|
961
|
-
const dataUrl = yield fetchImage(safeEncodeURI(rawUrl,
|
|
1126
|
+
const dataUrl = yield fetchImage(safeEncodeURI(rawUrl), options);
|
|
962
1127
|
imgEl.src = dataUrl;
|
|
963
1128
|
imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
|
|
964
1129
|
pseudoEl.appendChild(imgEl);
|
|
@@ -972,19 +1137,21 @@
|
|
|
972
1137
|
if (hasBg) {
|
|
973
1138
|
try {
|
|
974
1139
|
const bgSplits = splitBackgroundImage(bg);
|
|
975
|
-
const newBgParts = yield Promise.all(
|
|
976
|
-
bgSplits.map((entry) => inlineSingleBackgroundEntry(entry))
|
|
977
|
-
);
|
|
1140
|
+
const newBgParts = yield Promise.all(bgSplits.map(inlineSingleBackgroundEntry));
|
|
978
1141
|
pseudoEl.style.backgroundImage = newBgParts.join(", ");
|
|
979
1142
|
} catch (e) {
|
|
980
1143
|
console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
|
|
981
1144
|
}
|
|
982
1145
|
}
|
|
983
1146
|
if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
|
|
984
|
-
const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent
|
|
1147
|
+
const hasContent2 = pseudoEl.childNodes.length > 0 || ((_a = pseudoEl.textContent) == null ? void 0 : _a.trim()) !== "";
|
|
985
1148
|
const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox || hasBorder || hasTransform;
|
|
986
1149
|
if (!hasVisibleBox) continue;
|
|
987
|
-
pseudo === "::before"
|
|
1150
|
+
if (pseudo === "::before") {
|
|
1151
|
+
clone.insertBefore(pseudoEl, clone.firstChild);
|
|
1152
|
+
} else {
|
|
1153
|
+
clone.appendChild(pseudoEl);
|
|
1154
|
+
}
|
|
988
1155
|
} catch (e) {
|
|
989
1156
|
console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e);
|
|
990
1157
|
}
|
|
@@ -992,87 +1159,114 @@
|
|
|
992
1159
|
const sChildren = Array.from(source.children);
|
|
993
1160
|
const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
|
|
994
1161
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
995
|
-
yield inlinePseudoElements(
|
|
996
|
-
sChildren[i],
|
|
997
|
-
cChildren[i],
|
|
998
|
-
compress,
|
|
999
|
-
embedFonts,
|
|
1000
|
-
useProxy
|
|
1001
|
-
);
|
|
1162
|
+
yield inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, options);
|
|
1002
1163
|
}
|
|
1003
1164
|
});
|
|
1004
1165
|
}
|
|
1005
1166
|
|
|
1006
1167
|
// src/modules/svgDefs.js
|
|
1007
|
-
function
|
|
1008
|
-
if (!
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1168
|
+
function inlineExternalDefsAndSymbols(rootElement) {
|
|
1169
|
+
if (!rootElement) return;
|
|
1170
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
1171
|
+
rootElement.querySelectorAll("use").forEach((use) => {
|
|
1172
|
+
const href = use.getAttribute("xlink:href") || use.getAttribute("href");
|
|
1173
|
+
if (href && href.startsWith("#")) {
|
|
1174
|
+
usedIds.add(href.slice(1));
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
if (!usedIds.size) return;
|
|
1178
|
+
const allGlobal = Array.from(document.querySelectorAll("svg > symbol, svg > defs"));
|
|
1179
|
+
const globalSymbols = allGlobal.filter((el) => el.tagName.toLowerCase() === "symbol");
|
|
1180
|
+
const globalDefs = allGlobal.filter((el) => el.tagName.toLowerCase() === "defs");
|
|
1181
|
+
let container = rootElement.querySelector("svg.inline-defs-container");
|
|
1182
|
+
if (!container) {
|
|
1183
|
+
container = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1184
|
+
container.setAttribute("aria-hidden", "true");
|
|
1185
|
+
container.setAttribute("style", "position: absolute; width: 0; height: 0; overflow: hidden;");
|
|
1186
|
+
container.classList.add("inline-defs-container");
|
|
1187
|
+
rootElement.insertBefore(container, rootElement.firstChild);
|
|
1188
|
+
}
|
|
1189
|
+
const existingIds = /* @__PURE__ */ new Set();
|
|
1190
|
+
rootElement.querySelectorAll("symbol[id], defs > *[id]").forEach((el) => {
|
|
1191
|
+
existingIds.add(el.id);
|
|
1192
|
+
});
|
|
1193
|
+
usedIds.forEach((id) => {
|
|
1194
|
+
if (existingIds.has(id)) return;
|
|
1195
|
+
const symbol = globalSymbols.find((sym) => sym.id === id);
|
|
1196
|
+
if (symbol) {
|
|
1197
|
+
container.appendChild(symbol.cloneNode(true));
|
|
1198
|
+
existingIds.add(id);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
for (const defs of globalDefs) {
|
|
1202
|
+
const defEl = defs.querySelector(`#${CSS.escape(id)}`);
|
|
1203
|
+
if (defEl) {
|
|
1204
|
+
let defsContainer = container.querySelector("defs");
|
|
1205
|
+
if (!defsContainer) {
|
|
1206
|
+
defsContainer = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
1207
|
+
container.appendChild(defsContainer);
|
|
1027
1208
|
}
|
|
1209
|
+
defsContainer.appendChild(defEl.cloneNode(true));
|
|
1210
|
+
existingIds.add(id);
|
|
1211
|
+
break;
|
|
1028
1212
|
}
|
|
1029
1213
|
}
|
|
1030
|
-
if (defs.childNodes.length) {
|
|
1031
|
-
svg.insertBefore(defs, svg.firstChild);
|
|
1032
|
-
}
|
|
1033
1214
|
});
|
|
1034
1215
|
}
|
|
1035
1216
|
|
|
1036
1217
|
// src/core/prepare.js
|
|
1037
1218
|
function prepareClone(_0) {
|
|
1038
1219
|
return __async(this, arguments, function* (element, compress = false, embedFonts = false, options = {}) {
|
|
1039
|
-
var _a;
|
|
1220
|
+
var _a, _b;
|
|
1221
|
+
const styleMap = /* @__PURE__ */ new Map();
|
|
1222
|
+
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
1223
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
1040
1224
|
let clone;
|
|
1041
1225
|
let classCSS = "";
|
|
1226
|
+
stabilizeLayout(element);
|
|
1042
1227
|
try {
|
|
1043
|
-
|
|
1228
|
+
inlineExternalDefsAndSymbols(element);
|
|
1044
1229
|
} catch (e) {
|
|
1045
|
-
console.warn("
|
|
1046
|
-
throw e;
|
|
1230
|
+
console.warn("inlineExternal defs or symbol failed:", e);
|
|
1047
1231
|
}
|
|
1048
1232
|
try {
|
|
1049
|
-
|
|
1233
|
+
clone = deepClone(element, styleMap, styleCache, nodeMap, compress, options, element);
|
|
1050
1234
|
} catch (e) {
|
|
1051
|
-
console.warn("
|
|
1235
|
+
console.warn("deepClone failed:", e);
|
|
1236
|
+
throw e;
|
|
1052
1237
|
}
|
|
1053
1238
|
try {
|
|
1054
|
-
|
|
1239
|
+
yield inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts, options.useProxy);
|
|
1055
1240
|
} catch (e) {
|
|
1056
|
-
console.warn("
|
|
1241
|
+
console.warn("inlinePseudoElements failed:", e);
|
|
1057
1242
|
}
|
|
1243
|
+
yield resolveBlobUrlsInTree(clone);
|
|
1058
1244
|
if (compress) {
|
|
1059
|
-
const keyToClass = generateCSSClasses();
|
|
1245
|
+
const keyToClass = generateCSSClasses(styleMap);
|
|
1060
1246
|
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
1061
|
-
for (const [node, key] of
|
|
1247
|
+
for (const [node, key] of styleMap.entries()) {
|
|
1062
1248
|
if (node.tagName === "STYLE") continue;
|
|
1249
|
+
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
|
|
1250
|
+
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1063
1253
|
const className = keyToClass.get(key);
|
|
1064
1254
|
if (className) node.classList.add(className);
|
|
1065
1255
|
const bgImage = (_a = node.style) == null ? void 0 : _a.backgroundImage;
|
|
1066
|
-
node.
|
|
1256
|
+
const hasIcon = (_b = node.dataset) == null ? void 0 : _b.snapdomHasIcon;
|
|
1067
1257
|
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
1258
|
+
if (hasIcon) {
|
|
1259
|
+
node.style.verticalAlign = "middle";
|
|
1260
|
+
node.style.display = "inline";
|
|
1261
|
+
}
|
|
1068
1262
|
}
|
|
1069
1263
|
} else {
|
|
1070
|
-
for (const [node, key] of
|
|
1264
|
+
for (const [node, key] of styleMap.entries()) {
|
|
1071
1265
|
if (node.tagName === "STYLE") continue;
|
|
1072
1266
|
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
1073
1267
|
}
|
|
1074
1268
|
}
|
|
1075
|
-
for (const [cloneNode, originalNode] of
|
|
1269
|
+
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1076
1270
|
const scrollX = originalNode.scrollLeft;
|
|
1077
1271
|
const scrollY = originalNode.scrollTop;
|
|
1078
1272
|
const hasScroll = scrollX || scrollY;
|
|
@@ -1091,9 +1285,9 @@
|
|
|
1091
1285
|
cloneNode.appendChild(inner);
|
|
1092
1286
|
}
|
|
1093
1287
|
}
|
|
1094
|
-
if (element ===
|
|
1095
|
-
const computed =
|
|
1096
|
-
|
|
1288
|
+
if (element === nodeMap.get(clone)) {
|
|
1289
|
+
const computed = styleCache.get(element) || window.getComputedStyle(element);
|
|
1290
|
+
styleCache.set(element, computed);
|
|
1097
1291
|
const transform = stripTranslate(computed.transform);
|
|
1098
1292
|
clone.style.margin = "0";
|
|
1099
1293
|
clone.style.position = "static";
|
|
@@ -1106,13 +1300,155 @@
|
|
|
1106
1300
|
clone.style.clear = "none";
|
|
1107
1301
|
clone.style.transform = transform || "";
|
|
1108
1302
|
}
|
|
1109
|
-
for (const [cloneNode, originalNode] of
|
|
1303
|
+
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1110
1304
|
if (originalNode.tagName === "PRE") {
|
|
1111
1305
|
cloneNode.style.marginTop = "0";
|
|
1112
1306
|
cloneNode.style.marginBlockStart = "0";
|
|
1113
1307
|
}
|
|
1114
1308
|
}
|
|
1115
|
-
return { clone, classCSS };
|
|
1309
|
+
return { clone, classCSS, styleCache };
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
function stabilizeLayout(element) {
|
|
1313
|
+
const style = getComputedStyle(element);
|
|
1314
|
+
const outlineStyle = style.outlineStyle;
|
|
1315
|
+
const outlineWidth = style.outlineWidth;
|
|
1316
|
+
const borderStyle = style.borderStyle;
|
|
1317
|
+
const borderWidth = style.borderWidth;
|
|
1318
|
+
const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0;
|
|
1319
|
+
const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0;
|
|
1320
|
+
if (outlineVisible && borderAbsent) {
|
|
1321
|
+
element.style.border = `${outlineWidth} solid transparent`;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
var _blobToDataUrlCache = /* @__PURE__ */ new Map();
|
|
1325
|
+
function blobUrlToDataUrl(blobUrl) {
|
|
1326
|
+
return __async(this, null, function* () {
|
|
1327
|
+
if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl);
|
|
1328
|
+
const res = yield fetch(blobUrl);
|
|
1329
|
+
if (!res.ok) throw new Error(`[SnapDOM] HTTP ${res.status} on blob fetch (${blobUrl})`);
|
|
1330
|
+
const blob = yield res.blob();
|
|
1331
|
+
const dataUrl = yield new Promise((resolve, reject) => {
|
|
1332
|
+
const fr = new FileReader();
|
|
1333
|
+
fr.onloadend = () => {
|
|
1334
|
+
const v = fr.result;
|
|
1335
|
+
if (typeof v === "string" && v.startsWith("data:")) resolve(v);
|
|
1336
|
+
else reject(new Error("[SnapDOM] Invalid data URL from blob"));
|
|
1337
|
+
};
|
|
1338
|
+
fr.onerror = () => reject(new Error("[SnapDOM] FileReader error"));
|
|
1339
|
+
fr.readAsDataURL(blob);
|
|
1340
|
+
});
|
|
1341
|
+
_blobToDataUrlCache.set(blobUrl, dataUrl);
|
|
1342
|
+
return dataUrl;
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
var BLOB_URL_RE = /\bblob:[^)"'\s]+/g;
|
|
1346
|
+
function replaceBlobUrlsInCssText(cssText) {
|
|
1347
|
+
return __async(this, null, function* () {
|
|
1348
|
+
if (!cssText || cssText.indexOf("blob:") === -1) return cssText;
|
|
1349
|
+
const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || []));
|
|
1350
|
+
if (uniques.length === 0) return cssText;
|
|
1351
|
+
let out = cssText;
|
|
1352
|
+
for (const u of uniques) {
|
|
1353
|
+
try {
|
|
1354
|
+
const d = yield blobUrlToDataUrl(u);
|
|
1355
|
+
out = out.split(u).join(d);
|
|
1356
|
+
} catch (e) {
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return out;
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
function isBlobUrl(u) {
|
|
1363
|
+
return typeof u === "string" && u.startsWith("blob:");
|
|
1364
|
+
}
|
|
1365
|
+
function parseSrcset(srcset) {
|
|
1366
|
+
return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => {
|
|
1367
|
+
const m = item.match(/^(\S+)(\s+.+)?$/);
|
|
1368
|
+
return m ? { url: m[1], desc: m[2] || "" } : null;
|
|
1369
|
+
}).filter(Boolean);
|
|
1370
|
+
}
|
|
1371
|
+
function stringifySrcset(parts) {
|
|
1372
|
+
return parts.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", ");
|
|
1373
|
+
}
|
|
1374
|
+
function resolveBlobUrlsInTree(root) {
|
|
1375
|
+
return __async(this, null, function* () {
|
|
1376
|
+
var _a, _b;
|
|
1377
|
+
if (!root) return;
|
|
1378
|
+
const imgs = root.querySelectorAll ? root.querySelectorAll("img") : [];
|
|
1379
|
+
for (const img of imgs) {
|
|
1380
|
+
try {
|
|
1381
|
+
const srcAttr = img.getAttribute("src");
|
|
1382
|
+
const effective = srcAttr || img.currentSrc || "";
|
|
1383
|
+
if (isBlobUrl(effective)) {
|
|
1384
|
+
const data = yield blobUrlToDataUrl(effective);
|
|
1385
|
+
img.setAttribute("src", data);
|
|
1386
|
+
}
|
|
1387
|
+
const srcset = img.getAttribute("srcset");
|
|
1388
|
+
if (srcset && srcset.includes("blob:")) {
|
|
1389
|
+
const parts = parseSrcset(srcset);
|
|
1390
|
+
let changed = false;
|
|
1391
|
+
for (const p of parts) {
|
|
1392
|
+
if (isBlobUrl(p.url)) {
|
|
1393
|
+
try {
|
|
1394
|
+
p.url = yield blobUrlToDataUrl(p.url);
|
|
1395
|
+
changed = true;
|
|
1396
|
+
} catch (e) {
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
if (changed) img.setAttribute("srcset", stringifySrcset(parts));
|
|
1401
|
+
}
|
|
1402
|
+
} catch (e) {
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : [];
|
|
1406
|
+
for (const node of svgImages) {
|
|
1407
|
+
try {
|
|
1408
|
+
const XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
1409
|
+
const href = node.getAttribute("href") || ((_a = node.getAttributeNS) == null ? void 0 : _a.call(node, XLINK_NS, "href"));
|
|
1410
|
+
if (isBlobUrl(href)) {
|
|
1411
|
+
const d = yield blobUrlToDataUrl(href);
|
|
1412
|
+
node.setAttribute("href", d);
|
|
1413
|
+
(_b = node.removeAttributeNS) == null ? void 0 : _b.call(node, XLINK_NS, "href");
|
|
1414
|
+
}
|
|
1415
|
+
} catch (e) {
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
const styled = root.querySelectorAll ? root.querySelectorAll("[style*='blob:']") : [];
|
|
1419
|
+
for (const el of styled) {
|
|
1420
|
+
try {
|
|
1421
|
+
const styleText = el.getAttribute("style");
|
|
1422
|
+
if (styleText && styleText.includes("blob:")) {
|
|
1423
|
+
const replaced = yield replaceBlobUrlsInCssText(styleText);
|
|
1424
|
+
el.setAttribute("style", replaced);
|
|
1425
|
+
}
|
|
1426
|
+
} catch (e) {
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
const styleTags = root.querySelectorAll ? root.querySelectorAll("style") : [];
|
|
1430
|
+
for (const s of styleTags) {
|
|
1431
|
+
try {
|
|
1432
|
+
const css = s.textContent || "";
|
|
1433
|
+
if (css.includes("blob:")) {
|
|
1434
|
+
s.textContent = yield replaceBlobUrlsInCssText(css);
|
|
1435
|
+
}
|
|
1436
|
+
} catch (e) {
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
const urlAttrs = ["poster"];
|
|
1440
|
+
for (const attr of urlAttrs) {
|
|
1441
|
+
const nodes = root.querySelectorAll ? root.querySelectorAll(`[${attr}^='blob:']`) : [];
|
|
1442
|
+
for (const n of nodes) {
|
|
1443
|
+
try {
|
|
1444
|
+
const u = n.getAttribute(attr);
|
|
1445
|
+
if (isBlobUrl(u)) {
|
|
1446
|
+
n.setAttribute(attr, yield blobUrlToDataUrl(u));
|
|
1447
|
+
}
|
|
1448
|
+
} catch (e) {
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1116
1452
|
});
|
|
1117
1453
|
}
|
|
1118
1454
|
|
|
@@ -1121,6 +1457,12 @@
|
|
|
1121
1457
|
return __async(this, arguments, function* (clone, options = {}) {
|
|
1122
1458
|
const imgs = Array.from(clone.querySelectorAll("img"));
|
|
1123
1459
|
const processImg = (img) => __async(null, null, function* () {
|
|
1460
|
+
if (!img.getAttribute("src")) {
|
|
1461
|
+
const eff = img.currentSrc || img.src || "";
|
|
1462
|
+
if (eff) img.setAttribute("src", eff);
|
|
1463
|
+
}
|
|
1464
|
+
img.removeAttribute("srcset");
|
|
1465
|
+
img.removeAttribute("sizes");
|
|
1124
1466
|
const src = img.src;
|
|
1125
1467
|
try {
|
|
1126
1468
|
const dataUrl = yield fetchImage(src, { useProxy: options.useProxy });
|
|
@@ -1142,8 +1484,8 @@
|
|
|
1142
1484
|
}
|
|
1143
1485
|
|
|
1144
1486
|
// src/modules/background.js
|
|
1145
|
-
function inlineBackgroundImages(_0, _1) {
|
|
1146
|
-
return __async(this, arguments, function* (source, clone, options = {}) {
|
|
1487
|
+
function inlineBackgroundImages(_0, _1, _2) {
|
|
1488
|
+
return __async(this, arguments, function* (source, clone, styleCache, options = {}) {
|
|
1147
1489
|
const queue = [[source, clone]];
|
|
1148
1490
|
const imageProps = [
|
|
1149
1491
|
"background-image",
|
|
@@ -1153,13 +1495,27 @@
|
|
|
1153
1495
|
"mask-source",
|
|
1154
1496
|
"mask-box-image-source",
|
|
1155
1497
|
"mask-border-source",
|
|
1156
|
-
"-webkit-mask-box-image-source"
|
|
1498
|
+
"-webkit-mask-box-image-source",
|
|
1499
|
+
"border-image",
|
|
1500
|
+
"border-image-source",
|
|
1501
|
+
"border-image-slice",
|
|
1502
|
+
"border-image-width",
|
|
1503
|
+
"border-image-outset",
|
|
1504
|
+
"border-image-repeat"
|
|
1157
1505
|
];
|
|
1158
1506
|
while (queue.length) {
|
|
1159
1507
|
const [srcNode, cloneNode] = queue.shift();
|
|
1160
|
-
const style =
|
|
1161
|
-
if (!
|
|
1508
|
+
const style = styleCache.get(srcNode) || getStyle(srcNode);
|
|
1509
|
+
if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
|
|
1510
|
+
const hasBorderImage = (() => {
|
|
1511
|
+
const bi = style.getPropertyValue("border-image");
|
|
1512
|
+
const bis = style.getPropertyValue("border-image-source");
|
|
1513
|
+
return bi && bi !== "none" || bis && bis !== "none";
|
|
1514
|
+
})();
|
|
1162
1515
|
for (const prop of imageProps) {
|
|
1516
|
+
if (["border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat"].includes(prop) && !hasBorderImage) {
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1163
1519
|
const val = style.getPropertyValue(prop);
|
|
1164
1520
|
if (!val || val === "none") continue;
|
|
1165
1521
|
const splits = splitBackgroundImage(val);
|
|
@@ -1188,13 +1544,13 @@
|
|
|
1188
1544
|
return __async(this, arguments, function* (element, options = {}) {
|
|
1189
1545
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1190
1546
|
cache.reset();
|
|
1191
|
-
const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "" } = options;
|
|
1192
|
-
let clone, classCSS;
|
|
1547
|
+
const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "", localFonts = [] } = options;
|
|
1548
|
+
let clone, classCSS, styleCache;
|
|
1193
1549
|
let fontsCSS = "";
|
|
1194
1550
|
let baseCSS = "";
|
|
1195
1551
|
let dataURL;
|
|
1196
1552
|
let svgString;
|
|
1197
|
-
({ clone, classCSS } = yield prepareClone(element, compress, embedFonts, options));
|
|
1553
|
+
({ clone, classCSS, styleCache } = yield prepareClone(element, compress, embedFonts, options));
|
|
1198
1554
|
yield new Promise((resolve) => {
|
|
1199
1555
|
idle(() => __async(null, null, function* () {
|
|
1200
1556
|
yield inlineImages(clone, options);
|
|
@@ -1203,14 +1559,14 @@
|
|
|
1203
1559
|
});
|
|
1204
1560
|
yield new Promise((resolve) => {
|
|
1205
1561
|
idle(() => __async(null, null, function* () {
|
|
1206
|
-
yield inlineBackgroundImages(element, clone, options);
|
|
1562
|
+
yield inlineBackgroundImages(element, clone, styleCache, options);
|
|
1207
1563
|
resolve();
|
|
1208
1564
|
}), { fast });
|
|
1209
1565
|
});
|
|
1210
1566
|
if (embedFonts) {
|
|
1211
1567
|
yield new Promise((resolve) => {
|
|
1212
1568
|
idle(() => __async(null, null, function* () {
|
|
1213
|
-
fontsCSS = yield embedCustomFonts();
|
|
1569
|
+
fontsCSS = yield embedCustomFonts({ localFonts, useProxy });
|
|
1214
1570
|
resolve();
|
|
1215
1571
|
}), { fast });
|
|
1216
1572
|
});
|
|
@@ -1288,13 +1644,15 @@
|
|
|
1288
1644
|
}
|
|
1289
1645
|
|
|
1290
1646
|
// src/api/snapdom.js
|
|
1291
|
-
function toImg(_0
|
|
1292
|
-
return __async(this, arguments, function* (url, {
|
|
1647
|
+
function toImg(_0) {
|
|
1648
|
+
return __async(this, arguments, function* (url, { scale = 1 } = {}) {
|
|
1293
1649
|
const img = new Image();
|
|
1294
1650
|
img.src = url;
|
|
1295
1651
|
yield img.decode();
|
|
1296
|
-
|
|
1297
|
-
|
|
1652
|
+
if (scale !== 1) {
|
|
1653
|
+
img.style.width = `${img.naturalWidth * scale}px`;
|
|
1654
|
+
img.style.height = `${img.naturalHeight * scale}px`;
|
|
1655
|
+
}
|
|
1298
1656
|
return img;
|
|
1299
1657
|
});
|
|
1300
1658
|
}
|
|
@@ -1319,8 +1677,8 @@
|
|
|
1319
1677
|
if (appended) img.remove();
|
|
1320
1678
|
throw new Error("Image failed to load or has no dimensions");
|
|
1321
1679
|
}
|
|
1322
|
-
const width = img.
|
|
1323
|
-
const height = img.
|
|
1680
|
+
const width = img.naturalWidth * scale;
|
|
1681
|
+
const height = img.naturalHeight * scale;
|
|
1324
1682
|
const canvas = document.createElement("canvas");
|
|
1325
1683
|
canvas.width = Math.ceil(width * dpr);
|
|
1326
1684
|
canvas.height = Math.ceil(height * dpr);
|
|
@@ -1422,20 +1780,24 @@
|
|
|
1422
1780
|
});
|
|
1423
1781
|
}
|
|
1424
1782
|
snapdom.capture = (_0, ..._1) => __async(null, [_0, ..._1], function* (el, options = {}) {
|
|
1783
|
+
var _a;
|
|
1425
1784
|
const url = yield captureDOM(el, options);
|
|
1426
|
-
const dpr = window.devicePixelRatio || 1;
|
|
1785
|
+
const dpr = (_a = options.dpr) != null ? _a : window.devicePixelRatio || 1;
|
|
1427
1786
|
const scale = options.scale || 1;
|
|
1428
1787
|
return {
|
|
1429
1788
|
url,
|
|
1430
1789
|
options,
|
|
1431
1790
|
toRaw: () => url,
|
|
1432
|
-
toImg: () => toImg(url, { dpr, scale }),
|
|
1433
|
-
toCanvas: () => toCanvas(url, { dpr, scale }),
|
|
1434
|
-
toBlob: (
|
|
1435
|
-
toPng: (
|
|
1436
|
-
toJpg: (
|
|
1437
|
-
toWebp: (
|
|
1438
|
-
download: (
|
|
1791
|
+
toImg: (opts = {}) => toImg(url, __spreadValues({ dpr, scale }, opts)),
|
|
1792
|
+
toCanvas: (opts = {}) => toCanvas(url, __spreadValues({ dpr, scale }, opts)),
|
|
1793
|
+
toBlob: (opts = {}) => toBlob(url, __spreadValues({ dpr, scale }, opts)),
|
|
1794
|
+
toPng: (opts = {}) => toRasterImg(url, __spreadValues({ dpr, scale }, opts), "png"),
|
|
1795
|
+
toJpg: (opts = {}) => toRasterImg(url, __spreadValues({ dpr, scale }, opts), "jpeg"),
|
|
1796
|
+
toWebp: (opts = {}) => toRasterImg(url, __spreadValues({ dpr, scale }, opts), "webp"),
|
|
1797
|
+
download: (_b = {}) => {
|
|
1798
|
+
var _c = _b, { format = "png", filename = "snapDOM", backgroundColor } = _c, opts = __objRest(_c, ["format", "filename", "backgroundColor"]);
|
|
1799
|
+
return download(url, __spreadValues({ dpr, scale, format, filename, backgroundColor }, opts));
|
|
1800
|
+
}
|
|
1439
1801
|
};
|
|
1440
1802
|
});
|
|
1441
1803
|
snapdom.toRaw = (el, options) => __async(null, null, function* () {
|
|
@@ -1476,12 +1838,21 @@
|
|
|
1476
1838
|
// src/api/preCache.js
|
|
1477
1839
|
function preCache() {
|
|
1478
1840
|
return __async(this, arguments, function* (root = document, options = {}) {
|
|
1479
|
-
const { embedFonts = true, reset = false } = options;
|
|
1841
|
+
const { embedFonts = true, reset = false, useProxy } = options;
|
|
1480
1842
|
if (reset) {
|
|
1481
|
-
cache.
|
|
1843
|
+
cache.image.clear();
|
|
1844
|
+
cache.background.clear();
|
|
1845
|
+
cache.resource.clear();
|
|
1846
|
+
cache.defaultStyle.clear();
|
|
1847
|
+
cache.baseStyle.clear();
|
|
1848
|
+
cache.font.clear();
|
|
1849
|
+
cache.computedStyle = /* @__PURE__ */ new WeakMap();
|
|
1482
1850
|
return;
|
|
1483
1851
|
}
|
|
1484
|
-
|
|
1852
|
+
try {
|
|
1853
|
+
yield document.fonts.ready;
|
|
1854
|
+
} catch (e) {
|
|
1855
|
+
}
|
|
1485
1856
|
precacheCommonTags();
|
|
1486
1857
|
let imgEls = [], allEls = [];
|
|
1487
1858
|
if (root == null ? void 0 : root.querySelectorAll) {
|
|
@@ -1490,33 +1861,41 @@
|
|
|
1490
1861
|
}
|
|
1491
1862
|
const promises = [];
|
|
1492
1863
|
for (const img of imgEls) {
|
|
1493
|
-
const src = img.src;
|
|
1864
|
+
const src = img == null ? void 0 : img.src;
|
|
1865
|
+
if (!src) continue;
|
|
1494
1866
|
if (!cache.image.has(src)) {
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
);
|
|
1867
|
+
const p = Promise.resolve().then(() => fetchImage(src, { useProxy })).then((dataURL) => {
|
|
1868
|
+
cache.image.set(src, dataURL);
|
|
1869
|
+
}).catch(() => {
|
|
1870
|
+
});
|
|
1871
|
+
promises.push(p);
|
|
1499
1872
|
}
|
|
1500
1873
|
}
|
|
1501
1874
|
for (const el of allEls) {
|
|
1502
|
-
|
|
1875
|
+
let bg = "";
|
|
1876
|
+
try {
|
|
1877
|
+
bg = getStyle(el).backgroundImage;
|
|
1878
|
+
} catch (e) {
|
|
1879
|
+
}
|
|
1503
1880
|
if (bg && bg !== "none") {
|
|
1504
1881
|
const bgSplits = splitBackgroundImage(bg);
|
|
1505
1882
|
for (const entry of bgSplits) {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
})
|
|
1511
|
-
);
|
|
1883
|
+
if (entry.startsWith("url(")) {
|
|
1884
|
+
const p = Promise.resolve().then(() => inlineSingleBackgroundEntry(entry, __spreadProps(__spreadValues({}, options), { useProxy }))).catch(() => {
|
|
1885
|
+
});
|
|
1886
|
+
promises.push(p);
|
|
1512
1887
|
}
|
|
1513
1888
|
}
|
|
1514
1889
|
}
|
|
1515
1890
|
}
|
|
1516
1891
|
if (embedFonts) {
|
|
1517
|
-
|
|
1892
|
+
try {
|
|
1893
|
+
yield embedCustomFonts({ preCached: true, localFonts: options.localFonts, useProxy: options.useProxy });
|
|
1894
|
+
} catch (e) {
|
|
1895
|
+
}
|
|
1896
|
+
;
|
|
1518
1897
|
}
|
|
1519
|
-
yield Promise.
|
|
1898
|
+
yield Promise.allSettled(promises);
|
|
1520
1899
|
});
|
|
1521
1900
|
}
|
|
1522
1901
|
|