@zumer/snapdom 1.2.2 → 1.2.5

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
@@ -39,7 +39,7 @@ yarn add @zumer/snapdom
39
39
  ### CDN
40
40
 
41
41
  ```html
42
- <script src="https://unpkg.com/@zumer/snapdom@latest/dist/snapdom.min.js"></script>
42
+ <script src="https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.min.js"></script>
43
43
  ```
44
44
 
45
45
  ### Script tag (local)
@@ -58,7 +58,7 @@ import { snapdom } from './snapdom.mjs';
58
58
 
59
59
  ```html
60
60
  <script type="module">
61
- import { snapdom } from 'https://unpkg.com/@zumer/snapdom@latest/dist/snapdom.mjs';
61
+ import { snapdom } from 'https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.mjs';
62
62
  </script>
63
63
  ```
64
64
 
package/dist/snapdom.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.2.2
4
+ * v.1.2.5
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -186,9 +186,15 @@
186
186
  }
187
187
  return clean;
188
188
  }
189
- function extractUrl(cssValue) {
190
- const m = cssValue.match(/url\(["']?([^"')]+)["']?\)/);
191
- return m ? m[1] : null;
189
+ function extractURL(value) {
190
+ const urlStart = value.indexOf("url(");
191
+ if (urlStart === -1) return null;
192
+ let url = value.slice(urlStart + 4).trim();
193
+ if (url.endsWith(")")) url = url.slice(0, -1).trim();
194
+ if (url.startsWith('"') && url.endsWith('"') || url.startsWith("'") && url.endsWith("'")) {
195
+ url = url.slice(1, -1);
196
+ }
197
+ return url;
192
198
  }
193
199
  function isIconFont(familyOrUrl) {
194
200
  const iconFontPatterns = [
@@ -362,10 +368,17 @@
362
368
  const urlRegex = /url\(([^)]+)\)/g;
363
369
  const inlinedCSS = await Promise.all(
364
370
  Array.from(cssText.matchAll(urlRegex)).map(async (match) => {
365
- let url = match[1].replace(/["']/g, "");
371
+ let rawUrl = extractURL(match[0]);
372
+ if (!rawUrl) return null;
373
+ let url = rawUrl;
366
374
  if (!url.startsWith("http")) {
367
375
  url = new URL(url, link.href).href;
368
376
  }
377
+ try {
378
+ url = encodeURI(url);
379
+ } catch (e) {
380
+ console.warn("[snapdom] Failed to encode font URL:", url);
381
+ }
369
382
  if (ignoreIconFonts && isIconFont(url)) {
370
383
  return null;
371
384
  }
@@ -429,29 +442,43 @@
429
442
  const snapshot = snapshotComputedStyle(style);
430
443
  const key = getStyleKey(snapshot, "span", compress);
431
444
  styleMap.set(pseudoEl, key);
432
- const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(fontFamily);
445
+ const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(
446
+ fontFamily
447
+ );
433
448
  let cleanContent = parseContent(content);
434
449
  if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
435
450
  const imgEl = document.createElement("img");
436
- imgEl.src = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
451
+ imgEl.src = await iconToImage(
452
+ cleanContent,
453
+ fontFamily,
454
+ fontWeight,
455
+ fontSize,
456
+ color
457
+ );
437
458
  imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
438
459
  pseudoEl.appendChild(imgEl);
439
460
  } else if (cleanContent.startsWith("url(")) {
440
- const match = cleanContent.match(/url\(["']?([^"')]+)["']?\)/);
441
- if (match?.[1]) {
461
+ const rawUrl = extractURL(cleanContent);
462
+ if (rawUrl) {
442
463
  try {
443
464
  const imgEl = document.createElement("img");
444
- const dataUrl = await fetchImage(match[1]);
465
+ const dataUrl = await fetchImage(encodeURI(rawUrl));
445
466
  imgEl.src = dataUrl;
446
467
  imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
447
468
  pseudoEl.appendChild(imgEl);
448
469
  } catch (e) {
449
- console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
470
+ console.error(
471
+ `[snapdom] Error in pseudo ${pseudo} for`,
472
+ source,
473
+ e
474
+ );
450
475
  }
451
476
  }
452
- } else if (cleanContent && cleanContent !== "none") {
477
+ } else if (!isIconFont2 && cleanContent && cleanContent !== "none") {
453
478
  pseudoEl.textContent = cleanContent;
454
479
  }
480
+ const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent && pseudoEl.textContent.trim() !== "";
481
+ if (!hasContent2) continue;
455
482
  if (pseudo === "::before") {
456
483
  clone.insertBefore(pseudoEl, clone.firstChild);
457
484
  } else {
@@ -467,7 +494,14 @@
467
494
  (child) => !child.dataset.snapdomPseudo
468
495
  );
469
496
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
470
- await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress, embedFonts);
497
+ await inlinePseudoElements(
498
+ sChildren[i],
499
+ cChildren[i],
500
+ styleMap,
501
+ styleCache,
502
+ compress,
503
+ embedFonts
504
+ );
471
505
  }
472
506
  }
473
507
 
@@ -559,22 +593,20 @@
559
593
  const style = styleCache.get(srcNode) || getStyle(srcNode);
560
594
  if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
561
595
  const bg = style.getPropertyValue("background-image");
562
- if (bg && bg.includes("url(")) {
563
- const match = bg.match(/url\(["']?([^"')]+)["']?\)/);
564
- if (match?.[1]) {
565
- try {
566
- const bgUrl = match[1];
567
- let dataUrl;
568
- if (bgCache.has(bgUrl)) {
569
- dataUrl = bgCache.get(bgUrl);
570
- } else {
571
- dataUrl = await fetchImage(bgUrl);
572
- bgCache.set(bgUrl, dataUrl);
573
- }
574
- cloneNode.style.backgroundImage = `url(${dataUrl})`;
575
- } catch {
576
- cloneNode.style.backgroundImage = "none";
596
+ const rawUrl = extractURL(bg);
597
+ if (rawUrl) {
598
+ try {
599
+ let bgUrl = encodeURI(rawUrl);
600
+ let dataUrl;
601
+ if (bgCache.has(bgUrl)) {
602
+ dataUrl = bgCache.get(bgUrl);
603
+ } else {
604
+ dataUrl = await fetchImage(bgUrl);
605
+ bgCache.set(bgUrl, dataUrl);
577
606
  }
607
+ cloneNode.style.backgroundImage = `url(${dataUrl})`;
608
+ } catch {
609
+ cloneNode.style.backgroundImage = "none";
578
610
  }
579
611
  }
580
612
  const sChildren = Array.from(srcNode.children);
@@ -688,6 +720,8 @@
688
720
  const ctx = canvas.getContext("2d");
689
721
  ctx.scale(scale * dpr, scale * dpr);
690
722
  ctx.drawImage(img, 0, 0);
723
+ canvas.style.width = `${canvas.width / dpr}px`;
724
+ canvas.style.height = `${canvas.height / dpr}px`;
691
725
  return canvas;
692
726
  }
693
727
  async function toBlob(url) {
@@ -811,7 +845,7 @@
811
845
  }
812
846
  for (const el of allEls) {
813
847
  const bg = getComputedStyle(el).backgroundImage;
814
- const url = extractUrl(bg);
848
+ const url = extractURL(bg);
815
849
  if (url && !bgCache.has(url)) {
816
850
  promises.push(
817
851
  fetchImage(url).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
@@ -1,3 +1,3 @@
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
+ (()=>{var S=new Map,E=new Map,R=new Map,T=new Map,k=new Map,U=new WeakMap;var ct=["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 t of ct)H(t)}function H(t){if(T.has(t))return T.get(t);if(new Set(["script","style","meta","link","noscript","template"]).has(t)){let i={};return T.set(t,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(t);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(t,r),r}function B(t,e,o=!1){let n=[],a=H(e);for(let[r,i]of Object.entries(t))if(!o)i&&n.push(`${r}:${i}`);else{let c=a[r];i&&i!==c&&n.push(`${r}:${i}`)}return n.sort().join(";")}function Y(t){let e=new Set;return t.nodeType!==Node.ELEMENT_NODE&&t.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(t.tagName&&e.add(t.tagName.toLowerCase()),typeof t.querySelectorAll=="function"&&t.querySelectorAll("*").forEach(o=>e.add(o.tagName.toLowerCase())),Array.from(e))}function K(t){let e=new Map;for(let n of t){let a=T.get(n);if(!a)continue;let r=Object.entries(a).map(([i,c])=>`${i}:${c};`).sort().join("");e.has(r)||e.set(r,[]),e.get(r).push(n)}let o="";for(let[n,a]of e.entries())o+=`${a.join(",")} { ${n} }
2
+ `;return o}function J(t){let e=new Set(t.values()),o=new Map,n=1;for(let a of e)o.set(a,`c${n++}`);return o}function N(t,{fast:e=!1}={}){if(e)return t();"requestIdleCallback"in window?requestIdleCallback(t,{timeout:50}):setTimeout(t,1)}function A(t,e=null){if(!(t instanceof Element))return window.getComputedStyle(t,e);let o=U.get(t);if(o||(o=new Map,U.set(t,o)),!o.has(e)){let n=window.getComputedStyle(t,e);o.set(e,n)}return o.get(e)}function X(t){let e=t.replace(/^['"]|['"]$/g,"");if(e.startsWith("\\"))try{return String.fromCharCode(parseInt(e.replace("\\",""),16))}catch{return e}return e}function v(t){let e=t.indexOf("url(");if(e===-1)return null;let o=t.slice(e+4).trim();return o.endsWith(")")&&(o=o.slice(0,-1).trim()),(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.slice(1,-1)),o}function M(t){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(t))}function x(t,e=3e3){return S.has(t)?Promise.resolve(S.get(t)):new Promise((o,n)=>{let a=setTimeout(()=>{n(new Error("Image load timed out"))},e),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");S.set(t,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=t})}function F(t){let e={};for(let o of t)e[o]=t.getPropertyValue(o);return e}function D(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function V(t,e,o,n,a){if(t.tagName==="STYLE")return;n.has(t)||n.set(t,A(t));let r=n.get(t),i=F(r),c=t.tagName?.toLowerCase()||"div",s=B(i,c,a);o.set(e,s)}function O(t,e,o,n,a){if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),s=t.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${s.width}px; height: ${s.height}px; visibility: hidden;`,c}if(t.tagName==="IFRAME"){let c=document.createElement("div");return c.textContent="",c.style.cssText=`width: ${t.offsetWidth}px; height: ${t.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(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="placeholder"){let c=t.cloneNode(!1);n.set(c,t),V(t,c,e,o,a);let s=document.createElement("div");return s.textContent=t.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(t.tagName==="CANVAS"){let c=t.toDataURL(),s=document.createElement("img");return s.src=c,s.width=t.width,s.height=t.height,s.style.display="inline-block",s.style.width=`${t.width}px`,s.style.height=`${t.height}px`,s}if(t.nodeType===Node.TEXT_NODE){if(t.parentElement?.shadowRoot){let c=t.parentElement.tagName.toLowerCase();if(customElements.get(c))return null}return t.cloneNode(!0)}if(t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);let r=t.cloneNode(!1);n.set(r,t),V(t,r,e,o,a);let i=document.createDocumentFragment();if(t.childNodes.forEach(c=>{let s=O(c,e,o,n,a);s&&i.appendChild(s)}),r.appendChild(i),t.shadowRoot){let c=Array.from(t.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>O(l,e,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),r.appendChild(s)}return r}async function G(t,e,o,n=32,a="#000"){e=e.replace(/^['"]+|['"]+$/g,"");let r=window.devicePixelRatio||1,c=document.createElement("canvas").getContext("2d");c.font=o?`${o} ${n}px "${e}"`:`${n}px "${e}"`;let s=c.measureText(t),l=s.actualBoundingBoxAscent||n*.8,d=s.actualBoundingBoxDescent||n*.2,f=l+d,m=s.width,h=document.createElement("canvas");h.width=Math.ceil(m*r),h.height=Math.ceil(f*r);let p=h.getContext("2d");return p.scale(r,r),p.font=c.font,p.textAlign="left",p.textBaseline="alphabetic",p.fillStyle=a,p.fillText(t,0,l),h.toDataURL()}async function W({ignoreIconFonts:t=!0,preCached:e=!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(t&&(M(a.href)||M(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let f=v(d[0]);if(!f)return null;let m=f;m.startsWith("http")||(m=new URL(m,a.href).href);try{m=encodeURI(m)}catch{console.warn("[snapdom] Failed to encode font URL:",m)}if(t&&M(m))return null;if(R.has(m))return{original:d[0],inlined:`url(${R.get(m)})`};try{let p=await(await fetch(m)).blob(),w=await new Promise(C=>{let b=new FileReader;b.onload=()=>C(b.result),b.readAsDataURL(p)});return R.set(m,w),{original:d[0],inlined:`url(${w})`}}catch{return console.warn("\u274C Failed to fetch font:",m),null}})),l=i;for(let d of s)d&&(l=l.replace(d.original,d.inlined));n+=l+`
3
+ `}catch{console.warn("\u274C Failed to fetch CSS:",a.href)}if(n&&e){let a=document.createElement("style");a.setAttribute("data-snapdom","embedFonts"),a.textContent=n,document.head.appendChild(a)}return n}async function q(t,e,o,n,a,r=!1){if(!(t instanceof Element)||!(e instanceof Element))return;for(let s of["::before","::after"])try{let l=A(t,s);if(!l)continue;let d=l.getPropertyValue("content"),f=l.getPropertyValue("background-image"),m=d&&d!=="none"&&d!=='""'&&d!=="''",h=f&&f.startsWith("url(");if(m||h){let p=l.getPropertyValue("font-family"),w=parseInt(l.getPropertyValue("font-size"))||32,C=parseInt(l.getPropertyValue("font-weight"))||!1,b=l.getPropertyValue("color")||"#000",u=document.createElement("span");u.dataset.snapdomPseudo=s;let I=F(l),P=B(I,"span",a);o.set(u,P);let j=p&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(p),y=X(d);if(!r&&j&&y.length===1){let $=document.createElement("img");$.src=await G(y,p,C,w,b),$.style="display:block;width:100%;height:100%;object-fit:contain;",u.appendChild($)}else if(y.startsWith("url(")){let $=v(y);if($)try{let L=document.createElement("img"),it=await x(encodeURI($));L.src=it,L.style="display:block;width:100%;height:100%;object-fit:contain;",u.appendChild(L)}catch(L){console.error(`[snapdom] Error in pseudo ${s} for`,t,L)}}else!j&&y&&y!=="none"&&(u.textContent=y);if(!(u.childNodes.length>0||u.textContent&&u.textContent.trim()!==""))continue;s==="::before"?e.insertBefore(u,e.firstChild):e.appendChild(u)}}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,t,l)}let i=Array.from(t.children),c=Array.from(e.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s<Math.min(i.length,c.length);s++)await q(i[s],c[s],o,n,a,r)}async function Q(t,e=!1,o=!1){let n=new Map,a=new WeakMap,r=new Map,i;try{i=O(t,n,a,r,e)}catch(s){throw console.warn("deepClone failed:",s),s}try{await q(t,i,n,a,e,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(e){let s=J(n);c=Array.from(s.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of n.entries()){if(l.tagName==="STYLE")continue;let f=s.get(d);f&&l.classList.add(f);let m=l.style?.backgroundImage;l.removeAttribute("style"),m&&m!=="none"&&(l.style.backgroundImage=m)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of 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 h=document.createElement("div");for(h.style.transform=`translate(${-d}px, ${-f}px)`,h.style.willChange="transform",h.style.display="inline-block",h.style.width="100%";s.firstChild;)h.appendChild(s.firstChild);s.appendChild(h)}}return{clone:i,classCSS:c,styleCache:a}}async function Z(t){let e=Array.from(t.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<e.length;n+=4){let a=e.slice(n,n+4).map(o);await Promise.allSettled(a)}}async function tt(t,e,o){let n=[[t,e]];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"),s=v(c);if(s)try{let f=encodeURI(s),m;E.has(f)?m=E.get(f):(m=await x(f),E.set(f,m)),r.style.backgroundImage=`url(${m})`}catch{r.style.backgroundImage="none"}let l=Array.from(a.children),d=Array.from(r.children);for(let f=0;f<Math.min(l.length,d.length);f++)n.push([l[f],d[f]])}}async function et(t,e={}){if(!t)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:a=!0,scale:r=1}=e,i,c,s,l="",d="",f,m;if({clone:i,classCSS:c,styleCache:s}=await Q(t,o,n),await new Promise(p=>{N(async()=>{await Z(i),p()},{fast:a})}),await new Promise(p=>{N(async()=>{await tt(t,i,s),p()},{fast:a})}),n&&await new Promise(p=>{N(async()=>{l=await W({ignoreIconFonts:!n}),p()},{fast:a})}),o){let p=Y(i).sort(),w=p.join(",");k.has(w)?d=k.get(w):await new Promise(C=>{N(()=>{d=K(p),k.set(w,d),C()},{fast:a})})}await new Promise(p=>{N(()=>{let w=t.getBoundingClientRect(),C=Math.ceil(w.width),b=Math.ceil(w.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",r!==1&&D()&&(i.style.scale=`${r}`);let u="http://www.w3.org/2000/svg",I=document.createElementNS(u,"foreignObject");I.setAttribute("width","100%"),I.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,I.appendChild(P),I.appendChild(i);let y=new XMLSerializer().serializeToString(I);m=`<svg xmlns="${u}" width="${C}" height="${b}" viewBox="0 0 ${C} ${b}">`+y+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(m)}`,p()},{fast:a})});let h=document.getElementById("snapdom-sandbox");return h&&h.style.position==="absolute"&&h.remove(),f}async function lt(t,{dpr:e=1,scale:o=1}){let n=new Image;return n.src=t,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 nt(t,{dpr:e=1,scale:o=1}){let n=new Image;n.src=t,await n.decode();let a=document.createElement("canvas"),r=n.width*o*e,i=n.height*o*e;a.width=Math.ceil(r),a.height=Math.ceil(i);let c=a.getContext("2d");return c.scale(o*e,o*e),c.drawImage(n,0,0),a.style.width=`${a.width/e}px`,a.style.height=`${a.height/e}px`,a}async function ot(t){let e=decodeURIComponent(t.split(",")[1]);return new Blob([e],{type:"image/svg+xml"})}async function at(t,{dpr:e=1,scale:o=1},n){let a=await nt(t,{dpr:e,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 z(t,{dpr:e=1,scale:o=1,backgroundColor:n="#fff",quality:a},r="png"){let i=await at(t,{dpr:e,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${r}`,a),await c.decode(),c.style.width=`${i.width/e}px`,c.style.height=`${i.height/e}px`,c}async function dt(t,{dpr:e=1,scale:o=1,backgroundColor:n="#fff",format:a="png",filename:r="capture"}={}){if(a==="svg"){let m=await ot(t),h=URL.createObjectURL(m),p=document.createElement("a");p.href=h,p.download=`${r}.svg`,p.click(),URL.revokeObjectURL(h);return}let i=["jpg","jpeg","webp"].includes(a)?"#fff":void 0,s=await at(t,{dpr:e,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(t,e={}){if(e={scale:1,...e},!t)throw new Error("Element cannot be null or undefined");return await g.capture(t,e)}g.capture=async(t,e={})=>{let o=await et(t,e),n=window.devicePixelRatio||1,a=e.scale||1;return{url:o,options:e,toRaw:()=>o,toImg:()=>lt(o,{dpr:n,scale:a}),toCanvas:()=>nt(o,{dpr:n,scale:a}),toBlob:()=>ot(o),toPng:r=>z(o,{dpr:n,scale:a,...r},"png"),toJpg:r=>z(o,{dpr:n,scale:a,...r},"jpeg"),toWebp:r=>z(o,{dpr:n,scale:a,...r},"webp"),download:({format:r="png",filename:i="capture",backgroundColor:c}={})=>dt(o,{dpr:n,scale:a,backgroundColor:c,format:r,filename:i})}};g.toRaw=async(t,e)=>(await g.capture(t,e)).toRaw();g.toImg=async(t,e)=>(await g.capture(t,e)).toImg();g.toCanvas=async(t,e)=>(await g.capture(t,e)).toCanvas();g.toBlob=async(t,e)=>(await g.capture(t,e)).toBlob();g.toPng=async(t,e)=>(await g.capture(t,e)).toPng(e);g.toJpg=async(t,e)=>(await g.capture(t,e)).toJpg(e);g.toWebp=async(t,e)=>(await g.capture(t,e)).toWebp(e);g.download=async(t,e={})=>{let{format:o="png",filename:n="capture",backgroundColor:a,...r}=e;return await(await g.capture(t,r)).download({format:o,filename:n,backgroundColor:a})};async function rt(t=document,e={}){let{embedFonts:o=!0,reset:n=!1,preWarm:a=!0}=e;if(n){S.clear(),E.clear(),R.clear(),k.clear(),U.clear();return}await document.fonts.ready,_();let r=[],i=[];t?.querySelectorAll&&(r=Array.from(t.querySelectorAll("img[src]")),i=Array.from(t.querySelectorAll("*")));let c=[];for(let s of r){let l=s.src;S.has(l)||c.push(x(l).then(d=>S.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=v(l);d&&!E.has(d)&&c.push(x(d).then(f=>E.set(d,f)).catch(()=>{}))}o&&await W({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}window.snapdom=g;window.preCache=rt;})();
@@ -1,3 +1,3 @@
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};
1
+ var S=new Map,E=new Map,R=new Map,T=new Map,k=new Map,U=new WeakMap;var it=["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 t of it)H(t)}function H(t){if(T.has(t))return T.get(t);if(new Set(["script","style","meta","link","noscript","template"]).has(t)){let i={};return T.set(t,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(t);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(t,r),r}function B(t,e,o=!1){let n=[],a=H(e);for(let[r,i]of Object.entries(t))if(!o)i&&n.push(`${r}:${i}`);else{let c=a[r];i&&i!==c&&n.push(`${r}:${i}`)}return n.sort().join(";")}function Y(t){let e=new Set;return t.nodeType!==Node.ELEMENT_NODE&&t.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(t.tagName&&e.add(t.tagName.toLowerCase()),typeof t.querySelectorAll=="function"&&t.querySelectorAll("*").forEach(o=>e.add(o.tagName.toLowerCase())),Array.from(e))}function K(t){let e=new Map;for(let n of t){let a=T.get(n);if(!a)continue;let r=Object.entries(a).map(([i,c])=>`${i}:${c};`).sort().join("");e.has(r)||e.set(r,[]),e.get(r).push(n)}let o="";for(let[n,a]of e.entries())o+=`${a.join(",")} { ${n} }
2
+ `;return o}function J(t){let e=new Set(t.values()),o=new Map,n=1;for(let a of e)o.set(a,`c${n++}`);return o}function N(t,{fast:e=!1}={}){if(e)return t();"requestIdleCallback"in window?requestIdleCallback(t,{timeout:50}):setTimeout(t,1)}function A(t,e=null){if(!(t instanceof Element))return window.getComputedStyle(t,e);let o=U.get(t);if(o||(o=new Map,U.set(t,o)),!o.has(e)){let n=window.getComputedStyle(t,e);o.set(e,n)}return o.get(e)}function X(t){let e=t.replace(/^['"]|['"]$/g,"");if(e.startsWith("\\"))try{return String.fromCharCode(parseInt(e.replace("\\",""),16))}catch{return e}return e}function v(t){let e=t.indexOf("url(");if(e===-1)return null;let o=t.slice(e+4).trim();return o.endsWith(")")&&(o=o.slice(0,-1).trim()),(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.slice(1,-1)),o}function M(t){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(t))}function x(t,e=3e3){return S.has(t)?Promise.resolve(S.get(t)):new Promise((o,n)=>{let a=setTimeout(()=>{n(new Error("Image load timed out"))},e),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");S.set(t,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=t})}function F(t){let e={};for(let o of t)e[o]=t.getPropertyValue(o);return e}function D(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function V(t,e,o,n,a){if(t.tagName==="STYLE")return;n.has(t)||n.set(t,A(t));let r=n.get(t),i=F(r),c=t.tagName?.toLowerCase()||"div",s=B(i,c,a);o.set(e,s)}function O(t,e,o,n,a){if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),s=t.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${s.width}px; height: ${s.height}px; visibility: hidden;`,c}if(t.tagName==="IFRAME"){let c=document.createElement("div");return c.textContent="",c.style.cssText=`width: ${t.offsetWidth}px; height: ${t.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(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="placeholder"){let c=t.cloneNode(!1);n.set(c,t),V(t,c,e,o,a);let s=document.createElement("div");return s.textContent=t.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(t.tagName==="CANVAS"){let c=t.toDataURL(),s=document.createElement("img");return s.src=c,s.width=t.width,s.height=t.height,s.style.display="inline-block",s.style.width=`${t.width}px`,s.style.height=`${t.height}px`,s}if(t.nodeType===Node.TEXT_NODE){if(t.parentElement?.shadowRoot){let c=t.parentElement.tagName.toLowerCase();if(customElements.get(c))return null}return t.cloneNode(!0)}if(t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);let r=t.cloneNode(!1);n.set(r,t),V(t,r,e,o,a);let i=document.createDocumentFragment();if(t.childNodes.forEach(c=>{let s=O(c,e,o,n,a);s&&i.appendChild(s)}),r.appendChild(i),t.shadowRoot){let c=Array.from(t.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>O(l,e,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),r.appendChild(s)}return r}async function G(t,e,o,n=32,a="#000"){e=e.replace(/^['"]+|['"]+$/g,"");let r=window.devicePixelRatio||1,c=document.createElement("canvas").getContext("2d");c.font=o?`${o} ${n}px "${e}"`:`${n}px "${e}"`;let s=c.measureText(t),l=s.actualBoundingBoxAscent||n*.8,d=s.actualBoundingBoxDescent||n*.2,f=l+d,m=s.width,h=document.createElement("canvas");h.width=Math.ceil(m*r),h.height=Math.ceil(f*r);let p=h.getContext("2d");return p.scale(r,r),p.font=c.font,p.textAlign="left",p.textBaseline="alphabetic",p.fillStyle=a,p.fillText(t,0,l),h.toDataURL()}async function W({ignoreIconFonts:t=!0,preCached:e=!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(t&&(M(a.href)||M(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let f=v(d[0]);if(!f)return null;let m=f;m.startsWith("http")||(m=new URL(m,a.href).href);try{m=encodeURI(m)}catch{console.warn("[snapdom] Failed to encode font URL:",m)}if(t&&M(m))return null;if(R.has(m))return{original:d[0],inlined:`url(${R.get(m)})`};try{let p=await(await fetch(m)).blob(),w=await new Promise(C=>{let b=new FileReader;b.onload=()=>C(b.result),b.readAsDataURL(p)});return R.set(m,w),{original:d[0],inlined:`url(${w})`}}catch{return console.warn("\u274C Failed to fetch font:",m),null}})),l=i;for(let d of s)d&&(l=l.replace(d.original,d.inlined));n+=l+`
3
+ `}catch{console.warn("\u274C Failed to fetch CSS:",a.href)}if(n&&e){let a=document.createElement("style");a.setAttribute("data-snapdom","embedFonts"),a.textContent=n,document.head.appendChild(a)}return n}async function q(t,e,o,n,a,r=!1){if(!(t instanceof Element)||!(e instanceof Element))return;for(let s of["::before","::after"])try{let l=A(t,s);if(!l)continue;let d=l.getPropertyValue("content"),f=l.getPropertyValue("background-image"),m=d&&d!=="none"&&d!=='""'&&d!=="''",h=f&&f.startsWith("url(");if(m||h){let p=l.getPropertyValue("font-family"),w=parseInt(l.getPropertyValue("font-size"))||32,C=parseInt(l.getPropertyValue("font-weight"))||!1,b=l.getPropertyValue("color")||"#000",u=document.createElement("span");u.dataset.snapdomPseudo=s;let I=F(l),P=B(I,"span",a);o.set(u,P);let j=p&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(p),y=X(d);if(!r&&j&&y.length===1){let $=document.createElement("img");$.src=await G(y,p,C,w,b),$.style="display:block;width:100%;height:100%;object-fit:contain;",u.appendChild($)}else if(y.startsWith("url(")){let $=v(y);if($)try{let L=document.createElement("img"),st=await x(encodeURI($));L.src=st,L.style="display:block;width:100%;height:100%;object-fit:contain;",u.appendChild(L)}catch(L){console.error(`[snapdom] Error in pseudo ${s} for`,t,L)}}else!j&&y&&y!=="none"&&(u.textContent=y);if(!(u.childNodes.length>0||u.textContent&&u.textContent.trim()!==""))continue;s==="::before"?e.insertBefore(u,e.firstChild):e.appendChild(u)}}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,t,l)}let i=Array.from(t.children),c=Array.from(e.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s<Math.min(i.length,c.length);s++)await q(i[s],c[s],o,n,a,r)}async function Q(t,e=!1,o=!1){let n=new Map,a=new WeakMap,r=new Map,i;try{i=O(t,n,a,r,e)}catch(s){throw console.warn("deepClone failed:",s),s}try{await q(t,i,n,a,e,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(e){let s=J(n);c=Array.from(s.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of n.entries()){if(l.tagName==="STYLE")continue;let f=s.get(d);f&&l.classList.add(f);let m=l.style?.backgroundImage;l.removeAttribute("style"),m&&m!=="none"&&(l.style.backgroundImage=m)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of 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 h=document.createElement("div");for(h.style.transform=`translate(${-d}px, ${-f}px)`,h.style.willChange="transform",h.style.display="inline-block",h.style.width="100%";s.firstChild;)h.appendChild(s.firstChild);s.appendChild(h)}}return{clone:i,classCSS:c,styleCache:a}}async function Z(t){let e=Array.from(t.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<e.length;n+=4){let a=e.slice(n,n+4).map(o);await Promise.allSettled(a)}}async function tt(t,e,o){let n=[[t,e]];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"),s=v(c);if(s)try{let f=encodeURI(s),m;E.has(f)?m=E.get(f):(m=await x(f),E.set(f,m)),r.style.backgroundImage=`url(${m})`}catch{r.style.backgroundImage="none"}let l=Array.from(a.children),d=Array.from(r.children);for(let f=0;f<Math.min(l.length,d.length);f++)n.push([l[f],d[f]])}}async function et(t,e={}){if(!t)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:a=!0,scale:r=1}=e,i,c,s,l="",d="",f,m;if({clone:i,classCSS:c,styleCache:s}=await Q(t,o,n),await new Promise(p=>{N(async()=>{await Z(i),p()},{fast:a})}),await new Promise(p=>{N(async()=>{await tt(t,i,s),p()},{fast:a})}),n&&await new Promise(p=>{N(async()=>{l=await W({ignoreIconFonts:!n}),p()},{fast:a})}),o){let p=Y(i).sort(),w=p.join(",");k.has(w)?d=k.get(w):await new Promise(C=>{N(()=>{d=K(p),k.set(w,d),C()},{fast:a})})}await new Promise(p=>{N(()=>{let w=t.getBoundingClientRect(),C=Math.ceil(w.width),b=Math.ceil(w.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",r!==1&&D()&&(i.style.scale=`${r}`);let u="http://www.w3.org/2000/svg",I=document.createElementNS(u,"foreignObject");I.setAttribute("width","100%"),I.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,I.appendChild(P),I.appendChild(i);let y=new XMLSerializer().serializeToString(I);m=`<svg xmlns="${u}" width="${C}" height="${b}" viewBox="0 0 ${C} ${b}">`+y+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(m)}`,p()},{fast:a})});let h=document.getElementById("snapdom-sandbox");return h&&h.style.position==="absolute"&&h.remove(),f}async function ct(t,{dpr:e=1,scale:o=1}){let n=new Image;return n.src=t,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 nt(t,{dpr:e=1,scale:o=1}){let n=new Image;n.src=t,await n.decode();let a=document.createElement("canvas"),r=n.width*o*e,i=n.height*o*e;a.width=Math.ceil(r),a.height=Math.ceil(i);let c=a.getContext("2d");return c.scale(o*e,o*e),c.drawImage(n,0,0),a.style.width=`${a.width/e}px`,a.style.height=`${a.height/e}px`,a}async function ot(t){let e=decodeURIComponent(t.split(",")[1]);return new Blob([e],{type:"image/svg+xml"})}async function at(t,{dpr:e=1,scale:o=1},n){let a=await nt(t,{dpr:e,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 z(t,{dpr:e=1,scale:o=1,backgroundColor:n="#fff",quality:a},r="png"){let i=await at(t,{dpr:e,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${r}`,a),await c.decode(),c.style.width=`${i.width/e}px`,c.style.height=`${i.height/e}px`,c}async function lt(t,{dpr:e=1,scale:o=1,backgroundColor:n="#fff",format:a="png",filename:r="capture"}={}){if(a==="svg"){let m=await ot(t),h=URL.createObjectURL(m),p=document.createElement("a");p.href=h,p.download=`${r}.svg`,p.click(),URL.revokeObjectURL(h);return}let i=["jpg","jpeg","webp"].includes(a)?"#fff":void 0,s=await at(t,{dpr:e,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(t,e={}){if(e={scale:1,...e},!t)throw new Error("Element cannot be null or undefined");return await g.capture(t,e)}g.capture=async(t,e={})=>{let o=await et(t,e),n=window.devicePixelRatio||1,a=e.scale||1;return{url:o,options:e,toRaw:()=>o,toImg:()=>ct(o,{dpr:n,scale:a}),toCanvas:()=>nt(o,{dpr:n,scale:a}),toBlob:()=>ot(o),toPng:r=>z(o,{dpr:n,scale:a,...r},"png"),toJpg:r=>z(o,{dpr:n,scale:a,...r},"jpeg"),toWebp:r=>z(o,{dpr:n,scale:a,...r},"webp"),download:({format:r="png",filename:i="capture",backgroundColor:c}={})=>lt(o,{dpr:n,scale:a,backgroundColor:c,format:r,filename:i})}};g.toRaw=async(t,e)=>(await g.capture(t,e)).toRaw();g.toImg=async(t,e)=>(await g.capture(t,e)).toImg();g.toCanvas=async(t,e)=>(await g.capture(t,e)).toCanvas();g.toBlob=async(t,e)=>(await g.capture(t,e)).toBlob();g.toPng=async(t,e)=>(await g.capture(t,e)).toPng(e);g.toJpg=async(t,e)=>(await g.capture(t,e)).toJpg(e);g.toWebp=async(t,e)=>(await g.capture(t,e)).toWebp(e);g.download=async(t,e={})=>{let{format:o="png",filename:n="capture",backgroundColor:a,...r}=e;return await(await g.capture(t,r)).download({format:o,filename:n,backgroundColor:a})};async function dt(t=document,e={}){let{embedFonts:o=!0,reset:n=!1,preWarm:a=!0}=e;if(n){S.clear(),E.clear(),R.clear(),k.clear(),U.clear();return}await document.fonts.ready,_();let r=[],i=[];t?.querySelectorAll&&(r=Array.from(t.querySelectorAll("img[src]")),i=Array.from(t.querySelectorAll("*")));let c=[];for(let s of r){let l=s.src;S.has(l)||c.push(x(l).then(d=>S.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=v(l);d&&!E.has(d)&&c.push(x(d).then(f=>E.set(d,f)).catch(()=>{}))}o&&await W({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}export{dt as preCache,g as snapdom};
package/dist/snapdom.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.2.2
4
+ * v.1.2.5
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -186,9 +186,15 @@ function parseContent(content) {
186
186
  }
187
187
  return clean;
188
188
  }
189
- function extractUrl(cssValue) {
190
- const m = cssValue.match(/url\(["']?([^"')]+)["']?\)/);
191
- return m ? m[1] : null;
189
+ function extractURL(value) {
190
+ const urlStart = value.indexOf("url(");
191
+ if (urlStart === -1) return null;
192
+ let url = value.slice(urlStart + 4).trim();
193
+ if (url.endsWith(")")) url = url.slice(0, -1).trim();
194
+ if (url.startsWith('"') && url.endsWith('"') || url.startsWith("'") && url.endsWith("'")) {
195
+ url = url.slice(1, -1);
196
+ }
197
+ return url;
192
198
  }
193
199
  function isIconFont(familyOrUrl) {
194
200
  const iconFontPatterns = [
@@ -362,10 +368,17 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
362
368
  const urlRegex = /url\(([^)]+)\)/g;
363
369
  const inlinedCSS = await Promise.all(
364
370
  Array.from(cssText.matchAll(urlRegex)).map(async (match) => {
365
- let url = match[1].replace(/["']/g, "");
371
+ let rawUrl = extractURL(match[0]);
372
+ if (!rawUrl) return null;
373
+ let url = rawUrl;
366
374
  if (!url.startsWith("http")) {
367
375
  url = new URL(url, link.href).href;
368
376
  }
377
+ try {
378
+ url = encodeURI(url);
379
+ } catch (e) {
380
+ console.warn("[snapdom] Failed to encode font URL:", url);
381
+ }
369
382
  if (ignoreIconFonts && isIconFont(url)) {
370
383
  return null;
371
384
  }
@@ -429,29 +442,43 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
429
442
  const snapshot = snapshotComputedStyle(style);
430
443
  const key = getStyleKey(snapshot, "span", compress);
431
444
  styleMap.set(pseudoEl, key);
432
- const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(fontFamily);
445
+ const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(
446
+ fontFamily
447
+ );
433
448
  let cleanContent = parseContent(content);
434
449
  if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
435
450
  const imgEl = document.createElement("img");
436
- imgEl.src = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
451
+ imgEl.src = await iconToImage(
452
+ cleanContent,
453
+ fontFamily,
454
+ fontWeight,
455
+ fontSize,
456
+ color
457
+ );
437
458
  imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
438
459
  pseudoEl.appendChild(imgEl);
439
460
  } else if (cleanContent.startsWith("url(")) {
440
- const match = cleanContent.match(/url\(["']?([^"')]+)["']?\)/);
441
- if (match?.[1]) {
461
+ const rawUrl = extractURL(cleanContent);
462
+ if (rawUrl) {
442
463
  try {
443
464
  const imgEl = document.createElement("img");
444
- const dataUrl = await fetchImage(match[1]);
465
+ const dataUrl = await fetchImage(encodeURI(rawUrl));
445
466
  imgEl.src = dataUrl;
446
467
  imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
447
468
  pseudoEl.appendChild(imgEl);
448
469
  } catch (e) {
449
- console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
470
+ console.error(
471
+ `[snapdom] Error in pseudo ${pseudo} for`,
472
+ source,
473
+ e
474
+ );
450
475
  }
451
476
  }
452
- } else if (cleanContent && cleanContent !== "none") {
477
+ } else if (!isIconFont2 && cleanContent && cleanContent !== "none") {
453
478
  pseudoEl.textContent = cleanContent;
454
479
  }
480
+ const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent && pseudoEl.textContent.trim() !== "";
481
+ if (!hasContent2) continue;
455
482
  if (pseudo === "::before") {
456
483
  clone.insertBefore(pseudoEl, clone.firstChild);
457
484
  } else {
@@ -467,7 +494,14 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
467
494
  (child) => !child.dataset.snapdomPseudo
468
495
  );
469
496
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
470
- await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress, embedFonts);
497
+ await inlinePseudoElements(
498
+ sChildren[i],
499
+ cChildren[i],
500
+ styleMap,
501
+ styleCache,
502
+ compress,
503
+ embedFonts
504
+ );
471
505
  }
472
506
  }
473
507
 
@@ -559,22 +593,20 @@ async function inlineBackgroundImages(source, clone, styleCache) {
559
593
  const style = styleCache.get(srcNode) || getStyle(srcNode);
560
594
  if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
561
595
  const bg = style.getPropertyValue("background-image");
562
- if (bg && bg.includes("url(")) {
563
- const match = bg.match(/url\(["']?([^"')]+)["']?\)/);
564
- if (match?.[1]) {
565
- try {
566
- const bgUrl = match[1];
567
- let dataUrl;
568
- if (bgCache.has(bgUrl)) {
569
- dataUrl = bgCache.get(bgUrl);
570
- } else {
571
- dataUrl = await fetchImage(bgUrl);
572
- bgCache.set(bgUrl, dataUrl);
573
- }
574
- cloneNode.style.backgroundImage = `url(${dataUrl})`;
575
- } catch {
576
- cloneNode.style.backgroundImage = "none";
596
+ const rawUrl = extractURL(bg);
597
+ if (rawUrl) {
598
+ try {
599
+ let bgUrl = encodeURI(rawUrl);
600
+ let dataUrl;
601
+ if (bgCache.has(bgUrl)) {
602
+ dataUrl = bgCache.get(bgUrl);
603
+ } else {
604
+ dataUrl = await fetchImage(bgUrl);
605
+ bgCache.set(bgUrl, dataUrl);
577
606
  }
607
+ cloneNode.style.backgroundImage = `url(${dataUrl})`;
608
+ } catch {
609
+ cloneNode.style.backgroundImage = "none";
578
610
  }
579
611
  }
580
612
  const sChildren = Array.from(srcNode.children);
@@ -688,6 +720,8 @@ async function toCanvas(url, { dpr = 1, scale = 1 }) {
688
720
  const ctx = canvas.getContext("2d");
689
721
  ctx.scale(scale * dpr, scale * dpr);
690
722
  ctx.drawImage(img, 0, 0);
723
+ canvas.style.width = `${canvas.width / dpr}px`;
724
+ canvas.style.height = `${canvas.height / dpr}px`;
691
725
  return canvas;
692
726
  }
693
727
  async function toBlob(url) {
@@ -811,7 +845,7 @@ async function preCache(root = document, options = {}) {
811
845
  }
812
846
  for (const el of allEls) {
813
847
  const bg = getComputedStyle(el).backgroundImage;
814
- const url = extractUrl(bg);
848
+ const url = extractURL(bg);
815
849
  if (url && !bgCache.has(url)) {
816
850
  promises.push(
817
851
  fetchImage(url).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zumer/snapdom",
3
- "version": "1.2.2",
3
+ "version": "1.2.5",
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",
@@ -63,7 +63,7 @@
63
63
  "bugs": {
64
64
  "url": "https://github.com/zumerlab/snapdom/issues"
65
65
  },
66
- "homepage": "https://github.com/zumerlab/snapdom#readme",
66
+ "homepage": "https://zumerlab.github.io/snapdom/",
67
67
  "devDependencies": {
68
68
  "@vitest/browser": "^3.1.2",
69
69
  "playwright": "^1.52.0"