@zumer/snapdom 1.3.0 → 1.7.1

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
@@ -122,14 +122,34 @@ Returns an object with reusable export methods:
122
122
 
123
123
  All capture methods accept an `options` object:
124
124
 
125
- | Option | Type | Default | Description |
126
- | ----------------- | ------- | -------- | ------------------------------------------ |
127
- | `compress` | boolean | `true` | Removes redundant styles |
128
- | `fast` | boolean | `true` | Skips idle delay for faster results |
129
- | `embedFonts` | boolean | `false` | Inlines fonts (icon fonts always embedded) |
130
- | `scale` | number | `1` | Output scale multiplier |
131
- | `backgroundColor` | string | `"#fff"` | Fallback color for JPG/WebP |
132
- | `quality` | number | `1` | Quality for JPG/WebP (0 to 1) |
125
+ | Option | Type | Default | Description |
126
+ | ----------------- | -------- | -------- | ------------------------------------------ |
127
+ | `compress` | boolean | `true` | Removes redundant styles |
128
+ | `fast` | boolean | `true` | Skips idle delay for faster results |
129
+ | `embedFonts` | boolean | `false` | Inlines fonts (icon fonts always embedded) |
130
+ | `scale` | number | `1` | Output scale multiplier |
131
+ | `backgroundColor` | string | `"#fff"` | Fallback color for JPG/WebP |
132
+ | `quality` | number | `1` | Quality for JPG/WebP (0 to 1) |
133
+ | `crossOrigin` | function | - | Function to determine CORS mode per image URL |
134
+
135
+ ### Cross-Origin Images
136
+
137
+ By default, snapDOM loads images with `crossOrigin="anonymous"`. You can customize this behavior using the `crossOrigin` option:
138
+
139
+ ```js
140
+ const result = await snapdom(element, {
141
+ crossOrigin: (url) => {
142
+ // Use credentials for same-origin images
143
+ if (url.startsWith(window.location.origin)) {
144
+ return "use-credentials";
145
+ }
146
+ // Use anonymous for cross-origin images
147
+ return "anonymous";
148
+ }
149
+ });
150
+ ```
151
+
152
+ This is useful when your images require authentication or when dealing with credentialed requests.
133
153
 
134
154
  ### Download options
135
155
 
@@ -164,6 +184,7 @@ import { snapdom, preCache } from './snapdom.mjs';
164
184
 
165
185
  * `embedFonts` *(boolean, default: true)* — Inlines non-icon fonts during preload.
166
186
  * `reset` *(boolean, default: false)* — Clears all existing internal caches.
187
+ * `crossOrigin` *(function)* — Function to determine CORS mode per image URL during preload.
167
188
 
168
189
 
169
190
  ## Features
@@ -177,9 +198,10 @@ import { snapdom, preCache } from './snapdom.mjs';
177
198
 
178
199
  ## Limitations
179
200
 
180
- * External images must be CORS-accessible
201
+ * External images must be CORS-accessible (use `crossOrigin` option for credentialed requests)
181
202
  * Iframes are not supported
182
203
  * When WebP format is used on Safari, it will fallback to PNG rendering.
204
+ * `@font-face` CSS rule is well supported, but if need to use JS `FontFace()`, see this workaround [`#43`](https://github.com/zumerlab/snapdom/issues/43)
183
205
 
184
206
  ## Benchmarks
185
207
 
@@ -215,6 +237,11 @@ npm run test:benchmark
215
237
 
216
238
  They execute in **headless Chromium** using real DOM nodes.
217
239
 
240
+ ## Contributors 🙌
241
+
242
+ <a href="https://github.com/zumerlab/snapdom/graphs/contributors">
243
+ <img src="https://contrib.rocks/image?repo=zumerlab/snapdom" />
244
+ </a>
218
245
 
219
246
  ## License
220
247
 
package/dist/snapdom.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.3.0
4
+ * v.1.7.1
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -209,7 +209,7 @@
209
209
  ];
210
210
  return iconFontPatterns.some((rx) => rx.test(familyOrUrl));
211
211
  }
212
- function fetchImage(src, timeout = 3e3) {
212
+ function fetchImage(src, timeout = 3e3, crossOrigin = "anonymous") {
213
213
  if (imageCache.has(src)) {
214
214
  return Promise.resolve(imageCache.get(src));
215
215
  }
@@ -218,7 +218,7 @@
218
218
  reject(new Error("Image load timed out"));
219
219
  }, timeout);
220
220
  const image = new Image();
221
- image.crossOrigin = "anonymous";
221
+ image.crossOrigin = crossOrigin;
222
222
  image.onload = async () => {
223
223
  clearTimeout(timeoutId);
224
224
  try {
@@ -375,7 +375,7 @@
375
375
  ctx.fillText(unicodeChar, 0, ascent);
376
376
  return canvas.toDataURL();
377
377
  }
378
- async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
378
+ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false } = {}) {
379
379
  const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((link) => link.href);
380
380
  let finalCSS = "";
381
381
  for (const link of links) {
@@ -416,7 +416,7 @@
416
416
  resourceCache.set(url, b64);
417
417
  return { original: match[0], inlined: `url(${b64})` };
418
418
  } catch (err) {
419
- console.warn("\u274C Failed to fetch font:", url);
419
+ console.warn("[snapdom] Failed to fetch font:", url);
420
420
  return null;
421
421
  }
422
422
  })
@@ -429,7 +429,90 @@
429
429
  }
430
430
  finalCSS += cssFinal + "\n";
