@zumer/snapdom 1.0.0-pre.1747581859131 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # snapDOM
2
2
 
3
3
  <p align="center">
4
- <a href="http://zumerlab.github.io/orbit-docs">
4
+ <a href="http://zumerlab.github.io/snapdom">
5
5
  <img src="https://raw.githubusercontent.com/zumerlab/snapdom/main/docs/assets/hero.png" width="80%">
6
6
  </a>
7
7
  </p>
@@ -64,7 +64,7 @@ import { snapdom } from './snapdom.mjs';
64
64
 
65
65
  ## Basic usage
66
66
 
67
- ### Reusable result (recommended)
67
+ ### Reusable capture
68
68
 
69
69
  ```js
70
70
  const el = document.querySelector('#target');
@@ -141,6 +141,31 @@ All capture methods accept an `options` object:
141
141
  }
142
142
  ```
143
143
 
144
+
145
+ ### `preCache()` – Optional helper
146
+
147
+ The `preCache()` function can be used to load external resources (like images and fonts) in advance. It is specially useful when the element to capure is big and complex.
148
+
149
+ ```js
150
+ import { preCache } from '@zumer/snapdom';
151
+
152
+ await preCache(document.body);
153
+ ```
154
+
155
+ ```js
156
+ import { snapdom, preCache } from './snapdom.mjs';
157
+ window.addEventListener('load', async () => {
158
+ await preCache();
159
+ console.log('📦 Resources preloaded');
160
+ });
161
+ ```
162
+
163
+ **Options for `preCache()`:**
164
+
165
+ * `embedFonts` *(boolean, default: true)* — Inlines non-icon fonts during preload.
166
+ * `reset` *(boolean, default: false)* — Clears all existing internal caches.
167
+
168
+
144
169
  ## Features
145
170
 
146
171
  * Captures **shadow DOM** and Web Components
@@ -156,6 +181,41 @@ All capture methods accept an `options` object:
156
181
  * Iframes are not supported
157
182
  * When WebP format is used on Safari, it will fallback to PNG rendering.
158
183
 
184
+ ## Benchmarks
185
+
186
+ `snapDOM` is not only highly accurate — it’s **extremely fast**.
187
+
188
+
189
+ Latest benchmarks show significant performance improvements against other libraries:
190
+
191
+ | Scenario | vs. `modern-screenshot` | vs. `html2canvas` |
192
+ | -------------------------------- | :---------------------: | :---------------: |
193
+ | Small element (200×100) | 6.46× faster | 32.27× faster |
194
+ | Modal size (400×300) | 7.28× faster | 32.66× faster |
195
+ | Page view (1200×800) | 13.17× faster | 35.29× faster |
196
+ | Large scroll area (2000×1500) | 38.23× faster | 68.85× faster |
197
+ | Very large element (4000×2000) | 93.31× faster | 133.12× faster |
198
+ | Complex small element (200×100) | 3.97× faster | 15.23× faster |
199
+ | Complex modal (400×300) | 2.32× faster | 5.33× faster |
200
+ | Complex page (1200×800) | 1.62× faster | 1.65× faster |
201
+ | Complex large scroll (2000×1500) | 1.66× faster | 1.24× faster |
202
+ | Complex very large (4000×2000) | 1.52× faster | 1.28× faster |
203
+
204
+
205
+ ### Run the benchmarks
206
+
207
+ To run these benchmarks yourself:
208
+
209
+ ```sh
210
+ git clone https://github.com/zumerlab/snapdom.git
211
+ cd snapdom
212
+ npm install
213
+ npm run test:benchmark
214
+ ```
215
+
216
+ They execute in **headless Chromium** using real DOM nodes.
217
+
218
+
159
219
  ## License
160
220
 
161
221
  MIT © Zumerlab
package/dist/snapdom.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.0.0-pre.1747581859131
4
+ * v.1.1.0
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -300,8 +300,6 @@
300
300
  return img;
301
301
  }
