@zumer/snapdom 1.8.0 → 1.9.3-next-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -34
- package/dist/snapdom.js +340 -114
- package/dist/snapdom.min.js +13 -13
- package/dist/snapdom.min.mjs +12 -12
- package/dist/snapdom.mjs +334 -110
- package/package.json +1 -1
- package/types/snapdom.d.ts +7 -2
package/dist/snapdom.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.
|
|
4
|
+
* v.1.9.3-next-dev.0
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -205,19 +205,17 @@
|
|
|
205
205
|
// src/utils/helpers.js
|
|
206
206
|
function inlineSingleBackgroundEntry(_0) {
|
|
207
207
|
return __async(this, arguments, function* (entry, options = {}) {
|
|
208
|
-
const
|
|
208
|
+
const match = entry.match(/url\(["']?(.*?)["']?\)/);
|
|
209
|
+
const rawUrl = match == null ? void 0 : match[1];
|
|
209
210
|
const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry);
|
|
210
|
-
if (
|
|
211
|
-
const rawUrl = extractURL(entry);
|
|
212
|
-
if (!rawUrl) return entry;
|
|
211
|
+
if (rawUrl) {
|
|
213
212
|
const encodedUrl = safeEncodeURI(rawUrl);
|
|
214
213
|
if (bgCache.has(encodedUrl)) {
|
|
215
214
|
return options.skipInline ? void 0 : `url(${bgCache.get(encodedUrl)})`;
|
|
216
215
|
} else {
|
|
217
|
-
const
|
|
218
|
-
const dataUrl = yield fetchImage(encodedUrl, 3e3, crossOrigin);
|
|
216
|
+
const dataUrl = yield fetchImage(encodedUrl, { useProxy: options.useProxy });
|
|
219
217
|
bgCache.set(encodedUrl, dataUrl);
|
|
220
|
-
return options.skipInline ? void 0 : `url(${dataUrl})`;
|
|
218
|
+
return options.skipInline ? void 0 : `url("${dataUrl}")`;
|
|
221
219
|
}
|
|
222
220
|
}
|
|
223
221
|
if (isGradient || entry === "none") {
|
|
@@ -270,30 +268,86 @@
|
|
|
270
268
|
}
|
|
271
269
|
return url;
|
|
272
270
|
}
|
|
273
|
-
function
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
271
|
+
function fetchImage(src, { timeout = 3e3, useProxy = "" } = {}) {
|
|
272
|
+
function getCrossOriginMode(url) {
|
|
273
|
+
try {
|
|
274
|
+
const parsed = new URL(url, window.location.href);
|
|
275
|
+
return parsed.origin === window.location.origin ? "use-credentials" : "anonymous";
|
|
276
|
+
} catch (e) {
|
|
277
|
+
return "anonymous";
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function fetchWithFallback(url) {
|
|
281
|
+
return __async(this, null, function* () {
|
|
282
|
+
const fetchBlobAsDataURL = (fetchUrl) => fetch(fetchUrl, {
|
|
283
|
+
mode: "cors",
|
|
284
|
+
credentials: getCrossOriginMode(fetchUrl) === "use-credentials" ? "include" : "omit"
|
|
285
|
+
}).then((r) => r.blob()).then((blob) => new Promise((resolve, reject) => {
|
|
286
|
+
const reader = new FileReader();
|
|
287
|
+
reader.onloadend = () => {
|
|
288
|
+
const base64 = reader.result;
|
|
289
|
+
if (typeof base64 !== "string" || !base64.startsWith("data:image/")) {
|
|
290
|
+
reject(new Error("Invalid image data URL"));
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
resolve(base64);
|
|
294
|
+
};
|
|
295
|
+
reader.onerror = () => reject(new Error("FileReader error"));
|
|
296
|
+
reader.readAsDataURL(blob);
|
|
297
|
+
}));
|
|
298
|
+
try {
|
|
299
|
+
return yield fetchBlobAsDataURL(url);
|
|
300
|
+
} catch (e) {
|
|
301
|
+
if (useProxy && typeof useProxy === "string") {
|
|
302
|
+
const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url);
|
|
303
|
+
try {
|
|
304
|
+
return yield fetchBlobAsDataURL(proxied);
|
|
305
|
+
} catch (e2) {
|
|
306
|
+
console.error(`[SnapDOM - fetchImage] Proxy fallback failed for: ${url}`);
|
|
307
|
+
throw new Error("CORS restrictions prevented image capture (even via proxy)");
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
console.error(`[SnapDOM - fetchImage] No valid proxy URL provided for fallback: ${url}`);
|
|
311
|
+
throw new Error("Fetch fallback failed and no proxy provided");
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
const crossOriginValue = getCrossOriginMode(src);
|
|
317
|
+
console.log(`[SnapDOM - fetchImage] Start loading image: ${src} with crossOrigin=${crossOriginValue}`);
|
|
288
318
|
if (imageCache.has(src)) {
|
|
319
|
+
console.log(`[SnapDOM - fetchImage] Cache hit for: ${src}`);
|
|
289
320
|
return Promise.resolve(imageCache.get(src));
|
|
290
321
|
}
|
|
322
|
+
const isDataURI = src.startsWith("data:image/");
|
|
323
|
+
if (isDataURI) {
|
|
324
|
+
imageCache.set(src, src);
|
|
325
|
+
return Promise.resolve(src);
|
|
326
|
+
}
|
|
327
|
+
const isSVG = /\.svg(\?.*)?$/i.test(src);
|
|
328
|
+
if (isSVG) {
|
|
329
|
+
return (() => __async(null, null, function* () {
|
|
330
|
+
try {
|
|
331
|
+
const response = yield fetch(src, {
|
|
332
|
+
mode: "cors",
|
|
333
|
+
credentials: crossOriginValue === "use-credentials" ? "include" : "omit"
|
|
334
|
+
});
|
|
335
|
+
const svgText = yield response.text();
|
|
336
|
+
const encoded = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
|
|
337
|
+
imageCache.set(src, encoded);
|
|
338
|
+
return encoded;
|
|
339
|
+
} catch (e) {
|
|
340
|
+
return fetchWithFallback(src);
|
|
341
|
+
}
|
|
342
|
+
}))();
|
|
343
|
+
}
|
|
291
344
|
return new Promise((resolve, reject) => {
|
|
292
345
|
const timeoutId = setTimeout(() => {
|
|
346
|
+
console.log(`[SnapDOM - fetchImage] Timeout after ${timeout}ms for image: ${src}`);
|
|
293
347
|
reject(new Error("Image load timed out"));
|
|
294
348
|
}, timeout);
|
|
295
349
|
const image = new Image();
|
|
296
|
-
image.crossOrigin =
|
|
350
|
+
image.crossOrigin = crossOriginValue;
|
|
297
351
|
image.onload = () => __async(null, null, function* () {
|
|
298
352
|
clearTimeout(timeoutId);
|
|
299
353
|
try {
|
|
@@ -303,21 +357,30 @@
|
|
|
303
357
|
canvas.height = image.height;
|
|
304
358
|
const ctx = canvas.getContext("2d");
|
|
305
359
|
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
360
|
+
const dataURL = canvas.toDataURL("image/png");
|
|
361
|
+
imageCache.set(src, dataURL);
|
|
362
|
+
resolve(dataURL);
|
|
363
|
+
} catch (e) {
|
|
306
364
|
try {
|
|
307
|
-
const
|
|
308
|
-
imageCache.set(src,
|
|
309
|
-
resolve(
|
|
310
|
-
} catch (
|
|
311
|
-
reject(
|
|
365
|
+
const fallbackDataURL = yield fetchWithFallback(src);
|
|
366
|
+
imageCache.set(src, fallbackDataURL);
|
|
367
|
+
resolve(fallbackDataURL);
|
|
368
|
+
} catch (e2) {
|
|
369
|
+
reject(e2);
|
|
312
370
|
}
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
image.onerror = () => __async(null, null, function* () {
|
|
374
|
+
clearTimeout(timeoutId);
|
|
375
|
+
console.error(`[SnapDOM - fetchImage] Image failed to load: ${src}`);
|
|
376
|
+
try {
|
|
377
|
+
const fallbackDataURL = yield fetchWithFallback(src);
|
|
378
|
+
imageCache.set(src, fallbackDataURL);
|
|
379
|
+
resolve(fallbackDataURL);
|
|
313
380
|
} catch (e) {
|
|
314
381
|
reject(e);
|
|
315
382
|
}
|
|
316
383
|
});
|
|
317
|
-
image.onerror = (e) => {
|
|
318
|
-
clearTimeout(timeoutId);
|
|
319
|
-
reject(new Error("Failed to load image: " + (e.message || "Unknown error")));
|
|
320
|
-
};
|
|
321
384
|
image.src = src;
|
|
322
385
|
});
|
|
323
386
|
}
|
|
@@ -351,9 +414,9 @@
|
|
|
351
414
|
return cleaned.trim().replace(/\s{2,}/g, " ");
|
|
352
415
|
}
|
|
353
416
|
function safeEncodeURI(uri) {
|
|
417
|
+
if (/%[0-9A-Fa-f]{2}/.test(uri)) return uri;
|
|
354
418
|
try {
|
|
355
|
-
|
|
356
|
-
return encodeURI(newURI);
|
|
419
|
+
return encodeURI(uri);
|
|
357
420
|
} catch (e) {
|
|
358
421
|
return uri;
|
|
359
422
|
}
|
|
@@ -376,6 +439,20 @@
|
|
|
376
439
|
}
|
|
377
440
|
|
|
378
441
|
// src/modules/styles.js
|
|
442
|
+
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
443
|
+
var snapshotKeyCache = /* @__PURE__ */ new Map();
|
|
444
|
+
function snapshotComputedStyleFull(style) {
|
|
445
|
+
const result = {};
|
|
446
|
+
for (let i = 0; i < style.length; i++) {
|
|
447
|
+
const prop = style[i];
|
|
448
|
+
let val = style.getPropertyValue(prop);
|
|
449
|
+
if ((prop === "background-image" || prop === "content") && val.includes("url(") && !val.includes("data:")) {
|
|
450
|
+
val = "none";
|
|
451
|
+
}
|
|
452
|
+
result[prop] = val;
|
|
453
|
+
}
|
|
454
|
+
return result;
|
|
455
|
+
}
|
|
379
456
|
function inlineAllStyles(source, clone, styleMap, cache, compress) {
|
|
380
457
|
var _a;
|
|
381
458
|
if (source.tagName === "STYLE") return;
|
|
@@ -383,28 +460,71 @@
|
|
|
383
460
|
cache.set(source, getStyle(source));
|
|
384
461
|
}
|
|
385
462
|
const style = cache.get(source);
|
|
386
|
-
|
|
463
|
+
if (!snapshotCache.has(source)) {
|
|
464
|
+
const snapshot2 = snapshotComputedStyleFull(style);
|
|
465
|
+
snapshotCache.set(source, snapshot2);
|
|
466
|
+
}
|
|
467
|
+
const snapshot = snapshotCache.get(source);
|
|
468
|
+
const hash = Object.entries(snapshot).sort(([a], [b]) => a.localeCompare(b)).map(([prop, val]) => `${prop}:${val}`).join(";");
|
|
469
|
+
if (snapshotKeyCache.has(hash)) {
|
|
470
|
+
styleMap.set(clone, snapshotKeyCache.get(hash));
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
387
473
|
const tagName = ((_a = source.tagName) == null ? void 0 : _a.toLowerCase()) || "div";
|
|
388
474
|
const key = getStyleKey(snapshot, tagName, compress);
|
|
475
|
+
snapshotKeyCache.set(hash, key);
|
|
389
476
|
styleMap.set(clone, key);
|
|
390
477
|
}
|
|
391
478
|
|
|
392
479
|
// src/core/clone.js
|
|
393
|
-
function
|
|
394
|
-
|
|
395
|
-
|
|
480
|
+
function isSlotElement(node) {
|
|
481
|
+
return node.nodeType === Node.ELEMENT_NODE && node.tagName === "SLOT";
|
|
482
|
+
}
|
|
483
|
+
function deepClone(node, styleMap, styleCache, nodeMap, compress, options = {}, originalRoot) {
|
|
484
|
+
var _a, _b;
|
|
485
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
486
|
+
return node.cloneNode(true);
|
|
487
|
+
}
|
|
488
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return node.cloneNode(true);
|
|
489
|
+
if (node.getAttribute("data-capture") === "exclude") {
|
|
396
490
|
const spacer = document.createElement("div");
|
|
397
491
|
const rect = node.getBoundingClientRect();
|
|
398
492
|
spacer.style.cssText = `display: inline-block; width: ${rect.width}px; height: ${rect.height}px; visibility: hidden;`;
|
|
399
493
|
return spacer;
|
|
400
494
|
}
|
|
495
|
+
if (options.exclude && Array.isArray(options.exclude)) {
|
|
496
|
+
for (const selector of options.exclude) {
|
|
497
|
+
try {
|
|
498
|
+
if ((_a = node.matches) == null ? void 0 : _a.call(node, selector)) {
|
|
499
|
+
const spacer = document.createElement("div");
|
|
500
|
+
const rect = node.getBoundingClientRect();
|
|
501
|
+
spacer.style.cssText = `display: inline-block; width: ${rect.width}px; height: ${rect.height}px; visibility: hidden;`;
|
|
502
|
+
return spacer;
|
|
503
|
+
}
|
|
504
|
+
} catch (err) {
|
|
505
|
+
console.warn(`Invalid selector in exclude option: ${selector}`, err);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (typeof options.filter === "function") {
|
|
510
|
+
try {
|
|
511
|
+
if (!options.filter(node, originalRoot || node)) {
|
|
512
|
+
const spacer = document.createElement("div");
|
|
513
|
+
const rect = node.getBoundingClientRect();
|
|
514
|
+
spacer.style.cssText = `display: inline-block; width: ${rect.width}px; height: ${rect.height}px; visibility: hidden;`;
|
|
515
|
+
return spacer;
|
|
516
|
+
}
|
|
517
|
+
} catch (err) {
|
|
518
|
+
console.warn("Error in filter function:", err);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
401
521
|
if (node.tagName === "IFRAME") {
|
|
402
522
|
const fallback = document.createElement("div");
|
|
403
523
|
fallback.textContent = "";
|
|
404
524
|
fallback.style.cssText = `width: ${node.offsetWidth}px; height: ${node.offsetHeight}px; background-image: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`;
|
|
405
525
|
return fallback;
|
|
406
526
|
}
|
|
407
|
-
if (node.
|
|
527
|
+
if (node.getAttribute("data-capture") === "placeholder") {
|
|
408
528
|
const clone2 = node.cloneNode(false);
|
|
409
529
|
nodeMap.set(clone2, node);
|
|
410
530
|
inlineAllStyles(node, clone2, styleMap, styleCache, compress);
|
|
@@ -425,14 +545,6 @@
|
|
|
425
545
|
img.style.height = node.style.height || `${node.height}px`;
|
|
426
546
|
return img;
|
|
427
547
|
}
|
|
428
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
429
|
-
if ((_a = node.parentElement) == null ? void 0 : _a.shadowRoot) {
|
|
430
|
-
const tag = node.parentElement.tagName.toLowerCase();
|
|
431
|
-
if (customElements.get(tag)) return null;
|
|
432
|
-
}
|
|
433
|
-
return node.cloneNode(true);
|
|
434
|
-
}
|
|
435
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return node.cloneNode(true);
|
|
436
548
|
const clone = node.cloneNode(false);
|
|
437
549
|
nodeMap.set(clone, node);
|
|
438
550
|
if (node instanceof HTMLInputElement) {
|
|
@@ -443,8 +555,10 @@
|
|
|
443
555
|
if (node.checked) clone.setAttribute("checked", "");
|
|
444
556
|
}
|
|
445
557
|
} else if (node instanceof HTMLTextAreaElement) {
|
|
446
|
-
|
|
558
|
+
const rect = node.getBoundingClientRect();
|
|
447
559
|
clone.textContent = node.value;
|
|
560
|
+
clone.style.width = `${rect.width}px`;
|
|
561
|
+
clone.style.height = `${rect.height}px`;
|
|
448
562
|
} else if (node instanceof HTMLSelectElement) {
|
|
449
563
|
clone.value = node.value;
|
|
450
564
|
Array.from(clone.options).forEach((opt) => {
|
|
@@ -456,21 +570,71 @@
|
|
|
456
570
|
});
|
|
457
571
|
}
|
|
458
572
|
inlineAllStyles(node, clone, styleMap, styleCache, compress);
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
573
|
+
if (isSlotElement(node)) {
|
|
574
|
+
const assigned = ((_b = node.assignedNodes) == null ? void 0 : _b.call(node, { flatten: true })) || [];
|
|
575
|
+
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
576
|
+
const fragment = document.createDocumentFragment();
|
|
577
|
+
for (const child of nodesToClone) {
|
|
578
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
579
|
+
if (clonedChild) fragment.appendChild(clonedChild);
|
|
580
|
+
}
|
|
581
|
+
return fragment;
|
|
582
|
+
} else if (node instanceof HTMLTextAreaElement) {
|
|
583
|
+
} else {
|
|
584
|
+
const baseChildren = node.shadowRoot ? node.shadowRoot.childNodes : node.childNodes;
|
|
585
|
+
for (const child of baseChildren) {
|
|
586
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
587
|
+
if (clonedChild) clone.appendChild(clonedChild);
|
|
588
|
+
}
|
|
589
|
+
if (node.shadowRoot && node.childNodes.length > 0 && !node.shadowRoot.querySelector("slot")) {
|
|
590
|
+
const lightDomContent = document.createDocumentFragment();
|
|
591
|
+
for (const child of node.childNodes) {
|
|
592
|
+
const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node);
|
|
593
|
+
if (clonedChild) lightDomContent.appendChild(clonedChild);
|
|
594
|
+
}
|
|
595
|
+
clone.appendChild(lightDomContent);
|
|
596
|
+
}
|
|
470
597
|
}
|
|
471
598
|
return clone;
|
|
472
599
|
}
|
|
473
600
|
|
|
601
|
+
// src/modules/iconFonts.js
|
|
602
|
+
var defaultIconFonts = [
|
|
603
|
+
// /uicons/i,
|
|
604
|
+
/font\s*awesome/i,
|
|
605
|
+
/material\s*icons/i,
|
|
606
|
+
/ionicons/i,
|
|
607
|
+
/glyphicons/i,
|
|
608
|
+
/feather/i,
|
|
609
|
+
/bootstrap\s*icons/i,
|
|
610
|
+
/remix\s*icons/i,
|
|
611
|
+
/heroicons/i,
|
|
612
|
+
/layui/i,
|
|
613
|
+
/lucide/i
|
|
614
|
+
];
|
|
615
|
+
var userIconFonts = [];
|
|
616
|
+
function extendIconFonts(fonts) {
|
|
617
|
+
const list = Array.isArray(fonts) ? fonts : [fonts];
|
|
618
|
+
for (const f of list) {
|
|
619
|
+
if (f instanceof RegExp) {
|
|
620
|
+
userIconFonts.push(f);
|
|
621
|
+
} else if (typeof f === "string") {
|
|
622
|
+
userIconFonts.push(new RegExp(f, "i"));
|
|
623
|
+
} else {
|
|
624
|
+
console.warn("[snapdom] Ignored invalid iconFont value:", f);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function isIconFont(input) {
|
|
629
|
+
const text = typeof input === "string" ? input : "";
|
|
630
|
+
const candidates = [...defaultIconFonts, ...userIconFonts];
|
|
631
|
+
for (const rx of candidates) {
|
|
632
|
+
if (rx instanceof RegExp && rx.test(text)) return true;
|
|
633
|
+
}
|
|
634
|
+
if (/icon/i.test(text) || /glyph/i.test(text) || /symbols/i.test(text) || /feather/i.test(text) || /fontawesome/i.test(text)) return true;
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
|
|
474
638
|
// src/modules/fonts.js
|
|
475
639
|
function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
476
640
|
return __async(this, null, function* () {
|
|
@@ -513,7 +677,7 @@
|
|
|
513
677
|
});
|
|
514
678
|
}
|
|
515
679
|
function embedCustomFonts() {
|
|
516
|
-
return __async(this, arguments, function* ({
|
|
680
|
+
return __async(this, arguments, function* ({ preCached = false } = {}) {
|
|
517
681
|
if (resourceCache.has("fonts-embed-css")) {
|
|
518
682
|
if (preCached) {
|
|
519
683
|
const style = document.createElement("style");
|
|
@@ -530,6 +694,7 @@
|
|
|
530
694
|
const matches = Array.from(cssText.matchAll(importRegex));
|
|
531
695
|
for (const match of matches) {
|
|
532
696
|
const importUrl = match[1];
|
|
697
|
+
if (isIconFont(importUrl)) continue;
|
|
533
698
|
if (!isStylesheetLoaded(importUrl)) {
|
|
534
699
|
styleImports.push(importUrl);
|
|
535
700
|
}
|
|
@@ -542,7 +707,7 @@
|
|
|
542
707
|
try {
|
|
543
708
|
const res = yield fetch(link.href);
|
|
544
709
|
const cssText = yield res.text();
|
|
545
|
-
if (
|
|
710
|
+
if (isIconFont(link.href) || isIconFont(cssText)) continue;
|
|
546
711
|
const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
|
|
547
712
|
const inlinedCSS = yield Promise.all(
|
|
548
713
|
Array.from(cssText.matchAll(urlRegex)).map((match) => __async(null, null, function* () {
|
|
@@ -552,7 +717,7 @@
|
|
|
552
717
|
if (!url.startsWith("http") && !url.startsWith("data:")) {
|
|
553
718
|
url = new URL(url, link.href).href;
|
|
554
719
|
}
|
|
555
|
-
if (
|
|
720
|
+
if (isIconFont(url)) return null;
|
|
556
721
|
if (resourceCache.has(url)) {
|
|
557
722
|
processedFontURLs.add(url);
|
|
558
723
|
return { original: match[0], inlined: `url(${resourceCache.get(url)})` };
|
|
@@ -590,7 +755,8 @@
|
|
|
590
755
|
for (const rule of sheet.cssRules) {
|
|
591
756
|
if (rule.type === CSSRule.FONT_FACE_RULE) {
|
|
592
757
|
const src = rule.style.getPropertyValue("src");
|
|
593
|
-
|
|
758
|
+
const family = rule.style.getPropertyValue("font-family");
|
|
759
|
+
if (!src || isIconFont(family)) continue;
|
|
594
760
|
const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
|
|
595
761
|
let inlinedSrc = src;
|
|
596
762
|
const matches = Array.from(src.matchAll(urlRegex));
|
|
@@ -601,7 +767,7 @@
|
|
|
601
767
|
if (!url.startsWith("http") && !url.startsWith("data:")) {
|
|
602
768
|
url = new URL(url, sheet.href || location.href).href;
|
|
603
769
|
}
|
|
604
|
-
if (
|
|
770
|
+
if (isIconFont(url)) continue;
|
|
605
771
|
if (resourceCache.has(url)) {
|
|
606
772
|
processedFontURLs.add(url);
|
|
607
773
|
inlinedSrc = inlinedSrc.replace(match[0], `url(${resourceCache.get(url)})`);
|
|
@@ -624,7 +790,7 @@
|
|
|
624
790
|
}
|
|
625
791
|
}
|
|
626
792
|
finalCSS += `@font-face {
|
|
627
|
-
font-family: ${
|
|
793
|
+
font-family: ${family};
|
|
628
794
|
src: ${inlinedSrc};
|
|
629
795
|
font-style: ${rule.style.getPropertyValue("font-style") || "normal"};
|
|
630
796
|
font-weight: ${rule.style.getPropertyValue("font-weight") || "normal"};
|
|
@@ -639,6 +805,7 @@
|
|
|
639
805
|
}
|
|
640
806
|
for (const font of document.fonts) {
|
|
641
807
|
if (font.family && font.status === "loaded" && font._snapdomSrc) {
|
|
808
|
+
if (isIconFont(font.family)) continue;
|
|
642
809
|
let b64 = font._snapdomSrc;
|
|
643
810
|
if (!b64.startsWith("data:")) {
|
|
644
811
|
if (resourceCache.has(font._snapdomSrc)) {
|
|
@@ -684,7 +851,7 @@
|
|
|
684
851
|
}
|
|
685
852
|
|
|
686
853
|
// src/modules/pseudo.js
|
|
687
|
-
function inlinePseudoElements(source, clone, styleMap, styleCache, compress, embedFonts = false) {
|
|
854
|
+
function inlinePseudoElements(source, clone, styleMap, styleCache, compress, embedFonts = false, useProxy) {
|
|
688
855
|
return __async(this, null, function* () {
|
|
689
856
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
690
857
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
@@ -731,23 +898,21 @@
|
|
|
731
898
|
const snapshot = snapshotComputedStyle(style);
|
|
732
899
|
const key = getStyleKey(snapshot, "span", compress);
|
|
733
900
|
styleMap.set(pseudoEl, key);
|
|
734
|
-
const isIconFont2 = fontFamily
|
|
735
|
-
fontFamily
|
|
736
|
-
);
|
|
901
|
+
const isIconFont2 = isIconFont(fontFamily);
|
|
737
902
|
const cleanContent = parseContent(content);
|
|
738
|
-
if (
|
|
903
|
+
if (isIconFont2 && cleanContent.length === 1) {
|
|
739
904
|
const imgEl = document.createElement("img");
|
|
740
905
|
imgEl.src = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
741
|
-
imgEl.style =
|
|
906
|
+
imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
|
|
742
907
|
pseudoEl.appendChild(imgEl);
|
|
743
908
|
} else if (cleanContent.startsWith("url(")) {
|
|
744
909
|
const rawUrl = extractURL(cleanContent);
|
|
745
910
|
if (rawUrl && rawUrl.trim() !== "") {
|
|
746
911
|
try {
|
|
747
912
|
const imgEl = document.createElement("img");
|
|
748
|
-
const dataUrl = yield fetchImage(safeEncodeURI(rawUrl));
|
|
913
|
+
const dataUrl = yield fetchImage(safeEncodeURI(rawUrl, { useProxy }));
|
|
749
914
|
imgEl.src = dataUrl;
|
|
750
|
-
imgEl.style =
|
|
915
|
+
imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
|
|
751
916
|
pseudoEl.appendChild(imgEl);
|
|
752
917
|
} catch (e) {
|
|
753
918
|
console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
|
|
@@ -786,31 +951,67 @@
|
|
|
786
951
|
styleMap,
|
|
787
952
|
styleCache,
|
|
788
953
|
compress,
|
|
789
|
-
embedFonts
|
|
954
|
+
embedFonts,
|
|
955
|
+
useProxy
|
|
790
956
|
);
|
|
791
957
|
}
|
|
792
958
|
});
|
|
793
959
|
}
|
|
794
960
|
|
|
961
|
+
// src/modules/svgDefs.js
|
|
962
|
+
function inlineExternalDef(root) {
|
|
963
|
+
if (!root) return;
|
|
964
|
+
const defsSources = document.querySelectorAll("svg > defs");
|
|
965
|
+
if (!defsSources.length) return;
|
|
966
|
+
root.querySelectorAll("svg").forEach((svg) => {
|
|
967
|
+
const uses = svg.querySelectorAll("use");
|
|
968
|
+
if (!uses.length) return;
|
|
969
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
970
|
+
uses.forEach((use) => {
|
|
971
|
+
const href = use.getAttribute("xlink:href") || use.getAttribute("href");
|
|
972
|
+
if (href && href.startsWith("#")) usedIds.add(href.slice(1));
|
|
973
|
+
});
|
|
974
|
+
if (!usedIds.size) return;
|
|
975
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
976
|
+
for (const id of usedIds) {
|
|
977
|
+
for (const source of defsSources) {
|
|
978
|
+
const def = source.querySelector(`#${CSS.escape(id)}`);
|
|
979
|
+
if (def) {
|
|
980
|
+
defs.appendChild(def.cloneNode(true));
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
if (defs.childNodes.length) {
|
|
986
|
+
svg.insertBefore(defs, svg.firstChild);
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
|
|
795
991
|
// src/core/prepare.js
|
|
796
|
-
function prepareClone(
|
|
797
|
-
return __async(this,
|
|
992
|
+
function prepareClone(_0) {
|
|
993
|
+
return __async(this, arguments, function* (element, compress = false, embedFonts = false, options = {}) {
|
|
798
994
|
var _a;
|
|
799
995
|
const styleMap = /* @__PURE__ */ new Map();
|
|
800
996
|
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
801
997
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
802
998
|
let clone;
|
|
803
999
|
try {
|
|
804
|
-
clone = deepClone(element, styleMap, styleCache, nodeMap, compress);
|
|
1000
|
+
clone = deepClone(element, styleMap, styleCache, nodeMap, compress, options, element);
|
|
805
1001
|
} catch (e) {
|
|
806
1002
|
console.warn("deepClone failed:", e);
|
|
807
1003
|
throw e;
|
|
808
1004
|
}
|
|
809
1005
|
try {
|
|
810
|
-
yield inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts);
|
|
1006
|
+
yield inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts, options.useProxy);
|
|
811
1007
|
} catch (e) {
|
|
812
1008
|
console.warn("inlinePseudoElements failed:", e);
|
|
813
1009
|
}
|
|
1010
|
+
try {
|
|
1011
|
+
inlineExternalDef(clone);
|
|
1012
|
+
} catch (e) {
|
|
1013
|
+
console.warn("inlineExternalDef failed:", e);
|
|
1014
|
+
}
|
|
814
1015
|
let classCSS = "";
|
|
815
1016
|
if (compress) {
|
|
816
1017
|
const keyToClass = generateCSSClasses(styleMap);
|
|
@@ -863,6 +1064,12 @@
|
|
|
863
1064
|
clone.style.clear = "none";
|
|
864
1065
|
clone.style.transform = transform || "";
|
|
865
1066
|
}
|
|
1067
|
+
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
1068
|
+
if (originalNode.tagName === "PRE") {
|
|
1069
|
+
cloneNode.style.marginTop = "0";
|
|
1070
|
+
cloneNode.style.marginBlockStart = "0";
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
866
1073
|
return { clone, classCSS, styleCache };
|
|
867
1074
|
});
|
|
868
1075
|
}
|
|
@@ -874,8 +1081,7 @@
|
|
|
874
1081
|
const processImg = (img) => __async(null, null, function* () {
|
|
875
1082
|
const src = img.src;
|
|
876
1083
|
try {
|
|
877
|
-
const
|
|
878
|
-
const dataUrl = yield fetchImage(src, 3e3, crossOrigin);
|
|
1084
|
+
const dataUrl = yield fetchImage(src, { useProxy: options.useProxy });
|
|
879
1085
|
img.src = dataUrl;
|
|
880
1086
|
if (!img.width) img.width = img.naturalWidth || 100;
|
|
881
1087
|
if (!img.height) img.height = img.naturalHeight || 100;
|
|
@@ -897,30 +1103,32 @@
|
|
|
897
1103
|
function inlineBackgroundImages(_0, _1, _2) {
|
|
898
1104
|
return __async(this, arguments, function* (source, clone, styleCache, options = {}) {
|
|
899
1105
|
const queue = [[source, clone]];
|
|
1106
|
+
const imageProps = [
|
|
1107
|
+
"background-image",
|
|
1108
|
+
"mask",
|
|
1109
|
+
"mask-image",
|
|
1110
|
+
"-webkit-mask-image",
|
|
1111
|
+
"mask-source",
|
|
1112
|
+
"mask-box-image-source",
|
|
1113
|
+
"mask-border-source",
|
|
1114
|
+
"-webkit-mask-box-image-source"
|
|
1115
|
+
];
|
|
900
1116
|
while (queue.length) {
|
|
901
1117
|
const [srcNode, cloneNode] = queue.shift();
|
|
902
1118
|
const style = styleCache.get(srcNode) || getStyle(srcNode);
|
|
903
1119
|
if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
|
|
904
|
-
const
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
const
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
|
|
1120
|
+
for (const prop of imageProps) {
|
|
1121
|
+
const val = style.getPropertyValue(prop);
|
|
1122
|
+
if (!val || val === "none") continue;
|
|
1123
|
+
const splits = splitBackgroundImage(val);
|
|
1124
|
+
const inlined = yield Promise.all(
|
|
1125
|
+
splits.map((entry) => inlineSingleBackgroundEntry(entry, options))
|
|
1126
|
+
);
|
|
1127
|
+
if (inlined.some((p) => p && p !== "none" && !/^url\(undefined/.test(p))) {
|
|
1128
|
+
cloneNode.style.setProperty(prop, inlined.join(", "));
|
|
911
1129
|
}
|
|
912
|
-
continue;
|
|
913
|
-
}
|
|
914
|
-
const bgSplits = splitBackgroundImage(bg);
|
|
915
|
-
const newBgParts = yield Promise.all(
|
|
916
|
-
bgSplits.map((entry) => inlineSingleBackgroundEntry(entry, options))
|
|
917
|
-
);
|
|
918
|
-
const hasRealBg = newBgParts.some(
|
|
919
|
-
(p) => p && p !== "none" && !/^url\(undefined\)/.test(p)
|
|
920
|
-
);
|
|
921
|
-
if (hasRealBg) {
|
|
922
|
-
cloneNode.style.backgroundImage = newBgParts.join(", ");
|
|
923
1130
|
}
|
|
1131
|
+
const bgColor = style.getPropertyValue("background-color");
|
|
924
1132
|
if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") {
|
|
925
1133
|
cloneNode.style.backgroundColor = bgColor;
|
|
926
1134
|
}
|
|
@@ -937,14 +1145,13 @@
|
|
|
937
1145
|
function captureDOM(_0) {
|
|
938
1146
|
return __async(this, arguments, function* (element, options = {}) {
|
|
939
1147
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
940
|
-
|
|
941
|
-
const { compress = true, embedFonts = false, fast = true, scale = 1 } = options;
|
|
1148
|
+
const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "" } = options;
|
|
942
1149
|
let clone, classCSS, styleCache;
|
|
943
1150
|
let fontsCSS = "";
|
|
944
1151
|
let baseCSS = "";
|
|
945
1152
|
let dataURL;
|
|
946
1153
|
let svgString;
|
|
947
|
-
({ clone, classCSS, styleCache } = yield prepareClone(element, compress, embedFonts));
|
|
1154
|
+
({ clone, classCSS, styleCache } = yield prepareClone(element, compress, embedFonts, options));
|
|
948
1155
|
yield new Promise((resolve) => {
|
|
949
1156
|
idle(() => __async(null, null, function* () {
|
|
950
1157
|
yield inlineImages(clone, options);
|
|
@@ -960,7 +1167,7 @@
|
|
|
960
1167
|
if (embedFonts) {
|
|
961
1168
|
yield new Promise((resolve) => {
|
|
962
1169
|
idle(() => __async(null, null, function* () {
|
|
963
|
-
fontsCSS = yield embedCustomFonts(
|
|
1170
|
+
fontsCSS = yield embedCustomFonts();
|
|
964
1171
|
resolve();
|
|
965
1172
|
}), { fast });
|
|
966
1173
|
});
|
|
@@ -1073,10 +1280,27 @@
|
|
|
1073
1280
|
return canvas;
|
|
1074
1281
|
});
|
|
1075
1282
|
}
|
|
1076
|
-
function toBlob(
|
|
1077
|
-
return __async(this,
|
|
1078
|
-
|
|
1079
|
-
|
|
1283
|
+
function toBlob(_0) {
|
|
1284
|
+
return __async(this, arguments, function* (url, {
|
|
1285
|
+
type = "svg",
|
|
1286
|
+
scale = 1,
|
|
1287
|
+
backgroundColor = "#fff",
|
|
1288
|
+
quality
|
|
1289
|
+
} = {}) {
|
|
1290
|
+
const mime = {
|
|
1291
|
+
jpg: "image/jpeg",
|
|
1292
|
+
jpeg: "image/jpeg",
|
|
1293
|
+
png: "image/png",
|
|
1294
|
+
webp: "image/webp"
|
|
1295
|
+
}[type] || "image/png";
|
|
1296
|
+
if (type === "svg") {
|
|
1297
|
+
const svgText = decodeURIComponent(url.split(",")[1]);
|
|
1298
|
+
return new Blob([svgText], { type: "image/svg+xml" });
|
|
1299
|
+
}
|
|
1300
|
+
const canvas = yield createBackground(url, { dpr: 1, scale }, backgroundColor);
|
|
1301
|
+
return new Promise((resolve) => {
|
|
1302
|
+
canvas.toBlob((blob) => resolve(blob), `${mime}`, quality);
|
|
1303
|
+
});
|
|
1080
1304
|
});
|
|
1081
1305
|
}
|
|
1082
1306
|
function createBackground(_0, _1, _2) {
|
|
@@ -1136,6 +1360,9 @@
|
|
|
1136
1360
|
return __async(this, arguments, function* (element, options = {}) {
|
|
1137
1361
|
options = __spreadValues({ scale: 1 }, options);
|
|
1138
1362
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1363
|
+
if (options.iconFonts) {
|
|
1364
|
+
extendIconFonts(options.iconFonts);
|
|
1365
|
+
}
|
|
1139
1366
|
return yield snapdom.capture(element, options);
|
|
1140
1367
|
});
|
|
1141
1368
|
}
|
|
@@ -1149,7 +1376,7 @@
|
|
|
1149
1376
|
toRaw: () => url,
|
|
1150
1377
|
toImg: () => toImg(url, { dpr, scale }),
|
|
1151
1378
|
toCanvas: () => toCanvas(url, { dpr, scale }),
|
|
1152
|
-
toBlob: () => toBlob(url),
|
|
1379
|
+
toBlob: (options2) => toBlob(url, __spreadValues({ dpr, scale }, options2)),
|
|
1153
1380
|
toPng: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "png"),
|
|
1154
1381
|
toJpg: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "jpeg"),
|
|
1155
1382
|
toWebp: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "webp"),
|
|
@@ -1166,7 +1393,7 @@
|
|
|
1166
1393
|
return (yield snapdom.capture(el, options)).toCanvas();
|
|
1167
1394
|
});
|
|
1168
1395
|
snapdom.toBlob = (el, options) => __async(null, null, function* () {
|
|
1169
|
-
return (yield snapdom.capture(el, options)).toBlob();
|
|
1396
|
+
return (yield snapdom.capture(el, options)).toBlob(options);
|
|
1170
1397
|
});
|
|
1171
1398
|
snapdom.toPng = (el, options) => __async(null, null, function* () {
|
|
1172
1399
|
return (yield snapdom.capture(el, options)).toPng(options);
|
|
@@ -1194,7 +1421,7 @@
|
|
|
1194
1421
|
// src/api/preCache.js
|
|
1195
1422
|
function preCache() {
|
|
1196
1423
|
return __async(this, arguments, function* (root = document, options = {}) {
|
|
1197
|
-
const { embedFonts = true, reset = false
|
|
1424
|
+
const { embedFonts = true, reset = false } = options;
|
|
1198
1425
|
if (reset) {
|
|
1199
1426
|
imageCache.clear();
|
|
1200
1427
|
bgCache.clear();
|
|
@@ -1213,9 +1440,8 @@
|
|
|
1213
1440
|
for (const img of imgEls) {
|
|
1214
1441
|
const src = img.src;
|
|
1215
1442
|
if (!imageCache.has(src)) {
|
|
1216
|
-
const crossOrigin = crossOriginFn ? crossOriginFn(src) : "anonymous";
|
|
1217
1443
|
promises.push(
|
|
1218
|
-
fetchImage(src,
|
|
1444
|
+
fetchImage(src, { useProxy: options.useProxy }).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
|
|
1219
1445
|
})
|
|
1220
1446
|
);
|
|
1221
1447
|
}
|
|
@@ -1228,7 +1454,7 @@
|
|
|
1228
1454
|
const isUrl = entry.startsWith("url(");
|
|
1229
1455
|
if (isUrl) {
|
|
1230
1456
|
promises.push(
|
|
1231
|
-
inlineSingleBackgroundEntry(entry,
|
|
1457
|
+
inlineSingleBackgroundEntry(entry, options).catch(() => {
|
|
1232
1458
|
})
|
|
1233
1459
|
);
|
|
1234
1460
|
}
|
|
@@ -1236,7 +1462,7 @@
|
|
|
1236
1462
|
}
|
|
1237
1463
|
}
|
|
1238
1464
|
if (embedFonts) {
|
|
1239
|
-
yield embedCustomFonts({
|
|
1465
|
+
yield embedCustomFonts({ preCached: true });
|
|
1240
1466
|
}
|
|
1241
1467
|
yield Promise.all(promises);
|
|
1242
1468
|
});
|