@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 CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.1.0
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
- await document.fonts.ready;
332
- await document.fonts.load(`${fontSize}px "${fontFamily}"`);
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 = fontSize;
335
- canvas.height = fontSize;
341
+ canvas.width = Math.ceil(width * dpr);
342
+ canvas.height = Math.ceil(height * dpr);
336
343
  const ctx = canvas.getContext("2d");
337
- ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : ` ${fontSize}px "${fontFamily}"`;
338
- ctx.textAlign = "center";
339
- ctx.textBaseline = "middle";
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, canvas.width / 2, canvas.height / 2);
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 No pude fetch font", url);
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 No pude fetch CSS", link.href);
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
- const keyToClass = generateCSSClasses(styleMap);
486
- const classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
487
- for (const [node, key] of styleMap.entries()) {
488
- if (node.tagName === "STYLE") continue;
489
- const className = keyToClass.get(key);
490
- if (className) node.classList.add(className);
491
- const bgImage = node.style?.backgroundImage;
492
- node.removeAttribute("style");
493
- if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
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: true });
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: true, preCached: true });
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
  })();
@@ -1,3 +1,3 @@
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
+ (()=>{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;})();
@@ -1,3 +1,3 @@
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};
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.1.0
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
- await document.fonts.ready;
332
- await document.fonts.load(`${fontSize}px "${fontFamily}"`);
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 = fontSize;
335
- canvas.height = fontSize;
341
+ canvas.width = Math.ceil(width * dpr);
342
+ canvas.height = Math.ceil(height * dpr);
336
343
  const ctx = canvas.getContext("2d");
337
- ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : ` ${fontSize}px "${fontFamily}"`;
338
- ctx.textAlign = "center";
339
- ctx.textBaseline = "middle";
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, canvas.width / 2, canvas.height / 2);
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 No pude fetch font", url);
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 No pude fetch CSS", link.href);
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
- const keyToClass = generateCSSClasses(styleMap);
486
- const classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
487
- for (const [node, key] of styleMap.entries()) {
488
- if (node.tagName === "STYLE") continue;
489
- const className = keyToClass.get(key);
490
- if (className) node.classList.add(className);
491
- const bgImage = node.style?.backgroundImage;
492
- node.removeAttribute("style");
493
- if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
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: true });
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: true, preCached: true });
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.1.0",
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
  }