@zumer/snapdom 1.2.2 → 1.3.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 +2 -2
- package/dist/snapdom.js +98 -39
- package/dist/snapdom.min.js +3 -3
- package/dist/snapdom.min.mjs +3 -3
- package/dist/snapdom.mjs +98 -39
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ yarn add @zumer/snapdom
|
|
|
39
39
|
### CDN
|
|
40
40
|
|
|
41
41
|
```html
|
|
42
|
-
<script src="https://
|
|
42
|
+
<script src="https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.min.js"></script>
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
### Script tag (local)
|
|
@@ -58,7 +58,7 @@ import { snapdom } from './snapdom.mjs';
|
|
|
58
58
|
|
|
59
59
|
```html
|
|
60
60
|
<script type="module">
|
|
61
|
-
import { snapdom } from 'https://
|
|
61
|
+
import { snapdom } from 'https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.mjs';
|
|
62
62
|
</script>
|
|
63
63
|
```
|
|
64
64
|
|
package/dist/snapdom.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.
|
|
4
|
+
* v.1.3.0
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -186,9 +186,15 @@
|
|
|
186
186
|
}
|
|
187
187
|
return clean;
|
|
188
188
|
}
|
|
189
|
-
function
|
|
190
|
-
const
|
|
191
|
-
|
|
189
|
+
function extractURL(value) {
|
|
190
|
+
const urlStart = value.indexOf("url(");
|
|
191
|
+
if (urlStart === -1) return null;
|
|
192
|
+
let url = value.slice(urlStart + 4).trim();
|
|
193
|
+
if (url.endsWith(")")) url = url.slice(0, -1).trim();
|
|
194
|
+
if (url.startsWith('"') && url.endsWith('"') || url.startsWith("'") && url.endsWith("'")) {
|
|
195
|
+
url = url.slice(1, -1);
|
|
196
|
+
}
|
|
197
|
+
return url;
|
|
192
198
|
}
|
|
193
199
|
function isIconFont(familyOrUrl) {
|
|
194
200
|
const iconFontPatterns = [
|
|
@@ -221,7 +227,7 @@
|
|
|
221
227
|
canvas.width = image.width;
|
|
222
228
|
canvas.height = image.height;
|
|
223
229
|
const ctx = canvas.getContext("2d");
|
|
224
|
-
ctx.drawImage(image, 0, 0);
|
|
230
|
+
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
225
231
|
try {
|
|
226
232
|
const dataURL = canvas.toDataURL("image/png");
|
|
227
233
|
imageCache.set(src, dataURL);
|
|
@@ -309,6 +315,26 @@
|
|
|
309
315
|
if (node.nodeType !== Node.ELEMENT_NODE) return node.cloneNode(true);
|
|
310
316
|
const clone = node.cloneNode(false);
|
|
311
317
|
nodeMap.set(clone, node);
|
|
318
|
+
if (node instanceof HTMLInputElement) {
|
|
319
|
+
clone.value = node.value;
|
|
320
|
+
clone.setAttribute("value", node.value);
|
|
321
|
+
if (node.checked !== void 0) {
|
|
322
|
+
clone.checked = node.checked;
|
|
323
|
+
if (node.checked) clone.setAttribute("checked", "");
|
|
324
|
+
}
|
|
325
|
+
} else if (node instanceof HTMLTextAreaElement) {
|
|
326
|
+
clone.value = node.value;
|
|
327
|
+
clone.textContent = node.value;
|
|
328
|
+
} else if (node instanceof HTMLSelectElement) {
|
|
329
|
+
clone.value = node.value;
|
|
330
|
+
Array.from(clone.options).forEach((opt) => {
|
|
331
|
+
if (opt.value === node.value) {
|
|
332
|
+
opt.setAttribute("selected", "");
|
|
333
|
+
} else {
|
|
334
|
+
opt.removeAttribute("selected");
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
312
338
|
inlineAllStyles(node, clone, styleMap, styleCache, compress);
|
|
313
339
|
const frag = document.createDocumentFragment();
|
|
314
340
|
node.childNodes.forEach((child) => {
|
|
@@ -362,10 +388,17 @@
|
|
|
362
388
|
const urlRegex = /url\(([^)]+)\)/g;
|
|
363
389
|
const inlinedCSS = await Promise.all(
|
|
364
390
|
Array.from(cssText.matchAll(urlRegex)).map(async (match) => {
|
|
365
|
-
let
|
|
391
|
+
let rawUrl = extractURL(match[0]);
|
|
392
|
+
if (!rawUrl) return null;
|
|
393
|
+
let url = rawUrl;
|
|
366
394
|
if (!url.startsWith("http")) {
|
|
367
395
|
url = new URL(url, link.href).href;
|
|
368
396
|
}
|
|
397
|
+
try {
|
|
398
|
+
url = encodeURI(url);
|
|
399
|
+
} catch (e) {
|
|
400
|
+
console.warn("[snapdom] Failed to encode font URL:", url);
|
|
401
|
+
}
|
|
369
402
|
if (ignoreIconFonts && isIconFont(url)) {
|
|
370
403
|
return null;
|
|
371
404
|
}
|
|
@@ -429,19 +462,27 @@
|
|
|
429
462
|
const snapshot = snapshotComputedStyle(style);
|
|
430
463
|
const key = getStyleKey(snapshot, "span", compress);
|
|
431
464
|
styleMap.set(pseudoEl, key);
|
|
432
|
-
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(
|
|
465
|
+
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(
|
|
466
|
+
fontFamily
|
|
467
|
+
);
|
|
433
468
|
let cleanContent = parseContent(content);
|
|
434
469
|
if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
|
|
435
470
|
const imgEl = document.createElement("img");
|
|
436
|
-
imgEl.src = await iconToImage(
|
|
471
|
+
imgEl.src = await iconToImage(
|
|
472
|
+
cleanContent,
|
|
473
|
+
fontFamily,
|
|
474
|
+
fontWeight,
|
|
475
|
+
fontSize,
|
|
476
|
+
color
|
|
477
|
+
);
|
|
437
478
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
438
479
|
pseudoEl.appendChild(imgEl);
|
|
439
480
|
} else if (cleanContent.startsWith("url(")) {
|
|
440
|
-
const
|
|
441
|
-
if (
|
|
481
|
+
const rawUrl = extractURL(cleanContent);
|
|
482
|
+
if (rawUrl) {
|
|
442
483
|
try {
|
|
443
484
|
const imgEl = document.createElement("img");
|
|
444
|
-
const dataUrl = await fetchImage(
|
|
485
|
+
const dataUrl = await fetchImage(encodeURI(rawUrl));
|
|
445
486
|
imgEl.src = dataUrl;
|
|
446
487
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
447
488
|
pseudoEl.appendChild(imgEl);
|
|
@@ -449,9 +490,22 @@
|
|
|
449
490
|
console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
|
|
450
491
|
}
|
|
451
492
|
}
|
|
452
|
-
} else if (cleanContent && cleanContent !== "none") {
|
|
493
|
+
} else if (!isIconFont2 && cleanContent && cleanContent !== "none") {
|
|
453
494
|
pseudoEl.textContent = cleanContent;
|
|
454
495
|
}
|
|
496
|
+
if (hasBg && bg) {
|
|
497
|
+
const rawUrl = extractURL(bg);
|
|
498
|
+
if (rawUrl) {
|
|
499
|
+
try {
|
|
500
|
+
const dataUrl = await fetchImage(encodeURI(rawUrl));
|
|
501
|
+
pseudoEl.style.backgroundImage = `url(${dataUrl})`;
|
|
502
|
+
} catch (e) {
|
|
503
|
+
console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent && pseudoEl.textContent.trim() !== "";
|
|
508
|
+
if (!hasContent2 && !hasBg) continue;
|
|
455
509
|
if (pseudo === "::before") {
|
|
456
510
|
clone.insertBefore(pseudoEl, clone.firstChild);
|
|
457
511
|
} else {
|
|
@@ -463,11 +517,16 @@
|
|
|
463
517
|
}
|
|
464
518
|
}
|
|
465
519
|
const sChildren = Array.from(source.children);
|
|
466
|
-
const cChildren = Array.from(clone.children).filter(
|
|
467
|
-
(child) => !child.dataset.snapdomPseudo
|
|
468
|
-
);
|
|
520
|
+
const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
|
|
469
521
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
470
|
-
await inlinePseudoElements(
|
|
522
|
+
await inlinePseudoElements(
|
|
523
|
+
sChildren[i],
|
|
524
|
+
cChildren[i],
|
|
525
|
+
styleMap,
|
|
526
|
+
styleCache,
|
|
527
|
+
compress,
|
|
528
|
+
embedFonts
|
|
529
|
+
);
|
|
471
530
|
}
|
|
472
531
|
}
|
|
473
532
|
|
|
@@ -559,22 +618,20 @@
|
|
|
559
618
|
const style = styleCache.get(srcNode) || getStyle(srcNode);
|
|
560
619
|
if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
|
|
561
620
|
const bg = style.getPropertyValue("background-image");
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
bgCache.set(bgUrl, dataUrl);
|
|
573
|
-
}
|
|
574
|
-
cloneNode.style.backgroundImage = `url(${dataUrl})`;
|
|
575
|
-
} catch {
|
|
576
|
-
cloneNode.style.backgroundImage = "none";
|
|
621
|
+
const rawUrl = extractURL(bg);
|
|
622
|
+
if (rawUrl) {
|
|
623
|
+
try {
|
|
624
|
+
let bgUrl = encodeURI(rawUrl);
|
|
625
|
+
let dataUrl;
|
|
626
|
+
if (bgCache.has(bgUrl)) {
|
|
627
|
+
dataUrl = bgCache.get(bgUrl);
|
|
628
|
+
} else {
|
|
629
|
+
dataUrl = await fetchImage(bgUrl);
|
|
630
|
+
bgCache.set(bgUrl, dataUrl);
|
|
577
631
|
}
|
|
632
|
+
cloneNode.style.backgroundImage = `url(${dataUrl})`;
|
|
633
|
+
} catch {
|
|
634
|
+
cloneNode.style.backgroundImage = "none";
|
|
578
635
|
}
|
|
579
636
|
}
|
|
580
637
|
const sChildren = Array.from(srcNode.children);
|
|
@@ -676,18 +733,20 @@
|
|
|
676
733
|
}
|
|
677
734
|
return img;
|
|
678
735
|
}
|
|
679
|
-
async function toCanvas(url, { dpr = 1, scale = 1 }) {
|
|
736
|
+
async function toCanvas(url, { dpr = 1, scale = 1 } = {}) {
|
|
680
737
|
const img = new Image();
|
|
681
738
|
img.src = url;
|
|
682
739
|
await img.decode();
|
|
683
740
|
const canvas = document.createElement("canvas");
|
|
684
|
-
const width = img.width * scale
|
|
685
|
-
const height = img.height * scale
|
|
686
|
-
canvas.width = Math.ceil(width);
|
|
687
|
-
canvas.height = Math.ceil(height);
|
|
741
|
+
const width = img.width * scale;
|
|
742
|
+
const height = img.height * scale;
|
|
743
|
+
canvas.width = Math.ceil(width * dpr);
|
|
744
|
+
canvas.height = Math.ceil(height * dpr);
|
|
688
745
|
const ctx = canvas.getContext("2d");
|
|
689
|
-
ctx.scale(
|
|
690
|
-
ctx.drawImage(img, 0, 0);
|
|
746
|
+
ctx.scale(dpr, dpr);
|
|
747
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
748
|
+
canvas.style.width = `${width}px`;
|
|
749
|
+
canvas.style.height = `${height}px`;
|
|
691
750
|
return canvas;
|
|
692
751
|
}
|
|
693
752
|
async function toBlob(url) {
|
|
@@ -811,7 +870,7 @@
|
|
|
811
870
|
}
|
|
812
871
|
for (const el of allEls) {
|
|
813
872
|
const bg = getComputedStyle(el).backgroundImage;
|
|
814
|
-
const url =
|
|
873
|
+
const url = extractURL(bg);
|
|
815
874
|
if (url && !bgCache.has(url)) {
|
|
816
875
|
promises.push(
|
|
817
876
|
fetchImage(url).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
|
package/dist/snapdom.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(()=>{var
|
|
2
|
-
`;return o}function
|
|
3
|
-
`}catch{console.warn("\u274C Failed to fetch CSS:",
|
|
1
|
+
(()=>{var I=new Map,$=new Map,T=new Map,R=new Map,A=new Map,U=new WeakMap;var ce=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function z(){for(let e of ce)_(e)}function _(e){if(R.has(e))return R.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let i={};return R.set(e,i),i}let o=document.getElementById("snapdom-sandbox");o||(o=document.createElement("div"),o.id="snapdom-sandbox",o.style.position="absolute",o.style.left="-9999px",o.style.top="-9999px",o.style.width="0",o.style.height="0",o.style.overflow="hidden",document.body.appendChild(o));let n=document.createElement(e);n.style.all="initial",o.appendChild(n);let r=getComputedStyle(n),a={};for(let i of r)a[i]=r.getPropertyValue(i);return o.removeChild(n),R.set(e,a),a}function M(e,t,o=!1){let n=[],r=_(t);for(let[a,i]of Object.entries(e))if(!o)i&&n.push(`${a}:${i}`);else{let c=r[a];i&&i!==c&&n.push(`${a}:${i}`)}return n.sort().join(";")}function Y(e){let t=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&t.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(o=>t.add(o.tagName.toLowerCase())),Array.from(t))}function K(e){let t=new Map;for(let n of e){let r=R.get(n);if(!r)continue;let a=Object.entries(r).map(([i,c])=>`${i}:${c};`).sort().join("");t.has(a)||t.set(a,[]),t.get(a).push(n)}let o="";for(let[n,r]of t.entries())o+=`${r.join(",")} { ${n} }
|
|
2
|
+
`;return o}function J(e){let t=new Set(e.values()),o=new Map,n=1;for(let r of t)o.set(r,`c${n++}`);return o}function L(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function N(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let o=U.get(e);if(o||(o=new Map,U.set(e,o)),!o.has(t)){let n=window.getComputedStyle(e,t);o.set(t,n)}return o.get(t)}function X(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function S(e){let t=e.indexOf("url(");if(t===-1)return null;let o=e.slice(t+4).trim();return o.endsWith(")")&&(o=o.slice(0,-1).trim()),(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.slice(1,-1)),o}function B(e){return[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i].some(o=>o.test(e))}function x(e,t=3e3){return I.has(e)?Promise.resolve(I.get(e)):new Promise((o,n)=>{let r=setTimeout(()=>{n(new Error("Image load timed out"))},t),a=new Image;a.crossOrigin="anonymous",a.onload=async()=>{clearTimeout(r);try{await a.decode();let i=document.createElement("canvas");i.width=a.width,i.height=a.height,i.getContext("2d").drawImage(a,0,0,i.width,i.height);try{let s=i.toDataURL("image/png");I.set(e,s),o(s)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(i){n(i)}},a.onerror=i=>{clearTimeout(r),n(new Error("Failed to load image: "+(i.message||"Unknown error")))},a.src=e})}function F(e){let t={};for(let o of e)t[o]=e.getPropertyValue(o);return t}function D(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function V(e,t,o,n,r){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,N(e));let a=n.get(e),i=F(a),c=e.tagName?.toLowerCase()||"div",s=M(i,c,r);o.set(t,s)}function O(e,t,o,n,r){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),s=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${s.width}px; height: ${s.height}px; visibility: hidden;`,c}if(e.tagName==="IFRAME"){let c=document.createElement("div");return c.textContent="",c.style.cssText=`width: ${e.offsetWidth}px; height: ${e.offsetHeight}px; background: 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;`,c}if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="placeholder"){let c=e.cloneNode(!1);n.set(c,e),V(e,c,t,o,r);let s=document.createElement("div");return s.textContent=e.getAttribute("data-placeholder-text")||"",s.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(s),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),s=document.createElement("img");return s.src=c,s.width=e.width,s.height=e.height,s.style.display="inline-block",s.style.width=`${e.width}px`,s.style.height=`${e.height}px`,s}if(e.nodeType===Node.TEXT_NODE){if(e.parentElement?.shadowRoot){let c=e.parentElement.tagName.toLowerCase();if(customElements.get(c))return null}return e.cloneNode(!0)}if(e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);let a=e.cloneNode(!1);n.set(a,e),e instanceof HTMLInputElement?(a.value=e.value,a.setAttribute("value",e.value),e.checked!==void 0&&(a.checked=e.checked,e.checked&&a.setAttribute("checked",""))):e instanceof HTMLTextAreaElement?(a.value=e.value,a.textContent=e.value):e instanceof HTMLSelectElement&&(a.value=e.value,Array.from(a.options).forEach(c=>{c.value===e.value?c.setAttribute("selected",""):c.removeAttribute("selected")})),V(e,a,t,o,r);let i=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let s=O(c,t,o,n,r);s&&i.appendChild(s)}),a.appendChild(i),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>O(l,t,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),a.appendChild(s)}return a}async function G(e,t,o,n=32,r="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let a=window.devicePixelRatio||1,c=document.createElement("canvas").getContext("2d");c.font=o?`${o} ${n}px "${t}"`:`${n}px "${t}"`;let s=c.measureText(e),l=s.actualBoundingBoxAscent||n*.8,d=s.actualBoundingBoxDescent||n*.2,f=l+d,m=s.width,p=document.createElement("canvas");p.width=Math.ceil(m*a),p.height=Math.ceil(f*a);let h=p.getContext("2d");return h.scale(a,a),h.font=c.font,h.textAlign="left",h.textBaseline="alphabetic",h.fillStyle=r,h.fillText(e,0,l),p.toDataURL()}async function W({ignoreIconFonts:e=!0,preCached:t=!1}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(r=>r.href),n="";for(let r of o)try{let i=await(await fetch(r.href)).text();if(e&&(B(r.href)||B(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let f=S(d[0]);if(!f)return null;let m=f;m.startsWith("http")||(m=new URL(m,r.href).href);try{m=encodeURI(m)}catch{console.warn("[snapdom] Failed to encode font URL:",m)}if(e&&B(m))return null;if(T.has(m))return{original:d[0],inlined:`url(${T.get(m)})`};try{let h=await(await fetch(m)).blob(),w=await new Promise(E=>{let v=new FileReader;v.onload=()=>E(v.result),v.readAsDataURL(h)});return T.set(m,w),{original:d[0],inlined:`url(${w})`}}catch{return console.warn("\u274C Failed to fetch font:",m),null}})),l=i;for(let d of s)d&&(l=l.replace(d.original,d.inlined));n+=l+`
|
|
3
|
+
`}catch{console.warn("\u274C Failed to fetch CSS:",r.href)}if(n&&t){let r=document.createElement("style");r.setAttribute("data-snapdom","embedFonts"),r.textContent=n,document.head.appendChild(r)}return n}async function q(e,t,o,n,r,a=!1){if(!(e instanceof Element)||!(t instanceof Element))return;for(let s of["::before","::after"])try{let l=N(e,s);if(!l)continue;let d=l.getPropertyValue("content"),f=l.getPropertyValue("background-image"),m=d&&d!=="none"&&d!=='""'&&d!=="''",p=f&&f.startsWith("url(");if(m||p){let h=l.getPropertyValue("font-family"),w=parseInt(l.getPropertyValue("font-size"))||32,E=parseInt(l.getPropertyValue("font-weight"))||!1,v=l.getPropertyValue("color")||"#000",g=document.createElement("span");g.dataset.snapdomPseudo=s;let k=F(l),P=M(k,"span",r);o.set(g,P);let j=h&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(h),C=X(d);if(!a&&j&&C.length===1){let y=document.createElement("img");y.src=await G(C,h,E,w,v),y.style="display:block;width:100%;height:100%;object-fit:contain;",g.appendChild(y)}else if(C.startsWith("url(")){let y=S(C);if(y)try{let b=document.createElement("img"),ie=await x(encodeURI(y));b.src=ie,b.style="display:block;width:100%;height:100%;object-fit:contain;",g.appendChild(b)}catch(b){console.error(`[snapdom] Error in pseudo ${s} for`,e,b)}}else!j&&C&&C!=="none"&&(g.textContent=C);if(p&&f){let y=S(f);if(y)try{let b=await x(encodeURI(y));g.style.backgroundImage=`url(${b})`}catch(b){console.warn(`[snapdom] Failed to inline background-image for ${s}`,b)}}if(!(g.childNodes.length>0||g.textContent&&g.textContent.trim()!=="")&&!p)continue;s==="::before"?t.insertBefore(g,t.firstChild):t.appendChild(g)}}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,e,l)}let i=Array.from(e.children),c=Array.from(t.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s<Math.min(i.length,c.length);s++)await q(i[s],c[s],o,n,r,a)}async function Q(e,t=!1,o=!1){let n=new Map,r=new WeakMap,a=new Map,i;try{i=O(e,n,r,a,t)}catch(s){throw console.warn("deepClone failed:",s),s}try{await q(e,i,n,r,t,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(t){let s=J(n);c=Array.from(s.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of n.entries()){if(l.tagName==="STYLE")continue;let f=s.get(d);f&&l.classList.add(f);let m=l.style?.backgroundImage;l.removeAttribute("style"),m&&m!=="none"&&(l.style.backgroundImage=m)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of a.entries()){let d=l.scrollLeft,f=l.scrollTop;if((d||f)&&s instanceof HTMLElement){s.style.overflow="hidden",s.style.scrollbarWidth="none",s.style.msOverflowStyle="none";let p=document.createElement("div");for(p.style.transform=`translate(${-d}px, ${-f}px)`,p.style.willChange="transform",p.style.display="inline-block",p.style.width="100%";s.firstChild;)p.appendChild(s.firstChild);s.appendChild(p)}}return{clone:i,classCSS:c,styleCache:r}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let r=n.src;try{let a=await x(r);n.src=a,n.width||(n.width=n.naturalWidth||100),n.height||(n.height=n.naturalHeight||100)}catch{let a=document.createElement("div");a.style=`width: ${n.width||100}px; height: ${n.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${n.height||100}px; color: #666; font-size: 12px;`,a.innerText="img",n.replaceWith(a)}};for(let n=0;n<t.length;n+=4){let r=t.slice(n,n+4).map(o);await Promise.allSettled(r)}}async function ee(e,t,o){let n=[[e,t]];for(;n.length;){let[r,a]=n.shift(),i=o.get(r)||N(r);o.has(r)||o.set(r,i);let c=i.getPropertyValue("background-image"),s=S(c);if(s)try{let f=encodeURI(s),m;$.has(f)?m=$.get(f):(m=await x(f),$.set(f,m)),a.style.backgroundImage=`url(${m})`}catch{a.style.backgroundImage="none"}let l=Array.from(r.children),d=Array.from(a.children);for(let f=0;f<Math.min(l.length,d.length);f++)n.push([l[f],d[f]])}}async function te(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:r=!0,scale:a=1}=t,i,c,s,l="",d="",f,m;if({clone:i,classCSS:c,styleCache:s}=await Q(e,o,n),await new Promise(h=>{L(async()=>{await Z(i),h()},{fast:r})}),await new Promise(h=>{L(async()=>{await ee(e,i,s),h()},{fast:r})}),n&&await new Promise(h=>{L(async()=>{l=await W({ignoreIconFonts:!n}),h()},{fast:r})}),o){let h=Y(i).sort(),w=h.join(",");A.has(w)?d=A.get(w):await new Promise(E=>{L(()=>{d=K(h),A.set(w,d),E()},{fast:r})})}await new Promise(h=>{L(()=>{let w=e.getBoundingClientRect(),E=Math.ceil(w.width),v=Math.ceil(w.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",a!==1&&D()&&(i.style.scale=`${a}`);let g="http://www.w3.org/2000/svg",k=document.createElementNS(g,"foreignObject");k.setAttribute("width","100%"),k.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,k.appendChild(P),k.appendChild(i);let C=new XMLSerializer().serializeToString(k);m=`<svg xmlns="${g}" width="${E}" height="${v}" viewBox="0 0 ${E} ${v}">`+C+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(m)}`,h()},{fast:r})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),f}async function le(e,{dpr:t=1,scale:o=1}){let n=new Image;return n.src=e,await n.decode(),D?(n.width=n.width*o,n.height=n.height*o):(n.width=n.width/o,n.height=n.height/o),n}async function ne(e,{dpr:t=1,scale:o=1}={}){let n=new Image;n.src=e,await n.decode();let r=document.createElement("canvas"),a=n.width*o,i=n.height*o;r.width=Math.ceil(a*t),r.height=Math.ceil(i*t);let c=r.getContext("2d");return c.scale(t,t),c.drawImage(n,0,0,a,i),r.style.width=`${a}px`,r.style.height=`${i}px`,r}async function oe(e){let t=decodeURIComponent(e.split(",")[1]);return new Blob([t],{type:"image/svg+xml"})}async function ae(e,{dpr:t=1,scale:o=1},n){let r=await ne(e,{dpr:t,scale:o});if(!n)return r;let a=document.createElement("canvas");a.width=r.width,a.height=r.height;let i=a.getContext("2d");return i.fillStyle=n,i.fillRect(0,0,a.width,a.height),i.drawImage(r,0,0),a}async function H(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:r},a="png"){let i=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${a}`,r),await c.decode(),c.style.width=`${i.width/t}px`,c.style.height=`${i.height/t}px`,c}async function de(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:r="png",filename:a="capture"}={}){if(r==="svg"){let m=await oe(e),p=URL.createObjectURL(m),h=document.createElement("a");h.href=p,h.download=`${a}.svg`,h.click(),URL.revokeObjectURL(p);return}let i=["jpg","jpeg","webp"].includes(r)?"#fff":void 0,s=await ae(e,{dpr:t,scale:o},n??i),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[r]||"image/png",d=s.toDataURL(l),f=document.createElement("a");f.href=d,f.download=`${a}.${r}`,f.click()}async function u(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await u.capture(e,t)}u.capture=async(e,t={})=>{let o=await te(e,t),n=window.devicePixelRatio||1,r=t.scale||1;return{url:o,options:t,toRaw:()=>o,toImg:()=>le(o,{dpr:n,scale:r}),toCanvas:()=>ne(o,{dpr:n,scale:r}),toBlob:()=>oe(o),toPng:a=>H(o,{dpr:n,scale:r,...a},"png"),toJpg:a=>H(o,{dpr:n,scale:r,...a},"jpeg"),toWebp:a=>H(o,{dpr:n,scale:r,...a},"webp"),download:({format:a="png",filename:i="capture",backgroundColor:c}={})=>de(o,{dpr:n,scale:r,backgroundColor:c,format:a,filename:i})}};u.toRaw=async(e,t)=>(await u.capture(e,t)).toRaw();u.toImg=async(e,t)=>(await u.capture(e,t)).toImg();u.toCanvas=async(e,t)=>(await u.capture(e,t)).toCanvas();u.toBlob=async(e,t)=>(await u.capture(e,t)).toBlob();u.toPng=async(e,t)=>(await u.capture(e,t)).toPng(t);u.toJpg=async(e,t)=>(await u.capture(e,t)).toJpg(t);u.toWebp=async(e,t)=>(await u.capture(e,t)).toWebp(t);u.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:r,...a}=t;return await(await u.capture(e,a)).download({format:o,filename:n,backgroundColor:r})};async function re(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:r=!0}=t;if(n){I.clear(),$.clear(),T.clear(),A.clear(),U.clear();return}await document.fonts.ready,z();let a=[],i=[];e?.querySelectorAll&&(a=Array.from(e.querySelectorAll("img[src]")),i=Array.from(e.querySelectorAll("*")));let c=[];for(let s of a){let l=s.src;I.has(l)||c.push(x(l).then(d=>I.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=S(l);d&&!$.has(d)&&c.push(x(d).then(f=>$.set(d,f)).catch(()=>{}))}o&&await W({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}window.snapdom=u;window.preCache=re;})();
|
package/dist/snapdom.min.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`;return o}function
|
|
3
|
-
`}catch{console.warn("\u274C Failed to fetch CSS:",
|
|
1
|
+
var I=new Map,$=new Map,T=new Map,R=new Map,A=new Map,U=new WeakMap;var ie=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function z(){for(let e of ie)_(e)}function _(e){if(R.has(e))return R.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let i={};return R.set(e,i),i}let o=document.getElementById("snapdom-sandbox");o||(o=document.createElement("div"),o.id="snapdom-sandbox",o.style.position="absolute",o.style.left="-9999px",o.style.top="-9999px",o.style.width="0",o.style.height="0",o.style.overflow="hidden",document.body.appendChild(o));let n=document.createElement(e);n.style.all="initial",o.appendChild(n);let r=getComputedStyle(n),a={};for(let i of r)a[i]=r.getPropertyValue(i);return o.removeChild(n),R.set(e,a),a}function M(e,t,o=!1){let n=[],r=_(t);for(let[a,i]of Object.entries(e))if(!o)i&&n.push(`${a}:${i}`);else{let c=r[a];i&&i!==c&&n.push(`${a}:${i}`)}return n.sort().join(";")}function Y(e){let t=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&t.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(o=>t.add(o.tagName.toLowerCase())),Array.from(t))}function K(e){let t=new Map;for(let n of e){let r=R.get(n);if(!r)continue;let a=Object.entries(r).map(([i,c])=>`${i}:${c};`).sort().join("");t.has(a)||t.set(a,[]),t.get(a).push(n)}let o="";for(let[n,r]of t.entries())o+=`${r.join(",")} { ${n} }
|
|
2
|
+
`;return o}function J(e){let t=new Set(e.values()),o=new Map,n=1;for(let r of t)o.set(r,`c${n++}`);return o}function L(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function N(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let o=U.get(e);if(o||(o=new Map,U.set(e,o)),!o.has(t)){let n=window.getComputedStyle(e,t);o.set(t,n)}return o.get(t)}function X(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function S(e){let t=e.indexOf("url(");if(t===-1)return null;let o=e.slice(t+4).trim();return o.endsWith(")")&&(o=o.slice(0,-1).trim()),(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.slice(1,-1)),o}function B(e){return[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i].some(o=>o.test(e))}function x(e,t=3e3){return I.has(e)?Promise.resolve(I.get(e)):new Promise((o,n)=>{let r=setTimeout(()=>{n(new Error("Image load timed out"))},t),a=new Image;a.crossOrigin="anonymous",a.onload=async()=>{clearTimeout(r);try{await a.decode();let i=document.createElement("canvas");i.width=a.width,i.height=a.height,i.getContext("2d").drawImage(a,0,0,i.width,i.height);try{let s=i.toDataURL("image/png");I.set(e,s),o(s)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(i){n(i)}},a.onerror=i=>{clearTimeout(r),n(new Error("Failed to load image: "+(i.message||"Unknown error")))},a.src=e})}function F(e){let t={};for(let o of e)t[o]=e.getPropertyValue(o);return t}function D(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function V(e,t,o,n,r){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,N(e));let a=n.get(e),i=F(a),c=e.tagName?.toLowerCase()||"div",s=M(i,c,r);o.set(t,s)}function O(e,t,o,n,r){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),s=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${s.width}px; height: ${s.height}px; visibility: hidden;`,c}if(e.tagName==="IFRAME"){let c=document.createElement("div");return c.textContent="",c.style.cssText=`width: ${e.offsetWidth}px; height: ${e.offsetHeight}px; background: 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;`,c}if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="placeholder"){let c=e.cloneNode(!1);n.set(c,e),V(e,c,t,o,r);let s=document.createElement("div");return s.textContent=e.getAttribute("data-placeholder-text")||"",s.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(s),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),s=document.createElement("img");return s.src=c,s.width=e.width,s.height=e.height,s.style.display="inline-block",s.style.width=`${e.width}px`,s.style.height=`${e.height}px`,s}if(e.nodeType===Node.TEXT_NODE){if(e.parentElement?.shadowRoot){let c=e.parentElement.tagName.toLowerCase();if(customElements.get(c))return null}return e.cloneNode(!0)}if(e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);let a=e.cloneNode(!1);n.set(a,e),e instanceof HTMLInputElement?(a.value=e.value,a.setAttribute("value",e.value),e.checked!==void 0&&(a.checked=e.checked,e.checked&&a.setAttribute("checked",""))):e instanceof HTMLTextAreaElement?(a.value=e.value,a.textContent=e.value):e instanceof HTMLSelectElement&&(a.value=e.value,Array.from(a.options).forEach(c=>{c.value===e.value?c.setAttribute("selected",""):c.removeAttribute("selected")})),V(e,a,t,o,r);let i=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let s=O(c,t,o,n,r);s&&i.appendChild(s)}),a.appendChild(i),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>O(l,t,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),a.appendChild(s)}return a}async function G(e,t,o,n=32,r="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let a=window.devicePixelRatio||1,c=document.createElement("canvas").getContext("2d");c.font=o?`${o} ${n}px "${t}"`:`${n}px "${t}"`;let s=c.measureText(e),l=s.actualBoundingBoxAscent||n*.8,d=s.actualBoundingBoxDescent||n*.2,f=l+d,m=s.width,p=document.createElement("canvas");p.width=Math.ceil(m*a),p.height=Math.ceil(f*a);let h=p.getContext("2d");return h.scale(a,a),h.font=c.font,h.textAlign="left",h.textBaseline="alphabetic",h.fillStyle=r,h.fillText(e,0,l),p.toDataURL()}async function W({ignoreIconFonts:e=!0,preCached:t=!1}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(r=>r.href),n="";for(let r of o)try{let i=await(await fetch(r.href)).text();if(e&&(B(r.href)||B(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let f=S(d[0]);if(!f)return null;let m=f;m.startsWith("http")||(m=new URL(m,r.href).href);try{m=encodeURI(m)}catch{console.warn("[snapdom] Failed to encode font URL:",m)}if(e&&B(m))return null;if(T.has(m))return{original:d[0],inlined:`url(${T.get(m)})`};try{let h=await(await fetch(m)).blob(),w=await new Promise(E=>{let v=new FileReader;v.onload=()=>E(v.result),v.readAsDataURL(h)});return T.set(m,w),{original:d[0],inlined:`url(${w})`}}catch{return console.warn("\u274C Failed to fetch font:",m),null}})),l=i;for(let d of s)d&&(l=l.replace(d.original,d.inlined));n+=l+`
|
|
3
|
+
`}catch{console.warn("\u274C Failed to fetch CSS:",r.href)}if(n&&t){let r=document.createElement("style");r.setAttribute("data-snapdom","embedFonts"),r.textContent=n,document.head.appendChild(r)}return n}async function q(e,t,o,n,r,a=!1){if(!(e instanceof Element)||!(t instanceof Element))return;for(let s of["::before","::after"])try{let l=N(e,s);if(!l)continue;let d=l.getPropertyValue("content"),f=l.getPropertyValue("background-image"),m=d&&d!=="none"&&d!=='""'&&d!=="''",p=f&&f.startsWith("url(");if(m||p){let h=l.getPropertyValue("font-family"),w=parseInt(l.getPropertyValue("font-size"))||32,E=parseInt(l.getPropertyValue("font-weight"))||!1,v=l.getPropertyValue("color")||"#000",g=document.createElement("span");g.dataset.snapdomPseudo=s;let k=F(l),P=M(k,"span",r);o.set(g,P);let j=h&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(h),C=X(d);if(!a&&j&&C.length===1){let y=document.createElement("img");y.src=await G(C,h,E,w,v),y.style="display:block;width:100%;height:100%;object-fit:contain;",g.appendChild(y)}else if(C.startsWith("url(")){let y=S(C);if(y)try{let b=document.createElement("img"),se=await x(encodeURI(y));b.src=se,b.style="display:block;width:100%;height:100%;object-fit:contain;",g.appendChild(b)}catch(b){console.error(`[snapdom] Error in pseudo ${s} for`,e,b)}}else!j&&C&&C!=="none"&&(g.textContent=C);if(p&&f){let y=S(f);if(y)try{let b=await x(encodeURI(y));g.style.backgroundImage=`url(${b})`}catch(b){console.warn(`[snapdom] Failed to inline background-image for ${s}`,b)}}if(!(g.childNodes.length>0||g.textContent&&g.textContent.trim()!=="")&&!p)continue;s==="::before"?t.insertBefore(g,t.firstChild):t.appendChild(g)}}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,e,l)}let i=Array.from(e.children),c=Array.from(t.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s<Math.min(i.length,c.length);s++)await q(i[s],c[s],o,n,r,a)}async function Q(e,t=!1,o=!1){let n=new Map,r=new WeakMap,a=new Map,i;try{i=O(e,n,r,a,t)}catch(s){throw console.warn("deepClone failed:",s),s}try{await q(e,i,n,r,t,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(t){let s=J(n);c=Array.from(s.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of n.entries()){if(l.tagName==="STYLE")continue;let f=s.get(d);f&&l.classList.add(f);let m=l.style?.backgroundImage;l.removeAttribute("style"),m&&m!=="none"&&(l.style.backgroundImage=m)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of a.entries()){let d=l.scrollLeft,f=l.scrollTop;if((d||f)&&s instanceof HTMLElement){s.style.overflow="hidden",s.style.scrollbarWidth="none",s.style.msOverflowStyle="none";let p=document.createElement("div");for(p.style.transform=`translate(${-d}px, ${-f}px)`,p.style.willChange="transform",p.style.display="inline-block",p.style.width="100%";s.firstChild;)p.appendChild(s.firstChild);s.appendChild(p)}}return{clone:i,classCSS:c,styleCache:r}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let r=n.src;try{let a=await x(r);n.src=a,n.width||(n.width=n.naturalWidth||100),n.height||(n.height=n.naturalHeight||100)}catch{let a=document.createElement("div");a.style=`width: ${n.width||100}px; height: ${n.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${n.height||100}px; color: #666; font-size: 12px;`,a.innerText="img",n.replaceWith(a)}};for(let n=0;n<t.length;n+=4){let r=t.slice(n,n+4).map(o);await Promise.allSettled(r)}}async function ee(e,t,o){let n=[[e,t]];for(;n.length;){let[r,a]=n.shift(),i=o.get(r)||N(r);o.has(r)||o.set(r,i);let c=i.getPropertyValue("background-image"),s=S(c);if(s)try{let f=encodeURI(s),m;$.has(f)?m=$.get(f):(m=await x(f),$.set(f,m)),a.style.backgroundImage=`url(${m})`}catch{a.style.backgroundImage="none"}let l=Array.from(r.children),d=Array.from(a.children);for(let f=0;f<Math.min(l.length,d.length);f++)n.push([l[f],d[f]])}}async function te(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:r=!0,scale:a=1}=t,i,c,s,l="",d="",f,m;if({clone:i,classCSS:c,styleCache:s}=await Q(e,o,n),await new Promise(h=>{L(async()=>{await Z(i),h()},{fast:r})}),await new Promise(h=>{L(async()=>{await ee(e,i,s),h()},{fast:r})}),n&&await new Promise(h=>{L(async()=>{l=await W({ignoreIconFonts:!n}),h()},{fast:r})}),o){let h=Y(i).sort(),w=h.join(",");A.has(w)?d=A.get(w):await new Promise(E=>{L(()=>{d=K(h),A.set(w,d),E()},{fast:r})})}await new Promise(h=>{L(()=>{let w=e.getBoundingClientRect(),E=Math.ceil(w.width),v=Math.ceil(w.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",a!==1&&D()&&(i.style.scale=`${a}`);let g="http://www.w3.org/2000/svg",k=document.createElementNS(g,"foreignObject");k.setAttribute("width","100%"),k.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,k.appendChild(P),k.appendChild(i);let C=new XMLSerializer().serializeToString(k);m=`<svg xmlns="${g}" width="${E}" height="${v}" viewBox="0 0 ${E} ${v}">`+C+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(m)}`,h()},{fast:r})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),f}async function ce(e,{dpr:t=1,scale:o=1}){let n=new Image;return n.src=e,await n.decode(),D?(n.width=n.width*o,n.height=n.height*o):(n.width=n.width/o,n.height=n.height/o),n}async function ne(e,{dpr:t=1,scale:o=1}={}){let n=new Image;n.src=e,await n.decode();let r=document.createElement("canvas"),a=n.width*o,i=n.height*o;r.width=Math.ceil(a*t),r.height=Math.ceil(i*t);let c=r.getContext("2d");return c.scale(t,t),c.drawImage(n,0,0,a,i),r.style.width=`${a}px`,r.style.height=`${i}px`,r}async function oe(e){let t=decodeURIComponent(e.split(",")[1]);return new Blob([t],{type:"image/svg+xml"})}async function ae(e,{dpr:t=1,scale:o=1},n){let r=await ne(e,{dpr:t,scale:o});if(!n)return r;let a=document.createElement("canvas");a.width=r.width,a.height=r.height;let i=a.getContext("2d");return i.fillStyle=n,i.fillRect(0,0,a.width,a.height),i.drawImage(r,0,0),a}async function H(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:r},a="png"){let i=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${a}`,r),await c.decode(),c.style.width=`${i.width/t}px`,c.style.height=`${i.height/t}px`,c}async function le(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:r="png",filename:a="capture"}={}){if(r==="svg"){let m=await oe(e),p=URL.createObjectURL(m),h=document.createElement("a");h.href=p,h.download=`${a}.svg`,h.click(),URL.revokeObjectURL(p);return}let i=["jpg","jpeg","webp"].includes(r)?"#fff":void 0,s=await ae(e,{dpr:t,scale:o},n??i),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[r]||"image/png",d=s.toDataURL(l),f=document.createElement("a");f.href=d,f.download=`${a}.${r}`,f.click()}async function u(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await u.capture(e,t)}u.capture=async(e,t={})=>{let o=await te(e,t),n=window.devicePixelRatio||1,r=t.scale||1;return{url:o,options:t,toRaw:()=>o,toImg:()=>ce(o,{dpr:n,scale:r}),toCanvas:()=>ne(o,{dpr:n,scale:r}),toBlob:()=>oe(o),toPng:a=>H(o,{dpr:n,scale:r,...a},"png"),toJpg:a=>H(o,{dpr:n,scale:r,...a},"jpeg"),toWebp:a=>H(o,{dpr:n,scale:r,...a},"webp"),download:({format:a="png",filename:i="capture",backgroundColor:c}={})=>le(o,{dpr:n,scale:r,backgroundColor:c,format:a,filename:i})}};u.toRaw=async(e,t)=>(await u.capture(e,t)).toRaw();u.toImg=async(e,t)=>(await u.capture(e,t)).toImg();u.toCanvas=async(e,t)=>(await u.capture(e,t)).toCanvas();u.toBlob=async(e,t)=>(await u.capture(e,t)).toBlob();u.toPng=async(e,t)=>(await u.capture(e,t)).toPng(t);u.toJpg=async(e,t)=>(await u.capture(e,t)).toJpg(t);u.toWebp=async(e,t)=>(await u.capture(e,t)).toWebp(t);u.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:r,...a}=t;return await(await u.capture(e,a)).download({format:o,filename:n,backgroundColor:r})};async function de(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:r=!0}=t;if(n){I.clear(),$.clear(),T.clear(),A.clear(),U.clear();return}await document.fonts.ready,z();let a=[],i=[];e?.querySelectorAll&&(a=Array.from(e.querySelectorAll("img[src]")),i=Array.from(e.querySelectorAll("*")));let c=[];for(let s of a){let l=s.src;I.has(l)||c.push(x(l).then(d=>I.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=S(l);d&&!$.has(d)&&c.push(x(d).then(f=>$.set(d,f)).catch(()=>{}))}o&&await W({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}export{de as preCache,u as snapdom};
|
package/dist/snapdom.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.
|
|
4
|
+
* v.1.3.0
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -186,9 +186,15 @@ function parseContent(content) {
|
|
|
186
186
|
}
|
|
187
187
|
return clean;
|
|
188
188
|
}
|
|
189
|
-
function
|
|
190
|
-
const
|
|
191
|
-
|
|
189
|
+
function extractURL(value) {
|
|
190
|
+
const urlStart = value.indexOf("url(");
|
|
191
|
+
if (urlStart === -1) return null;
|
|
192
|
+
let url = value.slice(urlStart + 4).trim();
|
|
193
|
+
if (url.endsWith(")")) url = url.slice(0, -1).trim();
|
|
194
|
+
if (url.startsWith('"') && url.endsWith('"') || url.startsWith("'") && url.endsWith("'")) {
|
|
195
|
+
url = url.slice(1, -1);
|
|
196
|
+
}
|
|
197
|
+
return url;
|
|
192
198
|
}
|
|
193
199
|
function isIconFont(familyOrUrl) {
|
|
194
200
|
const iconFontPatterns = [
|
|
@@ -221,7 +227,7 @@ function fetchImage(src, timeout = 3e3) {
|
|
|
221
227
|
canvas.width = image.width;
|
|
222
228
|
canvas.height = image.height;
|
|
223
229
|
const ctx = canvas.getContext("2d");
|
|
224
|
-
ctx.drawImage(image, 0, 0);
|
|
230
|
+
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
225
231
|
try {
|
|
226
232
|
const dataURL = canvas.toDataURL("image/png");
|
|
227
233
|
imageCache.set(src, dataURL);
|
|
@@ -309,6 +315,26 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress) {
|
|
|
309
315
|
if (node.nodeType !== Node.ELEMENT_NODE) return node.cloneNode(true);
|
|
310
316
|
const clone = node.cloneNode(false);
|
|
311
317
|
nodeMap.set(clone, node);
|
|
318
|
+
if (node instanceof HTMLInputElement) {
|
|
319
|
+
clone.value = node.value;
|
|
320
|
+
clone.setAttribute("value", node.value);
|
|
321
|
+
if (node.checked !== void 0) {
|
|
322
|
+
clone.checked = node.checked;
|
|
323
|
+
if (node.checked) clone.setAttribute("checked", "");
|
|
324
|
+
}
|
|
325
|
+
} else if (node instanceof HTMLTextAreaElement) {
|
|
326
|
+
clone.value = node.value;
|
|
327
|
+
clone.textContent = node.value;
|
|
328
|
+
} else if (node instanceof HTMLSelectElement) {
|
|
329
|
+
clone.value = node.value;
|
|
330
|
+
Array.from(clone.options).forEach((opt) => {
|
|
331
|
+
if (opt.value === node.value) {
|
|
332
|
+
opt.setAttribute("selected", "");
|
|
333
|
+
} else {
|
|
334
|
+
opt.removeAttribute("selected");
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
312
338
|
inlineAllStyles(node, clone, styleMap, styleCache, compress);
|
|
313
339
|
const frag = document.createDocumentFragment();
|
|
314
340
|
node.childNodes.forEach((child) => {
|
|
@@ -362,10 +388,17 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
362
388
|
const urlRegex = /url\(([^)]+)\)/g;
|
|
363
389
|
const inlinedCSS = await Promise.all(
|
|
364
390
|
Array.from(cssText.matchAll(urlRegex)).map(async (match) => {
|
|
365
|
-
let
|
|
391
|
+
let rawUrl = extractURL(match[0]);
|
|
392
|
+
if (!rawUrl) return null;
|
|
393
|
+
let url = rawUrl;
|
|
366
394
|
if (!url.startsWith("http")) {
|
|
367
395
|
url = new URL(url, link.href).href;
|
|
368
396
|
}
|
|
397
|
+
try {
|
|
398
|
+
url = encodeURI(url);
|
|
399
|
+
} catch (e) {
|
|
400
|
+
console.warn("[snapdom] Failed to encode font URL:", url);
|
|
401
|
+
}
|
|
369
402
|
if (ignoreIconFonts && isIconFont(url)) {
|
|
370
403
|
return null;
|
|
371
404
|
}
|
|
@@ -429,19 +462,27 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
429
462
|
const snapshot = snapshotComputedStyle(style);
|
|
430
463
|
const key = getStyleKey(snapshot, "span", compress);
|
|
431
464
|
styleMap.set(pseudoEl, key);
|
|
432
|
-
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(
|
|
465
|
+
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(
|
|
466
|
+
fontFamily
|
|
467
|
+
);
|
|
433
468
|
let cleanContent = parseContent(content);
|
|
434
469
|
if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
|
|
435
470
|
const imgEl = document.createElement("img");
|
|
436
|
-
imgEl.src = await iconToImage(
|
|
471
|
+
imgEl.src = await iconToImage(
|
|
472
|
+
cleanContent,
|
|
473
|
+
fontFamily,
|
|
474
|
+
fontWeight,
|
|
475
|
+
fontSize,
|
|
476
|
+
color
|
|
477
|
+
);
|
|
437
478
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
438
479
|
pseudoEl.appendChild(imgEl);
|
|
439
480
|
} else if (cleanContent.startsWith("url(")) {
|
|
440
|
-
const
|
|
441
|
-
if (
|
|
481
|
+
const rawUrl = extractURL(cleanContent);
|
|
482
|
+
if (rawUrl) {
|
|
442
483
|
try {
|
|
443
484
|
const imgEl = document.createElement("img");
|
|
444
|
-
const dataUrl = await fetchImage(
|
|
485
|
+
const dataUrl = await fetchImage(encodeURI(rawUrl));
|
|
445
486
|
imgEl.src = dataUrl;
|
|
446
487
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
447
488
|
pseudoEl.appendChild(imgEl);
|
|
@@ -449,9 +490,22 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
449
490
|
console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
|
|
450
491
|
}
|
|
451
492
|
}
|
|
452
|
-
} else if (cleanContent && cleanContent !== "none") {
|
|
493
|
+
} else if (!isIconFont2 && cleanContent && cleanContent !== "none") {
|
|
453
494
|
pseudoEl.textContent = cleanContent;
|
|
454
495
|
}
|
|
496
|
+
if (hasBg && bg) {
|
|
497
|
+
const rawUrl = extractURL(bg);
|
|
498
|
+
if (rawUrl) {
|
|
499
|
+
try {
|
|
500
|
+
const dataUrl = await fetchImage(encodeURI(rawUrl));
|
|
501
|
+
pseudoEl.style.backgroundImage = `url(${dataUrl})`;
|
|
502
|
+
} catch (e) {
|
|
503
|
+
console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent && pseudoEl.textContent.trim() !== "";
|
|
508
|
+
if (!hasContent2 && !hasBg) continue;
|
|
455
509
|
if (pseudo === "::before") {
|
|
456
510
|
clone.insertBefore(pseudoEl, clone.firstChild);
|
|
457
511
|
} else {
|
|
@@ -463,11 +517,16 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
463
517
|
}
|
|
464
518
|
}
|
|
465
519
|
const sChildren = Array.from(source.children);
|
|
466
|
-
const cChildren = Array.from(clone.children).filter(
|
|
467
|
-
(child) => !child.dataset.snapdomPseudo
|
|
468
|
-
);
|
|
520
|
+
const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
|
|
469
521
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
470
|
-
await inlinePseudoElements(
|
|
522
|
+
await inlinePseudoElements(
|
|
523
|
+
sChildren[i],
|
|
524
|
+
cChildren[i],
|
|
525
|
+
styleMap,
|
|
526
|
+
styleCache,
|
|
527
|
+
compress,
|
|
528
|
+
embedFonts
|
|
529
|
+
);
|
|
471
530
|
}
|
|
472
531
|
}
|
|
473
532
|
|
|
@@ -559,22 +618,20 @@ async function inlineBackgroundImages(source, clone, styleCache) {
|
|
|
559
618
|
const style = styleCache.get(srcNode) || getStyle(srcNode);
|
|
560
619
|
if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
|
|
561
620
|
const bg = style.getPropertyValue("background-image");
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
bgCache.set(bgUrl, dataUrl);
|
|
573
|
-
}
|
|
574
|
-
cloneNode.style.backgroundImage = `url(${dataUrl})`;
|
|
575
|
-
} catch {
|
|
576
|
-
cloneNode.style.backgroundImage = "none";
|
|
621
|
+
const rawUrl = extractURL(bg);
|
|
622
|
+
if (rawUrl) {
|
|
623
|
+
try {
|
|
624
|
+
let bgUrl = encodeURI(rawUrl);
|
|
625
|
+
let dataUrl;
|
|
626
|
+
if (bgCache.has(bgUrl)) {
|
|
627
|
+
dataUrl = bgCache.get(bgUrl);
|
|
628
|
+
} else {
|
|
629
|
+
dataUrl = await fetchImage(bgUrl);
|
|
630
|
+
bgCache.set(bgUrl, dataUrl);
|
|
577
631
|
}
|
|
632
|
+
cloneNode.style.backgroundImage = `url(${dataUrl})`;
|
|
633
|
+
} catch {
|
|
634
|
+
cloneNode.style.backgroundImage = "none";
|
|
578
635
|
}
|
|
579
636
|
}
|
|
580
637
|
const sChildren = Array.from(srcNode.children);
|
|
@@ -676,18 +733,20 @@ async function toImg(url, { dpr = 1, scale = 1 }) {
|
|
|
676
733
|
}
|
|
677
734
|
return img;
|
|
678
735
|
}
|
|
679
|
-
async function toCanvas(url, { dpr = 1, scale = 1 }) {
|
|
736
|
+
async function toCanvas(url, { dpr = 1, scale = 1 } = {}) {
|
|
680
737
|
const img = new Image();
|
|
681
738
|
img.src = url;
|
|
682
739
|
await img.decode();
|
|
683
740
|
const canvas = document.createElement("canvas");
|
|
684
|
-
const width = img.width * scale
|
|
685
|
-
const height = img.height * scale
|
|
686
|
-
canvas.width = Math.ceil(width);
|
|
687
|
-
canvas.height = Math.ceil(height);
|
|
741
|
+
const width = img.width * scale;
|
|
742
|
+
const height = img.height * scale;
|
|
743
|
+
canvas.width = Math.ceil(width * dpr);
|
|
744
|
+
canvas.height = Math.ceil(height * dpr);
|
|
688
745
|
const ctx = canvas.getContext("2d");
|
|
689
|
-
ctx.scale(
|
|
690
|
-
ctx.drawImage(img, 0, 0);
|
|
746
|
+
ctx.scale(dpr, dpr);
|
|
747
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
748
|
+
canvas.style.width = `${width}px`;
|
|
749
|
+
canvas.style.height = `${height}px`;
|
|
691
750
|
return canvas;
|
|
692
751
|
}
|
|
693
752
|
async function toBlob(url) {
|
|
@@ -811,7 +870,7 @@ async function preCache(root = document, options = {}) {
|
|
|
811
870
|
}
|
|
812
871
|
for (const el of allEls) {
|
|
813
872
|
const bg = getComputedStyle(el).backgroundImage;
|
|
814
|
-
const url =
|
|
873
|
+
const url = extractURL(bg);
|
|
815
874
|
if (url && !bgCache.has(url)) {
|
|
816
875
|
promises.push(
|
|
817
876
|
fetchImage(url).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zumer/snapdom",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "snapDOM captures
|
|
3
|
+
"version": "1.3.0",
|
|
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",
|
|
7
7
|
"scripts": {
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"bugs": {
|
|
64
64
|
"url": "https://github.com/zumerlab/snapdom/issues"
|
|
65
65
|
},
|
|
66
|
-
"homepage": "https://github.
|
|
66
|
+
"homepage": "https://zumerlab.github.io/snapdom/",
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@vitest/browser": "^3.1.2",
|
|
69
69
|
"playwright": "^1.52.0"
|