@zumer/snapdom 1.1.0 → 1.2.2
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/dist/snapdom.js +48 -30
- package/dist/snapdom.min.js +3 -3
- package/dist/snapdom.min.mjs +3 -3
- package/dist/snapdom.mjs +44 -30
- package/package.json +4 -2
package/dist/snapdom.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.
|
|
4
|
+
* v.1.2.2
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -328,17 +328,25 @@
|
|
|
328
328
|
// src/modules/fonts.js
|
|
329
329
|
async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
330
330
|
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
const dpr = window.devicePixelRatio || 1;
|
|
332
|
+
const tempCanvas = document.createElement("canvas");
|
|
333
|
+
const tempCtx = tempCanvas.getContext("2d");
|
|
334
|
+
tempCtx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
|
|
335
|
+
const metrics = tempCtx.measureText(unicodeChar);
|
|
336
|
+
const ascent = metrics.actualBoundingBoxAscent || fontSize * 0.8;
|
|
337
|
+
const descent = metrics.actualBoundingBoxDescent || fontSize * 0.2;
|
|
338
|
+
const height = ascent + descent;
|
|
339
|
+
const width = metrics.width;
|
|
333
340
|
const canvas = document.createElement("canvas");
|
|
334
|
-
canvas.width =
|
|
335
|
-
canvas.height =
|
|
341
|
+
canvas.width = Math.ceil(width * dpr);
|
|
342
|
+
canvas.height = Math.ceil(height * dpr);
|
|
336
343
|
const ctx = canvas.getContext("2d");
|
|
337
|
-
ctx.
|
|
338
|
-
ctx.
|
|
339
|
-
ctx.
|
|
344
|
+
ctx.scale(dpr, dpr);
|
|
345
|
+
ctx.font = tempCtx.font;
|
|
346
|
+
ctx.textAlign = "left";
|
|
347
|
+
ctx.textBaseline = "alphabetic";
|
|
340
348
|
ctx.fillStyle = color;
|
|
341
|
-
ctx.fillText(unicodeChar,
|
|
349
|
+
ctx.fillText(unicodeChar, 0, ascent);
|
|
342
350
|
return canvas.toDataURL();
|
|
343
351
|
}
|
|
344
352
|
async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
@@ -349,7 +357,6 @@
|
|
|
349
357
|
const res = await fetch(link.href);
|
|
350
358
|
const cssText = await res.text();
|
|
351
359
|
if (ignoreIconFonts && (isIconFont(link.href) || isIconFont(cssText))) {
|
|
352
|
-
console.log("\u23ED\uFE0F Ignorando icon font CSS:", link.href);
|
|
353
360
|
continue;
|
|
354
361
|
}
|
|
355
362
|
const urlRegex = /url\(([^)]+)\)/g;
|
|
@@ -360,7 +367,6 @@
|
|
|
360
367
|
url = new URL(url, link.href).href;
|
|
361
368
|
}
|
|
362
369
|
if (ignoreIconFonts && isIconFont(url)) {
|
|
363
|
-
console.log("\u23ED\uFE0F Ignorando icon font URL:", url);
|
|
364
370
|
return null;
|
|
365
371
|
}
|
|
366
372
|
if (resourceCache.has(url)) {
|
|
@@ -377,7 +383,7 @@
|
|
|
377
383
|
resourceCache.set(url, b64);
|
|
378
384
|
return { original: match[0], inlined: `url(${b64})` };
|
|
379
385
|
} catch (err) {
|
|
380
|
-
console.warn("\u274C
|
|
386
|
+
console.warn("\u274C Failed to fetch font:", url);
|
|
381
387
|
return null;
|
|
382
388
|
}
|
|
383
389
|
})
|
|
@@ -390,7 +396,7 @@
|
|
|
390
396
|
}
|
|
391
397
|
finalCSS += cssFinal + "\n";
|
|
392
398
|
} catch (e) {
|
|
393
|
-
console.warn("\u274C
|
|
399
|
+
console.warn("\u274C Failed to fetch CSS:", link.href);
|
|
394
400
|
}
|
|
395
401
|
}
|
|
396
402
|
if (finalCSS && preCached) {
|
|
@@ -403,7 +409,7 @@
|
|
|
403
409
|
}
|
|
404
410
|
|
|
405
411
|
// src/modules/pseudo.js
|
|
406
|
-
async function inlinePseudoElements(source, clone, styleMap, styleCache, compress) {
|
|
412
|
+
async function inlinePseudoElements(source, clone, styleMap, styleCache, compress, embedFonts = false) {
|
|
407
413
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
408
414
|
for (const pseudo of ["::before", "::after"]) {
|
|
409
415
|
try {
|
|
@@ -425,7 +431,7 @@
|
|
|
425
431
|
styleMap.set(pseudoEl, key);
|
|
426
432
|
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(fontFamily);
|
|
427
433
|
let cleanContent = parseContent(content);
|
|
428
|
-
if (isIconFont2 && cleanContent.length === 1) {
|
|
434
|
+
if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
|
|
429
435
|
const imgEl = document.createElement("img");
|
|
430
436
|
imgEl.src = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
431
437
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
@@ -461,12 +467,12 @@
|
|
|
461
467
|
(child) => !child.dataset.snapdomPseudo
|
|
462
468
|
);
|
|
463
469
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
464
|
-
await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress);
|
|
470
|
+
await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress, embedFonts);
|
|
465
471
|
}
|
|
466
472
|
}
|
|
467
473
|
|
|
468
474
|
// src/core/prepare.js
|
|
469
|
-
async function prepareClone(element, compress = false) {
|
|
475
|
+
async function prepareClone(element, compress = false, embedFonts = false) {
|
|
470
476
|
const styleMap = /* @__PURE__ */ new Map();
|
|
471
477
|
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
472
478
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
@@ -478,19 +484,27 @@
|
|
|
478
484
|
throw e;
|
|
479
485
|
}
|
|
480
486
|
try {
|
|
481
|
-
await inlinePseudoElements(element, clone, styleMap, styleCache, compress);
|
|
487
|
+
await inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts);
|
|
482
488
|
} catch (e) {
|
|
483
489
|
console.warn("inlinePseudoElements failed:", e);
|
|
484
490
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
491
|
+
let classCSS = "";
|
|
492
|
+
if (compress) {
|
|
493
|
+
const keyToClass = generateCSSClasses(styleMap);
|
|
494
|
+
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
495
|
+
for (const [node, key] of styleMap.entries()) {
|
|
496
|
+
if (node.tagName === "STYLE") continue;
|
|
497
|
+
const className = keyToClass.get(key);
|
|
498
|
+
if (className) node.classList.add(className);
|
|
499
|
+
const bgImage = node.style?.backgroundImage;
|
|
500
|
+
node.removeAttribute("style");
|
|
501
|
+
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
for (const [node, key] of styleMap.entries()) {
|
|
505
|
+
if (node.tagName === "STYLE") continue;
|
|
506
|
+
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
507
|
+
}
|
|
494
508
|
}
|
|
495
509
|
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
496
510
|
const scrollX = originalNode.scrollLeft;
|
|
@@ -580,7 +594,7 @@
|
|
|
580
594
|
let baseCSS = "";
|
|
581
595
|
let dataURL;
|
|
582
596
|
let svgString;
|
|
583
|
-
({ clone, classCSS, styleCache } = await prepareClone(element, compress));
|
|
597
|
+
({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
|
|
584
598
|
await new Promise((resolve) => {
|
|
585
599
|
idle(async () => {
|
|
586
600
|
await inlineImages(clone);
|
|
@@ -596,7 +610,7 @@
|
|
|
596
610
|
if (embedFonts) {
|
|
597
611
|
await new Promise((resolve) => {
|
|
598
612
|
idle(async () => {
|
|
599
|
-
fontsCSS = await embedCustomFonts({ ignoreIconFonts:
|
|
613
|
+
fontsCSS = await embedCustomFonts({ ignoreIconFonts: !embedFonts });
|
|
600
614
|
resolve();
|
|
601
615
|
}, { fast });
|
|
602
616
|
});
|
|
@@ -806,8 +820,12 @@
|
|
|
806
820
|
}
|
|
807
821
|
}
|
|
808
822
|
if (embedFonts) {
|
|
809
|
-
await embedCustomFonts({ ignoreIconFonts:
|
|
823
|
+
await embedCustomFonts({ ignoreIconFonts: !embedFonts, preCached: true });
|
|
810
824
|
}
|
|
811
825
|
await Promise.all(promises);
|
|
812
826
|
}
|
|
827
|
+
|
|
828
|
+
// src/index.browser.js
|
|
829
|
+
window.snapdom = snapdom;
|
|
830
|
+
window.preCache = preCache;
|
|
813
831
|
})();
|
package/dist/snapdom.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(()=>{var
|
|
2
|
-
`;return o}function
|
|
3
|
-
`}catch{console.warn("\u274C
|
|
1
|
+
(()=>{var b=new Map,S=new Map,I=new Map,T=new Map,k=new Map,R=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(T.has(e))return T.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let i={};return T.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 a=getComputedStyle(n),r={};for(let i of a)r[i]=a.getPropertyValue(i);return o.removeChild(n),T.set(e,r),r}function U(e,t,o=!1){let n=[],a=_(t);for(let[r,i]of Object.entries(e))if(!o)i&&n.push(`${r}:${i}`);else{let c=a[r];i&&i!==c&&n.push(`${r}:${i}`)}return n.sort().join(";")}function H(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 Y(e){let t=new Map;for(let n of e){let a=T.get(n);if(!a)continue;let r=Object.entries(a).map(([i,c])=>`${i}:${c};`).sort().join("");t.has(r)||t.set(r,[]),t.get(r).push(n)}let o="";for(let[n,a]of t.entries())o+=`${a.join(",")} { ${n} }
|
|
2
|
+
`;return o}function K(e){let t=new Set(e.values()),o=new Map,n=1;for(let a of t)o.set(a,`c${n++}`);return o}function N(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function A(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let o=R.get(e);if(o||(o=new Map,R.set(e,o)),!o.has(t)){let n=window.getComputedStyle(e,t);o.set(t,n)}return o.get(t)}function J(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function X(e){let t=e.match(/url\(["']?([^"')]+)["']?\)/);return t?t[1]:null}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 b.has(e)?Promise.resolve(b.get(e)):new Promise((o,n)=>{let a=setTimeout(()=>{n(new Error("Image load timed out"))},t),r=new Image;r.crossOrigin="anonymous",r.onload=async()=>{clearTimeout(a);try{await r.decode();let i=document.createElement("canvas");i.width=r.width,i.height=r.height,i.getContext("2d").drawImage(r,0,0);try{let s=i.toDataURL("image/png");b.set(e,s),o(s)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(i){n(i)}},r.onerror=i=>{clearTimeout(a),n(new Error("Failed to load image: "+(i.message||"Unknown error")))},r.src=e})}function M(e){let t={};for(let o of e)t[o]=e.getPropertyValue(o);return t}function F(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function O(e,t,o,n,a){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,A(e));let r=n.get(e),i=M(r),c=e.tagName?.toLowerCase()||"div",s=U(i,c,a);o.set(t,s)}function D(e,t,o,n,a){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),O(e,c,t,o,a);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 r=e.cloneNode(!1);n.set(r,e),O(e,r,t,o,a);let i=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let s=D(c,t,o,n,a);s&&i.appendChild(s)}),r.appendChild(i),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>D(l,t,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),r.appendChild(s)}return r}async function G(e,t,o,n=32,a="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let r=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,m=l+d,h=s.width,p=document.createElement("canvas");p.width=Math.ceil(h*r),p.height=Math.ceil(m*r);let f=p.getContext("2d");return f.scale(r,r),f.font=c.font,f.textAlign="left",f.textBaseline="alphabetic",f.fillStyle=a,f.fillText(e,0,l),p.toDataURL()}async function j({ignoreIconFonts:e=!0,preCached:t=!1}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(a=>a.href),n="";for(let a of o)try{let i=await(await fetch(a.href)).text();if(e&&(B(a.href)||B(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let m=d[1].replace(/["']/g,"");if(m.startsWith("http")||(m=new URL(m,a.href).href),e&&B(m))return null;if(I.has(m))return{original:d[0],inlined:`url(${I.get(m)})`};try{let p=await(await fetch(m)).blob(),f=await new Promise(u=>{let w=new FileReader;w.onload=()=>u(w.result),w.readAsDataURL(p)});return I.set(m,f),{original:d[0],inlined:`url(${f})`}}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:",a.href)}if(n&&t){let a=document.createElement("style");a.setAttribute("data-snapdom","embedFonts"),a.textContent=n,document.head.appendChild(a)}return n}async function W(e,t,o,n,a,r=!1){if(!(e instanceof Element)||!(t instanceof Element))return;for(let s of["::before","::after"])try{let l=A(e,s);if(!l)continue;let d=l.getPropertyValue("content"),m=l.getPropertyValue("background-image"),h=d&&d!=="none"&&d!=='""'&&d!=="''",p=m&&m.startsWith("url(");if(h||p){let f=l.getPropertyValue("font-family"),u=parseInt(l.getPropertyValue("font-size"))||32,w=parseInt(l.getPropertyValue("font-weight"))||!1,L=l.getPropertyValue("color")||"#000",y=document.createElement("span");y.dataset.snapdomPseudo=s;let E=M(l),P=U(E,"span",a);o.set(y,P);let q=f&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(f),C=J(d);if(!r&&q&&C.length===1){let v=document.createElement("img");v.src=await G(C,f,w,u,L),v.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(v)}else if(C.startsWith("url(")){let v=C.match(/url\(["']?([^"')]+)["']?\)/);if(v?.[1])try{let $=document.createElement("img"),se=await x(v[1]);$.src=se,$.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild($)}catch($){console.error(`[snapdom] Error in pseudo ${s} for`,e,$)}}else C&&C!=="none"&&(y.textContent=C);s==="::before"?t.insertBefore(y,t.firstChild):t.appendChild(y)}}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 W(i[s],c[s],o,n,a,r)}async function Q(e,t=!1,o=!1){let n=new Map,a=new WeakMap,r=new Map,i;try{i=D(e,n,a,r,t)}catch(s){throw console.warn("deepClone failed:",s),s}try{await W(e,i,n,a,t,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(t){let s=K(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 m=s.get(d);m&&l.classList.add(m);let h=l.style?.backgroundImage;l.removeAttribute("style"),h&&h!=="none"&&(l.style.backgroundImage=h)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of r.entries()){let d=l.scrollLeft,m=l.scrollTop;if((d||m)&&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, ${-m}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:a}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let a=n.src;try{let r=await x(a);n.src=r,n.width||(n.width=n.naturalWidth||100),n.height||(n.height=n.naturalHeight||100)}catch{let r=document.createElement("div");r.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;`,r.innerText="img",n.replaceWith(r)}};for(let n=0;n<t.length;n+=4){let a=t.slice(n,n+4).map(o);await Promise.allSettled(a)}}async function ee(e,t,o){let n=[[e,t]];for(;n.length;){let[a,r]=n.shift(),i=o.get(a)||A(a);o.has(a)||o.set(a,i);let c=i.getPropertyValue("background-image");if(c&&c.includes("url(")){let d=c.match(/url\(["']?([^"')]+)["']?\)/);if(d?.[1])try{let m=d[1],h;S.has(m)?h=S.get(m):(h=await x(m),S.set(m,h)),r.style.backgroundImage=`url(${h})`}catch{r.style.backgroundImage="none"}}let s=Array.from(a.children),l=Array.from(r.children);for(let d=0;d<Math.min(s.length,l.length);d++)n.push([s[d],l[d]])}}async function te(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:a=!0,scale:r=1}=t,i,c,s,l="",d="",m,h;if({clone:i,classCSS:c,styleCache:s}=await Q(e,o,n),await new Promise(f=>{N(async()=>{await Z(i),f()},{fast:a})}),await new Promise(f=>{N(async()=>{await ee(e,i,s),f()},{fast:a})}),n&&await new Promise(f=>{N(async()=>{l=await j({ignoreIconFonts:!n}),f()},{fast:a})}),o){let f=H(i).sort(),u=f.join(",");k.has(u)?d=k.get(u):await new Promise(w=>{N(()=>{d=Y(f),k.set(u,d),w()},{fast:a})})}await new Promise(f=>{N(()=>{let u=e.getBoundingClientRect(),w=Math.ceil(u.width),L=Math.ceil(u.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",r!==1&&F()&&(i.style.scale=`${r}`);let y="http://www.w3.org/2000/svg",E=document.createElementNS(y,"foreignObject");E.setAttribute("width","100%"),E.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,E.appendChild(P),E.appendChild(i);let C=new XMLSerializer().serializeToString(E);h=`<svg xmlns="${y}" width="${w}" height="${L}" viewBox="0 0 ${w} ${L}">`+C+"</svg>",m=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(h)}`,f()},{fast:a})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),m}async function ce(e,{dpr:t=1,scale:o=1}){let n=new Image;return n.src=e,await n.decode(),F?(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 a=document.createElement("canvas"),r=n.width*o*t,i=n.height*o*t;a.width=Math.ceil(r),a.height=Math.ceil(i);let c=a.getContext("2d");return c.scale(o*t,o*t),c.drawImage(n,0,0),a}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 a=await ne(e,{dpr:t,scale:o});if(!n)return a;let r=document.createElement("canvas");r.width=a.width,r.height=a.height;let i=r.getContext("2d");return i.fillStyle=n,i.fillRect(0,0,r.width,r.height),i.drawImage(a,0,0),r}async function V(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:a},r="png"){let i=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${r}`,a),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:a="png",filename:r="capture"}={}){if(a==="svg"){let h=await oe(e),p=URL.createObjectURL(h),f=document.createElement("a");f.href=p,f.download=`${r}.svg`,f.click(),URL.revokeObjectURL(p);return}let i=["jpg","jpeg","webp"].includes(a)?"#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"}[a]||"image/png",d=s.toDataURL(l),m=document.createElement("a");m.href=d,m.download=`${r}.${a}`,m.click()}async function g(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await g.capture(e,t)}g.capture=async(e,t={})=>{let o=await te(e,t),n=window.devicePixelRatio||1,a=t.scale||1;return{url:o,options:t,toRaw:()=>o,toImg:()=>ce(o,{dpr:n,scale:a}),toCanvas:()=>ne(o,{dpr:n,scale:a}),toBlob:()=>oe(o),toPng:r=>V(o,{dpr:n,scale:a,...r},"png"),toJpg:r=>V(o,{dpr:n,scale:a,...r},"jpeg"),toWebp:r=>V(o,{dpr:n,scale:a,...r},"webp"),download:({format:r="png",filename:i="capture",backgroundColor:c}={})=>le(o,{dpr:n,scale:a,backgroundColor:c,format:r,filename:i})}};g.toRaw=async(e,t)=>(await g.capture(e,t)).toRaw();g.toImg=async(e,t)=>(await g.capture(e,t)).toImg();g.toCanvas=async(e,t)=>(await g.capture(e,t)).toCanvas();g.toBlob=async(e,t)=>(await g.capture(e,t)).toBlob();g.toPng=async(e,t)=>(await g.capture(e,t)).toPng(t);g.toJpg=async(e,t)=>(await g.capture(e,t)).toJpg(t);g.toWebp=async(e,t)=>(await g.capture(e,t)).toWebp(t);g.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:a,...r}=t;return await(await g.capture(e,r)).download({format:o,filename:n,backgroundColor:a})};async function re(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:a=!0}=t;if(n){b.clear(),S.clear(),I.clear(),k.clear(),R.clear();return}await document.fonts.ready,z();let r=[],i=[];e?.querySelectorAll&&(r=Array.from(e.querySelectorAll("img[src]")),i=Array.from(e.querySelectorAll("*")));let c=[];for(let s of r){let l=s.src;b.has(l)||c.push(x(l).then(d=>b.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=X(l);d&&!S.has(d)&&c.push(x(d).then(m=>S.set(d,m)).catch(()=>{}))}o&&await j({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}window.snapdom=g;window.preCache=re;})();
|
package/dist/snapdom.min.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`;return o}function
|
|
3
|
-
`}catch{console.warn("\u274C
|
|
1
|
+
var b=new Map,S=new Map,I=new Map,T=new Map,k=new Map,R=new WeakMap;var se=["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 se)_(e)}function _(e){if(T.has(e))return T.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let i={};return T.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 a=getComputedStyle(n),r={};for(let i of a)r[i]=a.getPropertyValue(i);return o.removeChild(n),T.set(e,r),r}function U(e,t,o=!1){let n=[],a=_(t);for(let[r,i]of Object.entries(e))if(!o)i&&n.push(`${r}:${i}`);else{let c=a[r];i&&i!==c&&n.push(`${r}:${i}`)}return n.sort().join(";")}function H(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 Y(e){let t=new Map;for(let n of e){let a=T.get(n);if(!a)continue;let r=Object.entries(a).map(([i,c])=>`${i}:${c};`).sort().join("");t.has(r)||t.set(r,[]),t.get(r).push(n)}let o="";for(let[n,a]of t.entries())o+=`${a.join(",")} { ${n} }
|
|
2
|
+
`;return o}function K(e){let t=new Set(e.values()),o=new Map,n=1;for(let a of t)o.set(a,`c${n++}`);return o}function N(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function A(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let o=R.get(e);if(o||(o=new Map,R.set(e,o)),!o.has(t)){let n=window.getComputedStyle(e,t);o.set(t,n)}return o.get(t)}function J(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function X(e){let t=e.match(/url\(["']?([^"')]+)["']?\)/);return t?t[1]:null}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 b.has(e)?Promise.resolve(b.get(e)):new Promise((o,n)=>{let a=setTimeout(()=>{n(new Error("Image load timed out"))},t),r=new Image;r.crossOrigin="anonymous",r.onload=async()=>{clearTimeout(a);try{await r.decode();let i=document.createElement("canvas");i.width=r.width,i.height=r.height,i.getContext("2d").drawImage(r,0,0);try{let s=i.toDataURL("image/png");b.set(e,s),o(s)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(i){n(i)}},r.onerror=i=>{clearTimeout(a),n(new Error("Failed to load image: "+(i.message||"Unknown error")))},r.src=e})}function M(e){let t={};for(let o of e)t[o]=e.getPropertyValue(o);return t}function F(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function O(e,t,o,n,a){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,A(e));let r=n.get(e),i=M(r),c=e.tagName?.toLowerCase()||"div",s=U(i,c,a);o.set(t,s)}function D(e,t,o,n,a){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),O(e,c,t,o,a);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 r=e.cloneNode(!1);n.set(r,e),O(e,r,t,o,a);let i=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let s=D(c,t,o,n,a);s&&i.appendChild(s)}),r.appendChild(i),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>D(l,t,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),r.appendChild(s)}return r}async function G(e,t,o,n=32,a="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let r=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,h=s.width,p=document.createElement("canvas");p.width=Math.ceil(h*r),p.height=Math.ceil(f*r);let m=p.getContext("2d");return m.scale(r,r),m.font=c.font,m.textAlign="left",m.textBaseline="alphabetic",m.fillStyle=a,m.fillText(e,0,l),p.toDataURL()}async function j({ignoreIconFonts:e=!0,preCached:t=!1}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(a=>a.href),n="";for(let a of o)try{let i=await(await fetch(a.href)).text();if(e&&(B(a.href)||B(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let f=d[1].replace(/["']/g,"");if(f.startsWith("http")||(f=new URL(f,a.href).href),e&&B(f))return null;if(I.has(f))return{original:d[0],inlined:`url(${I.get(f)})`};try{let p=await(await fetch(f)).blob(),m=await new Promise(u=>{let w=new FileReader;w.onload=()=>u(w.result),w.readAsDataURL(p)});return I.set(f,m),{original:d[0],inlined:`url(${m})`}}catch{return console.warn("\u274C Failed to fetch font:",f),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:",a.href)}if(n&&t){let a=document.createElement("style");a.setAttribute("data-snapdom","embedFonts"),a.textContent=n,document.head.appendChild(a)}return n}async function W(e,t,o,n,a,r=!1){if(!(e instanceof Element)||!(t instanceof Element))return;for(let s of["::before","::after"])try{let l=A(e,s);if(!l)continue;let d=l.getPropertyValue("content"),f=l.getPropertyValue("background-image"),h=d&&d!=="none"&&d!=='""'&&d!=="''",p=f&&f.startsWith("url(");if(h||p){let m=l.getPropertyValue("font-family"),u=parseInt(l.getPropertyValue("font-size"))||32,w=parseInt(l.getPropertyValue("font-weight"))||!1,L=l.getPropertyValue("color")||"#000",y=document.createElement("span");y.dataset.snapdomPseudo=s;let E=M(l),P=U(E,"span",a);o.set(y,P);let q=m&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(m),C=J(d);if(!r&&q&&C.length===1){let v=document.createElement("img");v.src=await G(C,m,w,u,L),v.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(v)}else if(C.startsWith("url(")){let v=C.match(/url\(["']?([^"')]+)["']?\)/);if(v?.[1])try{let $=document.createElement("img"),re=await x(v[1]);$.src=re,$.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild($)}catch($){console.error(`[snapdom] Error in pseudo ${s} for`,e,$)}}else C&&C!=="none"&&(y.textContent=C);s==="::before"?t.insertBefore(y,t.firstChild):t.appendChild(y)}}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 W(i[s],c[s],o,n,a,r)}async function Q(e,t=!1,o=!1){let n=new Map,a=new WeakMap,r=new Map,i;try{i=D(e,n,a,r,t)}catch(s){throw console.warn("deepClone failed:",s),s}try{await W(e,i,n,a,t,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(t){let s=K(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 h=l.style?.backgroundImage;l.removeAttribute("style"),h&&h!=="none"&&(l.style.backgroundImage=h)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of r.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:a}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let a=n.src;try{let r=await x(a);n.src=r,n.width||(n.width=n.naturalWidth||100),n.height||(n.height=n.naturalHeight||100)}catch{let r=document.createElement("div");r.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;`,r.innerText="img",n.replaceWith(r)}};for(let n=0;n<t.length;n+=4){let a=t.slice(n,n+4).map(o);await Promise.allSettled(a)}}async function ee(e,t,o){let n=[[e,t]];for(;n.length;){let[a,r]=n.shift(),i=o.get(a)||A(a);o.has(a)||o.set(a,i);let c=i.getPropertyValue("background-image");if(c&&c.includes("url(")){let d=c.match(/url\(["']?([^"')]+)["']?\)/);if(d?.[1])try{let f=d[1],h;S.has(f)?h=S.get(f):(h=await x(f),S.set(f,h)),r.style.backgroundImage=`url(${h})`}catch{r.style.backgroundImage="none"}}let s=Array.from(a.children),l=Array.from(r.children);for(let d=0;d<Math.min(s.length,l.length);d++)n.push([s[d],l[d]])}}async function te(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:a=!0,scale:r=1}=t,i,c,s,l="",d="",f,h;if({clone:i,classCSS:c,styleCache:s}=await Q(e,o,n),await new Promise(m=>{N(async()=>{await Z(i),m()},{fast:a})}),await new Promise(m=>{N(async()=>{await ee(e,i,s),m()},{fast:a})}),n&&await new Promise(m=>{N(async()=>{l=await j({ignoreIconFonts:!n}),m()},{fast:a})}),o){let m=H(i).sort(),u=m.join(",");k.has(u)?d=k.get(u):await new Promise(w=>{N(()=>{d=Y(m),k.set(u,d),w()},{fast:a})})}await new Promise(m=>{N(()=>{let u=e.getBoundingClientRect(),w=Math.ceil(u.width),L=Math.ceil(u.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",r!==1&&F()&&(i.style.scale=`${r}`);let y="http://www.w3.org/2000/svg",E=document.createElementNS(y,"foreignObject");E.setAttribute("width","100%"),E.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,E.appendChild(P),E.appendChild(i);let C=new XMLSerializer().serializeToString(E);h=`<svg xmlns="${y}" width="${w}" height="${L}" viewBox="0 0 ${w} ${L}">`+C+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(h)}`,m()},{fast:a})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),f}async function ie(e,{dpr:t=1,scale:o=1}){let n=new Image;return n.src=e,await n.decode(),F?(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 a=document.createElement("canvas"),r=n.width*o*t,i=n.height*o*t;a.width=Math.ceil(r),a.height=Math.ceil(i);let c=a.getContext("2d");return c.scale(o*t,o*t),c.drawImage(n,0,0),a}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 a=await ne(e,{dpr:t,scale:o});if(!n)return a;let r=document.createElement("canvas");r.width=a.width,r.height=a.height;let i=r.getContext("2d");return i.fillStyle=n,i.fillRect(0,0,r.width,r.height),i.drawImage(a,0,0),r}async function V(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:a},r="png"){let i=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${r}`,a),await c.decode(),c.style.width=`${i.width/t}px`,c.style.height=`${i.height/t}px`,c}async function ce(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:a="png",filename:r="capture"}={}){if(a==="svg"){let h=await oe(e),p=URL.createObjectURL(h),m=document.createElement("a");m.href=p,m.download=`${r}.svg`,m.click(),URL.revokeObjectURL(p);return}let i=["jpg","jpeg","webp"].includes(a)?"#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"}[a]||"image/png",d=s.toDataURL(l),f=document.createElement("a");f.href=d,f.download=`${r}.${a}`,f.click()}async function g(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await g.capture(e,t)}g.capture=async(e,t={})=>{let o=await te(e,t),n=window.devicePixelRatio||1,a=t.scale||1;return{url:o,options:t,toRaw:()=>o,toImg:()=>ie(o,{dpr:n,scale:a}),toCanvas:()=>ne(o,{dpr:n,scale:a}),toBlob:()=>oe(o),toPng:r=>V(o,{dpr:n,scale:a,...r},"png"),toJpg:r=>V(o,{dpr:n,scale:a,...r},"jpeg"),toWebp:r=>V(o,{dpr:n,scale:a,...r},"webp"),download:({format:r="png",filename:i="capture",backgroundColor:c}={})=>ce(o,{dpr:n,scale:a,backgroundColor:c,format:r,filename:i})}};g.toRaw=async(e,t)=>(await g.capture(e,t)).toRaw();g.toImg=async(e,t)=>(await g.capture(e,t)).toImg();g.toCanvas=async(e,t)=>(await g.capture(e,t)).toCanvas();g.toBlob=async(e,t)=>(await g.capture(e,t)).toBlob();g.toPng=async(e,t)=>(await g.capture(e,t)).toPng(t);g.toJpg=async(e,t)=>(await g.capture(e,t)).toJpg(t);g.toWebp=async(e,t)=>(await g.capture(e,t)).toWebp(t);g.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:a,...r}=t;return await(await g.capture(e,r)).download({format:o,filename:n,backgroundColor:a})};async function le(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:a=!0}=t;if(n){b.clear(),S.clear(),I.clear(),k.clear(),R.clear();return}await document.fonts.ready,z();let r=[],i=[];e?.querySelectorAll&&(r=Array.from(e.querySelectorAll("img[src]")),i=Array.from(e.querySelectorAll("*")));let c=[];for(let s of r){let l=s.src;b.has(l)||c.push(x(l).then(d=>b.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=X(l);d&&!S.has(d)&&c.push(x(d).then(f=>S.set(d,f)).catch(()=>{}))}o&&await j({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}export{le as preCache,g as snapdom};
|
package/dist/snapdom.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.
|
|
4
|
+
* v.1.2.2
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -328,17 +328,25 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress) {
|
|
|
328
328
|
// src/modules/fonts.js
|
|
329
329
|
async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
330
330
|
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
const dpr = window.devicePixelRatio || 1;
|
|
332
|
+
const tempCanvas = document.createElement("canvas");
|
|
333
|
+
const tempCtx = tempCanvas.getContext("2d");
|
|
334
|
+
tempCtx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
|
|
335
|
+
const metrics = tempCtx.measureText(unicodeChar);
|
|
336
|
+
const ascent = metrics.actualBoundingBoxAscent || fontSize * 0.8;
|
|
337
|
+
const descent = metrics.actualBoundingBoxDescent || fontSize * 0.2;
|
|
338
|
+
const height = ascent + descent;
|
|
339
|
+
const width = metrics.width;
|
|
333
340
|
const canvas = document.createElement("canvas");
|
|
334
|
-
canvas.width =
|
|
335
|
-
canvas.height =
|
|
341
|
+
canvas.width = Math.ceil(width * dpr);
|
|
342
|
+
canvas.height = Math.ceil(height * dpr);
|
|
336
343
|
const ctx = canvas.getContext("2d");
|
|
337
|
-
ctx.
|
|
338
|
-
ctx.
|
|
339
|
-
ctx.
|
|
344
|
+
ctx.scale(dpr, dpr);
|
|
345
|
+
ctx.font = tempCtx.font;
|
|
346
|
+
ctx.textAlign = "left";
|
|
347
|
+
ctx.textBaseline = "alphabetic";
|
|
340
348
|
ctx.fillStyle = color;
|
|
341
|
-
ctx.fillText(unicodeChar,
|
|
349
|
+
ctx.fillText(unicodeChar, 0, ascent);
|
|
342
350
|
return canvas.toDataURL();
|
|
343
351
|
}
|
|
344
352
|
async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
@@ -349,7 +357,6 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
349
357
|
const res = await fetch(link.href);
|
|
350
358
|
const cssText = await res.text();
|
|
351
359
|
if (ignoreIconFonts && (isIconFont(link.href) || isIconFont(cssText))) {
|
|
352
|
-
console.log("\u23ED\uFE0F Ignorando icon font CSS:", link.href);
|
|
353
360
|
continue;
|
|
354
361
|
}
|
|
355
362
|
const urlRegex = /url\(([^)]+)\)/g;
|
|
@@ -360,7 +367,6 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
360
367
|
url = new URL(url, link.href).href;
|
|
361
368
|
}
|
|
362
369
|
if (ignoreIconFonts && isIconFont(url)) {
|
|
363
|
-
console.log("\u23ED\uFE0F Ignorando icon font URL:", url);
|
|
364
370
|
return null;
|
|
365
371
|
}
|
|
366
372
|
if (resourceCache.has(url)) {
|
|
@@ -377,7 +383,7 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
377
383
|
resourceCache.set(url, b64);
|
|
378
384
|
return { original: match[0], inlined: `url(${b64})` };
|
|
379
385
|
} catch (err) {
|
|
380
|
-
console.warn("\u274C
|
|
386
|
+
console.warn("\u274C Failed to fetch font:", url);
|
|
381
387
|
return null;
|
|
382
388
|
}
|
|
383
389
|
})
|
|
@@ -390,7 +396,7 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
390
396
|
}
|
|
391
397
|
finalCSS += cssFinal + "\n";
|
|
392
398
|
} catch (e) {
|
|
393
|
-
console.warn("\u274C
|
|
399
|
+
console.warn("\u274C Failed to fetch CSS:", link.href);
|
|
394
400
|
}
|
|
395
401
|
}
|
|
396
402
|
if (finalCSS && preCached) {
|
|
@@ -403,7 +409,7 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
403
409
|
}
|
|
404
410
|
|
|
405
411
|
// src/modules/pseudo.js
|
|
406
|
-
async function inlinePseudoElements(source, clone, styleMap, styleCache, compress) {
|
|
412
|
+
async function inlinePseudoElements(source, clone, styleMap, styleCache, compress, embedFonts = false) {
|
|
407
413
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
408
414
|
for (const pseudo of ["::before", "::after"]) {
|
|
409
415
|
try {
|
|
@@ -425,7 +431,7 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
425
431
|
styleMap.set(pseudoEl, key);
|
|
426
432
|
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(fontFamily);
|
|
427
433
|
let cleanContent = parseContent(content);
|
|
428
|
-
if (isIconFont2 && cleanContent.length === 1) {
|
|
434
|
+
if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
|
|
429
435
|
const imgEl = document.createElement("img");
|
|
430
436
|
imgEl.src = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
431
437
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
@@ -461,12 +467,12 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
461
467
|
(child) => !child.dataset.snapdomPseudo
|
|
462
468
|
);
|
|
463
469
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
464
|
-
await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress);
|
|
470
|
+
await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress, embedFonts);
|
|
465
471
|
}
|
|
466
472
|
}
|
|
467
473
|
|
|
468
474
|
// src/core/prepare.js
|
|
469
|
-
async function prepareClone(element, compress = false) {
|
|
475
|
+
async function prepareClone(element, compress = false, embedFonts = false) {
|
|
470
476
|
const styleMap = /* @__PURE__ */ new Map();
|
|
471
477
|
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
472
478
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
@@ -478,19 +484,27 @@ async function prepareClone(element, compress = false) {
|
|
|
478
484
|
throw e;
|
|
479
485
|
}
|
|
480
486
|
try {
|
|
481
|
-
await inlinePseudoElements(element, clone, styleMap, styleCache, compress);
|
|
487
|
+
await inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts);
|
|
482
488
|
} catch (e) {
|
|
483
489
|
console.warn("inlinePseudoElements failed:", e);
|
|
484
490
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
491
|
+
let classCSS = "";
|
|
492
|
+
if (compress) {
|
|
493
|
+
const keyToClass = generateCSSClasses(styleMap);
|
|
494
|
+
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
495
|
+
for (const [node, key] of styleMap.entries()) {
|
|
496
|
+
if (node.tagName === "STYLE") continue;
|
|
497
|
+
const className = keyToClass.get(key);
|
|
498
|
+
if (className) node.classList.add(className);
|
|
499
|
+
const bgImage = node.style?.backgroundImage;
|
|
500
|
+
node.removeAttribute("style");
|
|
501
|
+
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
for (const [node, key] of styleMap.entries()) {
|
|
505
|
+
if (node.tagName === "STYLE") continue;
|
|
506
|
+
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
507
|
+
}
|
|
494
508
|
}
|
|
495
509
|
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
496
510
|
const scrollX = originalNode.scrollLeft;
|
|
@@ -580,7 +594,7 @@ async function captureDOM(element, options = {}) {
|
|
|
580
594
|
let baseCSS = "";
|
|
581
595
|
let dataURL;
|
|
582
596
|
let svgString;
|
|
583
|
-
({ clone, classCSS, styleCache } = await prepareClone(element, compress));
|
|
597
|
+
({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
|
|
584
598
|
await new Promise((resolve) => {
|
|
585
599
|
idle(async () => {
|
|
586
600
|
await inlineImages(clone);
|
|
@@ -596,7 +610,7 @@ async function captureDOM(element, options = {}) {
|
|
|
596
610
|
if (embedFonts) {
|
|
597
611
|
await new Promise((resolve) => {
|
|
598
612
|
idle(async () => {
|
|
599
|
-
fontsCSS = await embedCustomFonts({ ignoreIconFonts:
|
|
613
|
+
fontsCSS = await embedCustomFonts({ ignoreIconFonts: !embedFonts });
|
|
600
614
|
resolve();
|
|
601
615
|
}, { fast });
|
|
602
616
|
});
|
|
@@ -806,7 +820,7 @@ async function preCache(root = document, options = {}) {
|
|
|
806
820
|
}
|
|
807
821
|
}
|
|
808
822
|
if (embedFonts) {
|
|
809
|
-
await embedCustomFonts({ ignoreIconFonts:
|
|
823
|
+
await embedCustomFonts({ ignoreIconFonts: !embedFonts, preCached: true });
|
|
810
824
|
}
|
|
811
825
|
await Promise.all(promises);
|
|
812
826
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zumer/snapdom",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "snapDOM captures DOM elements as images with exceptional speed and minimal overhead. Designed to avoid bottlenecks and long tasks.",
|
|
5
5
|
"main": "dist/snapdom.js",
|
|
6
6
|
"module": "dist/snapdom.mjs",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "npx vitest run --browser.headless --reporter=verbose",
|
|
9
9
|
"test:benchmark": "npx vitest bench --browser.headless --watch=false",
|
|
10
|
-
"compile-browser": "npx @zumerbox/build -n snapdom -j src/index.js",
|
|
10
|
+
"compile-browser": "npx @zumerbox/build -n snapdom -j src/index.browser.js",
|
|
11
11
|
"compile-esm": "npx @zumerbox/build -n snapdom -j src/index.js -p neutral -x",
|
|
12
12
|
"compile": "npm run compile-browser && npm run compile-esm",
|
|
13
13
|
"build": "npm run compile && npm pack",
|
|
14
|
+
"bump:dry": "npx @zumerbox/bump -d",
|
|
14
15
|
"bump": "npx @zumerbox/bump && npx @zumerbox/changelog",
|
|
15
16
|
"prebuild": "git add CHANGELOG.md && git commit -m \"Bumped version\" && git push --follow-tags"
|
|
16
17
|
},
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
"types": "types/snapdom.d.ts",
|
|
22
23
|
"exports": {
|
|
23
24
|
".": {
|
|
25
|
+
"types": "./types/snapdom.d.ts",
|
|
24
26
|
"import": "./dist/snapdom.mjs",
|
|
25
27
|
"require": "./dist/snapdom.js"
|
|
26
28
|
}
|