@zumer/snapdom 1.9.6-dev.0 → 1.9.6-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/snapdom.js +386 -218
- package/dist/snapdom.min.js +8 -8
- package/dist/snapdom.min.mjs +13 -13
- package/dist/snapdom.mjs +137 -99
- package/package.json +4 -1
- package/types/snapdom.d.ts +1 -1
package/dist/snapdom.mjs
CHANGED
|
@@ -440,120 +440,155 @@ function isSlotElement(node) {
|
|
|
440
440
|
return node.nodeType === Node.ELEMENT_NODE && node.tagName === "SLOT";
|
|
441
441
|
}
|
|
442
442
|
function deepClone(node, compress, options = {}, originalRoot) {
|
|
443
|
-
if (node
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
443
|
+
if (!node) {
|
|
444
|
+
throw new Error("Invalid node: node is null or undefined");
|
|
445
|
+
}
|
|
446
|
+
try {
|
|
447
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
448
|
+
return node.cloneNode(true);
|
|
449
|
+
}
|
|
450
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
451
|
+
return node.cloneNode(true);
|
|
452
|
+
}
|
|
453
|
+
if (node.getAttribute("data-capture") === "exclude") {
|
|
454
|
+
const spacer = document.createElement("div");
|
|
455
|
+
const rect = node.getBoundingClientRect();
|
|
456
|
+
spacer.style.cssText = `display: inline-block; width: ${rect.width}px; height: ${rect.height}px; visibility: hidden;`;
|
|
457
|
+
return spacer;
|
|
458
|
+
}
|
|
459
|
+
if (options.exclude && Array.isArray(options.exclude)) {
|
|
460
|
+
for (const selector of options.exclude) {
|
|
461
|
+
try {
|
|
462
|
+
if (node.matches?.(selector)) {
|
|
463
|
+
const spacer = document.createElement("div");
|
|
464
|
+
const rect = node.getBoundingClientRect();
|
|
465
|
+
spacer.style.cssText = `display: inline-block; width: ${rect.width}px; height: ${rect.height}px; visibility: hidden;`;
|
|
466
|
+
return spacer;
|
|
467
|
+
}
|
|
468
|
+
} catch (err) {
|
|
469
|
+
console.warn(`Invalid selector in exclude option: ${selector}`, err);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (typeof options.filter === "function") {
|
|
455
474
|
try {
|
|
456
|
-
if (
|
|
475
|
+
if (!options.filter(node, originalRoot || node)) {
|
|
457
476
|
const spacer = document.createElement("div");
|
|
458
477
|
const rect = node.getBoundingClientRect();
|
|
459
478
|
spacer.style.cssText = `display: inline-block; width: ${rect.width}px; height: ${rect.height}px; visibility: hidden;`;
|
|
460
479
|
return spacer;
|
|
461
480
|
}
|
|
462
481
|
} catch (err) {
|
|
463
|
-
console.warn(
|
|
482
|
+
console.warn("Error in filter function:", err);
|
|
464
483
|
}
|
|
465
484
|
}
|
|
466
|
-
|
|
467
|
-
|
|
485
|
+
if (node.tagName === "IFRAME") {
|
|
486
|
+
const fallback = document.createElement("div");
|
|
487
|
+
fallback.textContent = "";
|
|
488
|
+
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;`;
|
|
489
|
+
return fallback;
|
|
490
|
+
}
|
|
491
|
+
if (node.getAttribute("data-capture") === "placeholder") {
|
|
492
|
+
const clone2 = node.cloneNode(false);
|
|
493
|
+
cache.preNodeMap.set(clone2, node);
|
|
494
|
+
inlineAllStyles(node, clone2, compress);
|
|
495
|
+
const placeholder = document.createElement("div");
|
|
496
|
+
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
497
|
+
placeholder.style.cssText = `color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;`;
|
|
498
|
+
clone2.appendChild(placeholder);
|
|
499
|
+
return clone2;
|
|
500
|
+
}
|
|
501
|
+
if (node.tagName === "CANVAS") {
|
|
502
|
+
const dataURL = node.toDataURL();
|
|
503
|
+
const img = document.createElement("img");
|
|
504
|
+
img.src = dataURL;
|
|
505
|
+
img.width = node.width;
|
|
506
|
+
img.height = node.height;
|
|
507
|
+
img.style.display = "inline-block";
|
|
508
|
+
img.style.width = node.style.width || `${node.width}px`;
|
|
509
|
+
img.style.height = node.style.height || `${node.height}px`;
|
|
510
|
+
return img;
|
|
511
|
+
}
|
|
512
|
+
let clone;
|
|
468
513
|
try {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
spacer.style.cssText = `display: inline-block; width: ${rect.width}px; height: ${rect.height}px; visibility: hidden;`;
|
|
473
|
-
return spacer;
|
|
514
|
+
clone = node.cloneNode(false);
|
|
515
|
+
if (!clone) {
|
|
516
|
+
throw new Error("Failed to clone node: cloneNode returned null");
|
|
474
517
|
}
|
|
518
|
+
cache.preNodeMap.set(clone, node);
|
|
475
519
|
} catch (err) {
|
|
476
|
-
console.
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
if (node.tagName === "IFRAME") {
|
|
480
|
-
const fallback = document.createElement("div");
|
|
481
|
-
fallback.textContent = "";
|
|
482
|
-
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;`;
|
|
483
|
-
return fallback;
|
|
484
|
-
}
|
|
485
|
-
if (node.getAttribute("data-capture") === "placeholder") {
|
|
486
|
-
const clone2 = node.cloneNode(false);
|
|
487
|
-
cache.preNodeMap.set(clone2, node);
|
|
488
|
-
inlineAllStyles(node, clone2, compress);
|
|
489
|
-
const placeholder = document.createElement("div");
|
|
490
|
-
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
491
|
-
placeholder.style.cssText = `color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;`;
|
|
492
|
-
clone2.appendChild(placeholder);
|
|
493
|
-
return clone2;
|
|
494
|
-
}
|
|
495
|
-
if (node.tagName === "CANVAS") {
|
|
496
|
-
const dataURL = node.toDataURL();
|
|
497
|
-
const img = document.createElement("img");
|
|
498
|
-
img.src = dataURL;
|
|
499
|
-
img.width = node.width;
|
|
500
|
-
img.height = node.height;
|
|
501
|
-
img.style.display = "inline-block";
|
|
502
|
-
img.style.width = node.style.width || `${node.width}px`;
|
|
503
|
-
img.style.height = node.style.height || `${node.height}px`;
|
|
504
|
-
return img;
|
|
505
|
-
}
|
|
506
|
-
const clone = node.cloneNode(false);
|
|
507
|
-
cache.preNodeMap.set(clone, node);
|
|
508
|
-
if (node instanceof HTMLInputElement) {
|
|
509
|
-
clone.value = node.value;
|
|
510
|
-
clone.setAttribute("value", node.value);
|
|
511
|
-
if (node.checked !== void 0) {
|
|
512
|
-
clone.checked = node.checked;
|
|
513
|
-
if (node.checked) clone.setAttribute("checked", "");
|
|
520
|
+
console.error("[Snapdom] Failed to clone node:", node, err);
|
|
521
|
+
throw err;
|
|
514
522
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
clone.value = node.value;
|
|
522
|
-
Array.from(clone.options).forEach((opt) => {
|
|
523
|
-
if (opt.value === node.value) {
|
|
524
|
-
opt.setAttribute("selected", "");
|
|
525
|
-
} else {
|
|
526
|
-
opt.removeAttribute("selected");
|
|
523
|
+
if (node instanceof HTMLInputElement) {
|
|
524
|
+
clone.value = node.value;
|
|
525
|
+
clone.setAttribute("value", node.value);
|
|
526
|
+
if (node.checked !== void 0) {
|
|
527
|
+
clone.checked = node.checked;
|
|
528
|
+
if (node.checked) clone.setAttribute("checked", "");
|
|
527
529
|
}
|
|
528
|
-
})
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const baseChildren = node.shadowRoot ? node.shadowRoot.childNodes : node.childNodes;
|
|
543
|
-
for (const child of baseChildren) {
|
|
544
|
-
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
545
|
-
if (clonedChild) clone.appendChild(clonedChild);
|
|
530
|
+
} else if (node instanceof HTMLTextAreaElement) {
|
|
531
|
+
const rect = node.getBoundingClientRect();
|
|
532
|
+
clone.textContent = node.value;
|
|
533
|
+
clone.style.width = `${rect.width}px`;
|
|
534
|
+
clone.style.height = `${rect.height}px`;
|
|
535
|
+
} else if (node instanceof HTMLSelectElement) {
|
|
536
|
+
clone.value = node.value;
|
|
537
|
+
Array.from(clone.options).forEach((opt) => {
|
|
538
|
+
if (opt.value === node.value) {
|
|
539
|
+
opt.setAttribute("selected", "");
|
|
540
|
+
} else {
|
|
541
|
+
opt.removeAttribute("selected");
|
|
542
|
+
}
|
|
543
|
+
});
|
|
546
544
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
const
|
|
551
|
-
|
|
545
|
+
inlineAllStyles(node, clone, compress);
|
|
546
|
+
try {
|
|
547
|
+
if (isSlotElement(node)) {
|
|
548
|
+
const assigned = node.assignedNodes?.({ flatten: true }) || [];
|
|
549
|
+
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
550
|
+
const fragment = document.createDocumentFragment();
|
|
551
|
+
for (const child of nodesToClone) {
|
|
552
|
+
try {
|
|
553
|
+
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
554
|
+
if (clonedChild) fragment.appendChild(clonedChild);
|
|
555
|
+
} catch (err) {
|
|
556
|
+
console.warn("[Snapdom] Failed to clone slot content:", child, err);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return fragment;
|
|
560
|
+
} else {
|
|
561
|
+
const baseChildren = node.shadowRoot ? node.shadowRoot.childNodes : node.childNodes;
|
|
562
|
+
for (const child of baseChildren) {
|
|
563
|
+
try {
|
|
564
|
+
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
565
|
+
if (clonedChild) clone.appendChild(clonedChild);
|
|
566
|
+
} catch (err) {
|
|
567
|
+
console.warn("[Snapdom] Failed to clone child node:", child, err);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (node.shadowRoot && node.childNodes.length > 0 && !node.shadowRoot.querySelector("slot")) {
|
|
571
|
+
const lightDomContent = document.createDocumentFragment();
|
|
572
|
+
for (const child of node.childNodes) {
|
|
573
|
+
try {
|
|
574
|
+
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
575
|
+
if (clonedChild) lightDomContent.appendChild(clonedChild);
|
|
576
|
+
} catch (err) {
|
|
577
|
+
console.warn("[Snapdom] Failed to clone light DOM child:", child, err);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
clone.appendChild(lightDomContent);
|
|
581
|
+
}
|
|
552
582
|
}
|
|
553
|
-
|
|
583
|
+
} catch (err) {
|
|
584
|
+
console.error("[Snapdom] Error cloning children:", err);
|
|
585
|
+
throw err;
|
|
554
586
|
}
|
|
587
|
+
return clone;
|
|
588
|
+
} catch (error) {
|
|
589
|
+
console.error("[Snapdom] Error in deepClone:", error);
|
|
590
|
+
throw error;
|
|
555
591
|
}
|
|
556
|
-
return clone;
|
|
557
592
|
}
|
|
558
593
|
|
|
559
594
|
// src/modules/iconFonts.js
|
|
@@ -940,7 +975,8 @@ function inlineExternalDef(root) {
|
|
|
940
975
|
|
|
941
976
|
// src/core/prepare.js
|
|
942
977
|
async function prepareClone(element, compress = false, embedFonts = false, options = {}) {
|
|
943
|
-
let clone
|
|
978
|
+
let clone;
|
|
979
|
+
let classCSS = "";
|
|
944
980
|
try {
|
|
945
981
|
clone = deepClone(element, compress, options, element);
|
|
946
982
|
} catch (e) {
|
|
@@ -1247,8 +1283,10 @@ async function createBackground(url, { dpr = 1, scale = 1 }, backgroundColor) {
|
|
|
1247
1283
|
ctx.drawImage(baseCanvas, 0, 0);
|
|
1248
1284
|
return temp;
|
|
1249
1285
|
}
|
|
1250
|
-
async function toRasterImg(url, { dpr = 1, scale = 1, backgroundColor
|
|
1251
|
-
const
|
|
1286
|
+
async function toRasterImg(url, { dpr = 1, scale = 1, backgroundColor, quality }, format = "png") {
|
|
1287
|
+
const defaultBg = ["jpg", "jpeg", "webp"].includes(format) ? "#fff" : void 0;
|
|
1288
|
+
const finalBg = backgroundColor ?? defaultBg;
|
|
1289
|
+
const canvas = await createBackground(url, { dpr, scale }, finalBg);
|
|
1252
1290
|
const img = new Image();
|
|
1253
1291
|
img.src = canvas.toDataURL(`image/${format}`, quality);
|
|
1254
1292
|
await img.decode();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zumer/snapdom",
|
|
3
|
-
"version": "1.9.6-dev.
|
|
3
|
+
"version": "1.9.6-dev.1",
|
|
4
4
|
"description": "snapDOM captures HTML elements to images with exceptional speed and accuracy.",
|
|
5
5
|
"main": "dist/snapdom.js",
|
|
6
6
|
"module": "dist/snapdom.mjs",
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
"bump": "npx @zumerbox/bump && npx @zumerbox/changelog",
|
|
16
16
|
"prebuild": "git add CHANGELOG.md && git commit -m \"Bumped version\" && git push --follow-tags"
|
|
17
17
|
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">= 20"
|
|
20
|
+
},
|
|
18
21
|
"repository": {
|
|
19
22
|
"type": "git",
|
|
20
23
|
"url": "git+https://github.com/zumerlab/snapdom.git"
|
package/types/snapdom.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ declare module "@zumer/snapdom" {
|
|
|
23
23
|
toRaw(): string;
|
|
24
24
|
toImg(): Promise<HTMLImageElement>;
|
|
25
25
|
toCanvas(): Promise<HTMLCanvasElement>;
|
|
26
|
-
toBlob(): Promise<Blob>;
|
|
26
|
+
toBlob(options?: SnapOptions): Promise<Blob>;
|
|
27
27
|
toPng(options?: SnapOptions): Promise<HTMLImageElement>;
|
|
28
28
|
toJpg(options?: SnapOptions): Promise<HTMLImageElement>;
|
|
29
29
|
toWebp(options?: SnapOptions): Promise<HTMLImageElement>;
|