302
302
  if (node.nodeType === Node.TEXT_NODE) {
303
- const trimmed = node.textContent.trim();
304
- if (!trimmed) return null;
305
303
  if (node.parentElement?.shadowRoot) {
306
304
  const tag = node.parentElement.tagName.toLowerCase();
307
305
  if (customElements.get(tag)) return null;
@@ -471,7 +469,7 @@
471
469
  async function prepareClone(element, compress = false) {
472
470
  const styleMap = /* @__PURE__ */ new Map();
473
471
  const styleCache = /* @__PURE__ */ new WeakMap();
474
- const nodeMap = /* @__PURE__ */ new WeakMap();
472
+ const nodeMap = /* @__PURE__ */ new Map();
475
473
  let clone;
476
474
  try {
477
475
  clone = deepClone(element, styleMap, styleCache, nodeMap, compress);
@@ -494,6 +492,25 @@
494
492
  node.removeAttribute("style");
495
493
  if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
496
494
  }
495
+ for (const [cloneNode, originalNode] of nodeMap.entries()) {
496
+ const scrollX = originalNode.scrollLeft;
497
+ const scrollY = originalNode.scrollTop;
498
+ const hasScroll = scrollX || scrollY;
499
+ if (hasScroll && cloneNode instanceof HTMLElement) {
500
+ cloneNode.style.overflow = "hidden";
501
+ cloneNode.style.scrollbarWidth = "none";
502
+ cloneNode.style.msOverflowStyle = "none";
503
+ const inner = document.createElement("div");
504
+ inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`;
505
+ inner.style.willChange = "transform";
506
+ inner.style.display = "inline-block";
507
+ inner.style.width = "100%";
508
+ while (cloneNode.firstChild) {
509
+ inner.appendChild(cloneNode.firstChild);
510
+ }
511
+ cloneNode.appendChild(inner);
512
+ }
513
+ }
497
514
  return { clone, classCSS, styleCache };
498
515
  }
499
516
 
@@ -726,7 +743,7 @@
726
743
  toImg: () => toImg(url, { dpr, scale }),
727
744
  toCanvas: () => toCanvas(url, { dpr, scale }),
728
745
  toBlob: () => toBlob(url),
729
- toPng: () => toRasterImg(url, { dpr, scale }, "png"),
746
+ toPng: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "png"),
730
747
  toJpg: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "jpeg"),
731
748
  toWebp: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "webp"),
732
749
  download: ({ format = "png", filename = "capture", backgroundColor } = {}) => download(url, { dpr, scale, backgroundColor, format, filename })
@@ -1,3 +1,3 @@
1
- (()=>{var S=new Map,E=new Map,$=new Map,T=new Map,k=new Map,R=new WeakMap;var re=["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 _(){for(let e of re)z(e)}function z(e){if(T.has(e))return T.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let s={};return T.set(e,s),s}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 s of a)r[s]=a.getPropertyValue(s);return o.removeChild(n),T.set(e,r),r}function U(e,t,o=!1){let n=[],a=z(t);for(let[r,s]of Object.entries(e))if(!o)s&&n.push(`${r}:${s}`);else{let c=a[r];s&&s!==c&&n.push(`${r}:${s}`)}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 K(e){let t=new Map;for(let n of e){let a=T.get(n);if(!a)continue;let r=Object.entries(a).map(([s,c])=>`${s}:${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 J(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 Y(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 C(e,t=3e3){return S.has(e)?Promise.resolve(S.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 s=document.createElement("canvas");s.width=r.width,s.height=r.height,s.getContext("2d").drawImage(r,0,0);try{let i=s.toDataURL("image/png");S.set(e,i),o(i)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(s){n(s)}},r.onerror=s=>{clearTimeout(a),n(new Error("Failed to load image: "+(s.message||"Unknown error")))},r.src=e})}function M(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 O(e,t,o,n,a){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,A(e));let r=n.get(e),s=M(r),c=e.tagName?.toLowerCase()||"div",i=U(s,c,a);o.set(t,i)}function F(e,t,o,n,a){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),i=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${i.width}px; height: ${i.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 i=document.createElement("div");return i.textContent=e.getAttribute("data-placeholder-text")||"",i.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(i),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),i=document.createElement("img");return i.src=c,i.width=e.width,i.height=e.height,i.style.display="inline-block",i.style.width=`${e.width}px`,i.style.height=`${e.height}px`,i}if(e.nodeType===Node.TEXT_NODE){if(!e.textContent.trim())return null;if(e.parentElement?.shadowRoot){let i=e.parentElement.tagName.toLowerCase();if(customElements.get(i))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 s=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let i=F(c,t,o,n,a);i&&s.appendChild(i)}),r.appendChild(s),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(m=>m.tagName!=="STYLE").map(m=>F(m,t,o,n)).filter(Boolean),i=document.createDocumentFragment();c.forEach(m=>i.appendChild(m)),r.appendChild(i)}return r}async function G(e,t,o,n=32,a="#000"){t=t.replace(/^['"]+|['"]+$/g,""),await document.fonts.ready,await document.fonts.load(`${n}px "${t}"`);let r=document.createElement("canvas");r.width=n,r.height=n;let s=r.getContext("2d");return s.font=o?`${o} ${n}px "${t}"`:` ${n}px "${t}"`,s.textAlign="center",s.textBaseline="middle",s.fillStyle=a,s.fillText(e,r.width/2,r.height/2),r.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 s=await(await fetch(a.href)).text();if(e&&(B(a.href)||B(s))){console.log("\u23ED\uFE0F Ignorando icon font CSS:",a.href);continue}let c=/url\(([^)]+)\)/g,i=await Promise.all(Array.from(s.matchAll(c)).map(async l=>{let d=l[1].replace(/["']/g,"");if(d.startsWith("http")||(d=new URL(d,a.href).href),e&&B(d))return console.log("\u23ED\uFE0F Ignorando icon font URL:",d),null;if($.has(d))return{original:l[0],inlined:`url(${$.get(d)})`};try{let h=await(await fetch(d)).blob(),p=await new Promise(u=>{let w=new FileReader;w.onload=()=>u(w.result),w.readAsDataURL(h)});return $.set(d,p),{original:l[0],inlined:`url(${p})`}}catch{return console.warn("\u274C No pude fetch font",d),null}})),m=s;for(let l of i)l&&(m=m.replace(l.original,l.inlined));n+=m+`
3
- `}catch{console.warn("\u274C No pude 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){if(!(e instanceof Element)||!(t instanceof Element))return;for(let c of["::before","::after"])try{let i=A(e,c);if(!i)continue;let m=i.getPropertyValue("content"),l=i.getPropertyValue("background-image"),d=m&&m!=="none"&&m!=='""'&&m!=="''",g=l&&l.startsWith("url(");if(d||g){let h=i.getPropertyValue("font-family"),p=parseInt(i.getPropertyValue("font-size"))||32,u=parseInt(i.getPropertyValue("font-weight"))||!1,w=i.getPropertyValue("color")||"#000",y=document.createElement("span");y.dataset.snapdomPseudo=c;let P=M(i),v=U(P,"span",a);o.set(y,v);let L=h&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(h),x=Y(m);if(L&&x.length===1){let b=document.createElement("img");b.src=await G(x,h,u,p,w),b.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(b)}else if(x.startsWith("url(")){let b=x.match(/url\(["']?([^"')]+)["']?\)/);if(b?.[1])try{let I=document.createElement("img"),q=await C(b[1]);I.src=q,I.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(I)}catch(I){console.error(`[snapdom] Error in pseudo ${c} for`,e,I)}}else x&&x!=="none"&&(y.textContent=x);c==="::before"?t.insertBefore(y,t.firstChild):t.appendChild(y)}}catch(i){console.warn(`[snapdom] Failed to capture ${c} for`,e,i)}let r=Array.from(e.children),s=Array.from(t.children).filter(c=>!c.dataset.snapdomPseudo);for(let c=0;c<Math.min(r.length,s.length);c++)await W(r[c],s[c],o,n,a)}async function Q(e,t=!1){let o=new Map,n=new WeakMap,a=new WeakMap,r;try{r=F(e,o,n,a,t)}catch(i){throw console.warn("deepClone failed:",i),i}try{await W(e,r,o,n,t)}catch(i){console.warn("inlinePseudoElements failed:",i)}let s=J(o),c=Array.from(s.entries()).map(([i,m])=>`.${m}{${i}}`).join("");for(let[i,m]of o.entries()){if(i.tagName==="STYLE")continue;let l=s.get(m);l&&i.classList.add(l);let d=i.style?.backgroundImage;i.removeAttribute("style"),d&&d!=="none"&&(i.style.backgroundImage=d)}return{clone:r,classCSS:c,styleCache:n}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let a=n.src;try{let r=await C(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(),s=o.get(a)||A(a);o.has(a)||o.set(a,s);let c=s.getPropertyValue("background-image");if(c&&c.includes("url(")){let l=c.match(/url\(["']?([^"')]+)["']?\)/);if(l?.[1])try{let d=l[1],g;E.has(d)?g=E.get(d):(g=await C(d),E.set(d,g)),r.style.backgroundImage=`url(${g})`}catch{r.style.backgroundImage="none"}}let i=Array.from(a.children),m=Array.from(r.children);for(let l=0;l<Math.min(i.length,m.length);l++)n.push([i[l],m[l]])}}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,s,c,i,m="",l="",d,g;if({clone:s,classCSS:c,styleCache:i}=await Q(e,o),await new Promise(p=>{N(async()=>{await Z(s),p()},{fast:a})}),await new Promise(p=>{N(async()=>{await ee(e,s,i),p()},{fast:a})}),n&&await new Promise(p=>{N(async()=>{m=await j({ignoreIconFonts:!0}),p()},{fast:a})}),o){let p=H(s).sort(),u=p.join(",");k.has(u)?l=k.get(u):await new Promise(w=>{N(()=>{l=K(p),k.set(u,l),w()},{fast:a})})}await new Promise(p=>{N(()=>{let u=e.getBoundingClientRect(),w=Math.ceil(u.width),y=Math.ceil(u.height);s.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),s.style.transformOrigin="top left",r!==1&&D()&&(s.style.scale=`${r}`);let P="http://www.w3.org/2000/svg",v=document.createElementNS(P,"foreignObject");v.setAttribute("width","100%"),v.setAttribute("height","100%");let L=document.createElement("style");L.textContent=l+m+"svg{overflow:visible;}"+c,v.appendChild(L),v.appendChild(s);let b=new XMLSerializer().serializeToString(v);g=`<svg xmlns="${P}" width="${w}" height="${y}" viewBox="0 0 ${w} ${y}">`+b+"</svg>",d=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(g)}`,p()},{fast:a})});let h=document.getElementById("snapdom-sandbox");return h&&h.style.position==="absolute"&&h.remove(),d}async function se(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 a=document.createElement("canvas"),r=n.width*o*t,s=n.height*o*t;a.width=Math.ceil(r),a.height=Math.ceil(s);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 s=r.getContext("2d");return s.fillStyle=n,s.fillRect(0,0,r.width,r.height),s.drawImage(a,0,0),r}async function V(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:a},r="png"){let s=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=s.toDataURL(`image/${r}`,a),await c.decode(),c.style.width=`${s.width/t}px`,c.style.height=`${s.height/t}px`,c}async function ie(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:a="png",filename:r="capture"}={}){if(a==="svg"){let g=await oe(e),h=URL.createObjectURL(g),p=document.createElement("a");p.href=h,p.download=`${r}.svg`,p.click(),URL.revokeObjectURL(h);return}let s=["jpg","jpeg","webp"].includes(a)?"#fff":void 0,i=await ae(e,{dpr:t,scale:o},n??s),m={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[a]||"image/png",l=i.toDataURL(m),d=document.createElement("a");d.href=l,d.download=`${r}.${a}`,d.click()}async function f(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await f.capture(e,t)}f.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:()=>se(o,{dpr:n,scale:a}),toCanvas:()=>ne(o,{dpr:n,scale:a}),toBlob:()=>oe(o),toPng:()=>V(o,{dpr:n,scale:a},"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:s="capture",backgroundColor:c}={})=>ie(o,{dpr:n,scale:a,backgroundColor:c,format:r,filename:s})}};f.toRaw=async(e,t)=>(await f.capture(e,t)).toRaw();f.toImg=async(e,t)=>(await f.capture(e,t)).toImg();f.toCanvas=async(e,t)=>(await f.capture(e,t)).toCanvas();f.toBlob=async(e,t)=>(await f.capture(e,t)).toBlob();f.toPng=async(e,t)=>(await f.capture(e,t)).toPng(t);f.toJpg=async(e,t)=>(await f.capture(e,t)).toJpg(t);f.toWebp=async(e,t)=>(await f.capture(e,t)).toWebp(t);f.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:a,...r}=t;return await(await f.capture(e,r)).download({format:o,filename:n,backgroundColor:a})};async function ce(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:a=!0}=t;if(n){S.clear(),E.clear(),$.clear(),k.clear(),R.clear();return}await document.fonts.ready,_();let r=[],s=[];e?.querySelectorAll&&(r=Array.from(e.querySelectorAll("img[src]")),s=Array.from(e.querySelectorAll("*")));let c=[];for(let i of r){let m=i.src;S.has(m)||c.push(C(m).then(l=>S.set(m,l)).catch(()=>{}))}for(let i of s){let m=getComputedStyle(i).backgroundImage,l=X(m);l&&!E.has(l)&&c.push(C(l).then(d=>E.set(l,d)).catch(()=>{}))}o&&await j({ignoreIconFonts:!0,preCached:!0}),await Promise.all(c)}})();
1
+ (()=>{var S=new Map,E=new Map,I=new Map,T=new Map,k=new Map,R=new WeakMap;var re=["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 _(){for(let e of re)z(e)}function z(e){if(T.has(e))return T.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let s={};return T.set(e,s),s}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 s of r)a[s]=r.getPropertyValue(s);return o.removeChild(n),T.set(e,a),a}function U(e,t,o=!1){let n=[],r=z(t);for(let[a,s]of Object.entries(e))if(!o)s&&n.push(`${a}:${s}`);else{let c=r[a];s&&s!==c&&n.push(`${a}:${s}`)}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 K(e){let t=new Map;for(let n of e){let r=T.get(n);if(!r)continue;let a=Object.entries(r).map(([s,c])=>`${s}:${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 Y(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 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 M(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 C(e,t=3e3){return S.has(e)?Promise.resolve(S.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 s=document.createElement("canvas");s.width=a.width,s.height=a.height,s.getContext("2d").drawImage(a,0,0);try{let i=s.toDataURL("image/png");S.set(e,i),o(i)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(s){n(s)}},a.onerror=s=>{clearTimeout(r),n(new Error("Failed to load image: "+(s.message||"Unknown error")))},a.src=e})}function B(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,r){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,A(e));let a=n.get(e),s=B(a),c=e.tagName?.toLowerCase()||"div",i=U(s,c,r);o.set(t,i)}function D(e,t,o,n,r){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),i=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${i.width}px; height: ${i.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,r);let i=document.createElement("div");return i.textContent=e.getAttribute("data-placeholder-text")||"",i.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(i),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),i=document.createElement("img");return i.src=c,i.width=e.width,i.height=e.height,i.style.display="inline-block",i.style.width=`${e.width}px`,i.style.height=`${e.height}px`,i}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),O(e,a,t,o,r);let s=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let i=D(c,t,o,n,r);i&&s.appendChild(i)}),a.appendChild(s),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(d=>d.tagName!=="STYLE").map(d=>D(d,t,o,n)).filter(Boolean),i=document.createDocumentFragment();c.forEach(d=>i.appendChild(d)),a.appendChild(i)}return a}async function G(e,t,o,n=32,r="#000"){t=t.replace(/^['"]+|['"]+$/g,""),await document.fonts.ready,await document.fonts.load(`${n}px "${t}"`);let a=document.createElement("canvas");a.width=n,a.height=n;let s=a.getContext("2d");return s.font=o?`${o} ${n}px "${t}"`:` ${n}px "${t}"`,s.textAlign="center",s.textBaseline="middle",s.fillStyle=r,s.fillText(e,a.width/2,a.height/2),a.toDataURL()}async function j({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 s=await(await fetch(r.href)).text();if(e&&(M(r.href)||M(s))){console.log("\u23ED\uFE0F Ignorando icon font CSS:",r.href);continue}let c=/url\(([^)]+)\)/g,i=await Promise.all(Array.from(s.matchAll(c)).map(async l=>{let f=l[1].replace(/["']/g,"");if(f.startsWith("http")||(f=new URL(f,r.href).href),e&&M(f))return console.log("\u23ED\uFE0F Ignorando icon font URL:",f),null;if(I.has(f))return{original:l[0],inlined:`url(${I.get(f)})`};try{let m=await(await fetch(f)).blob(),p=await new Promise(u=>{let w=new FileReader;w.onload=()=>u(w.result),w.readAsDataURL(m)});return I.set(f,p),{original:l[0],inlined:`url(${p})`}}catch{return console.warn("\u274C No pude fetch font",f),null}})),d=s;for(let l of i)l&&(d=d.replace(l.original,l.inlined));n+=d+`
3
+ `}catch{console.warn("\u274C No pude 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 W(e,t,o,n,r){if(!(e instanceof Element)||!(t instanceof Element))return;for(let c of["::before","::after"])try{let i=A(e,c);if(!i)continue;let d=i.getPropertyValue("content"),l=i.getPropertyValue("background-image"),f=d&&d!=="none"&&d!=='""'&&d!=="''",g=l&&l.startsWith("url(");if(f||g){let m=i.getPropertyValue("font-family"),p=parseInt(i.getPropertyValue("font-size"))||32,u=parseInt(i.getPropertyValue("font-weight"))||!1,w=i.getPropertyValue("color")||"#000",y=document.createElement("span");y.dataset.snapdomPseudo=c;let L=B(i),v=U(L,"span",r);o.set(y,v);let P=m&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(m),x=J(d);if(P&&x.length===1){let b=document.createElement("img");b.src=await G(x,m,u,p,w),b.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(b)}else if(x.startsWith("url(")){let b=x.match(/url\(["']?([^"')]+)["']?\)/);if(b?.[1])try{let $=document.createElement("img"),q=await C(b[1]);$.src=q,$.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild($)}catch($){console.error(`[snapdom] Error in pseudo ${c} for`,e,$)}}else x&&x!=="none"&&(y.textContent=x);c==="::before"?t.insertBefore(y,t.firstChild):t.appendChild(y)}}catch(i){console.warn(`[snapdom] Failed to capture ${c} for`,e,i)}let a=Array.from(e.children),s=Array.from(t.children).filter(c=>!c.dataset.snapdomPseudo);for(let c=0;c<Math.min(a.length,s.length);c++)await W(a[c],s[c],o,n,r)}async function Q(e,t=!1){let o=new Map,n=new WeakMap,r=new Map,a;try{a=D(e,o,n,r,t)}catch(i){throw console.warn("deepClone failed:",i),i}try{await W(e,a,o,n,t)}catch(i){console.warn("inlinePseudoElements failed:",i)}let s=Y(o),c=Array.from(s.entries()).map(([i,d])=>`.${d}{${i}}`).join("");for(let[i,d]of o.entries()){if(i.tagName==="STYLE")continue;let l=s.get(d);l&&i.classList.add(l);let f=i.style?.backgroundImage;i.removeAttribute("style"),f&&f!=="none"&&(i.style.backgroundImage=f)}for(let[i,d]of r.entries()){let l=d.scrollLeft,f=d.scrollTop;if((l||f)&&i instanceof HTMLElement){i.style.overflow="hidden",i.style.scrollbarWidth="none",i.style.msOverflowStyle="none";let m=document.createElement("div");for(m.style.transform=`translate(${-l}px, ${-f}px)`,m.style.willChange="transform",m.style.display="inline-block",m.style.width="100%";i.firstChild;)m.appendChild(i.firstChild);i.appendChild(m)}}return{clone:a,classCSS:c,styleCache:n}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let r=n.src;try{let a=await C(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(),s=o.get(r)||A(r);o.has(r)||o.set(r,s);let c=s.getPropertyValue("background-image");if(c&&c.includes("url(")){let l=c.match(/url\(["']?([^"')]+)["']?\)/);if(l?.[1])try{let f=l[1],g;E.has(f)?g=E.get(f):(g=await C(f),E.set(f,g)),a.style.backgroundImage=`url(${g})`}catch{a.style.backgroundImage="none"}}let i=Array.from(r.children),d=Array.from(a.children);for(let l=0;l<Math.min(i.length,d.length);l++)n.push([i[l],d[l]])}}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,s,c,i,d="",l="",f,g;if({clone:s,classCSS:c,styleCache:i}=await Q(e,o),await new Promise(p=>{N(async()=>{await Z(s),p()},{fast:r})}),await new Promise(p=>{N(async()=>{await ee(e,s,i),p()},{fast:r})}),n&&await new Promise(p=>{N(async()=>{d=await j({ignoreIconFonts:!0}),p()},{fast:r})}),o){let p=H(s).sort(),u=p.join(",");k.has(u)?l=k.get(u):await new Promise(w=>{N(()=>{l=K(p),k.set(u,l),w()},{fast:r})})}await new Promise(p=>{N(()=>{let u=e.getBoundingClientRect(),w=Math.ceil(u.width),y=Math.ceil(u.height);s.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),s.style.transformOrigin="top left",a!==1&&F()&&(s.style.scale=`${a}`);let L="http://www.w3.org/2000/svg",v=document.createElementNS(L,"foreignObject");v.setAttribute("width","100%"),v.setAttribute("height","100%");let P=document.createElement("style");P.textContent=l+d+"svg{overflow:visible;}"+c,v.appendChild(P),v.appendChild(s);let b=new XMLSerializer().serializeToString(v);g=`<svg xmlns="${L}" width="${w}" height="${y}" viewBox="0 0 ${w} ${y}">`+b+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(g)}`,p()},{fast:r})});let m=document.getElementById("snapdom-sandbox");return m&&m.style.position==="absolute"&&m.remove(),f}async function se(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 r=document.createElement("canvas"),a=n.width*o*t,s=n.height*o*t;r.width=Math.ceil(a),r.height=Math.ceil(s);let c=r.getContext("2d");return c.scale(o*t,o*t),c.drawImage(n,0,0),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 s=a.getContext("2d");return s.fillStyle=n,s.fillRect(0,0,a.width,a.height),s.drawImage(r,0,0),a}async function V(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:r},a="png"){let s=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=s.toDataURL(`image/${a}`,r),await c.decode(),c.style.width=`${s.width/t}px`,c.style.height=`${s.height/t}px`,c}async function ie(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:r="png",filename:a="capture"}={}){if(r==="svg"){let g=await oe(e),m=URL.createObjectURL(g),p=document.createElement("a");p.href=m,p.download=`${a}.svg`,p.click(),URL.revokeObjectURL(m);return}let s=["jpg","jpeg","webp"].includes(r)?"#fff":void 0,i=await ae(e,{dpr:t,scale:o},n??s),d={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[r]||"image/png",l=i.toDataURL(d),f=document.createElement("a");f.href=l,f.download=`${a}.${r}`,f.click()}async function h(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await h.capture(e,t)}h.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:()=>se(o,{dpr:n,scale:r}),toCanvas:()=>ne(o,{dpr:n,scale:r}),toBlob:()=>oe(o),toPng:a=>V(o,{dpr:n,scale:r,...a},"png"),toJpg:a=>V(o,{dpr:n,scale:r,...a},"jpeg"),toWebp:a=>V(o,{dpr:n,scale:r,...a},"webp"),download:({format:a="png",filename:s="capture",backgroundColor:c}={})=>ie(o,{dpr:n,scale:r,backgroundColor:c,format:a,filename:s})}};h.toRaw=async(e,t)=>(await h.capture(e,t)).toRaw();h.toImg=async(e,t)=>(await h.capture(e,t)).toImg();h.toCanvas=async(e,t)=>(await h.capture(e,t)).toCanvas();h.toBlob=async(e,t)=>(await h.capture(e,t)).toBlob();h.toPng=async(e,t)=>(await h.capture(e,t)).toPng(t);h.toJpg=async(e,t)=>(await h.capture(e,t)).toJpg(t);h.toWebp=async(e,t)=>(await h.capture(e,t)).toWebp(t);h.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:r,...a}=t;return await(await h.capture(e,a)).download({format:o,filename:n,backgroundColor:r})};async function ce(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:r=!0}=t;if(n){S.clear(),E.clear(),I.clear(),k.clear(),R.clear();return}await document.fonts.ready,_();let a=[],s=[];e?.querySelectorAll&&(a=Array.from(e.querySelectorAll("img[src]")),s=Array.from(e.querySelectorAll("*")));let c=[];for(let i of a){let d=i.src;S.has(d)||c.push(C(d).then(l=>S.set(d,l)).catch(()=>{}))}for(let i of s){let d=getComputedStyle(i).backgroundImage,l=X(d);l&&!E.has(l)&&c.push(C(l).then(f=>E.set(l,f)).catch(()=>{}))}o&&await j({ignoreIconFonts:!0,preCached:!0}),await Promise.all(c)}})();
@@ -1,3 +1,3 @@
1
- var S=new Map,E=new Map,$=new Map,T=new Map,k=new Map,R=new WeakMap;var re=["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 _(){for(let e of re)z(e)}function z(e){if(T.has(e))return T.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let s={};return T.set(e,s),s}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 s of a)r[s]=a.getPropertyValue(s);return o.removeChild(n),T.set(e,r),r}function U(e,t,o=!1){let n=[],a=z(t);for(let[r,s]of Object.entries(e))if(!o)s&&n.push(`${r}:${s}`);else{let c=a[r];s&&s!==c&&n.push(`${r}:${s}`)}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 K(e){let t=new Map;for(let n of e){let a=T.get(n);if(!a)continue;let r=Object.entries(a).map(([s,c])=>`${s}:${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 J(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 Y(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 C(e,t=3e3){return S.has(e)?Promise.resolve(S.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 s=document.createElement("canvas");s.width=r.width,s.height=r.height,s.getContext("2d").drawImage(r,0,0);try{let i=s.toDataURL("image/png");S.set(e,i),o(i)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(s){n(s)}},r.onerror=s=>{clearTimeout(a),n(new Error("Failed to load image: "+(s.message||"Unknown error")))},r.src=e})}function M(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 O(e,t,o,n,a){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,A(e));let r=n.get(e),s=M(r),c=e.tagName?.toLowerCase()||"div",i=U(s,c,a);o.set(t,i)}function F(e,t,o,n,a){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),i=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${i.width}px; height: ${i.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 i=document.createElement("div");return i.textContent=e.getAttribute("data-placeholder-text")||"",i.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(i),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),i=document.createElement("img");return i.src=c,i.width=e.width,i.height=e.height,i.style.display="inline-block",i.style.width=`${e.width}px`,i.style.height=`${e.height}px`,i}if(e.nodeType===Node.TEXT_NODE){if(!e.textContent.trim())return null;if(e.parentElement?.shadowRoot){let i=e.parentElement.tagName.toLowerCase();if(customElements.get(i))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 s=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let i=F(c,t,o,n,a);i&&s.appendChild(i)}),r.appendChild(s),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(m=>m.tagName!=="STYLE").map(m=>F(m,t,o,n)).filter(Boolean),i=document.createDocumentFragment();c.forEach(m=>i.appendChild(m)),r.appendChild(i)}return r}async function G(e,t,o,n=32,a="#000"){t=t.replace(/^['"]+|['"]+$/g,""),await document.fonts.ready,await document.fonts.load(`${n}px "${t}"`);let r=document.createElement("canvas");r.width=n,r.height=n;let s=r.getContext("2d");return s.font=o?`${o} ${n}px "${t}"`:` ${n}px "${t}"`,s.textAlign="center",s.textBaseline="middle",s.fillStyle=a,s.fillText(e,r.width/2,r.height/2),r.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 s=await(await fetch(a.href)).text();if(e&&(B(a.href)||B(s))){console.log("\u23ED\uFE0F Ignorando icon font CSS:",a.href);continue}let c=/url\(([^)]+)\)/g,i=await Promise.all(Array.from(s.matchAll(c)).map(async l=>{let d=l[1].replace(/["']/g,"");if(d.startsWith("http")||(d=new URL(d,a.href).href),e&&B(d))return console.log("\u23ED\uFE0F Ignorando icon font URL:",d),null;if($.has(d))return{original:l[0],inlined:`url(${$.get(d)})`};try{let h=await(await fetch(d)).blob(),p=await new Promise(u=>{let w=new FileReader;w.onload=()=>u(w.result),w.readAsDataURL(h)});return $.set(d,p),{original:l[0],inlined:`url(${p})`}}catch{return console.warn("\u274C No pude fetch font",d),null}})),m=s;for(let l of i)l&&(m=m.replace(l.original,l.inlined));n+=m+`
3
- `}catch{console.warn("\u274C No pude 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){if(!(e instanceof Element)||!(t instanceof Element))return;for(let c of["::before","::after"])try{let i=A(e,c);if(!i)continue;let m=i.getPropertyValue("content"),l=i.getPropertyValue("background-image"),d=m&&m!=="none"&&m!=='""'&&m!=="''",g=l&&l.startsWith("url(");if(d||g){let h=i.getPropertyValue("font-family"),p=parseInt(i.getPropertyValue("font-size"))||32,u=parseInt(i.getPropertyValue("font-weight"))||!1,w=i.getPropertyValue("color")||"#000",y=document.createElement("span");y.dataset.snapdomPseudo=c;let P=M(i),v=U(P,"span",a);o.set(y,v);let L=h&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(h),x=Y(m);if(L&&x.length===1){let b=document.createElement("img");b.src=await G(x,h,u,p,w),b.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(b)}else if(x.startsWith("url(")){let b=x.match(/url\(["']?([^"')]+)["']?\)/);if(b?.[1])try{let I=document.createElement("img"),q=await C(b[1]);I.src=q,I.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(I)}catch(I){console.error(`[snapdom] Error in pseudo ${c} for`,e,I)}}else x&&x!=="none"&&(y.textContent=x);c==="::before"?t.insertBefore(y,t.firstChild):t.appendChild(y)}}catch(i){console.warn(`[snapdom] Failed to capture ${c} for`,e,i)}let r=Array.from(e.children),s=Array.from(t.children).filter(c=>!c.dataset.snapdomPseudo);for(let c=0;c<Math.min(r.length,s.length);c++)await W(r[c],s[c],o,n,a)}async function Q(e,t=!1){let o=new Map,n=new WeakMap,a=new WeakMap,r;try{r=F(e,o,n,a,t)}catch(i){throw console.warn("deepClone failed:",i),i}try{await W(e,r,o,n,t)}catch(i){console.warn("inlinePseudoElements failed:",i)}let s=J(o),c=Array.from(s.entries()).map(([i,m])=>`.${m}{${i}}`).join("");for(let[i,m]of o.entries()){if(i.tagName==="STYLE")continue;let l=s.get(m);l&&i.classList.add(l);let d=i.style?.backgroundImage;i.removeAttribute("style"),d&&d!=="none"&&(i.style.backgroundImage=d)}return{clone:r,classCSS:c,styleCache:n}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let a=n.src;try{let r=await C(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(),s=o.get(a)||A(a);o.has(a)||o.set(a,s);let c=s.getPropertyValue("background-image");if(c&&c.includes("url(")){let l=c.match(/url\(["']?([^"')]+)["']?\)/);if(l?.[1])try{let d=l[1],g;E.has(d)?g=E.get(d):(g=await C(d),E.set(d,g)),r.style.backgroundImage=`url(${g})`}catch{r.style.backgroundImage="none"}}let i=Array.from(a.children),m=Array.from(r.children);for(let l=0;l<Math.min(i.length,m.length);l++)n.push([i[l],m[l]])}}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,s,c,i,m="",l="",d,g;if({clone:s,classCSS:c,styleCache:i}=await Q(e,o),await new Promise(p=>{N(async()=>{await Z(s),p()},{fast:a})}),await new Promise(p=>{N(async()=>{await ee(e,s,i),p()},{fast:a})}),n&&await new Promise(p=>{N(async()=>{m=await j({ignoreIconFonts:!0}),p()},{fast:a})}),o){let p=H(s).sort(),u=p.join(",");k.has(u)?l=k.get(u):await new Promise(w=>{N(()=>{l=K(p),k.set(u,l),w()},{fast:a})})}await new Promise(p=>{N(()=>{let u=e.getBoundingClientRect(),w=Math.ceil(u.width),y=Math.ceil(u.height);s.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),s.style.transformOrigin="top left",r!==1&&D()&&(s.style.scale=`${r}`);let P="http://www.w3.org/2000/svg",v=document.createElementNS(P,"foreignObject");v.setAttribute("width","100%"),v.setAttribute("height","100%");let L=document.createElement("style");L.textContent=l+m+"svg{overflow:visible;}"+c,v.appendChild(L),v.appendChild(s);let b=new XMLSerializer().serializeToString(v);g=`<svg xmlns="${P}" width="${w}" height="${y}" viewBox="0 0 ${w} ${y}">`+b+"</svg>",d=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(g)}`,p()},{fast:a})});let h=document.getElementById("snapdom-sandbox");return h&&h.style.position==="absolute"&&h.remove(),d}async function se(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 a=document.createElement("canvas"),r=n.width*o*t,s=n.height*o*t;a.width=Math.ceil(r),a.height=Math.ceil(s);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 s=r.getContext("2d");return s.fillStyle=n,s.fillRect(0,0,r.width,r.height),s.drawImage(a,0,0),r}async function V(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:a},r="png"){let s=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=s.toDataURL(`image/${r}`,a),await c.decode(),c.style.width=`${s.width/t}px`,c.style.height=`${s.height/t}px`,c}async function ie(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:a="png",filename:r="capture"}={}){if(a==="svg"){let g=await oe(e),h=URL.createObjectURL(g),p=document.createElement("a");p.href=h,p.download=`${r}.svg`,p.click(),URL.revokeObjectURL(h);return}let s=["jpg","jpeg","webp"].includes(a)?"#fff":void 0,i=await ae(e,{dpr:t,scale:o},n??s),m={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[a]||"image/png",l=i.toDataURL(m),d=document.createElement("a");d.href=l,d.download=`${r}.${a}`,d.click()}async function f(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await f.capture(e,t)}f.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:()=>se(o,{dpr:n,scale:a}),toCanvas:()=>ne(o,{dpr:n,scale:a}),toBlob:()=>oe(o),toPng:()=>V(o,{dpr:n,scale:a},"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:s="capture",backgroundColor:c}={})=>ie(o,{dpr:n,scale:a,backgroundColor:c,format:r,filename:s})}};f.toRaw=async(e,t)=>(await f.capture(e,t)).toRaw();f.toImg=async(e,t)=>(await f.capture(e,t)).toImg();f.toCanvas=async(e,t)=>(await f.capture(e,t)).toCanvas();f.toBlob=async(e,t)=>(await f.capture(e,t)).toBlob();f.toPng=async(e,t)=>(await f.capture(e,t)).toPng(t);f.toJpg=async(e,t)=>(await f.capture(e,t)).toJpg(t);f.toWebp=async(e,t)=>(await f.capture(e,t)).toWebp(t);f.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:a,...r}=t;return await(await f.capture(e,r)).download({format:o,filename:n,backgroundColor:a})};async function ce(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:a=!0}=t;if(n){S.clear(),E.clear(),$.clear(),k.clear(),R.clear();return}await document.fonts.ready,_();let r=[],s=[];e?.querySelectorAll&&(r=Array.from(e.querySelectorAll("img[src]")),s=Array.from(e.querySelectorAll("*")));let c=[];for(let i of r){let m=i.src;S.has(m)||c.push(C(m).then(l=>S.set(m,l)).catch(()=>{}))}for(let i of s){let m=getComputedStyle(i).backgroundImage,l=X(m);l&&!E.has(l)&&c.push(C(l).then(d=>E.set(l,d)).catch(()=>{}))}o&&await j({ignoreIconFonts:!0,preCached:!0}),await Promise.all(c)}export{ce as preCache,f as snapdom};
1
+ var S=new Map,E=new Map,I=new Map,T=new Map,k=new Map,R=new WeakMap;var re=["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 _(){for(let e of re)z(e)}function z(e){if(T.has(e))return T.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let s={};return T.set(e,s),s}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 s of r)a[s]=r.getPropertyValue(s);return o.removeChild(n),T.set(e,a),a}function U(e,t,o=!1){let n=[],r=z(t);for(let[a,s]of Object.entries(e))if(!o)s&&n.push(`${a}:${s}`);else{let c=r[a];s&&s!==c&&n.push(`${a}:${s}`)}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 K(e){let t=new Map;for(let n of e){let r=T.get(n);if(!r)continue;let a=Object.entries(r).map(([s,c])=>`${s}:${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 Y(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 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 M(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 C(e,t=3e3){return S.has(e)?Promise.resolve(S.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 s=document.createElement("canvas");s.width=a.width,s.height=a.height,s.getContext("2d").drawImage(a,0,0);try{let i=s.toDataURL("image/png");S.set(e,i),o(i)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(s){n(s)}},a.onerror=s=>{clearTimeout(r),n(new Error("Failed to load image: "+(s.message||"Unknown error")))},a.src=e})}function B(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,r){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,A(e));let a=n.get(e),s=B(a),c=e.tagName?.toLowerCase()||"div",i=U(s,c,r);o.set(t,i)}function D(e,t,o,n,r){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),i=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${i.width}px; height: ${i.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,r);let i=document.createElement("div");return i.textContent=e.getAttribute("data-placeholder-text")||"",i.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(i),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),i=document.createElement("img");return i.src=c,i.width=e.width,i.height=e.height,i.style.display="inline-block",i.style.width=`${e.width}px`,i.style.height=`${e.height}px`,i}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),O(e,a,t,o,r);let s=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let i=D(c,t,o,n,r);i&&s.appendChild(i)}),a.appendChild(s),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(d=>d.tagName!=="STYLE").map(d=>D(d,t,o,n)).filter(Boolean),i=document.createDocumentFragment();c.forEach(d=>i.appendChild(d)),a.appendChild(i)}return a}async function G(e,t,o,n=32,r="#000"){t=t.replace(/^['"]+|['"]+$/g,""),await document.fonts.ready,await document.fonts.load(`${n}px "${t}"`);let a=document.createElement("canvas");a.width=n,a.height=n;let s=a.getContext("2d");return s.font=o?`${o} ${n}px "${t}"`:` ${n}px "${t}"`,s.textAlign="center",s.textBaseline="middle",s.fillStyle=r,s.fillText(e,a.width/2,a.height/2),a.toDataURL()}async function j({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 s=await(await fetch(r.href)).text();if(e&&(M(r.href)||M(s))){console.log("\u23ED\uFE0F Ignorando icon font CSS:",r.href);continue}let c=/url\(([^)]+)\)/g,i=await Promise.all(Array.from(s.matchAll(c)).map(async l=>{let f=l[1].replace(/["']/g,"");if(f.startsWith("http")||(f=new URL(f,r.href).href),e&&M(f))return console.log("\u23ED\uFE0F Ignorando icon font URL:",f),null;if(I.has(f))return{original:l[0],inlined:`url(${I.get(f)})`};try{let m=await(await fetch(f)).blob(),p=await new Promise(u=>{let w=new FileReader;w.onload=()=>u(w.result),w.readAsDataURL(m)});return I.set(f,p),{original:l[0],inlined:`url(${p})`}}catch{return console.warn("\u274C No pude fetch font",f),null}})),d=s;for(let l of i)l&&(d=d.replace(l.original,l.inlined));n+=d+`
3
+ `}catch{console.warn("\u274C No pude 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 W(e,t,o,n,r){if(!(e instanceof Element)||!(t instanceof Element))return;for(let c of["::before","::after"])try{let i=A(e,c);if(!i)continue;let d=i.getPropertyValue("content"),l=i.getPropertyValue("background-image"),f=d&&d!=="none"&&d!=='""'&&d!=="''",g=l&&l.startsWith("url(");if(f||g){let m=i.getPropertyValue("font-family"),p=parseInt(i.getPropertyValue("font-size"))||32,u=parseInt(i.getPropertyValue("font-weight"))||!1,w=i.getPropertyValue("color")||"#000",y=document.createElement("span");y.dataset.snapdomPseudo=c;let L=B(i),v=U(L,"span",r);o.set(y,v);let P=m&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(m),x=J(d);if(P&&x.length===1){let b=document.createElement("img");b.src=await G(x,m,u,p,w),b.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(b)}else if(x.startsWith("url(")){let b=x.match(/url\(["']?([^"')]+)["']?\)/);if(b?.[1])try{let $=document.createElement("img"),q=await C(b[1]);$.src=q,$.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild($)}catch($){console.error(`[snapdom] Error in pseudo ${c} for`,e,$)}}else x&&x!=="none"&&(y.textContent=x);c==="::before"?t.insertBefore(y,t.firstChild):t.appendChild(y)}}catch(i){console.warn(`[snapdom] Failed to capture ${c} for`,e,i)}let a=Array.from(e.children),s=Array.from(t.children).filter(c=>!c.dataset.snapdomPseudo);for(let c=0;c<Math.min(a.length,s.length);c++)await W(a[c],s[c],o,n,r)}async function Q(e,t=!1){let o=new Map,n=new WeakMap,r=new Map,a;try{a=D(e,o,n,r,t)}catch(i){throw console.warn("deepClone failed:",i),i}try{await W(e,a,o,n,t)}catch(i){console.warn("inlinePseudoElements failed:",i)}let s=Y(o),c=Array.from(s.entries()).map(([i,d])=>`.${d}{${i}}`).join("");for(let[i,d]of o.entries()){if(i.tagName==="STYLE")continue;let l=s.get(d);l&&i.classList.add(l);let f=i.style?.backgroundImage;i.removeAttribute("style"),f&&f!=="none"&&(i.style.backgroundImage=f)}for(let[i,d]of r.entries()){let l=d.scrollLeft,f=d.scrollTop;if((l||f)&&i instanceof HTMLElement){i.style.overflow="hidden",i.style.scrollbarWidth="none",i.style.msOverflowStyle="none";let m=document.createElement("div");for(m.style.transform=`translate(${-l}px, ${-f}px)`,m.style.willChange="transform",m.style.display="inline-block",m.style.width="100%";i.firstChild;)m.appendChild(i.firstChild);i.appendChild(m)}}return{clone:a,classCSS:c,styleCache:n}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let r=n.src;try{let a=await C(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(),s=o.get(r)||A(r);o.has(r)||o.set(r,s);let c=s.getPropertyValue("background-image");if(c&&c.includes("url(")){let l=c.match(/url\(["']?([^"')]+)["']?\)/);if(l?.[1])try{let f=l[1],g;E.has(f)?g=E.get(f):(g=await C(f),E.set(f,g)),a.style.backgroundImage=`url(${g})`}catch{a.style.backgroundImage="none"}}let i=Array.from(r.children),d=Array.from(a.children);for(let l=0;l<Math.min(i.length,d.length);l++)n.push([i[l],d[l]])}}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,s,c,i,d="",l="",f,g;if({clone:s,classCSS:c,styleCache:i}=await Q(e,o),await new Promise(p=>{N(async()=>{await Z(s),p()},{fast:r})}),await new Promise(p=>{N(async()=>{await ee(e,s,i),p()},{fast:r})}),n&&await new Promise(p=>{N(async()=>{d=await j({ignoreIconFonts:!0}),p()},{fast:r})}),o){let p=H(s).sort(),u=p.join(",");k.has(u)?l=k.get(u):await new Promise(w=>{N(()=>{l=K(p),k.set(u,l),w()},{fast:r})})}await new Promise(p=>{N(()=>{let u=e.getBoundingClientRect(),w=Math.ceil(u.width),y=Math.ceil(u.height);s.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),s.style.transformOrigin="top left",a!==1&&F()&&(s.style.scale=`${a}`);let L="http://www.w3.org/2000/svg",v=document.createElementNS(L,"foreignObject");v.setAttribute("width","100%"),v.setAttribute("height","100%");let P=document.createElement("style");P.textContent=l+d+"svg{overflow:visible;}"+c,v.appendChild(P),v.appendChild(s);let b=new XMLSerializer().serializeToString(v);g=`<svg xmlns="${L}" width="${w}" height="${y}" viewBox="0 0 ${w} ${y}">`+b+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(g)}`,p()},{fast:r})});let m=document.getElementById("snapdom-sandbox");return m&&m.style.position==="absolute"&&m.remove(),f}async function se(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 r=document.createElement("canvas"),a=n.width*o*t,s=n.height*o*t;r.width=Math.ceil(a),r.height=Math.ceil(s);let c=r.getContext("2d");return c.scale(o*t,o*t),c.drawImage(n,0,0),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 s=a.getContext("2d");return s.fillStyle=n,s.fillRect(0,0,a.width,a.height),s.drawImage(r,0,0),a}async function V(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:r},a="png"){let s=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=s.toDataURL(`image/${a}`,r),await c.decode(),c.style.width=`${s.width/t}px`,c.style.height=`${s.height/t}px`,c}async function ie(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:r="png",filename:a="capture"}={}){if(r==="svg"){let g=await oe(e),m=URL.createObjectURL(g),p=document.createElement("a");p.href=m,p.download=`${a}.svg`,p.click(),URL.revokeObjectURL(m);return}let s=["jpg","jpeg","webp"].includes(r)?"#fff":void 0,i=await ae(e,{dpr:t,scale:o},n??s),d={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[r]||"image/png",l=i.toDataURL(d),f=document.createElement("a");f.href=l,f.download=`${a}.${r}`,f.click()}async function h(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await h.capture(e,t)}h.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:()=>se(o,{dpr:n,scale:r}),toCanvas:()=>ne(o,{dpr:n,scale:r}),toBlob:()=>oe(o),toPng:a=>V(o,{dpr:n,scale:r,...a},"png"),toJpg:a=>V(o,{dpr:n,scale:r,...a},"jpeg"),toWebp:a=>V(o,{dpr:n,scale:r,...a},"webp"),download:({format:a="png",filename:s="capture",backgroundColor:c}={})=>ie(o,{dpr:n,scale:r,backgroundColor:c,format:a,filename:s})}};h.toRaw=async(e,t)=>(await h.capture(e,t)).toRaw();h.toImg=async(e,t)=>(await h.capture(e,t)).toImg();h.toCanvas=async(e,t)=>(await h.capture(e,t)).toCanvas();h.toBlob=async(e,t)=>(await h.capture(e,t)).toBlob();h.toPng=async(e,t)=>(await h.capture(e,t)).toPng(t);h.toJpg=async(e,t)=>(await h.capture(e,t)).toJpg(t);h.toWebp=async(e,t)=>(await h.capture(e,t)).toWebp(t);h.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:r,...a}=t;return await(await h.capture(e,a)).download({format:o,filename:n,backgroundColor:r})};async function ce(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:r=!0}=t;if(n){S.clear(),E.clear(),I.clear(),k.clear(),R.clear();return}await document.fonts.ready,_();let a=[],s=[];e?.querySelectorAll&&(a=Array.from(e.querySelectorAll("img[src]")),s=Array.from(e.querySelectorAll("*")));let c=[];for(let i of a){let d=i.src;S.has(d)||c.push(C(d).then(l=>S.set(d,l)).catch(()=>{}))}for(let i of s){let d=getComputedStyle(i).backgroundImage,l=X(d);l&&!E.has(l)&&c.push(C(l).then(f=>E.set(l,f)).catch(()=>{}))}o&&await j({ignoreIconFonts:!0,preCached:!0}),await Promise.all(c)}export{ce as preCache,h as snapdom};
package/dist/snapdom.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.0.0-pre.1747581859131
4
+ * v.1.1.0
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -300,8 +300,6 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress) {
300
300
  return img;
301
301
  }