431
431
  } catch (e) {
432
- console.warn("\u274C Failed to fetch CSS:", link.href);
432
+ console.warn("[snapdom] Failed to fetch CSS:", link.href);
433
+ }
434
+ }
435
+ for (const sheet of document.styleSheets) {
436
+ try {
437
+ if (!sheet.href || links.every((link) => link.href !== sheet.href)) {
438
+ for (const rule of sheet.cssRules) {
439
+ if (rule.type === CSSRule.FONT_FACE_RULE) {
440
+ const src = rule.style.getPropertyValue("src");
441
+ if (!src) continue;
442
+ const urlRegex = /url\(([^)]+)\)/g;
443
+ let inlinedSrc = src;
444
+ const matches = Array.from(src.matchAll(urlRegex));
445
+ for (const match of matches) {
446
+ let rawUrl = match[1].trim().replace(/^["']|["']$/g, "");
447
+ if (!rawUrl) continue;
448
+ let url = rawUrl;
449
+ if (!url.startsWith("http") && !url.startsWith("data:")) {
450
+ url = new URL(url, sheet.href || location.href).href;
451
+ }
452
+ if (ignoreIconFonts && isIconFont(url)) {
453
+ continue;
454
+ }
455
+ if (resourceCache.has(url)) {
456
+ inlinedSrc = inlinedSrc.replace(match[0], `url(${resourceCache.get(url)})`);
457
+ continue;
458
+ }
459
+ try {
460
+ const res = await fetch(url);
461
+ const blob = await res.blob();
462
+ const b64 = await new Promise((resolve) => {
463
+ const reader = new FileReader();
464
+ reader.onload = () => resolve(reader.result);
465
+ reader.readAsDataURL(blob);
466
+ });
467
+ resourceCache.set(url, b64);
468
+ inlinedSrc = inlinedSrc.replace(match[0], `url(${b64})`);
469
+ } catch (e) {
470
+ console.warn("[snapdom] Failed to fetch font URL:", url);
471
+ }
472
+ }
473
+ finalCSS += `@font-face {
474
+ font-family: ${rule.style.getPropertyValue("font-family")};
475
+ src: ${inlinedSrc};
476
+ font-style: ${rule.style.getPropertyValue("font-style") || "normal"};
477
+ font-weight: ${rule.style.getPropertyValue("font-weight") || "normal"};
478
+ }
479
+ `;
480
+ }
481
+ }
482
+ }
483
+ } catch (e) {
484
+ console.warn("[snapdom] Cannot access stylesheet", sheet.href, e);
485
+ }
486
+ }
487
+ for (const font of document.fonts) {
488
+ if (font.family && font.status === "loaded" && font._snapdomSrc) {
489
+ let b64 = font._snapdomSrc;
490
+ if (!b64.startsWith("data:")) {
491
+ if (resourceCache.has(font._snapdomSrc)) {
492
+ b64 = resourceCache.get(font._snapdomSrc);
493
+ } else {
494
+ try {
495
+ const res = await fetch(font._snapdomSrc);
496
+ const blob = await res.blob();
497
+ b64 = await new Promise((resolve) => {
498
+ const reader = new FileReader();
499
+ reader.onload = () => resolve(reader.result);
500
+ reader.readAsDataURL(blob);
501
+ });
502
+ resourceCache.set(font._snapdomSrc, b64);
503
+ } catch (e) {
504
+ console.warn("[snapdom] Failed to fetch dynamic font src:", font._snapdomSrc);
505
+ continue;
506
+ }
507
+ }
508
+ }
509
+ finalCSS += `@font-face {
510
+ font-family: '${font.family}';
511
+ src: url(${b64});
512
+ font-style: ${font.style || "normal"};
513
+ font-weight: ${font.weight || "normal"};
514
+ }
515
+ `;
433
516
  }
434
517
  }
435
518
  if (finalCSS && preCached) {
@@ -588,12 +671,13 @@
588
671
  }
589
672
 
590
673
  // src/modules/images.js
591
- async function inlineImages(clone) {
674
+ async function inlineImages(clone, options = {}) {
592
675
  const imgs = Array.from(clone.querySelectorAll("img"));
593
676
  const processImg = async (img) => {
594
677
  const src = img.src;
595
678
  try {
596
- const dataUrl = await fetchImage(src);
679
+ const crossOrigin = options.crossOrigin ? options.crossOrigin(src) : "anonymous";
680
+ const dataUrl = await fetchImage(src, 3e3, crossOrigin);
597
681
  img.src = dataUrl;
598
682
  if (!img.width) img.width = img.naturalWidth || 100;
599
683
  if (!img.height) img.height = img.naturalHeight || 100;
@@ -611,29 +695,36 @@
611
695
  }
612
696
 
613
697
  // src/modules/background.js
614
- async function inlineBackgroundImages(source, clone, styleCache) {
698
+ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
615
699
  const queue = [[source, clone]];
616
700
  while (queue.length) {
617
701
  const [srcNode, cloneNode] = queue.shift();
618
702
  const style = styleCache.get(srcNode) || getStyle(srcNode);
619
703
  if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
620
704
  const bg = style.getPropertyValue("background-image");
621
- const rawUrl = extractURL(bg);
622
- if (rawUrl) {
623
- try {
624
- let bgUrl = encodeURI(rawUrl);
625
- let dataUrl;
626
- if (bgCache.has(bgUrl)) {
627
- dataUrl = bgCache.get(bgUrl);
628
- } else {
629
- dataUrl = await fetchImage(bgUrl);
630
- bgCache.set(bgUrl, dataUrl);
705
+ const bgSplits = bg.split(",");
706
+ for (let i = 0; i < bgSplits.length; i++) {
707
+ const rawUrl = extractURL(bgSplits[i]);
708
+ if (rawUrl) {
709
+ try {
710
+ let bgUrl = encodeURI(rawUrl);
711
+ let dataUrl;
712
+ if (bgCache.has(bgUrl)) {
713
+ dataUrl = bgCache.get(bgUrl);
714
+ } else {
715
+ const crossOrigin = options.crossOrigin ? options.crossOrigin(bgUrl) : "anonymous";
716
+ dataUrl = await fetchImage(bgUrl, 3e3, crossOrigin);
717
+ bgCache.set(bgUrl, dataUrl);
718
+ }
719
+ bgSplits[i] = `url(${dataUrl})`;
720
+ } catch {
721
+ bgSplits[i] = "none";
631
722
  }
632
- cloneNode.style.backgroundImage = `url(${dataUrl})`;
633
- } catch {
634
- cloneNode.style.backgroundImage = "none";
635
723
  }
636
724
  }
725
+ if (bgSplits.some((s) => s.includes("url("))) {
726
+ cloneNode.style.backgroundImage = bgSplits.join(",");
727
+ }
637
728
  const sChildren = Array.from(srcNode.children);
638
729
  const cChildren = Array.from(cloneNode.children);
639
730
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
@@ -654,13 +745,13 @@
654
745
  ({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
655
746
  await new Promise((resolve) => {
656
747
  idle(async () => {
657
- await inlineImages(clone);
748
+ await inlineImages(clone, options);
658
749
  resolve();
659
750
  }, { fast });
660
751
  });
661
752
  await new Promise((resolve) => {
662
753
  idle(async () => {
663
- await inlineBackgroundImages(element, clone, styleCache);
754
+ await inlineBackgroundImages(element, clone, styleCache, options);
664
755
  resolve();
665
756
  }, { fast });
666
757
  });
@@ -842,7 +933,7 @@
842
933
 
843
934
  // src/api/preCache.js
844
935
  async function preCache(root = document, options = {}) {
845
- const { embedFonts = true, reset = false, preWarm = true } = options;
936
+ const { embedFonts = true, reset = false, crossOrigin: crossOriginFn } = options;
846
937
  if (reset) {
847
938
  imageCache.clear();
848
939
  bgCache.clear();
@@ -862,8 +953,9 @@
862
953
  for (const img of imgEls) {
863
954
  const src = img.src;
864
955
  if (!imageCache.has(src)) {
956
+ const crossOrigin = crossOriginFn ? crossOriginFn(src) : "anonymous";
865
957
  promises.push(
866
- fetchImage(src).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
958
+ fetchImage(src, 3e3, crossOrigin).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
867
959
  })
868
960
  );
869
961
  }
@@ -872,8 +964,9 @@
872
964
  const bg = getComputedStyle(el).backgroundImage;
873
965
  const url = extractURL(bg);
874
966
  if (url && !bgCache.has(url)) {
967
+ const crossOrigin = crossOriginFn ? crossOriginFn(url) : "anonymous";
875
968
  promises.push(
876
- fetchImage(url).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
969
+ fetchImage(url, 3e3, crossOrigin).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
877
970
  })
878
971
  );
879
972
  }
@@ -1,3 +1,15 @@
1
- (()=>{var I=new Map,$=new Map,T=new Map,R=new Map,A=new Map,U=new WeakMap;var ce=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function z(){for(let e of ce)_(e)}function _(e){if(R.has(e))return R.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let i={};return R.set(e,i),i}let o=document.getElementById("snapdom-sandbox");o||(o=document.createElement("div"),o.id="snapdom-sandbox",o.style.position="absolute",o.style.left="-9999px",o.style.top="-9999px",o.style.width="0",o.style.height="0",o.style.overflow="hidden",document.body.appendChild(o));let n=document.createElement(e);n.style.all="initial",o.appendChild(n);let r=getComputedStyle(n),a={};for(let i of r)a[i]=r.getPropertyValue(i);return o.removeChild(n),R.set(e,a),a}function M(e,t,o=!1){let n=[],r=_(t);for(let[a,i]of Object.entries(e))if(!o)i&&n.push(`${a}:${i}`);else{let c=r[a];i&&i!==c&&n.push(`${a}:${i}`)}return n.sort().join(";")}function Y(e){let t=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&t.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(o=>t.add(o.tagName.toLowerCase())),Array.from(t))}function K(e){let t=new Map;for(let n of e){let r=R.get(n);if(!r)continue;let a=Object.entries(r).map(([i,c])=>`${i}:${c};`).sort().join("");t.has(a)||t.set(a,[]),t.get(a).push(n)}let o="";for(let[n,r]of t.entries())o+=`${r.join(",")} { ${n} }
2
- `;return o}function J(e){let t=new Set(e.values()),o=new Map,n=1;for(let r of t)o.set(r,`c${n++}`);return o}function L(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function N(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let o=U.get(e);if(o||(o=new Map,U.set(e,o)),!o.has(t)){let n=window.getComputedStyle(e,t);o.set(t,n)}return o.get(t)}function X(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function S(e){let t=e.indexOf("url(");if(t===-1)return null;let o=e.slice(t+4).trim();return o.endsWith(")")&&(o=o.slice(0,-1).trim()),(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.slice(1,-1)),o}function B(e){return[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i].some(o=>o.test(e))}function x(e,t=3e3){return I.has(e)?Promise.resolve(I.get(e)):new Promise((o,n)=>{let r=setTimeout(()=>{n(new Error("Image load timed out"))},t),a=new Image;a.crossOrigin="anonymous",a.onload=async()=>{clearTimeout(r);try{await a.decode();let i=document.createElement("canvas");i.width=a.width,i.height=a.height,i.getContext("2d").drawImage(a,0,0,i.width,i.height);try{let s=i.toDataURL("image/png");I.set(e,s),o(s)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(i){n(i)}},a.onerror=i=>{clearTimeout(r),n(new Error("Failed to load image: "+(i.message||"Unknown error")))},a.src=e})}function F(e){let t={};for(let o of e)t[o]=e.getPropertyValue(o);return t}function D(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function V(e,t,o,n,r){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,N(e));let a=n.get(e),i=F(a),c=e.tagName?.toLowerCase()||"div",s=M(i,c,r);o.set(t,s)}function O(e,t,o,n,r){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),s=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${s.width}px; height: ${s.height}px; visibility: hidden;`,c}if(e.tagName==="IFRAME"){let c=document.createElement("div");return c.textContent="",c.style.cssText=`width: ${e.offsetWidth}px; height: ${e.offsetHeight}px; background: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`,c}if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="placeholder"){let c=e.cloneNode(!1);n.set(c,e),V(e,c,t,o,r);let s=document.createElement("div");return s.textContent=e.getAttribute("data-placeholder-text")||"",s.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(s),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),s=document.createElement("img");return s.src=c,s.width=e.width,s.height=e.height,s.style.display="inline-block",s.style.width=`${e.width}px`,s.style.height=`${e.height}px`,s}if(e.nodeType===Node.TEXT_NODE){if(e.parentElement?.shadowRoot){let c=e.parentElement.tagName.toLowerCase();if(customElements.get(c))return null}return e.cloneNode(!0)}if(e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);let a=e.cloneNode(!1);n.set(a,e),e instanceof HTMLInputElement?(a.value=e.value,a.setAttribute("value",e.value),e.checked!==void 0&&(a.checked=e.checked,e.checked&&a.setAttribute("checked",""))):e instanceof HTMLTextAreaElement?(a.value=e.value,a.textContent=e.value):e instanceof HTMLSelectElement&&(a.value=e.value,Array.from(a.options).forEach(c=>{c.value===e.value?c.setAttribute("selected",""):c.removeAttribute("selected")})),V(e,a,t,o,r);let i=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let s=O(c,t,o,n,r);s&&i.appendChild(s)}),a.appendChild(i),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>O(l,t,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),a.appendChild(s)}return a}async function G(e,t,o,n=32,r="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let a=window.devicePixelRatio||1,c=document.createElement("canvas").getContext("2d");c.font=o?`${o} ${n}px "${t}"`:`${n}px "${t}"`;let s=c.measureText(e),l=s.actualBoundingBoxAscent||n*.8,d=s.actualBoundingBoxDescent||n*.2,f=l+d,m=s.width,p=document.createElement("canvas");p.width=Math.ceil(m*a),p.height=Math.ceil(f*a);let h=p.getContext("2d");return h.scale(a,a),h.font=c.font,h.textAlign="left",h.textBaseline="alphabetic",h.fillStyle=r,h.fillText(e,0,l),p.toDataURL()}async function W({ignoreIconFonts:e=!0,preCached:t=!1}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(r=>r.href),n="";for(let r of o)try{let i=await(await fetch(r.href)).text();if(e&&(B(r.href)||B(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let f=S(d[0]);if(!f)return null;let m=f;m.startsWith("http")||(m=new URL(m,r.href).href);try{m=encodeURI(m)}catch{console.warn("[snapdom] Failed to encode font URL:",m)}if(e&&B(m))return null;if(T.has(m))return{original:d[0],inlined:`url(${T.get(m)})`};try{let h=await(await fetch(m)).blob(),w=await new Promise(E=>{let v=new FileReader;v.onload=()=>E(v.result),v.readAsDataURL(h)});return T.set(m,w),{original:d[0],inlined:`url(${w})`}}catch{return console.warn("\u274C Failed to fetch font:",m),null}})),l=i;for(let d of s)d&&(l=l.replace(d.original,d.inlined));n+=l+`
3
- `}catch{console.warn("\u274C Failed to fetch CSS:",r.href)}if(n&&t){let r=document.createElement("style");r.setAttribute("data-snapdom","embedFonts"),r.textContent=n,document.head.appendChild(r)}return n}async function q(e,t,o,n,r,a=!1){if(!(e instanceof Element)||!(t instanceof Element))return;for(let s of["::before","::after"])try{let l=N(e,s);if(!l)continue;let d=l.getPropertyValue("content"),f=l.getPropertyValue("background-image"),m=d&&d!=="none"&&d!=='""'&&d!=="''",p=f&&f.startsWith("url(");if(m||p){let h=l.getPropertyValue("font-family"),w=parseInt(l.getPropertyValue("font-size"))||32,E=parseInt(l.getPropertyValue("font-weight"))||!1,v=l.getPropertyValue("color")||"#000",g=document.createElement("span");g.dataset.snapdomPseudo=s;let k=F(l),P=M(k,"span",r);o.set(g,P);let j=h&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(h),C=X(d);if(!a&&j&&C.length===1){let y=document.createElement("img");y.src=await G(C,h,E,w,v),y.style="display:block;width:100%;height:100%;object-fit:contain;",g.appendChild(y)}else if(C.startsWith("url(")){let y=S(C);if(y)try{let b=document.createElement("img"),ie=await x(encodeURI(y));b.src=ie,b.style="display:block;width:100%;height:100%;object-fit:contain;",g.appendChild(b)}catch(b){console.error(`[snapdom] Error in pseudo ${s} for`,e,b)}}else!j&&C&&C!=="none"&&(g.textContent=C);if(p&&f){let y=S(f);if(y)try{let b=await x(encodeURI(y));g.style.backgroundImage=`url(${b})`}catch(b){console.warn(`[snapdom] Failed to inline background-image for ${s}`,b)}}if(!(g.childNodes.length>0||g.textContent&&g.textContent.trim()!=="")&&!p)continue;s==="::before"?t.insertBefore(g,t.firstChild):t.appendChild(g)}}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,e,l)}let i=Array.from(e.children),c=Array.from(t.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s<Math.min(i.length,c.length);s++)await q(i[s],c[s],o,n,r,a)}async function Q(e,t=!1,o=!1){let n=new Map,r=new WeakMap,a=new Map,i;try{i=O(e,n,r,a,t)}catch(s){throw console.warn("deepClone failed:",s),s}try{await q(e,i,n,r,t,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(t){let s=J(n);c=Array.from(s.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of n.entries()){if(l.tagName==="STYLE")continue;let f=s.get(d);f&&l.classList.add(f);let m=l.style?.backgroundImage;l.removeAttribute("style"),m&&m!=="none"&&(l.style.backgroundImage=m)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of a.entries()){let d=l.scrollLeft,f=l.scrollTop;if((d||f)&&s instanceof HTMLElement){s.style.overflow="hidden",s.style.scrollbarWidth="none",s.style.msOverflowStyle="none";let p=document.createElement("div");for(p.style.transform=`translate(${-d}px, ${-f}px)`,p.style.willChange="transform",p.style.display="inline-block",p.style.width="100%";s.firstChild;)p.appendChild(s.firstChild);s.appendChild(p)}}return{clone:i,classCSS:c,styleCache:r}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let r=n.src;try{let a=await x(r);n.src=a,n.width||(n.width=n.naturalWidth||100),n.height||(n.height=n.naturalHeight||100)}catch{let a=document.createElement("div");a.style=`width: ${n.width||100}px; height: ${n.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${n.height||100}px; color: #666; font-size: 12px;`,a.innerText="img",n.replaceWith(a)}};for(let n=0;n<t.length;n+=4){let r=t.slice(n,n+4).map(o);await Promise.allSettled(r)}}async function ee(e,t,o){let n=[[e,t]];for(;n.length;){let[r,a]=n.shift(),i=o.get(r)||N(r);o.has(r)||o.set(r,i);let c=i.getPropertyValue("background-image"),s=S(c);if(s)try{let f=encodeURI(s),m;$.has(f)?m=$.get(f):(m=await x(f),$.set(f,m)),a.style.backgroundImage=`url(${m})`}catch{a.style.backgroundImage="none"}let l=Array.from(r.children),d=Array.from(a.children);for(let f=0;f<Math.min(l.length,d.length);f++)n.push([l[f],d[f]])}}async function te(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:r=!0,scale:a=1}=t,i,c,s,l="",d="",f,m;if({clone:i,classCSS:c,styleCache:s}=await Q(e,o,n),await new Promise(h=>{L(async()=>{await Z(i),h()},{fast:r})}),await new Promise(h=>{L(async()=>{await ee(e,i,s),h()},{fast:r})}),n&&await new Promise(h=>{L(async()=>{l=await W({ignoreIconFonts:!n}),h()},{fast:r})}),o){let h=Y(i).sort(),w=h.join(",");A.has(w)?d=A.get(w):await new Promise(E=>{L(()=>{d=K(h),A.set(w,d),E()},{fast:r})})}await new Promise(h=>{L(()=>{let w=e.getBoundingClientRect(),E=Math.ceil(w.width),v=Math.ceil(w.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",a!==1&&D()&&(i.style.scale=`${a}`);let g="http://www.w3.org/2000/svg",k=document.createElementNS(g,"foreignObject");k.setAttribute("width","100%"),k.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,k.appendChild(P),k.appendChild(i);let C=new XMLSerializer().serializeToString(k);m=`<svg xmlns="${g}" width="${E}" height="${v}" viewBox="0 0 ${E} ${v}">`+C+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(m)}`,h()},{fast:r})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),f}async function le(e,{dpr:t=1,scale:o=1}){let n=new Image;return n.src=e,await n.decode(),D?(n.width=n.width*o,n.height=n.height*o):(n.width=n.width/o,n.height=n.height/o),n}async function ne(e,{dpr:t=1,scale:o=1}={}){let n=new Image;n.src=e,await n.decode();let r=document.createElement("canvas"),a=n.width*o,i=n.height*o;r.width=Math.ceil(a*t),r.height=Math.ceil(i*t);let c=r.getContext("2d");return c.scale(t,t),c.drawImage(n,0,0,a,i),r.style.width=`${a}px`,r.style.height=`${i}px`,r}async function oe(e){let t=decodeURIComponent(e.split(",")[1]);return new Blob([t],{type:"image/svg+xml"})}async function ae(e,{dpr:t=1,scale:o=1},n){let r=await ne(e,{dpr:t,scale:o});if(!n)return r;let a=document.createElement("canvas");a.width=r.width,a.height=r.height;let i=a.getContext("2d");return i.fillStyle=n,i.fillRect(0,0,a.width,a.height),i.drawImage(r,0,0),a}async function H(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:r},a="png"){let i=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${a}`,r),await c.decode(),c.style.width=`${i.width/t}px`,c.style.height=`${i.height/t}px`,c}async function de(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:r="png",filename:a="capture"}={}){if(r==="svg"){let m=await oe(e),p=URL.createObjectURL(m),h=document.createElement("a");h.href=p,h.download=`${a}.svg`,h.click(),URL.revokeObjectURL(p);return}let i=["jpg","jpeg","webp"].includes(r)?"#fff":void 0,s=await ae(e,{dpr:t,scale:o},n??i),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[r]||"image/png",d=s.toDataURL(l),f=document.createElement("a");f.href=d,f.download=`${a}.${r}`,f.click()}async function u(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await u.capture(e,t)}u.capture=async(e,t={})=>{let o=await te(e,t),n=window.devicePixelRatio||1,r=t.scale||1;return{url:o,options:t,toRaw:()=>o,toImg:()=>le(o,{dpr:n,scale:r}),toCanvas:()=>ne(o,{dpr:n,scale:r}),toBlob:()=>oe(o),toPng:a=>H(o,{dpr:n,scale:r,...a},"png"),toJpg:a=>H(o,{dpr:n,scale:r,...a},"jpeg"),toWebp:a=>H(o,{dpr:n,scale:r,...a},"webp"),download:({format:a="png",filename:i="capture",backgroundColor:c}={})=>de(o,{dpr:n,scale:r,backgroundColor:c,format:a,filename:i})}};u.toRaw=async(e,t)=>(await u.capture(e,t)).toRaw();u.toImg=async(e,t)=>(await u.capture(e,t)).toImg();u.toCanvas=async(e,t)=>(await u.capture(e,t)).toCanvas();u.toBlob=async(e,t)=>(await u.capture(e,t)).toBlob();u.toPng=async(e,t)=>(await u.capture(e,t)).toPng(t);u.toJpg=async(e,t)=>(await u.capture(e,t)).toJpg(t);u.toWebp=async(e,t)=>(await u.capture(e,t)).toWebp(t);u.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:r,...a}=t;return await(await u.capture(e,a)).download({format:o,filename:n,backgroundColor:r})};async function re(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:r=!0}=t;if(n){I.clear(),$.clear(),T.clear(),A.clear(),U.clear();return}await document.fonts.ready,z();let a=[],i=[];e?.querySelectorAll&&(a=Array.from(e.querySelectorAll("img[src]")),i=Array.from(e.querySelectorAll("*")));let c=[];for(let s of a){let l=s.src;I.has(l)||c.push(x(l).then(d=>I.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=S(l);d&&!$.has(d)&&c.push(x(d).then(f=>$.set(d,f)).catch(()=>{}))}o&&await W({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}window.snapdom=u;window.preCache=re;})();
1
+ (()=>{var R=new Map,T=new Map,x=new Map,I=new Map,k=new Map,N=new WeakMap;var ce=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function H(){for(let e of ce)z(e)}function z(e){if(I.has(e))return I.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let s={};return I.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 r=document.createElement(e);r.style.all="initial",o.appendChild(r);let t=getComputedStyle(r),a={};for(let s of t)a[s]=t.getPropertyValue(s);return o.removeChild(r),I.set(e,a),a}function M(e,n,o=!1){let r=[],t=z(n);for(let[a,s]of Object.entries(e))if(!o)s&&r.push(`${a}:${s}`);else{let i=t[a];s&&s!==i&&r.push(`${a}:${s}`)}return r.sort().join(";")}function Y(e){let n=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&n.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(o=>n.add(o.tagName.toLowerCase())),Array.from(n))}function K(e){let n=new Map;for(let r of e){let t=I.get(r);if(!t)continue;let a=Object.entries(t).map(([s,i])=>`${s}:${i};`).sort().join("");n.has(a)||n.set(a,[]),n.get(a).push(r)}let o="";for(let[r,t]of n.entries())o+=`${t.join(",")} { ${r} }
2
+ `;return o}function J(e){let n=new Set(e.values()),o=new Map,r=1;for(let t of n)o.set(t,`c${r++}`);return o}function L(e,{fast:n=!1}={}){if(n)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function U(e,n=null){if(!(e instanceof Element))return window.getComputedStyle(e,n);let o=N.get(e);if(o||(o=new Map,N.set(e,o)),!o.has(n)){let r=window.getComputedStyle(e,n);o.set(n,r)}return o.get(n)}function X(e){let n=e.replace(/^['"]|['"]$/g,"");if(n.startsWith("\\"))try{return String.fromCharCode(parseInt(n.replace("\\",""),16))}catch{return n}return n}function $(e){let n=e.indexOf("url(");if(n===-1)return null;let o=e.slice(n+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 P(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 S(e,n=3e3,o="anonymous"){return R.has(e)?Promise.resolve(R.get(e)):new Promise((r,t)=>{let a=setTimeout(()=>{t(new Error("Image load timed out"))},n),s=new Image;s.crossOrigin=o,s.onload=async()=>{clearTimeout(a);try{await s.decode();let i=document.createElement("canvas");i.width=s.width,i.height=s.height,i.getContext("2d").drawImage(s,0,0,i.width,i.height);try{let l=i.toDataURL("image/png");R.set(e,l),r(l)}catch{t(new Error("CORS restrictions prevented image capture"))}}catch(i){t(i)}},s.onerror=i=>{clearTimeout(a),t(new Error("Failed to load image: "+(i.message||"Unknown error")))},s.src=e})}function O(e){let n={};for(let o of e)n[o]=e.getPropertyValue(o);return n}function B(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function _(e,n,o,r,t){if(e.tagName==="STYLE")return;r.has(e)||r.set(e,U(e));let a=r.get(e),s=O(a),i=e.tagName?.toLowerCase()||"div",c=M(s,i,t);o.set(n,c)}function D(e,n,o,r,t){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let i=document.createElement("div"),c=e.getBoundingClientRect();return i.style.cssText=`display: inline-block; width: ${c.width}px; height: ${c.height}px; visibility: hidden;`,i}if(e.tagName==="IFRAME"){let i=document.createElement("div");return i.textContent="",i.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;`,i}if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="placeholder"){let i=e.cloneNode(!1);r.set(i,e),_(e,i,n,o,t);let c=document.createElement("div");return c.textContent=e.getAttribute("data-placeholder-text")||"",c.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",i.appendChild(c),i}if(e.tagName==="CANVAS"){let i=e.toDataURL(),c=document.createElement("img");return c.src=i,c.width=e.width,c.height=e.height,c.style.display="inline-block",c.style.width=`${e.width}px`,c.style.height=`${e.height}px`,c}if(e.nodeType===Node.TEXT_NODE){if(e.parentElement?.shadowRoot){let i=e.parentElement.tagName.toLowerCase();if(customElements.get(i))return null}return e.cloneNode(!0)}if(e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);let a=e.cloneNode(!1);r.set(a,e),e instanceof HTMLInputElement?(a.value=e.value,a.setAttribute("value",e.value),e.checked!==void 0&&(a.checked=e.checked,e.checked&&a.setAttribute("checked",""))):e instanceof HTMLTextAreaElement?(a.value=e.value,a.textContent=e.value):e instanceof HTMLSelectElement&&(a.value=e.value,Array.from(a.options).forEach(i=>{i.value===e.value?i.setAttribute("selected",""):i.removeAttribute("selected")})),_(e,a,n,o,t);let s=document.createDocumentFragment();if(e.childNodes.forEach(i=>{let c=D(i,n,o,r,t);c&&s.appendChild(c)}),a.appendChild(s),e.shadowRoot){let i=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>D(l,n,o,r)).filter(Boolean),c=document.createDocumentFragment();i.forEach(l=>c.appendChild(l)),a.appendChild(c)}return a}async function G(e,n,o,r=32,t="#000"){n=n.replace(/^['"]+|['"]+$/g,"");let a=window.devicePixelRatio||1,i=document.createElement("canvas").getContext("2d");i.font=o?`${o} ${r}px "${n}"`:`${r}px "${n}"`;let c=i.measureText(e),l=c.actualBoundingBoxAscent||r*.8,d=c.actualBoundingBoxDescent||r*.2,h=l+d,f=c.width,p=document.createElement("canvas");p.width=Math.ceil(f*a),p.height=Math.ceil(h*a);let m=p.getContext("2d");return m.scale(a,a),m.font=i.font,m.textAlign="left",m.textBaseline="alphabetic",m.fillStyle=t,m.fillText(e,0,l),p.toDataURL()}async function W({ignoreIconFonts:e=!0,preCached:n=!1}={}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(t=>t.href),r="";for(let t of o)try{let s=await(await fetch(t.href)).text();if(e&&(P(t.href)||P(s)))continue;let i=/url\(([^)]+)\)/g,c=await Promise.all(Array.from(s.matchAll(i)).map(async d=>{let h=$(d[0]);if(!h)return null;let f=h;f.startsWith("http")||(f=new URL(f,t.href).href);try{f=encodeURI(f)}catch{console.warn("[snapdom] Failed to encode font URL:",f)}if(e&&P(f))return null;if(x.has(f))return{original:d[0],inlined:`url(${x.get(f)})`};try{let m=await(await fetch(f)).blob(),g=await new Promise(y=>{let C=new FileReader;C.onload=()=>y(C.result),C.readAsDataURL(m)});return x.set(f,g),{original:d[0],inlined:`url(${g})`}}catch{return console.warn("[snapdom] Failed to fetch font:",f),null}})),l=s;for(let d of c)d&&(l=l.replace(d.original,d.inlined));r+=l+`
3
+ `}catch{console.warn("[snapdom] Failed to fetch CSS:",t.href)}for(let t of document.styleSheets)try{if(!t.href||o.every(a=>a.href!==t.href)){for(let a of t.cssRules)if(a.type===CSSRule.FONT_FACE_RULE){let s=a.style.getPropertyValue("src");if(!s)continue;let i=/url\(([^)]+)\)/g,c=s,l=Array.from(s.matchAll(i));for(let d of l){let h=d[1].trim().replace(/^["']|["']$/g,"");if(!h)continue;let f=h;if(!f.startsWith("http")&&!f.startsWith("data:")&&(f=new URL(f,t.href||location.href).href),!(e&&P(f))){if(x.has(f)){c=c.replace(d[0],`url(${x.get(f)})`);continue}try{let m=await(await fetch(f)).blob(),g=await new Promise(y=>{let C=new FileReader;C.onload=()=>y(C.result),C.readAsDataURL(m)});x.set(f,g),c=c.replace(d[0],`url(${g})`)}catch{console.warn("[snapdom] Failed to fetch font URL:",f)}}}r+=`@font-face {
4
+ font-family: ${a.style.getPropertyValue("font-family")};
5
+ src: ${c};
6
+ font-style: ${a.style.getPropertyValue("font-style")||"normal"};
7
+ font-weight: ${a.style.getPropertyValue("font-weight")||"normal"};
8
+ }
9
+ `}}}catch(a){console.warn("[snapdom] Cannot access stylesheet",t.href,a)}for(let t of document.fonts)if(t.family&&t.status==="loaded"&&t._snapdomSrc){let a=t._snapdomSrc;if(!a.startsWith("data:"))if(x.has(t._snapdomSrc))a=x.get(t._snapdomSrc);else try{let i=await(await fetch(t._snapdomSrc)).blob();a=await new Promise(c=>{let l=new FileReader;l.onload=()=>c(l.result),l.readAsDataURL(i)}),x.set(t._snapdomSrc,a)}catch{console.warn("[snapdom] Failed to fetch dynamic font src:",t._snapdomSrc);continue}r+=`@font-face {
10
+ font-family: '${t.family}';
11
+ src: url(${a});
12
+ font-style: ${t.style||"normal"};
13
+ font-weight: ${t.weight||"normal"};
14
+ }
15
+ `}if(r&&n){let t=document.createElement("style");t.setAttribute("data-snapdom","embedFonts"),t.textContent=r,document.head.appendChild(t)}return r}async function V(e,n,o,r,t,a=!1){if(!(e instanceof Element)||!(n instanceof Element))return;for(let c of["::before","::after"])try{let l=U(e,c);if(!l)continue;let d=l.getPropertyValue("content"),h=l.getPropertyValue("background-image"),f=d&&d!=="none"&&d!=='""'&&d!=="''",p=h&&h.startsWith("url(");if(f||p){let m=l.getPropertyValue("font-family"),g=parseInt(l.getPropertyValue("font-size"))||32,y=parseInt(l.getPropertyValue("font-weight"))||!1,C=l.getPropertyValue("color")||"#000",w=document.createElement("span");w.dataset.snapdomPseudo=c;let A=O(l),F=M(A,"span",t);o.set(w,F);let j=m&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(m),E=X(d);if(!a&&j&&E.length===1){let b=document.createElement("img");b.src=await G(E,m,y,g,C),b.style="display:block;width:100%;height:100%;object-fit:contain;",w.appendChild(b)}else if(E.startsWith("url(")){let b=$(E);if(b)try{let v=document.createElement("img"),ie=await S(encodeURI(b));v.src=ie,v.style="display:block;width:100%;height:100%;object-fit:contain;",w.appendChild(v)}catch(v){console.error(`[snapdom] Error in pseudo ${c} for`,e,v)}}else!j&&E&&E!=="none"&&(w.textContent=E);if(p&&h){let b=$(h);if(b)try{let v=await S(encodeURI(b));w.style.backgroundImage=`url(${v})`}catch(v){console.warn(`[snapdom] Failed to inline background-image for ${c}`,v)}}if(!(w.childNodes.length>0||w.textContent&&w.textContent.trim()!=="")&&!p)continue;c==="::before"?n.insertBefore(w,n.firstChild):n.appendChild(w)}}catch(l){console.warn(`[snapdom] Failed to capture ${c} for`,e,l)}let s=Array.from(e.children),i=Array.from(n.children).filter(c=>!c.dataset.snapdomPseudo);for(let c=0;c<Math.min(s.length,i.length);c++)await V(s[c],i[c],o,r,t,a)}async function Q(e,n=!1,o=!1){let r=new Map,t=new WeakMap,a=new Map,s;try{s=D(e,r,t,a,n)}catch(c){throw console.warn("deepClone failed:",c),c}try{await V(e,s,r,t,n,o)}catch(c){console.warn("inlinePseudoElements failed:",c)}let i="";if(n){let c=J(r);i=Array.from(c.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of r.entries()){if(l.tagName==="STYLE")continue;let h=c.get(d);h&&l.classList.add(h);let f=l.style?.backgroundImage;l.removeAttribute("style"),f&&f!=="none"&&(l.style.backgroundImage=f)}}else for(let[c,l]of r.entries())c.tagName!=="STYLE"&&c.setAttribute("style",l.replace(/;/g,"; "));for(let[c,l]of a.entries()){let d=l.scrollLeft,h=l.scrollTop;if((d||h)&&c instanceof HTMLElement){c.style.overflow="hidden",c.style.scrollbarWidth="none",c.style.msOverflowStyle="none";let p=document.createElement("div");for(p.style.transform=`translate(${-d}px, ${-h}px)`,p.style.willChange="transform",p.style.display="inline-block",p.style.width="100%";c.firstChild;)p.appendChild(c.firstChild);c.appendChild(p)}}return{clone:s,classCSS:i,styleCache:t}}async function Z(e,n={}){let o=Array.from(e.querySelectorAll("img")),r=async t=>{let a=t.src;try{let s=n.crossOrigin?n.crossOrigin(a):"anonymous",i=await S(a,3e3,s);t.src=i,t.width||(t.width=t.naturalWidth||100),t.height||(t.height=t.naturalHeight||100)}catch{let s=document.createElement("div");s.style=`width: ${t.width||100}px; height: ${t.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${t.height||100}px; color: #666; font-size: 12px;`,s.innerText="img",t.replaceWith(s)}};for(let t=0;t<o.length;t+=4){let a=o.slice(t,t+4).map(r);await Promise.allSettled(a)}}async function ee(e,n,o,r={}){let t=[[e,n]];for(;t.length;){let[a,s]=t.shift(),i=o.get(a)||U(a);o.has(a)||o.set(a,i);let l=i.getPropertyValue("background-image").split(",");for(let f=0;f<l.length;f++){let p=$(l[f]);if(p)try{let m=encodeURI(p),g;if(T.has(m))g=T.get(m);else{let y=r.crossOrigin?r.crossOrigin(m):"anonymous";g=await S(m,3e3,y),T.set(m,g)}l[f]=`url(${g})`}catch{l[f]="none"}}l.some(f=>f.includes("url("))&&(s.style.backgroundImage=l.join(","));let d=Array.from(a.children),h=Array.from(s.children);for(let f=0;f<Math.min(d.length,h.length);f++)t.push([d[f],h[f]])}}async function te(e,n={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:r=!1,fast:t=!0,scale:a=1}=n,s,i,c,l="",d="",h,f;if({clone:s,classCSS:i,styleCache:c}=await Q(e,o,r),await new Promise(m=>{L(async()=>{await Z(s,n),m()},{fast:t})}),await new Promise(m=>{L(async()=>{await ee(e,s,c,n),m()},{fast:t})}),r&&await new Promise(m=>{L(async()=>{l=await W({ignoreIconFonts:!r}),m()},{fast:t})}),o){let m=Y(s).sort(),g=m.join(",");k.has(g)?d=k.get(g):await new Promise(y=>{L(()=>{d=K(m),k.set(g,d),y()},{fast:t})})}await new Promise(m=>{L(()=>{let g=e.getBoundingClientRect(),y=Math.ceil(g.width),C=Math.ceil(g.height);s.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),s.style.transformOrigin="top left",a!==1&&B()&&(s.style.scale=`${a}`);let w="http://www.w3.org/2000/svg",A=document.createElementNS(w,"foreignObject");A.setAttribute("width","100%"),A.setAttribute("height","100%");let F=document.createElement("style");F.textContent=d+l+"svg{overflow:visible;}"+i,A.appendChild(F),A.appendChild(s);let E=new XMLSerializer().serializeToString(A);f=`<svg xmlns="${w}" width="${y}" height="${C}" viewBox="0 0 ${y} ${C}">`+E+"</svg>",h=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(f)}`,m()},{fast:t})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),h}async function le(e,{dpr:n=1,scale:o=1}){let r=new Image;return r.src=e,await r.decode(),B?(r.width=r.width*o,r.height=r.height*o):(r.width=r.width/o,r.height=r.height/o),r}async function ne(e,{dpr:n=1,scale:o=1}={}){let r=new Image;r.src=e,await r.decode();let t=document.createElement("canvas"),a=r.width*o,s=r.height*o;t.width=Math.ceil(a*n),t.height=Math.ceil(s*n);let i=t.getContext("2d");return i.scale(n,n),i.drawImage(r,0,0,a,s),t.style.width=`${a}px`,t.style.height=`${s}px`,t}async function oe(e){let n=decodeURIComponent(e.split(",")[1]);return new Blob([n],{type:"image/svg+xml"})}async function ae(e,{dpr:n=1,scale:o=1},r){let t=await ne(e,{dpr:n,scale:o});if(!r)return t;let a=document.createElement("canvas");a.width=t.width,a.height=t.height;let s=a.getContext("2d");return s.fillStyle=r,s.fillRect(0,0,a.width,a.height),s.drawImage(t,0,0),a}async function q(e,{dpr:n=1,scale:o=1,backgroundColor:r="#fff",quality:t},a="png"){let s=await ae(e,{dpr:n,scale:o},r),i=new Image;return i.src=s.toDataURL(`image/${a}`,t),await i.decode(),i.style.width=`${s.width/n}px`,i.style.height=`${s.height/n}px`,i}async function fe(e,{dpr:n=1,scale:o=1,backgroundColor:r="#fff",format:t="png",filename:a="capture"}={}){if(t==="svg"){let f=await oe(e),p=URL.createObjectURL(f),m=document.createElement("a");m.href=p,m.download=`${a}.svg`,m.click(),URL.revokeObjectURL(p);return}let s=["jpg","jpeg","webp"].includes(t)?"#fff":void 0,c=await ae(e,{dpr:n,scale:o},r??s),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[t]||"image/png",d=c.toDataURL(l),h=document.createElement("a");h.href=d,h.download=`${a}.${t}`,h.click()}async function u(e,n={}){if(n={scale:1,...n},!e)throw new Error("Element cannot be null or undefined");return await u.capture(e,n)}u.capture=async(e,n={})=>{let o=await te(e,n),r=window.devicePixelRatio||1,t=n.scale||1;return{url:o,options:n,toRaw:()=>o,toImg:()=>le(o,{dpr:r,scale:t}),toCanvas:()=>ne(o,{dpr:r,scale:t}),toBlob:()=>oe(o),toPng:a=>q(o,{dpr:r,scale:t,...a},"png"),toJpg:a=>q(o,{dpr:r,scale:t,...a},"jpeg"),toWebp:a=>q(o,{dpr:r,scale:t,...a},"webp"),download:({format:a="png",filename:s="capture",backgroundColor:i}={})=>fe(o,{dpr:r,scale:t,backgroundColor:i,format:a,filename:s})}};u.toRaw=async(e,n)=>(await u.capture(e,n)).toRaw();u.toImg=async(e,n)=>(await u.capture(e,n)).toImg();u.toCanvas=async(e,n)=>(await u.capture(e,n)).toCanvas();u.toBlob=async(e,n)=>(await u.capture(e,n)).toBlob();u.toPng=async(e,n)=>(await u.capture(e,n)).toPng(n);u.toJpg=async(e,n)=>(await u.capture(e,n)).toJpg(n);u.toWebp=async(e,n)=>(await u.capture(e,n)).toWebp(n);u.download=async(e,n={})=>{let{format:o="png",filename:r="capture",backgroundColor:t,...a}=n;return await(await u.capture(e,a)).download({format:o,filename:r,backgroundColor:t})};async function re(e=document,n={}){let{embedFonts:o=!0,reset:r=!1,crossOrigin:t}=n;if(r){R.clear(),T.clear(),x.clear(),k.clear(),N.clear();return}await document.fonts.ready,H();let a=[],s=[];e?.querySelectorAll&&(a=Array.from(e.querySelectorAll("img[src]")),s=Array.from(e.querySelectorAll("*")));let i=[];for(let c of a){let l=c.src;if(!R.has(l)){let d=t?t(l):"anonymous";i.push(S(l,3e3,d).then(h=>R.set(l,h)).catch(()=>{}))}}for(let c of s){let l=getComputedStyle(c).backgroundImage,d=$(l);if(d&&!T.has(d)){let h=t?t(d):"anonymous";i.push(S(d,3e3,h).then(f=>T.set(d,f)).catch(()=>{}))}}o&&await W({ignoreIconFonts:!o,preCached:!0}),await Promise.all(i)}window.snapdom=u;window.preCache=re;})();
@@ -1,3 +1,15 @@
1
- var I=new Map,$=new Map,T=new Map,R=new Map,A=new Map,U=new WeakMap;var ie=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function z(){for(let e of ie)_(e)}function _(e){if(R.has(e))return R.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let i={};return R.set(e,i),i}let o=document.getElementById("snapdom-sandbox");o||(o=document.createElement("div"),o.id="snapdom-sandbox",o.style.position="absolute",o.style.left="-9999px",o.style.top="-9999px",o.style.width="0",o.style.height="0",o.style.overflow="hidden",document.body.appendChild(o));let n=document.createElement(e);n.style.all="initial",o.appendChild(n);let r=getComputedStyle(n),a={};for(let i of r)a[i]=r.getPropertyValue(i);return o.removeChild(n),R.set(e,a),a}function M(e,t,o=!1){let n=[],r=_(t);for(let[a,i]of Object.entries(e))if(!o)i&&n.push(`${a}:${i}`);else{let c=r[a];i&&i!==c&&n.push(`${a}:${i}`)}return n.sort().join(";")}function Y(e){let t=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&t.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(o=>t.add(o.tagName.toLowerCase())),Array.from(t))}function K(e){let t=new Map;for(let n of e){let r=R.get(n);if(!r)continue;let a=Object.entries(r).map(([i,c])=>`${i}:${c};`).sort().join("");t.has(a)||t.set(a,[]),t.get(a).push(n)}let o="";for(let[n,r]of t.entries())o+=`${r.join(",")} { ${n} }
2
- `;return o}function J(e){let t=new Set(e.values()),o=new Map,n=1;for(let r of t)o.set(r,`c${n++}`);return o}function L(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function N(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let o=U.get(e);if(o||(o=new Map,U.set(e,o)),!o.has(t)){let n=window.getComputedStyle(e,t);o.set(t,n)}return o.get(t)}function X(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function S(e){let t=e.indexOf("url(");if(t===-1)return null;let o=e.slice(t+4).trim();return o.endsWith(")")&&(o=o.slice(0,-1).trim()),(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.slice(1,-1)),o}function B(e){return[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i].some(o=>o.test(e))}function x(e,t=3e3){return I.has(e)?Promise.resolve(I.get(e)):new Promise((o,n)=>{let r=setTimeout(()=>{n(new Error("Image load timed out"))},t),a=new Image;a.crossOrigin="anonymous",a.onload=async()=>{clearTimeout(r);try{await a.decode();let i=document.createElement("canvas");i.width=a.width,i.height=a.height,i.getContext("2d").drawImage(a,0,0,i.width,i.height);try{let s=i.toDataURL("image/png");I.set(e,s),o(s)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(i){n(i)}},a.onerror=i=>{clearTimeout(r),n(new Error("Failed to load image: "+(i.message||"Unknown error")))},a.src=e})}function F(e){let t={};for(let o of e)t[o]=e.getPropertyValue(o);return t}function D(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function V(e,t,o,n,r){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,N(e));let a=n.get(e),i=F(a),c=e.tagName?.toLowerCase()||"div",s=M(i,c,r);o.set(t,s)}function O(e,t,o,n,r){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),s=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${s.width}px; height: ${s.height}px; visibility: hidden;`,c}if(e.tagName==="IFRAME"){let c=document.createElement("div");return c.textContent="",c.style.cssText=`width: ${e.offsetWidth}px; height: ${e.offsetHeight}px; background: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`,c}if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="placeholder"){let c=e.cloneNode(!1);n.set(c,e),V(e,c,t,o,r);let s=document.createElement("div");return s.textContent=e.getAttribute("data-placeholder-text")||"",s.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(s),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),s=document.createElement("img");return s.src=c,s.width=e.width,s.height=e.height,s.style.display="inline-block",s.style.width=`${e.width}px`,s.style.height=`${e.height}px`,s}if(e.nodeType===Node.TEXT_NODE){if(e.parentElement?.shadowRoot){let c=e.parentElement.tagName.toLowerCase();if(customElements.get(c))return null}return e.cloneNode(!0)}if(e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);let a=e.cloneNode(!1);n.set(a,e),e instanceof HTMLInputElement?(a.value=e.value,a.setAttribute("value",e.value),e.checked!==void 0&&(a.checked=e.checked,e.checked&&a.setAttribute("checked",""))):e instanceof HTMLTextAreaElement?(a.value=e.value,a.textContent=e.value):e instanceof HTMLSelectElement&&(a.value=e.value,Array.from(a.options).forEach(c=>{c.value===e.value?c.setAttribute("selected",""):c.removeAttribute("selected")})),V(e,a,t,o,r);let i=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let s=O(c,t,o,n,r);s&&i.appendChild(s)}),a.appendChild(i),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>O(l,t,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),a.appendChild(s)}return a}async function G(e,t,o,n=32,r="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let a=window.devicePixelRatio||1,c=document.createElement("canvas").getContext("2d");c.font=o?`${o} ${n}px "${t}"`:`${n}px "${t}"`;let s=c.measureText(e),l=s.actualBoundingBoxAscent||n*.8,d=s.actualBoundingBoxDescent||n*.2,f=l+d,m=s.width,p=document.createElement("canvas");p.width=Math.ceil(m*a),p.height=Math.ceil(f*a);let h=p.getContext("2d");return h.scale(a,a),h.font=c.font,h.textAlign="left",h.textBaseline="alphabetic",h.fillStyle=r,h.fillText(e,0,l),p.toDataURL()}async function W({ignoreIconFonts:e=!0,preCached:t=!1}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(r=>r.href),n="";for(let r of o)try{let i=await(await fetch(r.href)).text();if(e&&(B(r.href)||B(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let f=S(d[0]);if(!f)return null;let m=f;m.startsWith("http")||(m=new URL(m,r.href).href);try{m=encodeURI(m)}catch{console.warn("[snapdom] Failed to encode font URL:",m)}if(e&&B(m))return null;if(T.has(m))return{original:d[0],inlined:`url(${T.get(m)})`};try{let h=await(await fetch(m)).blob(),w=await new Promise(E=>{let v=new FileReader;v.onload=()=>E(v.result),v.readAsDataURL(h)});return T.set(m,w),{original:d[0],inlined:`url(${w})`}}catch{return console.warn("\u274C Failed to fetch font:",m),null}})),l=i;for(let d of s)d&&(l=l.replace(d.original,d.inlined));n+=l+`
3
- `}catch{console.warn("\u274C Failed to fetch CSS:",r.href)}if(n&&t){let r=document.createElement("style");r.setAttribute("data-snapdom","embedFonts"),r.textContent=n,document.head.appendChild(r)}return n}async function q(e,t,o,n,r,a=!1){if(!(e instanceof Element)||!(t instanceof Element))return;for(let s of["::before","::after"])try{let l=N(e,s);if(!l)continue;let d=l.getPropertyValue("content"),f=l.getPropertyValue("background-image"),m=d&&d!=="none"&&d!=='""'&&d!=="''",p=f&&f.startsWith("url(");if(m||p){let h=l.getPropertyValue("font-family"),w=parseInt(l.getPropertyValue("font-size"))||32,E=parseInt(l.getPropertyValue("font-weight"))||!1,v=l.getPropertyValue("color")||"#000",g=document.createElement("span");g.dataset.snapdomPseudo=s;let k=F(l),P=M(k,"span",r);o.set(g,P);let j=h&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(h),C=X(d);if(!a&&j&&C.length===1){let y=document.createElement("img");y.src=await G(C,h,E,w,v),y.style="display:block;width:100%;height:100%;object-fit:contain;",g.appendChild(y)}else if(C.startsWith("url(")){let y=S(C);if(y)try{let b=document.createElement("img"),se=await x(encodeURI(y));b.src=se,b.style="display:block;width:100%;height:100%;object-fit:contain;",g.appendChild(b)}catch(b){console.error(`[snapdom] Error in pseudo ${s} for`,e,b)}}else!j&&C&&C!=="none"&&(g.textContent=C);if(p&&f){let y=S(f);if(y)try{let b=await x(encodeURI(y));g.style.backgroundImage=`url(${b})`}catch(b){console.warn(`[snapdom] Failed to inline background-image for ${s}`,b)}}if(!(g.childNodes.length>0||g.textContent&&g.textContent.trim()!=="")&&!p)continue;s==="::before"?t.insertBefore(g,t.firstChild):t.appendChild(g)}}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,e,l)}let i=Array.from(e.children),c=Array.from(t.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s<Math.min(i.length,c.length);s++)await q(i[s],c[s],o,n,r,a)}async function Q(e,t=!1,o=!1){let n=new Map,r=new WeakMap,a=new Map,i;try{i=O(e,n,r,a,t)}catch(s){throw console.warn("deepClone failed:",s),s}try{await q(e,i,n,r,t,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(t){let s=J(n);c=Array.from(s.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of n.entries()){if(l.tagName==="STYLE")continue;let f=s.get(d);f&&l.classList.add(f);let m=l.style?.backgroundImage;l.removeAttribute("style"),m&&m!=="none"&&(l.style.backgroundImage=m)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of a.entries()){let d=l.scrollLeft,f=l.scrollTop;if((d||f)&&s instanceof HTMLElement){s.style.overflow="hidden",s.style.scrollbarWidth="none",s.style.msOverflowStyle="none";let p=document.createElement("div");for(p.style.transform=`translate(${-d}px, ${-f}px)`,p.style.willChange="transform",p.style.display="inline-block",p.style.width="100%";s.firstChild;)p.appendChild(s.firstChild);s.appendChild(p)}}return{clone:i,classCSS:c,styleCache:r}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let r=n.src;try{let a=await x(r);n.src=a,n.width||(n.width=n.naturalWidth||100),n.height||(n.height=n.naturalHeight||100)}catch{let a=document.createElement("div");a.style=`width: ${n.width||100}px; height: ${n.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${n.height||100}px; color: #666; font-size: 12px;`,a.innerText="img",n.replaceWith(a)}};for(let n=0;n<t.length;n+=4){let r=t.slice(n,n+4).map(o);await Promise.allSettled(r)}}async function ee(e,t,o){let n=[[e,t]];for(;n.length;){let[r,a]=n.shift(),i=o.get(r)||N(r);o.has(r)||o.set(r,i);let c=i.getPropertyValue("background-image"),s=S(c);if(s)try{let f=encodeURI(s),m;$.has(f)?m=$.get(f):(m=await x(f),$.set(f,m)),a.style.backgroundImage=`url(${m})`}catch{a.style.backgroundImage="none"}let l=Array.from(r.children),d=Array.from(a.children);for(let f=0;f<Math.min(l.length,d.length);f++)n.push([l[f],d[f]])}}async function te(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:r=!0,scale:a=1}=t,i,c,s,l="",d="",f,m;if({clone:i,classCSS:c,styleCache:s}=await Q(e,o,n),await new Promise(h=>{L(async()=>{await Z(i),h()},{fast:r})}),await new Promise(h=>{L(async()=>{await ee(e,i,s),h()},{fast:r})}),n&&await new Promise(h=>{L(async()=>{l=await W({ignoreIconFonts:!n}),h()},{fast:r})}),o){let h=Y(i).sort(),w=h.join(",");A.has(w)?d=A.get(w):await new Promise(E=>{L(()=>{d=K(h),A.set(w,d),E()},{fast:r})})}await new Promise(h=>{L(()=>{let w=e.getBoundingClientRect(),E=Math.ceil(w.width),v=Math.ceil(w.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",a!==1&&D()&&(i.style.scale=`${a}`);let g="http://www.w3.org/2000/svg",k=document.createElementNS(g,"foreignObject");k.setAttribute("width","100%"),k.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,k.appendChild(P),k.appendChild(i);let C=new XMLSerializer().serializeToString(k);m=`<svg xmlns="${g}" width="${E}" height="${v}" viewBox="0 0 ${E} ${v}">`+C+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(m)}`,h()},{fast:r})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),f}async function ce(e,{dpr:t=1,scale:o=1}){let n=new Image;return n.src=e,await n.decode(),D?(n.width=n.width*o,n.height=n.height*o):(n.width=n.width/o,n.height=n.height/o),n}async function ne(e,{dpr:t=1,scale:o=1}={}){let n=new Image;n.src=e,await n.decode();let r=document.createElement("canvas"),a=n.width*o,i=n.height*o;r.width=Math.ceil(a*t),r.height=Math.ceil(i*t);let c=r.getContext("2d");return c.scale(t,t),c.drawImage(n,0,0,a,i),r.style.width=`${a}px`,r.style.height=`${i}px`,r}async function oe(e){let t=decodeURIComponent(e.split(",")[1]);return new Blob([t],{type:"image/svg+xml"})}async function ae(e,{dpr:t=1,scale:o=1},n){let r=await ne(e,{dpr:t,scale:o});if(!n)return r;let a=document.createElement("canvas");a.width=r.width,a.height=r.height;let i=a.getContext("2d");return i.fillStyle=n,i.fillRect(0,0,a.width,a.height),i.drawImage(r,0,0),a}async function H(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:r},a="png"){let i=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${a}`,r),await c.decode(),c.style.width=`${i.width/t}px`,c.style.height=`${i.height/t}px`,c}async function le(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:r="png",filename:a="capture"}={}){if(r==="svg"){let m=await oe(e),p=URL.createObjectURL(m),h=document.createElement("a");h.href=p,h.download=`${a}.svg`,h.click(),URL.revokeObjectURL(p);return}let i=["jpg","jpeg","webp"].includes(r)?"#fff":void 0,s=await ae(e,{dpr:t,scale:o},n??i),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[r]||"image/png",d=s.toDataURL(l),f=document.createElement("a");f.href=d,f.download=`${a}.${r}`,f.click()}async function u(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await u.capture(e,t)}u.capture=async(e,t={})=>{let o=await te(e,t),n=window.devicePixelRatio||1,r=t.scale||1;return{url:o,options:t,toRaw:()=>o,toImg:()=>ce(o,{dpr:n,scale:r}),toCanvas:()=>ne(o,{dpr:n,scale:r}),toBlob:()=>oe(o),toPng:a=>H(o,{dpr:n,scale:r,...a},"png"),toJpg:a=>H(o,{dpr:n,scale:r,...a},"jpeg"),toWebp:a=>H(o,{dpr:n,scale:r,...a},"webp"),download:({format:a="png",filename:i="capture",backgroundColor:c}={})=>le(o,{dpr:n,scale:r,backgroundColor:c,format:a,filename:i})}};u.toRaw=async(e,t)=>(await u.capture(e,t)).toRaw();u.toImg=async(e,t)=>(await u.capture(e,t)).toImg();u.toCanvas=async(e,t)=>(await u.capture(e,t)).toCanvas();u.toBlob=async(e,t)=>(await u.capture(e,t)).toBlob();u.toPng=async(e,t)=>(await u.capture(e,t)).toPng(t);u.toJpg=async(e,t)=>(await u.capture(e,t)).toJpg(t);u.toWebp=async(e,t)=>(await u.capture(e,t)).toWebp(t);u.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:r,...a}=t;return await(await u.capture(e,a)).download({format:o,filename:n,backgroundColor:r})};async function de(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:r=!0}=t;if(n){I.clear(),$.clear(),T.clear(),A.clear(),U.clear();return}await document.fonts.ready,z();let a=[],i=[];e?.querySelectorAll&&(a=Array.from(e.querySelectorAll("img[src]")),i=Array.from(e.querySelectorAll("*")));let c=[];for(let s of a){let l=s.src;I.has(l)||c.push(x(l).then(d=>I.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=S(l);d&&!$.has(d)&&c.push(x(d).then(f=>$.set(d,f)).catch(()=>{}))}o&&await W({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}export{de as preCache,u as snapdom};
1
+ var R=new Map,T=new Map,C=new Map,I=new Map,k=new Map,N=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 H(){for(let e of ie)z(e)}function z(e){if(I.has(e))return I.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let s={};return I.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 r=document.createElement(e);r.style.all="initial",o.appendChild(r);let t=getComputedStyle(r),a={};for(let s of t)a[s]=t.getPropertyValue(s);return o.removeChild(r),I.set(e,a),a}function M(e,n,o=!1){let r=[],t=z(n);for(let[a,s]of Object.entries(e))if(!o)s&&r.push(`${a}:${s}`);else{let i=t[a];s&&s!==i&&r.push(`${a}:${s}`)}return r.sort().join(";")}function Y(e){let n=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&n.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(o=>n.add(o.tagName.toLowerCase())),Array.from(n))}function K(e){let n=new Map;for(let r of e){let t=I.get(r);if(!t)continue;let a=Object.entries(t).map(([s,i])=>`${s}:${i};`).sort().join("");n.has(a)||n.set(a,[]),n.get(a).push(r)}let o="";for(let[r,t]of n.entries())o+=`${t.join(",")} { ${r} }
2
+ `;return o}function J(e){let n=new Set(e.values()),o=new Map,r=1;for(let t of n)o.set(t,`c${r++}`);return o}function L(e,{fast:n=!1}={}){if(n)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function U(e,n=null){if(!(e instanceof Element))return window.getComputedStyle(e,n);let o=N.get(e);if(o||(o=new Map,N.set(e,o)),!o.has(n)){let r=window.getComputedStyle(e,n);o.set(n,r)}return o.get(n)}function X(e){let n=e.replace(/^['"]|['"]$/g,"");if(n.startsWith("\\"))try{return String.fromCharCode(parseInt(n.replace("\\",""),16))}catch{return n}return n}function $(e){let n=e.indexOf("url(");if(n===-1)return null;let o=e.slice(n+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 P(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 S(e,n=3e3,o="anonymous"){return R.has(e)?Promise.resolve(R.get(e)):new Promise((r,t)=>{let a=setTimeout(()=>{t(new Error("Image load timed out"))},n),s=new Image;s.crossOrigin=o,s.onload=async()=>{clearTimeout(a);try{await s.decode();let i=document.createElement("canvas");i.width=s.width,i.height=s.height,i.getContext("2d").drawImage(s,0,0,i.width,i.height);try{let l=i.toDataURL("image/png");R.set(e,l),r(l)}catch{t(new Error("CORS restrictions prevented image capture"))}}catch(i){t(i)}},s.onerror=i=>{clearTimeout(a),t(new Error("Failed to load image: "+(i.message||"Unknown error")))},s.src=e})}function O(e){let n={};for(let o of e)n[o]=e.getPropertyValue(o);return n}function B(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function _(e,n,o,r,t){if(e.tagName==="STYLE")return;r.has(e)||r.set(e,U(e));let a=r.get(e),s=O(a),i=e.tagName?.toLowerCase()||"div",c=M(s,i,t);o.set(n,c)}function D(e,n,o,r,t){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let i=document.createElement("div"),c=e.getBoundingClientRect();return i.style.cssText=`display: inline-block; width: ${c.width}px; height: ${c.height}px; visibility: hidden;`,i}if(e.tagName==="IFRAME"){let i=document.createElement("div");return i.textContent="",i.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;`,i}if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="placeholder"){let i=e.cloneNode(!1);r.set(i,e),_(e,i,n,o,t);let c=document.createElement("div");return c.textContent=e.getAttribute("data-placeholder-text")||"",c.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",i.appendChild(c),i}if(e.tagName==="CANVAS"){let i=e.toDataURL(),c=document.createElement("img");return c.src=i,c.width=e.width,c.height=e.height,c.style.display="inline-block",c.style.width=`${e.width}px`,c.style.height=`${e.height}px`,c}if(e.nodeType===Node.TEXT_NODE){if(e.parentElement?.shadowRoot){let i=e.parentElement.tagName.toLowerCase();if(customElements.get(i))return null}return e.cloneNode(!0)}if(e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);let a=e.cloneNode(!1);r.set(a,e),e instanceof HTMLInputElement?(a.value=e.value,a.setAttribute("value",e.value),e.checked!==void 0&&(a.checked=e.checked,e.checked&&a.setAttribute("checked",""))):e instanceof HTMLTextAreaElement?(a.value=e.value,a.textContent=e.value):e instanceof HTMLSelectElement&&(a.value=e.value,Array.from(a.options).forEach(i=>{i.value===e.value?i.setAttribute("selected",""):i.removeAttribute("selected")})),_(e,a,n,o,t);let s=document.createDocumentFragment();if(e.childNodes.forEach(i=>{let c=D(i,n,o,r,t);c&&s.appendChild(c)}),a.appendChild(s),e.shadowRoot){let i=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>D(l,n,o,r)).filter(Boolean),c=document.createDocumentFragment();i.forEach(l=>c.appendChild(l)),a.appendChild(c)}return a}async function G(e,n,o,r=32,t="#000"){n=n.replace(/^['"]+|['"]+$/g,"");let a=window.devicePixelRatio||1,i=document.createElement("canvas").getContext("2d");i.font=o?`${o} ${r}px "${n}"`:`${r}px "${n}"`;let c=i.measureText(e),l=c.actualBoundingBoxAscent||r*.8,d=c.actualBoundingBoxDescent||r*.2,h=l+d,f=c.width,p=document.createElement("canvas");p.width=Math.ceil(f*a),p.height=Math.ceil(h*a);let m=p.getContext("2d");return m.scale(a,a),m.font=i.font,m.textAlign="left",m.textBaseline="alphabetic",m.fillStyle=t,m.fillText(e,0,l),p.toDataURL()}async function W({ignoreIconFonts:e=!0,preCached:n=!1}={}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(t=>t.href),r="";for(let t of o)try{let s=await(await fetch(t.href)).text();if(e&&(P(t.href)||P(s)))continue;let i=/url\(([^)]+)\)/g,c=await Promise.all(Array.from(s.matchAll(i)).map(async d=>{let h=$(d[0]);if(!h)return null;let f=h;f.startsWith("http")||(f=new URL(f,t.href).href);try{f=encodeURI(f)}catch{console.warn("[snapdom] Failed to encode font URL:",f)}if(e&&P(f))return null;if(C.has(f))return{original:d[0],inlined:`url(${C.get(f)})`};try{let m=await(await fetch(f)).blob(),u=await new Promise(y=>{let x=new FileReader;x.onload=()=>y(x.result),x.readAsDataURL(m)});return C.set(f,u),{original:d[0],inlined:`url(${u})`}}catch{return console.warn("[snapdom] Failed to fetch font:",f),null}})),l=s;for(let d of c)d&&(l=l.replace(d.original,d.inlined));r+=l+`
3
+ `}catch{console.warn("[snapdom] Failed to fetch CSS:",t.href)}for(let t of document.styleSheets)try{if(!t.href||o.every(a=>a.href!==t.href)){for(let a of t.cssRules)if(a.type===CSSRule.FONT_FACE_RULE){let s=a.style.getPropertyValue("src");if(!s)continue;let i=/url\(([^)]+)\)/g,c=s,l=Array.from(s.matchAll(i));for(let d of l){let h=d[1].trim().replace(/^["']|["']$/g,"");if(!h)continue;let f=h;if(!f.startsWith("http")&&!f.startsWith("data:")&&(f=new URL(f,t.href||location.href).href),!(e&&P(f))){if(C.has(f)){c=c.replace(d[0],`url(${C.get(f)})`);continue}try{let m=await(await fetch(f)).blob(),u=await new Promise(y=>{let x=new FileReader;x.onload=()=>y(x.result),x.readAsDataURL(m)});C.set(f,u),c=c.replace(d[0],`url(${u})`)}catch{console.warn("[snapdom] Failed to fetch font URL:",f)}}}r+=`@font-face {
4
+ font-family: ${a.style.getPropertyValue("font-family")};
5
+ src: ${c};
6
+ font-style: ${a.style.getPropertyValue("font-style")||"normal"};
7
+ font-weight: ${a.style.getPropertyValue("font-weight")||"normal"};
8
+ }
9
+ `}}}catch(a){console.warn("[snapdom] Cannot access stylesheet",t.href,a)}for(let t of document.fonts)if(t.family&&t.status==="loaded"&&t._snapdomSrc){let a=t._snapdomSrc;if(!a.startsWith("data:"))if(C.has(t._snapdomSrc))a=C.get(t._snapdomSrc);else try{let i=await(await fetch(t._snapdomSrc)).blob();a=await new Promise(c=>{let l=new FileReader;l.onload=()=>c(l.result),l.readAsDataURL(i)}),C.set(t._snapdomSrc,a)}catch{console.warn("[snapdom] Failed to fetch dynamic font src:",t._snapdomSrc);continue}r+=`@font-face {
10
+ font-family: '${t.family}';
11
+ src: url(${a});
12
+ font-style: ${t.style||"normal"};
13
+ font-weight: ${t.weight||"normal"};
14
+ }
15
+ `}if(r&&n){let t=document.createElement("style");t.setAttribute("data-snapdom","embedFonts"),t.textContent=r,document.head.appendChild(t)}return r}async function V(e,n,o,r,t,a=!1){if(!(e instanceof Element)||!(n instanceof Element))return;for(let c of["::before","::after"])try{let l=U(e,c);if(!l)continue;let d=l.getPropertyValue("content"),h=l.getPropertyValue("background-image"),f=d&&d!=="none"&&d!=='""'&&d!=="''",p=h&&h.startsWith("url(");if(f||p){let m=l.getPropertyValue("font-family"),u=parseInt(l.getPropertyValue("font-size"))||32,y=parseInt(l.getPropertyValue("font-weight"))||!1,x=l.getPropertyValue("color")||"#000",w=document.createElement("span");w.dataset.snapdomPseudo=c;let A=O(l),F=M(A,"span",t);o.set(w,F);let j=m&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(m),E=X(d);if(!a&&j&&E.length===1){let b=document.createElement("img");b.src=await G(E,m,y,u,x),b.style="display:block;width:100%;height:100%;object-fit:contain;",w.appendChild(b)}else if(E.startsWith("url(")){let b=$(E);if(b)try{let v=document.createElement("img"),se=await S(encodeURI(b));v.src=se,v.style="display:block;width:100%;height:100%;object-fit:contain;",w.appendChild(v)}catch(v){console.error(`[snapdom] Error in pseudo ${c} for`,e,v)}}else!j&&E&&E!=="none"&&(w.textContent=E);if(p&&h){let b=$(h);if(b)try{let v=await S(encodeURI(b));w.style.backgroundImage=`url(${v})`}catch(v){console.warn(`[snapdom] Failed to inline background-image for ${c}`,v)}}if(!(w.childNodes.length>0||w.textContent&&w.textContent.trim()!=="")&&!p)continue;c==="::before"?n.insertBefore(w,n.firstChild):n.appendChild(w)}}catch(l){console.warn(`[snapdom] Failed to capture ${c} for`,e,l)}let s=Array.from(e.children),i=Array.from(n.children).filter(c=>!c.dataset.snapdomPseudo);for(let c=0;c<Math.min(s.length,i.length);c++)await V(s[c],i[c],o,r,t,a)}async function Q(e,n=!1,o=!1){let r=new Map,t=new WeakMap,a=new Map,s;try{s=D(e,r,t,a,n)}catch(c){throw console.warn("deepClone failed:",c),c}try{await V(e,s,r,t,n,o)}catch(c){console.warn("inlinePseudoElements failed:",c)}let i="";if(n){let c=J(r);i=Array.from(c.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of r.entries()){if(l.tagName==="STYLE")continue;let h=c.get(d);h&&l.classList.add(h);let f=l.style?.backgroundImage;l.removeAttribute("style"),f&&f!=="none"&&(l.style.backgroundImage=f)}}else for(let[c,l]of r.entries())c.tagName!=="STYLE"&&c.setAttribute("style",l.replace(/;/g,"; "));for(let[c,l]of a.entries()){let d=l.scrollLeft,h=l.scrollTop;if((d||h)&&c instanceof HTMLElement){c.style.overflow="hidden",c.style.scrollbarWidth="none",c.style.msOverflowStyle="none";let p=document.createElement("div");for(p.style.transform=`translate(${-d}px, ${-h}px)`,p.style.willChange="transform",p.style.display="inline-block",p.style.width="100%";c.firstChild;)p.appendChild(c.firstChild);c.appendChild(p)}}return{clone:s,classCSS:i,styleCache:t}}async function Z(e,n={}){let o=Array.from(e.querySelectorAll("img")),r=async t=>{let a=t.src;try{let s=n.crossOrigin?n.crossOrigin(a):"anonymous",i=await S(a,3e3,s);t.src=i,t.width||(t.width=t.naturalWidth||100),t.height||(t.height=t.naturalHeight||100)}catch{let s=document.createElement("div");s.style=`width: ${t.width||100}px; height: ${t.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${t.height||100}px; color: #666; font-size: 12px;`,s.innerText="img",t.replaceWith(s)}};for(let t=0;t<o.length;t+=4){let a=o.slice(t,t+4).map(r);await Promise.allSettled(a)}}async function ee(e,n,o,r={}){let t=[[e,n]];for(;t.length;){let[a,s]=t.shift(),i=o.get(a)||U(a);o.has(a)||o.set(a,i);let l=i.getPropertyValue("background-image").split(",");for(let f=0;f<l.length;f++){let p=$(l[f]);if(p)try{let m=encodeURI(p),u;if(T.has(m))u=T.get(m);else{let y=r.crossOrigin?r.crossOrigin(m):"anonymous";u=await S(m,3e3,y),T.set(m,u)}l[f]=`url(${u})`}catch{l[f]="none"}}l.some(f=>f.includes("url("))&&(s.style.backgroundImage=l.join(","));let d=Array.from(a.children),h=Array.from(s.children);for(let f=0;f<Math.min(d.length,h.length);f++)t.push([d[f],h[f]])}}async function te(e,n={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:r=!1,fast:t=!0,scale:a=1}=n,s,i,c,l="",d="",h,f;if({clone:s,classCSS:i,styleCache:c}=await Q(e,o,r),await new Promise(m=>{L(async()=>{await Z(s,n),m()},{fast:t})}),await new Promise(m=>{L(async()=>{await ee(e,s,c,n),m()},{fast:t})}),r&&await new Promise(m=>{L(async()=>{l=await W({ignoreIconFonts:!r}),m()},{fast:t})}),o){let m=Y(s).sort(),u=m.join(",");k.has(u)?d=k.get(u):await new Promise(y=>{L(()=>{d=K(m),k.set(u,d),y()},{fast:t})})}await new Promise(m=>{L(()=>{let u=e.getBoundingClientRect(),y=Math.ceil(u.width),x=Math.ceil(u.height);s.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),s.style.transformOrigin="top left",a!==1&&B()&&(s.style.scale=`${a}`);let w="http://www.w3.org/2000/svg",A=document.createElementNS(w,"foreignObject");A.setAttribute("width","100%"),A.setAttribute("height","100%");let F=document.createElement("style");F.textContent=d+l+"svg{overflow:visible;}"+i,A.appendChild(F),A.appendChild(s);let E=new XMLSerializer().serializeToString(A);f=`<svg xmlns="${w}" width="${y}" height="${x}" viewBox="0 0 ${y} ${x}">`+E+"</svg>",h=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(f)}`,m()},{fast:t})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),h}async function ce(e,{dpr:n=1,scale:o=1}){let r=new Image;return r.src=e,await r.decode(),B?(r.width=r.width*o,r.height=r.height*o):(r.width=r.width/o,r.height=r.height/o),r}async function ne(e,{dpr:n=1,scale:o=1}={}){let r=new Image;r.src=e,await r.decode();let t=document.createElement("canvas"),a=r.width*o,s=r.height*o;t.width=Math.ceil(a*n),t.height=Math.ceil(s*n);let i=t.getContext("2d");return i.scale(n,n),i.drawImage(r,0,0,a,s),t.style.width=`${a}px`,t.style.height=`${s}px`,t}async function oe(e){let n=decodeURIComponent(e.split(",")[1]);return new Blob([n],{type:"image/svg+xml"})}async function ae(e,{dpr:n=1,scale:o=1},r){let t=await ne(e,{dpr:n,scale:o});if(!r)return t;let a=document.createElement("canvas");a.width=t.width,a.height=t.height;let s=a.getContext("2d");return s.fillStyle=r,s.fillRect(0,0,a.width,a.height),s.drawImage(t,0,0),a}async function q(e,{dpr:n=1,scale:o=1,backgroundColor:r="#fff",quality:t},a="png"){let s=await ae(e,{dpr:n,scale:o},r),i=new Image;return i.src=s.toDataURL(`image/${a}`,t),await i.decode(),i.style.width=`${s.width/n}px`,i.style.height=`${s.height/n}px`,i}async function le(e,{dpr:n=1,scale:o=1,backgroundColor:r="#fff",format:t="png",filename:a="capture"}={}){if(t==="svg"){let f=await oe(e),p=URL.createObjectURL(f),m=document.createElement("a");m.href=p,m.download=`${a}.svg`,m.click(),URL.revokeObjectURL(p);return}let s=["jpg","jpeg","webp"].includes(t)?"#fff":void 0,c=await ae(e,{dpr:n,scale:o},r??s),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[t]||"image/png",d=c.toDataURL(l),h=document.createElement("a");h.href=d,h.download=`${a}.${t}`,h.click()}async function g(e,n={}){if(n={scale:1,...n},!e)throw new Error("Element cannot be null or undefined");return await g.capture(e,n)}g.capture=async(e,n={})=>{let o=await te(e,n),r=window.devicePixelRatio||1,t=n.scale||1;return{url:o,options:n,toRaw:()=>o,toImg:()=>ce(o,{dpr:r,scale:t}),toCanvas:()=>ne(o,{dpr:r,scale:t}),toBlob:()=>oe(o),toPng:a=>q(o,{dpr:r,scale:t,...a},"png"),toJpg:a=>q(o,{dpr:r,scale:t,...a},"jpeg"),toWebp:a=>q(o,{dpr:r,scale:t,...a},"webp"),download:({format:a="png",filename:s="capture",backgroundColor:i}={})=>le(o,{dpr:r,scale:t,backgroundColor:i,format:a,filename:s})}};g.toRaw=async(e,n)=>(await g.capture(e,n)).toRaw();g.toImg=async(e,n)=>(await g.capture(e,n)).toImg();g.toCanvas=async(e,n)=>(await g.capture(e,n)).toCanvas();g.toBlob=async(e,n)=>(await g.capture(e,n)).toBlob();g.toPng=async(e,n)=>(await g.capture(e,n)).toPng(n);g.toJpg=async(e,n)=>(await g.capture(e,n)).toJpg(n);g.toWebp=async(e,n)=>(await g.capture(e,n)).toWebp(n);g.download=async(e,n={})=>{let{format:o="png",filename:r="capture",backgroundColor:t,...a}=n;return await(await g.capture(e,a)).download({format:o,filename:r,backgroundColor:t})};async function fe(e=document,n={}){let{embedFonts:o=!0,reset:r=!1,crossOrigin:t}=n;if(r){R.clear(),T.clear(),C.clear(),k.clear(),N.clear();return}await document.fonts.ready,H();let a=[],s=[];e?.querySelectorAll&&(a=Array.from(e.querySelectorAll("img[src]")),s=Array.from(e.querySelectorAll("*")));let i=[];for(let c of a){let l=c.src;if(!R.has(l)){let d=t?t(l):"anonymous";i.push(S(l,3e3,d).then(h=>R.set(l,h)).catch(()=>{}))}}for(let c of s){let l=getComputedStyle(c).backgroundImage,d=$(l);if(d&&!T.has(d)){let h=t?t(d):"anonymous";i.push(S(d,3e3,h).then(f=>T.set(d,f)).catch(()=>{}))}}o&&await W({ignoreIconFonts:!o,preCached:!0}),await Promise.all(i)}export{fe as preCache,g as snapdom};
package/dist/snapdom.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.3.0
4
+ * v.1.7.1
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -209,7 +209,7 @@ function isIconFont(familyOrUrl) {
209
209
  ];
210
210
  return iconFontPatterns.some((rx) => rx.test(familyOrUrl));
211
211
  }
212
- function fetchImage(src, timeout = 3e3) {
212
+ function fetchImage(src, timeout = 3e3, crossOrigin = "anonymous") {
213
213
  if (imageCache.has(src)) {
214
214
  return Promise.resolve(imageCache.get(src));
215
215
  }
@@ -218,7 +218,7 @@ function fetchImage(src, timeout = 3e3) {
218
218
  reject(new Error("Image load timed out"));
219
219
  }, timeout);
220
220
  const image = new Image();
221
- image.crossOrigin = "anonymous";
221
+ image.crossOrigin = crossOrigin;
222
222
  image.onload = async () => {
223
223
  clearTimeout(timeoutId);
224
224
  try {
@@ -375,7 +375,7 @@ async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, c
375
375
  ctx.fillText(unicodeChar, 0, ascent);
376
376
  return canvas.toDataURL();
377
377
  }
378
- async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
378
+ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false } = {}) {
379
379
  const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((link) => link.href);
380
380
  let finalCSS = "";
381
381
  for (const link of links) {
@@ -416,7 +416,7 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
416
416
  resourceCache.set(url, b64);
417
417
  return { original: match[0], inlined: `url(${b64})` };
418
418
  } catch (err) {
419
- console.warn("\u274C Failed to fetch font:", url);
419
+ console.warn("[snapdom] Failed to fetch font:", url);
420
420
  return null;
421
421
  }
422
422
  })
@@ -429,7 +429,90 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
429
429
  }
430
430
  finalCSS += cssFinal + "\n";
431
431
  } catch (e) {
432
- console.warn("\u274C Failed to fetch CSS:", link.href);
432
+ console.warn("[snapdom] Failed to fetch CSS:", link.href);
433
+ }
434
+ }
435
+ for (const sheet of document.styleSheets) {
436
+ try {
437
+ if (!sheet.href || links.every((link) => link.href !== sheet.href)) {
438
+ for (const rule of sheet.cssRules) {
439
+ if (rule.type === CSSRule.FONT_FACE_RULE) {
440
+ const src = rule.style.getPropertyValue("src");
441
+ if (!src) continue;
442
+ const urlRegex = /url\(([^)]+)\)/g;
443
+ let inlinedSrc = src;
444
+ const matches = Array.from(src.matchAll(urlRegex));
445
+ for (const match of matches) {
446
+ let rawUrl = match[1].trim().replace(/^["']|["']$/g, "");
447
+ if (!rawUrl) continue;
448
+ let url = rawUrl;
449
+ if (!url.startsWith("http") && !url.startsWith("data:")) {
450
+ url = new URL(url, sheet.href || location.href).href;
451
+ }
452
+ if (ignoreIconFonts && isIconFont(url)) {
453
+ continue;
454
+ }
455
+ if (resourceCache.has(url)) {
456
+ inlinedSrc = inlinedSrc.replace(match[0], `url(${resourceCache.get(url)})`);
457
+ continue;
458
+ }
459
+ try {
460
+ const res = await fetch(url);
461
+ const blob = await res.blob();
462
+ const b64 = await new Promise((resolve) => {
463
+ const reader = new FileReader();
464
+ reader.onload = () => resolve(reader.result);
465
+ reader.readAsDataURL(blob);
466
+ });
467
+ resourceCache.set(url, b64);
468
+ inlinedSrc = inlinedSrc.replace(match[0], `url(${b64})`);
469
+ } catch (e) {
470
+ console.warn("[snapdom] Failed to fetch font URL:", url);
471
+ }
472
+ }
473
+ finalCSS += `@font-face {
474
+ font-family: ${rule.style.getPropertyValue("font-family")};
475
+ src: ${inlinedSrc};
476
+ font-style: ${rule.style.getPropertyValue("font-style") || "normal"};
477
+ font-weight: ${rule.style.getPropertyValue("font-weight") || "normal"};
478
+ }
479
+ `;
480
+ }
481
+ }
482
+ }
483
+ } catch (e) {
484
+ console.warn("[snapdom] Cannot access stylesheet", sheet.href, e);
485
+ }
486
+ }
487
+ for (const font of document.fonts) {
488
+ if (font.family && font.status === "loaded" && font._snapdomSrc) {
489
+ let b64 = font._snapdomSrc;
490
+ if (!b64.startsWith("data:")) {
491
+ if (resourceCache.has(font._snapdomSrc)) {
492
+ b64 = resourceCache.get(font._snapdomSrc);
493
+ } else {
494
+ try {
495
+ const res = await fetch(font._snapdomSrc);
496
+ const blob = await res.blob();
497
+ b64 = await new Promise((resolve) => {
498
+ const reader = new FileReader();
499
+ reader.onload = () => resolve(reader.result);
500
+ reader.readAsDataURL(blob);
501
+ });
502
+ resourceCache.set(font._snapdomSrc, b64);
503
+ } catch (e) {
504
+ console.warn("[snapdom] Failed to fetch dynamic font src:", font._snapdomSrc);
505
+ continue;
506
+ }
507
+ }
508
+ }
509
+ finalCSS += `@font-face {
510
+ font-family: '${font.family}';
511
+ src: url(${b64});
512
+ font-style: ${font.style || "normal"};
513
+ font-weight: ${font.weight || "normal"};
514
+ }
515
+ `;
433
516
  }
434
517
  }
435
518
  if (finalCSS && preCached) {
@@ -588,12 +671,13 @@ async function prepareClone(element, compress = false, embedFonts = false) {
588
671
  }
589
672
 
590
673
  // src/modules/images.js
591
- async function inlineImages(clone) {
674
+ async function inlineImages(clone, options = {}) {
592
675
  const imgs = Array.from(clone.querySelectorAll("img"));
593
676
  const processImg = async (img) => {
594
677
  const src = img.src;
595
678
  try {
596
- const dataUrl = await fetchImage(src);
679
+ const crossOrigin = options.crossOrigin ? options.crossOrigin(src) : "anonymous";
680
+ const dataUrl = await fetchImage(src, 3e3, crossOrigin);
597
681
  img.src = dataUrl;
598
682
  if (!img.width) img.width = img.naturalWidth || 100;
599
683
  if (!img.height) img.height = img.naturalHeight || 100;
@@ -611,29 +695,36 @@ async function inlineImages(clone) {
611
695
  }
612
696
 
613
697
  // src/modules/background.js
614
- async function inlineBackgroundImages(source, clone, styleCache) {
698
+ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
615
699
  const queue = [[source, clone]];
616
700
  while (queue.length) {
617
701
  const [srcNode, cloneNode] = queue.shift();
618
702
  const style = styleCache.get(srcNode) || getStyle(srcNode);
619
703
  if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
620
704
  const bg = style.getPropertyValue("background-image");
621
- const rawUrl = extractURL(bg);
622
- if (rawUrl) {
623
- try {
624
- let bgUrl = encodeURI(rawUrl);
625
- let dataUrl;
626
- if (bgCache.has(bgUrl)) {
627
- dataUrl = bgCache.get(bgUrl);
628
- } else {
629
- dataUrl = await fetchImage(bgUrl);
630
- bgCache.set(bgUrl, dataUrl);
705
+ const bgSplits = bg.split(",");
706
+ for (let i = 0; i < bgSplits.length; i++) {
707
+ const rawUrl = extractURL(bgSplits[i]);
708
+ if (rawUrl) {
709
+ try {
710
+ let bgUrl = encodeURI(rawUrl);
711
+ let dataUrl;
712
+ if (bgCache.has(bgUrl)) {
713
+ dataUrl = bgCache.get(bgUrl);
714
+ } else {
715
+ const crossOrigin = options.crossOrigin ? options.crossOrigin(bgUrl) : "anonymous";
716
+ dataUrl = await fetchImage(bgUrl, 3e3, crossOrigin);
717
+ bgCache.set(bgUrl, dataUrl);
718
+ }
719
+ bgSplits[i] = `url(${dataUrl})`;
720
+ } catch {
721
+ bgSplits[i] = "none";
631
722
  }
632
- cloneNode.style.backgroundImage = `url(${dataUrl})`;
633
- } catch {
634
- cloneNode.style.backgroundImage = "none";
635
723
  }
636
724
  }
725
+ if (bgSplits.some((s) => s.includes("url("))) {
726
+ cloneNode.style.backgroundImage = bgSplits.join(",");
727
+ }
637
728
  const sChildren = Array.from(srcNode.children);
638
729
  const cChildren = Array.from(cloneNode.children);
639
730
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
@@ -654,13 +745,13 @@ async function captureDOM(element, options = {}) {
654
745
  ({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
655
746
  await new Promise((resolve) => {
656
747
  idle(async () => {
657
- await inlineImages(clone);
748
+ await inlineImages(clone, options);
658
749
  resolve();
659
750
  }, { fast });
660
751
  });
661
752
  await new Promise((resolve) => {
662
753
  idle(async () => {
663
- await inlineBackgroundImages(element, clone, styleCache);
754
+ await inlineBackgroundImages(element, clone, styleCache, options);
664
755
  resolve();
665
756
  }, { fast });
666
757
  });
@@ -842,7 +933,7 @@ snapdom.download = async (el, options = {}) => {
842
933
 
843
934
  // src/api/preCache.js
844
935
  async function preCache(root = document, options = {}) {
845
- const { embedFonts = true, reset = false, preWarm = true } = options;
936
+ const { embedFonts = true, reset = false, crossOrigin: crossOriginFn } = options;
846
937
  if (reset) {
847
938
  imageCache.clear();
848
939
  bgCache.clear();
@@ -862,8 +953,9 @@ async function preCache(root = document, options = {}) {
862
953
  for (const img of imgEls) {
863
954
  const src = img.src;
864
955
  if (!imageCache.has(src)) {
956
+ const crossOrigin = crossOriginFn ? crossOriginFn(src) : "anonymous";
865
957
  promises.push(
866
- fetchImage(src).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
958
+ fetchImage(src, 3e3, crossOrigin).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
867
959
  })
868
960
  );
869
961
  }
@@ -872,8 +964,9 @@ async function preCache(root = document, options = {}) {
872
964
  const bg = getComputedStyle(el).backgroundImage;
873
965
  const url = extractURL(bg);
874
966
  if (url && !bgCache.has(url)) {
967
+ const crossOrigin = crossOriginFn ? crossOriginFn(url) : "anonymous";
875
968
  promises.push(
876
- fetchImage(url).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
969
+ fetchImage(url, 3e3, crossOrigin).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
877
970
  })
878
971
  );
879
972
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zumer/snapdom",
3
- "version": "1.3.0",
3
+ "version": "1.7.1",
4
4
  "description": "snapDOM captures HTML elements to images with exceptional speed and accuracy.",
5
5
  "main": "dist/snapdom.js",
6
6
  "module": "dist/snapdom.mjs",
@@ -9,6 +9,7 @@ declare module "@zumer/snapdom" {
9
9
  filename?: string;
10
10
  dpr?: number;
11
11
  quality?: number;
12
+ crossOrigin?: (url: string) => "anonymous" | "use-credentials";
12
13
  }
13
14
 
14
15
  export interface SnapResult {
@@ -51,6 +52,7 @@ declare module "@zumer/snapdom" {
51
52
  root?: Document | HTMLElement,
52
53
  options?: {
53
54
  embedFonts?: boolean;
55
+ crossOrigin?: (url: string) => "anonymous" | "use-credentials";
54
56
  }
55
57
  ): Promise<void>;
56
58
  }