@zumer/snapdom 1.9.8 → 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 +6 -2
- package/dist/snapdom.js +478 -182
- package/dist/snapdom.min.js +3 -3
- package/dist/snapdom.min.mjs +3 -3
- package/dist/snapdom.mjs +464 -180
- package/package.json +1 -1
- package/types/snapdom.d.ts +3 -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
|
|
@@ -212,8 +207,8 @@
|
|
|
212
207
|
}
|
|
213
208
|
return css;
|
|
214
209
|
}
|
|
215
|
-
function generateCSSClasses() {
|
|
216
|
-
const keySet = new Set(
|
|
210
|
+
function generateCSSClasses(styleMap) {
|
|
211
|
+
const keySet = new Set(styleMap.values());
|
|
217
212
|
const classMap = /* @__PURE__ */ new Map();
|
|
218
213
|
let counter = 1;
|
|
219
214
|
for (const key of keySet) {
|
|
@@ -285,7 +280,29 @@
|
|
|
285
280
|
if (url.startsWith("#")) return null;
|
|
286
281
|
return url;
|
|
287
282
|
}
|
|
288
|
-
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 } = {}) {
|
|
289
306
|
function getCrossOriginMode(url) {
|
|
290
307
|
try {
|
|
291
308
|
const parsed = new URL(url, window.location.href);
|
|
@@ -294,107 +311,157 @@
|
|
|
294
311
|
return "anonymous";
|
|
295
312
|
}
|
|
296
313
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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, {
|
|
300
319
|
mode: "cors",
|
|
301
320
|
credentials: getCrossOriginMode(fetchUrl) === "use-credentials" ? "include" : "omit"
|
|
302
|
-
}).then((r) =>
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
+
});
|
|
328
350
|
}
|
|
351
|
+
return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"));
|
|
329
352
|
});
|
|
330
353
|
}
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
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;
|
|
334
361
|
}
|
|
335
|
-
|
|
336
|
-
|
|
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/")) {
|
|
337
366
|
cache.image.set(src, src);
|
|
338
367
|
return Promise.resolve(src);
|
|
339
368
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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;
|
|
354
387
|
}
|
|
388
|
+
const via = yield fetchWithFallbackOnceSafe(src);
|
|
389
|
+
if (via.ok) {
|
|
390
|
+
cache.image.set(src, via.data);
|
|
391
|
+
return via.data;
|
|
392
|
+
}
|
|
393
|
+
_errorCache.set(src, now + errorTTL);
|
|
394
|
+
return Promise.reject(via.error);
|
|
355
395
|
}))();
|
|
396
|
+
_inflight.set(src, p2);
|
|
397
|
+
p2.finally(() => _inflight.delete(src));
|
|
398
|
+
p2.catch(() => {
|
|
399
|
+
});
|
|
400
|
+
return p2;
|
|
356
401
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
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;
|
|
364
408
|
clearTimeout(timeoutId);
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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(() => {
|
|
376
432
|
try {
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
+
});
|
|
382
444
|
}
|
|
383
|
-
}
|
|
445
|
+
}).catch(() => {
|
|
446
|
+
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
447
|
+
if (r.ok) onSuccess(r.data);
|
|
448
|
+
else onFinalError(r.error);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
384
451
|
});
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
cache.image.set(src, fallbackDataURL);
|
|
391
|
-
resolve(fallbackDataURL);
|
|
392
|
-
} catch (e) {
|
|
393
|
-
reject(e);
|
|
394
|
-
}
|
|
452
|
+
img.onerror = finish(() => {
|
|
453
|
+
fetchWithFallbackOnceSafe(src).then((r) => {
|
|
454
|
+
if (r.ok) onSuccess(r.data);
|
|
455
|
+
else onFinalError(r.error);
|
|
456
|
+
});
|
|
395
457
|
});
|
|
396
|
-
|
|
458
|
+
img.src = src;
|
|
397
459
|
});
|
|
460
|
+
_inflight.set(src, p);
|
|
461
|
+
p.finally(() => _inflight.delete(src));
|
|
462
|
+
p.catch(() => {
|
|
463
|
+
});
|
|
464
|
+
return p;
|
|
398
465
|
}
|
|
399
466
|
function snapshotComputedStyle(style) {
|
|
400
467
|
const snap = {};
|
|
@@ -451,6 +518,8 @@
|
|
|
451
518
|
}
|
|
452
519
|
|
|
453
520
|
// src/modules/styles.js
|
|
521
|
+
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
522
|
+
var snapshotKeyCache = /* @__PURE__ */ new Map();
|
|
454
523
|
function snapshotComputedStyleFull(style) {
|
|
455
524
|
const result = {};
|
|
456
525
|
const computedVisibility = style.getPropertyValue("visibility");
|
|
@@ -467,31 +536,43 @@
|
|
|
467
536
|
}
|
|
468
537
|
return result;
|
|
469
538
|
}
|
|
470
|
-
function inlineAllStyles(source, clone, compress) {
|
|
539
|
+
function inlineAllStyles(source, clone, styleMap, cache2, compress) {
|
|
471
540
|
var _a;
|
|
472
541
|
if (source.tagName === "STYLE") return;
|
|
473
|
-
if (!
|
|
474
|
-
|
|
542
|
+
if (!cache2.has(source)) {
|
|
543
|
+
cache2.set(source, getStyle(source));
|
|
475
544
|
}
|
|
476
|
-
const style =
|
|
477
|
-
if (!
|
|
545
|
+
const style = cache2.get(source);
|
|
546
|
+
if (!snapshotCache.has(source)) {
|
|
478
547
|
const snapshot2 = snapshotComputedStyleFull(style);
|
|
479
|
-
|
|
548
|
+
snapshotCache.set(source, snapshot2);
|
|
480
549
|
}
|
|
481
|
-
const snapshot =
|
|
550
|
+
const snapshot = snapshotCache.get(source);
|
|
482
551
|
const hash = Object.entries(snapshot).sort(([a], [b]) => a.localeCompare(b)).map(([prop, val]) => `${prop}:${val}`).join(";");
|
|
483
|
-
if (
|
|
484
|
-
|
|
552
|
+
if (snapshotKeyCache.has(hash)) {
|
|
553
|
+
styleMap.set(clone, snapshotKeyCache.get(hash));
|
|
485
554
|
return;
|
|
486
555
|
}
|
|
487
556
|
const tagName = ((_a = source.tagName) == null ? void 0 : _a.toLowerCase()) || "div";
|
|
488
557
|
const key = getStyleKey(snapshot, tagName, compress);
|
|
489
|
-
|
|
490
|
-
|
|
558
|
+
snapshotKeyCache.set(hash, key);
|
|
559
|
+
styleMap.set(clone, key);
|
|
491
560
|
}
|
|
492
561
|
|
|
493
562
|
// src/core/clone.js
|
|
494
|
-
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) {
|
|
495
576
|
var _a, _b;
|
|
496
577
|
if (!node) throw new Error("Invalid node");
|
|
497
578
|
const clonedAssignedNodes = /* @__PURE__ */ new Set();
|
|
@@ -541,8 +622,8 @@
|
|
|
541
622
|
}
|
|
542
623
|
if (node.getAttribute("data-capture") === "placeholder") {
|
|
543
624
|
const clone2 = node.cloneNode(false);
|
|
544
|
-
|
|
545
|
-
inlineAllStyles(node, clone2, compress);
|
|
625
|
+
nodeMap.set(clone2, node);
|
|
626
|
+
inlineAllStyles(node, clone2, styleMap, styleCache, compress);
|
|
546
627
|
const placeholder = document.createElement("div");
|
|
547
628
|
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
548
629
|
placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`;
|
|
@@ -555,14 +636,17 @@
|
|
|
555
636
|
img.src = dataURL;
|
|
556
637
|
img.width = node.width;
|
|
557
638
|
img.height = node.height;
|
|
558
|
-
|
|
559
|
-
inlineAllStyles(node, img, compress);
|
|
639
|
+
nodeMap.set(img, node);
|
|
640
|
+
inlineAllStyles(node, img, styleMap, styleCache, compress);
|
|
560
641
|
return img;
|
|
561
642
|
}
|
|
562
643
|
let clone;
|
|
563
644
|
try {
|
|
564
645
|
clone = node.cloneNode(false);
|
|
565
|
-
|
|
646
|
+
nodeMap.set(clone, node);
|
|
647
|
+
if (node.tagName === "IMG") {
|
|
648
|
+
freezeImgSrcset(node, clone);
|
|
649
|
+
}
|
|
566
650
|
} catch (err) {
|
|
567
651
|
console.error("[Snapdom] Failed to clone node:", node, err);
|
|
568
652
|
throw err;
|
|
@@ -587,7 +671,7 @@
|
|
|
587
671
|
if (node instanceof HTMLSelectElement) {
|
|
588
672
|
pendingSelectValue = node.value;
|
|
589
673
|
}
|
|
590
|
-
inlineAllStyles(node, clone, compress);
|
|
674
|
+
inlineAllStyles(node, clone, styleMap, styleCache, compress);
|
|
591
675
|
if (node.shadowRoot) {
|
|
592
676
|
const hasSlot = Array.from(node.shadowRoot.querySelectorAll("slot")).length > 0;
|
|
593
677
|
if (hasSlot) {
|
|
@@ -595,8 +679,7 @@
|
|
|
595
679
|
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
|
|
596
680
|
const cssText = child.textContent || "";
|
|
597
681
|
if (cssText.trim() && compress) {
|
|
598
|
-
|
|
599
|
-
cache.preStyle.set(child, cssText);
|
|
682
|
+
styleCache.set(child, cssText);
|
|
600
683
|
}
|
|
601
684
|
}
|
|
602
685
|
}
|
|
@@ -606,12 +689,11 @@
|
|
|
606
689
|
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
|
|
607
690
|
const cssText = child.textContent || "";
|
|
608
691
|
if (cssText.trim() && compress) {
|
|
609
|
-
|
|
610
|
-
cache.preStyle.set(child, cssText);
|
|
692
|
+
styleCache.set(child, cssText);
|
|
611
693
|
}
|
|
612
694
|
continue;
|
|
613
695
|
}
|
|
614
|
-
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
696
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
615
697
|
if (clonedChild) shadowFrag.appendChild(clonedChild);
|
|
616
698
|
}
|
|
617
699
|
clone.appendChild(shadowFrag);
|
|
@@ -622,14 +704,14 @@
|
|
|
622
704
|
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
623
705
|
const fragment = document.createDocumentFragment();
|
|
624
706
|
for (const child of nodesToClone) {
|
|
625
|
-
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
707
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
626
708
|
if (clonedChild) fragment.appendChild(clonedChild);
|
|
627
709
|
}
|
|
628
710
|
return fragment;
|
|
629
711
|
}
|
|
630
712
|
for (const child of node.childNodes) {
|
|
631
713
|
if (clonedAssignedNodes.has(child)) continue;
|
|
632
|
-
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
714
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
633
715
|
if (clonedChild) clone.appendChild(clonedChild);
|
|
634
716
|
}
|
|
635
717
|
if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
|
|
@@ -737,7 +819,7 @@
|
|
|
737
819
|
});
|
|
738
820
|
}
|
|
739
821
|
function embedCustomFonts() {
|
|
740
|
-
return __async(this, arguments, function* ({ preCached = false } = {}) {
|
|
822
|
+
return __async(this, arguments, function* ({ preCached = false, localFonts = [], useProxy = "" } = {}) {
|
|
741
823
|
if (cache.resource.has("fonts-embed-css")) {
|
|
742
824
|
if (preCached) {
|
|
743
825
|
const style = document.createElement("style");
|
|
@@ -747,6 +829,15 @@
|
|
|
747
829
|
}
|
|
748
830
|
return cache.resource.get("fonts-embed-css");
|
|
749
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
|
+
}
|
|
750
841
|
const importRegex = /@import\s+url\(["']?([^"')]+)["']?\)/g;
|
|
751
842
|
const styleImports = [];
|
|
752
843
|
for (const styleTag of document.querySelectorAll("style")) {
|
|
@@ -765,26 +856,48 @@
|
|
|
765
856
|
let finalCSS = "";
|
|
766
857
|
for (const link of links) {
|
|
767
858
|
try {
|
|
768
|
-
const res = yield
|
|
859
|
+
const res = yield fetchResource(link.href, { useProxy });
|
|
769
860
|
const cssText = yield res.text();
|
|
770
861
|
if (isIconFont(link.href) || isIconFont(cssText)) continue;
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
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) {
|
|
774
886
|
let rawUrl = extractURL(match[0]);
|
|
775
|
-
if (!rawUrl)
|
|
887
|
+
if (!rawUrl) continue;
|
|
776
888
|
let url = rawUrl;
|
|
777
889
|
if (!url.startsWith("http") && !url.startsWith("data:")) {
|
|
778
890
|
url = new URL(url, link.href).href;
|
|
779
891
|
}
|
|
780
|
-
if (isIconFont(url))
|
|
892
|
+
if (isIconFont(url)) continue;
|
|
781
893
|
if (cache.resource.has(url)) {
|
|
782
894
|
cache.font.add(url);
|
|
783
|
-
|
|
895
|
+
inlined = inlined.replace(match[0], `url(${cache.resource.get(url)})`);
|
|
896
|
+
continue;
|
|
784
897
|
}
|
|
785
|
-
if (cache.font.has(url))
|
|
898
|
+
if (cache.font.has(url)) continue;
|
|
786
899
|
try {
|
|
787
|
-
const fontRes = yield
|
|
900
|
+
const fontRes = yield fetchResource(url, { useProxy });
|
|
788
901
|
const blob = yield fontRes.blob();
|
|
789
902
|
const b64 = yield new Promise((resolve) => {
|
|
790
903
|
const reader = new FileReader();
|
|
@@ -793,16 +906,12 @@
|
|
|
793
906
|
});
|
|
794
907
|
cache.resource.set(url, b64);
|
|
795
908
|
cache.font.add(url);
|
|
796
|
-
|
|
909
|
+
inlined = inlined.replace(match[0], `url(${b64})`);
|
|
797
910
|
} catch (e) {
|
|
798
911
|
console.warn("[snapdom] Failed to fetch font resource:", url);
|
|
799
|
-
return null;
|
|
800
912
|
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
let cssFinal = cssText;
|
|
804
|
-
for (const r of inlinedCSS) {
|
|
805
|
-
if (r) cssFinal = cssFinal.replace(r.original, r.inlined);
|
|
913
|
+
}
|
|
914
|
+
cssFinal = cssFinal.replace(face, inlined);
|
|
806
915
|
}
|
|
807
916
|
finalCSS += cssFinal + "\n";
|
|
808
917
|
} catch (e) {
|
|
@@ -817,14 +926,18 @@
|
|
|
817
926
|
const src = rule.style.getPropertyValue("src");
|
|
818
927
|
const family = rule.style.getPropertyValue("font-family");
|
|
819
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}`;
|
|
820
932
|
const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
|
|
821
933
|
const localRegex = /local\((["']?)[^)]+?\1\)/g;
|
|
822
934
|
const hasURL = !!src.match(urlRegex);
|
|
823
935
|
const hasLocal = !!src.match(localRegex);
|
|
824
936
|
if (!hasURL && hasLocal) {
|
|
825
|
-
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};}`;
|
|
826
938
|
continue;
|
|
827
939
|
}
|
|
940
|
+
if (!loadedFonts.has(key)) continue;
|
|
828
941
|
let inlinedSrc = src;
|
|
829
942
|
const matches = Array.from(src.matchAll(urlRegex));
|
|
830
943
|
for (const match of matches) {
|
|
@@ -842,7 +955,7 @@
|
|
|
842
955
|
}
|
|
843
956
|
if (cache.font.has(url)) continue;
|
|
844
957
|
try {
|
|
845
|
-
const res = yield
|
|
958
|
+
const res = yield fetchResource(url, { useProxy });
|
|
846
959
|
const blob = yield res.blob();
|
|
847
960
|
const b64 = yield new Promise((resolve) => {
|
|
848
961
|
const reader = new FileReader();
|
|
@@ -856,7 +969,7 @@
|
|
|
856
969
|
console.warn("[snapdom] Failed to fetch font URL:", url);
|
|
857
970
|
}
|
|
858
971
|
}
|
|
859
|
-
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};}`;
|
|
860
973
|
}
|
|
861
974
|
}
|
|
862
975
|
}
|
|
@@ -874,7 +987,7 @@
|
|
|
874
987
|
cache.font.add(font._snapdomSrc);
|
|
875
988
|
} else if (!cache.font.has(font._snapdomSrc)) {
|
|
876
989
|
try {
|
|
877
|
-
const res = yield
|
|
990
|
+
const res = yield fetchResource(font._snapdomSrc, { useProxy });
|
|
878
991
|
const blob = yield res.blob();
|
|
879
992
|
b64 = yield new Promise((resolve) => {
|
|
880
993
|
const reader = new FileReader();
|
|
@@ -892,6 +1005,32 @@
|
|
|
892
1005
|
finalCSS += `@font-face{font-family:'${font.family}';src:url(${b64});font-style:${font.style || "normal"};font-weight:${font.weight || "normal"};}`;
|
|
893
1006
|
}
|
|
894
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
|
+
}
|
|
895
1034
|
if (finalCSS) {
|
|
896
1035
|
cache.resource.set("fonts-embed-css", finalCSS);
|
|
897
1036
|
if (preCached) {
|
|
@@ -906,7 +1045,7 @@
|
|
|
906
1045
|
}
|
|
907
1046
|
|
|
908
1047
|
// src/modules/pseudo.js
|
|
909
|
-
function inlinePseudoElements(source, clone, options) {
|
|
1048
|
+
function inlinePseudoElements(source, clone, styleMap, styleCache, options) {
|
|
910
1049
|
return __async(this, null, function* () {
|
|
911
1050
|
var _a;
|
|
912
1051
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
@@ -937,7 +1076,7 @@
|
|
|
937
1076
|
span.dataset.snapdomPseudo = "::first-letter";
|
|
938
1077
|
const snapshot2 = snapshotComputedStyle(style);
|
|
939
1078
|
const key2 = getStyleKey(snapshot2, "span", options);
|
|
940
|
-
|
|
1079
|
+
styleMap.set(span, key2);
|
|
941
1080
|
const restNode = document.createTextNode(rest);
|
|
942
1081
|
clone.replaceChild(restNode, textNode);
|
|
943
1082
|
clone.insertBefore(span, restNode);
|
|
@@ -971,7 +1110,7 @@
|
|
|
971
1110
|
pseudoEl.style.verticalAlign = "middle";
|
|
972
1111
|
const snapshot = snapshotComputedStyle(style);
|
|
973
1112
|
const key = getStyleKey(snapshot, "span", options);
|
|
974
|
-
|
|
1113
|
+
styleMap.set(pseudoEl, key);
|
|
975
1114
|
if (isIconFont2 && cleanContent.length === 1) {
|
|
976
1115
|
const { dataUrl, width: width2, height: height2 } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
977
1116
|
const imgEl = document.createElement("img");
|
|
@@ -1020,7 +1159,7 @@
|
|
|
1020
1159
|
const sChildren = Array.from(source.children);
|
|
1021
1160
|
const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
|
|
1022
1161
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
1023
|
-
yield inlinePseudoElements(sChildren[i], cChildren[i], options);
|
|
1162
|
+
yield inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, options);
|
|
1024
1163
|
}
|
|
1025
1164
|
});
|
|
1026
1165
|
}
|
|
@@ -1079,6 +1218,9 @@
|
|
|
1079
1218
|
function prepareClone(_0) {
|
|
1080
1219
|
return __async(this, arguments, function* (element, compress = false, embedFonts = false, options = {}) {
|
|
1081
1220
|
var _a, _b;
|
|
1221
|
+
const styleMap = /* @__PURE__ */ new Map();
|
|
1222
|
+
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
1223
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
1082
1224
|
let clone;
|
|
1083
1225
|
let classCSS = "";
|
|
1084
1226
|
stabilizeLayout(element);
|
|
@@ -1088,20 +1230,21 @@
|
|
|
1088
1230
|
console.warn("inlineExternal defs or symbol failed:", e);
|
|
1089
1231
|
}
|
|
1090
1232
|
try {
|
|
1091
|
-
clone = deepClone(element, compress, options, element);
|
|
1233
|
+
clone = deepClone(element, styleMap, styleCache, nodeMap, compress, options, element);
|
|
1092
1234
|
} catch (e) {
|
|
1093
1235
|
console.warn("deepClone failed:", e);
|
|
1094
1236
|
throw e;
|
|
1095
1237
|
}
|
|
1096
1238
|
try {
|
|
1097
|
-
yield inlinePseudoElements(element, clone, compress, embedFonts, options.useProxy);
|
|
1239
|
+
yield inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts, options.useProxy);
|
|
1098
1240
|
} catch (e) {
|
|
1099
1241
|
console.warn("inlinePseudoElements failed:", e);
|
|
1100
1242
|
}
|
|
1243
|
+
yield resolveBlobUrlsInTree(clone);
|
|
1101
1244
|
if (compress) {
|
|
1102
|
-
const keyToClass = generateCSSClasses();
|
|
1245
|
+
const keyToClass = generateCSSClasses(styleMap);
|
|
1103
1246
|
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
1104
|
-
for (const [node, key] of
|
|
1247
|
+
for (const [node, key] of styleMap.entries()) {
|
|
1105
1248
|
if (node.tagName === "STYLE") continue;
|
|
1106
1249
|
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
|
|
1107
1250
|
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
@@ -1118,12 +1261,12 @@
|
|
|
1118
1261
|
}
|
|
1119
1262
|
}
|
|
1120
1263
|
} else {
|
|
1121
|
-
for (const [node, key] of
|
|
1264
|
+
for (const [node, key] of styleMap.entries()) {
|
|
1122
1265
|
if (node.tagName === "STYLE") continue;
|
|
1123
1266
|
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
1124
1267
|
}
|
|
1125
1268
|
}
|
|
1126
|
-
for (const [cloneNode, originalNode] of
|
|
1269
|
+
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1127
1270
|
const scrollX = originalNode.scrollLeft;
|
|
1128
1271
|
const scrollY = originalNode.scrollTop;
|
|
1129
1272
|
const hasScroll = scrollX || scrollY;
|
|
@@ -1142,9 +1285,9 @@
|
|
|
1142
1285
|
cloneNode.appendChild(inner);
|
|
1143
1286
|
}
|
|
1144
1287
|
}
|
|
1145
|
-
if (element ===
|
|
1146
|
-
const computed =
|
|
1147
|
-
|
|
1288
|
+
if (element === nodeMap.get(clone)) {
|
|
1289
|
+
const computed = styleCache.get(element) || window.getComputedStyle(element);
|
|
1290
|
+
styleCache.set(element, computed);
|
|
1148
1291
|
const transform = stripTranslate(computed.transform);
|
|
1149
1292
|
clone.style.margin = "0";
|
|
1150
1293
|
clone.style.position = "static";
|
|
@@ -1157,13 +1300,13 @@
|
|
|
1157
1300
|
clone.style.clear = "none";
|
|
1158
1301
|
clone.style.transform = transform || "";
|
|
1159
1302
|
}
|
|
1160
|
-
for (const [cloneNode, originalNode] of
|
|
1303
|
+
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1161
1304
|
if (originalNode.tagName === "PRE") {
|
|
1162
1305
|
cloneNode.style.marginTop = "0";
|
|
1163
1306
|
cloneNode.style.marginBlockStart = "0";
|
|
1164
1307
|
}
|
|
1165
1308
|
}
|
|
1166
|
-
return { clone, classCSS };
|
|
1309
|
+
return { clone, classCSS, styleCache };
|
|
1167
1310
|
});
|
|
1168
1311
|
}
|
|
1169
1312
|
function stabilizeLayout(element) {
|
|
@@ -1178,12 +1321,148 @@
|
|
|
1178
1321
|
element.style.border = `${outlineWidth} solid transparent`;
|
|
1179
1322
|
}
|
|
1180
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
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1181
1454
|
|
|
1182
1455
|
// src/modules/images.js
|
|
1183
1456
|
function inlineImages(_0) {
|
|
1184
1457
|
return __async(this, arguments, function* (clone, options = {}) {
|
|
1185
1458
|
const imgs = Array.from(clone.querySelectorAll("img"));
|
|
1186
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");
|
|
1187
1466
|
const src = img.src;
|
|
1188
1467
|
try {
|
|
1189
1468
|
const dataUrl = yield fetchImage(src, { useProxy: options.useProxy });
|
|
@@ -1205,8 +1484,8 @@
|
|
|
1205
1484
|
}
|
|
1206
1485
|
|
|
1207
1486
|
// src/modules/background.js
|
|
1208
|
-
function inlineBackgroundImages(_0, _1) {
|
|
1209
|
-
return __async(this, arguments, function* (source, clone, options = {}) {
|
|
1487
|
+
function inlineBackgroundImages(_0, _1, _2) {
|
|
1488
|
+
return __async(this, arguments, function* (source, clone, styleCache, options = {}) {
|
|
1210
1489
|
const queue = [[source, clone]];
|
|
1211
1490
|
const imageProps = [
|
|
1212
1491
|
"background-image",
|
|
@@ -1226,8 +1505,8 @@
|
|
|
1226
1505
|
];
|
|
1227
1506
|
while (queue.length) {
|
|
1228
1507
|
const [srcNode, cloneNode] = queue.shift();
|
|
1229
|
-
const style =
|
|
1230
|
-
if (!
|
|
1508
|
+
const style = styleCache.get(srcNode) || getStyle(srcNode);
|
|
1509
|
+
if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
|
|
1231
1510
|
const hasBorderImage = (() => {
|
|
1232
1511
|
const bi = style.getPropertyValue("border-image");
|
|
1233
1512
|
const bis = style.getPropertyValue("border-image-source");
|
|
@@ -1265,13 +1544,13 @@
|
|
|
1265
1544
|
return __async(this, arguments, function* (element, options = {}) {
|
|
1266
1545
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1267
1546
|
cache.reset();
|
|
1268
|
-
const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "" } = options;
|
|
1269
|
-
let clone, classCSS;
|
|
1547
|
+
const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "", localFonts = [] } = options;
|
|
1548
|
+
let clone, classCSS, styleCache;
|
|
1270
1549
|
let fontsCSS = "";
|
|
1271
1550
|
let baseCSS = "";
|
|
1272
1551
|
let dataURL;
|
|
1273
1552
|
let svgString;
|
|
1274
|
-
({ clone, classCSS } = yield prepareClone(element, compress, embedFonts, options));
|
|
1553
|
+
({ clone, classCSS, styleCache } = yield prepareClone(element, compress, embedFonts, options));
|
|
1275
1554
|
yield new Promise((resolve) => {
|
|
1276
1555
|
idle(() => __async(null, null, function* () {
|
|
1277
1556
|
yield inlineImages(clone, options);
|
|
@@ -1280,14 +1559,14 @@
|
|
|
1280
1559
|
});
|
|
1281
1560
|
yield new Promise((resolve) => {
|
|
1282
1561
|
idle(() => __async(null, null, function* () {
|
|
1283
|
-
yield inlineBackgroundImages(element, clone, options);
|
|
1562
|
+
yield inlineBackgroundImages(element, clone, styleCache, options);
|
|
1284
1563
|
resolve();
|
|
1285
1564
|
}), { fast });
|
|
1286
1565
|
});
|
|
1287
1566
|
if (embedFonts) {
|
|
1288
1567
|
yield new Promise((resolve) => {
|
|
1289
1568
|
idle(() => __async(null, null, function* () {
|
|
1290
|
-
fontsCSS = yield embedCustomFonts();
|
|
1569
|
+
fontsCSS = yield embedCustomFonts({ localFonts, useProxy });
|
|
1291
1570
|
resolve();
|
|
1292
1571
|
}), { fast });
|
|
1293
1572
|
});
|
|
@@ -1559,12 +1838,21 @@
|
|
|
1559
1838
|
// src/api/preCache.js
|
|
1560
1839
|
function preCache() {
|
|
1561
1840
|
return __async(this, arguments, function* (root = document, options = {}) {
|
|
1562
|
-
const { embedFonts = true, reset = false } = options;
|
|
1841
|
+
const { embedFonts = true, reset = false, useProxy } = options;
|
|
1563
1842
|
if (reset) {
|
|
1564
|
-
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();
|
|
1565
1850
|
return;
|
|
1566
1851
|
}
|
|
1567
|
-
|
|
1852
|
+
try {
|
|
1853
|
+
yield document.fonts.ready;
|
|
1854
|
+
} catch (e) {
|
|
1855
|
+
}
|
|
1568
1856
|
precacheCommonTags();
|
|
1569
1857
|
let imgEls = [], allEls = [];
|
|
1570
1858
|
if (root == null ? void 0 : root.querySelectorAll) {
|
|
@@ -1573,33 +1861,41 @@
|
|
|
1573
1861
|
}
|
|
1574
1862
|
const promises = [];
|
|
1575
1863
|
for (const img of imgEls) {
|
|
1576
|
-
const src = img.src;
|
|
1864
|
+
const src = img == null ? void 0 : img.src;
|
|
1865
|
+
if (!src) continue;
|
|
1577
1866
|
if (!cache.image.has(src)) {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
);
|
|
1867
|
+
const p = Promise.resolve().then(() => fetchImage(src, { useProxy })).then((dataURL) => {
|
|
1868
|
+
cache.image.set(src, dataURL);
|
|
1869
|
+
}).catch(() => {
|
|
1870
|
+
});
|
|
1871
|
+
promises.push(p);
|
|
1582
1872
|
}
|
|
1583
1873
|
}
|
|
1584
1874
|
for (const el of allEls) {
|
|
1585
|
-
|
|
1875
|
+
let bg = "";
|
|
1876
|
+
try {
|
|
1877
|
+
bg = getStyle(el).backgroundImage;
|
|
1878
|
+
} catch (e) {
|
|
1879
|
+
}
|
|
1586
1880
|
if (bg && bg !== "none") {
|
|
1587
1881
|
const bgSplits = splitBackgroundImage(bg);
|
|
1588
1882
|
for (const entry of bgSplits) {
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
})
|
|
1594
|
-
);
|
|
1883
|
+
if (entry.startsWith("url(")) {
|
|
1884
|
+
const p = Promise.resolve().then(() => inlineSingleBackgroundEntry(entry, __spreadProps(__spreadValues({}, options), { useProxy }))).catch(() => {
|
|
1885
|
+
});
|
|
1886
|
+
promises.push(p);
|
|
1595
1887
|
}
|
|
1596
1888
|
}
|
|
1597
1889
|
}
|
|
1598
1890
|
}
|
|
1599
1891
|
if (embedFonts) {
|
|
1600
|
-
|
|
1892
|
+
try {
|
|
1893
|
+
yield embedCustomFonts({ preCached: true, localFonts: options.localFonts, useProxy: options.useProxy });
|
|
1894
|
+
} catch (e) {
|
|
1895
|
+
}
|
|
1896
|
+
;
|
|
1601
1897
|
}
|
|
1602
|
-
yield Promise.
|
|
1898
|
+
yield Promise.allSettled(promises);
|
|
1603
1899
|
});
|
|
1604
1900
|
}
|
|
1605
1901
|
|