302
302
  if (node.nodeType === Node.TEXT_NODE) {
303
- const trimmed = node.textContent.trim();
304
- if (!trimmed) return null;
305
303
  if (node.parentElement?.shadowRoot) {
306
304
  const tag = node.parentElement.tagName.toLowerCase();
307
305
  if (customElements.get(tag)) return null;
@@ -471,7 +469,7 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
471
469
  async function prepareClone(element, compress = false) {
472
470
  const styleMap = /* @__PURE__ */ new Map();
473
471
  const styleCache = /* @__PURE__ */ new WeakMap();
474
- const nodeMap = /* @__PURE__ */ new WeakMap();
472
+ const nodeMap = /* @__PURE__ */ new Map();
475
473
  let clone;
476
474
  try {
477
475
  clone = deepClone(element, styleMap, styleCache, nodeMap, compress);
@@ -494,6 +492,25 @@ async function prepareClone(element, compress = false) {
494
492
  node.removeAttribute("style");
495
493
  if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
496
494
  }
495
+ for (const [cloneNode, originalNode] of nodeMap.entries()) {
496
+ const scrollX = originalNode.scrollLeft;
497
+ const scrollY = originalNode.scrollTop;
498
+ const hasScroll = scrollX || scrollY;
499
+ if (hasScroll && cloneNode instanceof HTMLElement) {
500
+ cloneNode.style.overflow = "hidden";
501
+ cloneNode.style.scrollbarWidth = "none";
502
+ cloneNode.style.msOverflowStyle = "none";
503
+ const inner = document.createElement("div");
504
+ inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`;
505
+ inner.style.willChange = "transform";
506
+ inner.style.display = "inline-block";
507
+ inner.style.width = "100%";
508
+ while (cloneNode.firstChild) {
509
+ inner.appendChild(cloneNode.firstChild);
510
+ }
511
+ cloneNode.appendChild(inner);
512
+ }
513
+ }
497
514
  return { clone, classCSS, styleCache };
498
515
  }
499
516
 
@@ -726,7 +743,7 @@ snapdom.capture = async (el, options = {}) => {
726
743
  toImg: () => toImg(url, { dpr, scale }),
727
744
  toCanvas: () => toCanvas(url, { dpr, scale }),
728
745
  toBlob: () => toBlob(url),
729
- toPng: () => toRasterImg(url, { dpr, scale }, "png"),
746
+ toPng: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "png"),
730
747
  toJpg: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "jpeg"),
731
748
  toWebp: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "webp"),
732
749
  download: ({ format = "png", filename = "capture", backgroundColor } = {}) => download(url, { dpr, scale, backgroundColor, format, filename })
package/package.json CHANGED
@@ -1,15 +1,9 @@
1
1
  {
2
2
  "name": "@zumer/snapdom",
3
- "version": "1.0.0-pre.1747581859131",
3
+ "version": "1.1.0",
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
- "exports": {
8
- ".": {
9
- "import": "./dist/snapdom.mjs",
10
- "require": "./dist/snapdom.js"
11
- }
12
- },
13
7
  "scripts": {
14
8
  "test": "npx vitest run --browser.headless --reporter=verbose",
15
9
  "test:benchmark": "npx vitest bench --browser.headless --watch=false",
@@ -24,11 +18,19 @@
24
18
  "type": "git",
25
19
  "url": "git+https://github.com/zumerlab/snapdom.git"
26
20
  },
21
+ "types": "types/snapdom.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "import": "./dist/snapdom.mjs",
25
+ "require": "./dist/snapdom.js"
26
+ }
27
+ },
27
28
  "files": [
28
29
  "dist/snapdom.js",
29
30
  "dist/snapdom.min.js",
30
31
  "dist/snapdom.min.mjs",
31
32
  "dist/snapdom.mjs",
33
+ "types/snapdom.d.ts",
32
34
  "README.md"
33
35
  ],
34
36
  "keywords": [
@@ -0,0 +1,56 @@
1
+ declare module "@zumer/snapdom" {
2
+ export interface SnapOptions {
3
+ compress?: boolean;
4
+ embedFonts?: boolean;
5
+ fast?: boolean;
6
+ scale?: number;
7
+ backgroundColor?: string;
8
+ format?: "png" | "jpeg" | "jpg" | "webp" | "svg";
9
+ filename?: string;
10
+ dpr?: number;
11
+ quality?: number;
12
+ }
13
+
14
+ export interface SnapResult {
15
+ url: string;
16
+ options: SnapOptions;
17
+ toRaw(): string;
18
+ toImg(): Promise<HTMLImageElement>;
19
+ toCanvas(): Promise<HTMLCanvasElement>;
20
+ toBlob(): Promise<Blob>;
21
+ toPng(options?: SnapOptions): Promise<HTMLImageElement>;
22
+ toJpg(options?: SnapOptions): Promise<HTMLImageElement>;
23
+ toWebp(options?: SnapOptions): Promise<HTMLImageElement>;
24
+ download(options?: SnapOptions): Promise<void>;
25
+ }
26
+
27
+ /**
28
+ * Captura un elemento del DOM como imagen SVG + métodos de exportación.
29
+ */
30
+ export function snapdom(
31
+ element: HTMLElement,
32
+ options?: SnapOptions
33
+ ): Promise<SnapResult>;
34
+
35
+ export namespace snapdom {
36
+ function capture(element: HTMLElement, options?: SnapOptions): Promise<SnapResult>;
37
+ function toRaw(element: HTMLElement, options?: SnapOptions): Promise<string>;
38
+ function toImg(element: HTMLElement, options?: SnapOptions): Promise<HTMLImageElement>;
39
+ function toCanvas(element: HTMLElement, options?: SnapOptions): Promise<HTMLCanvasElement>;
40
+ function toBlob(element: HTMLElement, options?: SnapOptions): Promise<Blob>;
41
+ function toPng(element: HTMLElement, options?: SnapOptions): Promise<HTMLImageElement>;
42
+ function toJpg(element: HTMLElement, options?: SnapOptions): Promise<HTMLImageElement>;
43
+ function toWebp(element: HTMLElement, options?: SnapOptions): Promise<HTMLImageElement>;
44
+ function download(element: HTMLElement, options?: SnapOptions): Promise<void>;
45
+ }
46
+
47
+ /**
48
+ * Precarga imágenes, fondos y fuentes para acelerar capturas posteriores.
49
+ */
50
+ export function preCache(
51
+ root?: Document | HTMLElement,
52
+ options?: {
53
+ embedFonts?: boolean;
54
+ }
55
+ ): Promise<void>;
56
+ }