@zumer/snapdom 1.2.5 → 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.2.5
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 {
@@ -227,7 +227,7 @@
227
227
  canvas.width = image.width;
228
228
  canvas.height = image.height;
229
229
  const ctx = canvas.getContext("2d");
230
- ctx.drawImage(image, 0, 0);
230
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
231
231
  try {
232
232
  const dataURL = canvas.toDataURL("image/png");
233
233
  imageCache.set(src, dataURL);
@@ -315,6 +315,26 @@
315
315
  if (node.nodeType !== Node.ELEMENT_NODE) return node.cloneNode(true);
316
316
  const clone = node.cloneNode(false);
317
317
  nodeMap.set(clone, node);
318
+ if (node instanceof HTMLInputElement) {
319
+ clone.value = node.value;
320
+ clone.setAttribute("value", node.value);
321
+ if (node.checked !== void 0) {
322
+ clone.checked = node.checked;
323
+ if (node.checked) clone.setAttribute("checked", "");
324
+ }
325
+ } else if (node instanceof HTMLTextAreaElement) {
326
+ clone.value = node.value;
327
+ clone.textContent = node.value;
328
+ } else if (node instanceof HTMLSelectElement) {
329
+ clone.value = node.value;
330
+ Array.from(clone.options).forEach((opt) => {
331
+ if (opt.value === node.value) {
332
+ opt.setAttribute("selected", "");
333
+ } else {
334
+ opt.removeAttribute("selected");
335
+ }
336
+ });
337
+ }
318
338
  inlineAllStyles(node, clone, styleMap, styleCache, compress);
319
339
  const frag = document.createDocumentFragment();
320
340
  node.childNodes.forEach((child) => {
@@ -355,7 +375,7 @@
355
375
  ctx.fillText(unicodeChar, 0, ascent);
356
376
  return canvas.toDataURL();
357
377
  }
358
- async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
378
+ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false } = {}) {
359
379
  const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((link) => link.href);
360
380
  let finalCSS = "";
361
381
  for (const link of links) {
@@ -396,7 +416,7 @@
396
416
  resourceCache.set(url, b64);
397
417
  return { original: match[0], inlined: `url(${b64})` };
398
418
  } catch (err) {
399
- console.warn("\u274C Failed to fetch font:", url);
419
+ console.warn("[snapdom] Failed to fetch font:", url);
400
420
  return null;
401
421
  }
402
422
  })
@@ -409,7 +429,90 @@
409
429
  }
410
430
  finalCSS += cssFinal + "\n";
411
431
  } catch (e) {
412
- 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
+ `;
413
516
  }
414
517
  }
415
518
  if (finalCSS && preCached) {
@@ -467,18 +570,25 @@
467
570
  imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
468
571
  pseudoEl.appendChild(imgEl);
469
572
  } catch (e) {
470
- console.error(
471
- `[snapdom] Error in pseudo ${pseudo} for`,
472
- source,
473
- e
474
- );
573
+ console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
475
574
  }
476
575
  }
477
576
  } else if (!isIconFont2 && cleanContent && cleanContent !== "none") {
478
577
  pseudoEl.textContent = cleanContent;
479
578
  }
579
+ if (hasBg && bg) {
580
+ const rawUrl = extractURL(bg);
581
+ if (rawUrl) {
582
+ try {
583
+ const dataUrl = await fetchImage(encodeURI(rawUrl));
584
+ pseudoEl.style.backgroundImage = `url(${dataUrl})`;
585
+ } catch (e) {
586
+ console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
587
+ }
588
+ }
589
+ }
480
590
  const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent && pseudoEl.textContent.trim() !== "";
481
- if (!hasContent2) continue;
591
+ if (!hasContent2 && !hasBg) continue;
482
592
  if (pseudo === "::before") {
483
593
  clone.insertBefore(pseudoEl, clone.firstChild);
484
594
  } else {
@@ -490,9 +600,7 @@
490
600
  }
491
601
  }
492
602
  const sChildren = Array.from(source.children);
493
- const cChildren = Array.from(clone.children).filter(
494
- (child) => !child.dataset.snapdomPseudo
495
- );
603
+ const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
496
604
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
497
605
  await inlinePseudoElements(
498
606
  sChildren[i],
@@ -563,12 +671,13 @@
563
671
  }
564
672
 
565
673
  // src/modules/images.js
566
- async function inlineImages(clone) {
674
+ async function inlineImages(clone, options = {}) {
567
675
  const imgs = Array.from(clone.querySelectorAll("img"));
568
676
  const processImg = async (img) => {
569
677
  const src = img.src;
570
678
  try {
571
- const dataUrl = await fetchImage(src);
679
+ const crossOrigin = options.crossOrigin ? options.crossOrigin(src) : "anonymous";
680
+ const dataUrl = await fetchImage(src, 3e3, crossOrigin);
572
681
  img.src = dataUrl;
573
682
  if (!img.width) img.width = img.naturalWidth || 100;
574
683
  if (!img.height) img.height = img.naturalHeight || 100;
@@ -586,29 +695,36 @@
586
695
  }
587
696
 
588
697
  // src/modules/background.js
589
- async function inlineBackgroundImages(source, clone, styleCache) {
698
+ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
590
699
  const queue = [[source, clone]];
591
700
  while (queue.length) {
592
701
  const [srcNode, cloneNode] = queue.shift();
593
702
  const style = styleCache.get(srcNode) || getStyle(srcNode);
594
703
  if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
595
704
  const bg = style.getPropertyValue("background-image");
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);
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";
606
722
  }
607
- cloneNode.style.backgroundImage = `url(${dataUrl})`;
608
- } catch {
609
- cloneNode.style.backgroundImage = "none";
610
723
  }
611
724
  }
725
+ if (bgSplits.some((s) => s.includes("url("))) {
726
+ cloneNode.style.backgroundImage = bgSplits.join(",");
727
+ }
612
728
  const sChildren = Array.from(srcNode.children);
613
729
  const cChildren = Array.from(cloneNode.children);
614
730
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
@@ -629,13 +745,13 @@
629
745
  ({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
630
746
  await new Promise((resolve) => {
631
747
  idle(async () => {
632
- await inlineImages(clone);
748
+ await inlineImages(clone, options);
633
749
  resolve();
634
750
  }, { fast });
635
751
  });
636
752
  await new Promise((resolve) => {
637
753
  idle(async () => {
638
- await inlineBackgroundImages(element, clone, styleCache);
754
+ await inlineBackgroundImages(element, clone, styleCache, options);
639
755
  resolve();
640
756
  }, { fast });
641
757
  });
@@ -708,20 +824,20 @@
708
824
  }
709
825
  return img;
710
826
  }
711
- async function toCanvas(url, { dpr = 1, scale = 1 }) {
827
+ async function toCanvas(url, { dpr = 1, scale = 1 } = {}) {
712
828
  const img = new Image();
713
829
  img.src = url;
714
830
  await img.decode();
715
831
  const canvas = document.createElement("canvas");
716
- const width = img.width * scale * dpr;
717
- const height = img.height * scale * dpr;
718
- canvas.width = Math.ceil(width);
719
- canvas.height = Math.ceil(height);
832
+ const width = img.width * scale;
833
+ const height = img.height * scale;
834
+ canvas.width = Math.ceil(width * dpr);
835
+ canvas.height = Math.ceil(height * dpr);
720
836
  const ctx = canvas.getContext("2d");
721
- ctx.scale(scale * dpr, scale * dpr);
722
- ctx.drawImage(img, 0, 0);
723
- canvas.style.width = `${canvas.width / dpr}px`;
724
- canvas.style.height = `${canvas.height / dpr}px`;
837
+ ctx.scale(dpr, dpr);
838
+ ctx.drawImage(img, 0, 0, width, height);
839
+ canvas.style.width = `${width}px`;
840
+ canvas.style.height = `${height}px`;
725
841
  return canvas;
726
842
  }
727
843
  async function toBlob(url) {
@@ -817,7 +933,7 @@
817
933
 
818
934
  // src/api/preCache.js
819
935
  async function preCache(root = document, options = {}) {
820
- const { embedFonts = true, reset = false, preWarm = true } = options;
936
+ const { embedFonts = true, reset = false, crossOrigin: crossOriginFn } = options;
821
937
  if (reset) {
822
938
  imageCache.clear();
823
939
  bgCache.clear();
@@ -837,8 +953,9 @@
837
953
  for (const img of imgEls) {
838
954
  const src = img.src;
839
955
  if (!imageCache.has(src)) {
956
+ const crossOrigin = crossOriginFn ? crossOriginFn(src) : "anonymous";
840
957
  promises.push(
841
- fetchImage(src).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
958
+ fetchImage(src, 3e3, crossOrigin).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
842
959
  })
843
960
  );
844
961
  }
@@ -847,8 +964,9 @@
847
964
  const bg = getComputedStyle(el).backgroundImage;
848
965
  const url = extractURL(bg);
849
966
  if (url && !bgCache.has(url)) {
967
+ const crossOrigin = crossOriginFn ? crossOriginFn(url) : "anonymous";
850
968
  promises.push(
851
- fetchImage(url).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
969
+ fetchImage(url, 3e3, crossOrigin).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
852
970
  })
853
971
  );
854
972
  }
@@ -1,3 +1,15 @@
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
+ (()=>{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 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};
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.2.5
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 {
@@ -227,7 +227,7 @@ function fetchImage(src, timeout = 3e3) {
227
227
  canvas.width = image.width;
228
228
  canvas.height = image.height;
229
229
  const ctx = canvas.getContext("2d");
230
- ctx.drawImage(image, 0, 0);
230
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
231
231
  try {
232
232
  const dataURL = canvas.toDataURL("image/png");
233
233
  imageCache.set(src, dataURL);
@@ -315,6 +315,26 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress) {
315
315
  if (node.nodeType !== Node.ELEMENT_NODE) return node.cloneNode(true);
316
316
  const clone = node.cloneNode(false);
317
317
  nodeMap.set(clone, node);
318
+ if (node instanceof HTMLInputElement) {
319
+ clone.value = node.value;
320
+ clone.setAttribute("value", node.value);
321
+ if (node.checked !== void 0) {
322
+ clone.checked = node.checked;
323
+ if (node.checked) clone.setAttribute("checked", "");
324
+ }
325
+ } else if (node instanceof HTMLTextAreaElement) {
326
+ clone.value = node.value;
327
+ clone.textContent = node.value;
328
+ } else if (node instanceof HTMLSelectElement) {
329
+ clone.value = node.value;
330
+ Array.from(clone.options).forEach((opt) => {
331
+ if (opt.value === node.value) {
332
+ opt.setAttribute("selected", "");
333
+ } else {
334
+ opt.removeAttribute("selected");
335
+ }
336
+ });
337
+ }
318
338
  inlineAllStyles(node, clone, styleMap, styleCache, compress);
319
339
  const frag = document.createDocumentFragment();
320
340
  node.childNodes.forEach((child) => {
@@ -355,7 +375,7 @@ async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, c
355
375
  ctx.fillText(unicodeChar, 0, ascent);
356
376
  return canvas.toDataURL();
357
377
  }
358
- async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
378
+ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false } = {}) {
359
379
  const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((link) => link.href);
360
380
  let finalCSS = "";
361
381
  for (const link of links) {
@@ -396,7 +416,7 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
396
416
  resourceCache.set(url, b64);
397
417
  return { original: match[0], inlined: `url(${b64})` };
398
418
  } catch (err) {
399
- console.warn("\u274C Failed to fetch font:", url);
419
+ console.warn("[snapdom] Failed to fetch font:", url);
400
420
  return null;
401
421
  }
402
422
  })
@@ -409,7 +429,90 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
409
429
  }
410
430
  finalCSS += cssFinal + "\n";
411
431
  } catch (e) {
412
- 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
+ `;
413
516
  }
414
517
  }
415
518
  if (finalCSS && preCached) {
@@ -467,18 +570,25 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
467
570
  imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
468
571
  pseudoEl.appendChild(imgEl);
469
572
  } catch (e) {
470
- console.error(
471
- `[snapdom] Error in pseudo ${pseudo} for`,
472
- source,
473
- e
474
- );
573
+ console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
475
574
  }
476
575
  }
477
576
  } else if (!isIconFont2 && cleanContent && cleanContent !== "none") {
478
577
  pseudoEl.textContent = cleanContent;
479
578
  }
579
+ if (hasBg && bg) {
580
+ const rawUrl = extractURL(bg);
581
+ if (rawUrl) {
582
+ try {
583
+ const dataUrl = await fetchImage(encodeURI(rawUrl));
584
+ pseudoEl.style.backgroundImage = `url(${dataUrl})`;
585
+ } catch (e) {
586
+ console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
587
+ }
588
+ }
589
+ }
480
590
  const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent && pseudoEl.textContent.trim() !== "";
481
- if (!hasContent2) continue;
591
+ if (!hasContent2 && !hasBg) continue;
482
592
  if (pseudo === "::before") {
483
593
  clone.insertBefore(pseudoEl, clone.firstChild);
484
594
  } else {
@@ -490,9 +600,7 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
490
600
  }
491
601
  }
492
602
  const sChildren = Array.from(source.children);
493
- const cChildren = Array.from(clone.children).filter(
494
- (child) => !child.dataset.snapdomPseudo
495
- );
603
+ const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
496
604
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
497
605
  await inlinePseudoElements(
498
606
  sChildren[i],
@@ -563,12 +671,13 @@ async function prepareClone(element, compress = false, embedFonts = false) {
563
671
  }
564
672
 
565
673
  // src/modules/images.js
566
- async function inlineImages(clone) {
674
+ async function inlineImages(clone, options = {}) {
567
675
  const imgs = Array.from(clone.querySelectorAll("img"));
568
676
  const processImg = async (img) => {
569
677
  const src = img.src;
570
678
  try {
571
- const dataUrl = await fetchImage(src);
679
+ const crossOrigin = options.crossOrigin ? options.crossOrigin(src) : "anonymous";
680
+ const dataUrl = await fetchImage(src, 3e3, crossOrigin);
572
681
  img.src = dataUrl;
573
682
  if (!img.width) img.width = img.naturalWidth || 100;
574
683
  if (!img.height) img.height = img.naturalHeight || 100;
@@ -586,29 +695,36 @@ async function inlineImages(clone) {
586
695
  }
587
696
 
588
697
  // src/modules/background.js
589
- async function inlineBackgroundImages(source, clone, styleCache) {
698
+ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
590
699
  const queue = [[source, clone]];
591
700
  while (queue.length) {
592
701
  const [srcNode, cloneNode] = queue.shift();
593
702
  const style = styleCache.get(srcNode) || getStyle(srcNode);
594
703
  if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
595
704
  const bg = style.getPropertyValue("background-image");
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);
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";
606
722
  }
607
- cloneNode.style.backgroundImage = `url(${dataUrl})`;
608
- } catch {
609
- cloneNode.style.backgroundImage = "none";
610
723
  }
611
724
  }
725
+ if (bgSplits.some((s) => s.includes("url("))) {
726
+ cloneNode.style.backgroundImage = bgSplits.join(",");
727
+ }
612
728
  const sChildren = Array.from(srcNode.children);
613
729
  const cChildren = Array.from(cloneNode.children);
614
730
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
@@ -629,13 +745,13 @@ async function captureDOM(element, options = {}) {
629
745
  ({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
630
746
  await new Promise((resolve) => {
631
747
  idle(async () => {
632
- await inlineImages(clone);
748
+ await inlineImages(clone, options);
633
749
  resolve();
634
750
  }, { fast });
635
751
  });
636
752
  await new Promise((resolve) => {
637
753
  idle(async () => {
638
- await inlineBackgroundImages(element, clone, styleCache);
754
+ await inlineBackgroundImages(element, clone, styleCache, options);
639
755
  resolve();
640
756
  }, { fast });
641
757
  });
@@ -708,20 +824,20 @@ async function toImg(url, { dpr = 1, scale = 1 }) {
708
824
  }
709
825
  return img;
710
826
  }
711
- async function toCanvas(url, { dpr = 1, scale = 1 }) {
827
+ async function toCanvas(url, { dpr = 1, scale = 1 } = {}) {
712
828
  const img = new Image();
713
829
  img.src = url;
714
830
  await img.decode();
715
831
  const canvas = document.createElement("canvas");
716
- const width = img.width * scale * dpr;
717
- const height = img.height * scale * dpr;
718
- canvas.width = Math.ceil(width);
719
- canvas.height = Math.ceil(height);
832
+ const width = img.width * scale;
833
+ const height = img.height * scale;
834
+ canvas.width = Math.ceil(width * dpr);
835
+ canvas.height = Math.ceil(height * dpr);
720
836
  const ctx = canvas.getContext("2d");
721
- ctx.scale(scale * dpr, scale * dpr);
722
- ctx.drawImage(img, 0, 0);
723
- canvas.style.width = `${canvas.width / dpr}px`;
724
- canvas.style.height = `${canvas.height / dpr}px`;
837
+ ctx.scale(dpr, dpr);
838
+ ctx.drawImage(img, 0, 0, width, height);
839
+ canvas.style.width = `${width}px`;
840
+ canvas.style.height = `${height}px`;
725
841
  return canvas;
726
842
  }
727
843
  async function toBlob(url) {
@@ -817,7 +933,7 @@ snapdom.download = async (el, options = {}) => {
817
933
 
818
934
  // src/api/preCache.js
819
935
  async function preCache(root = document, options = {}) {
820
- const { embedFonts = true, reset = false, preWarm = true } = options;
936
+ const { embedFonts = true, reset = false, crossOrigin: crossOriginFn } = options;
821
937
  if (reset) {
822
938
  imageCache.clear();
823
939
  bgCache.clear();
@@ -837,8 +953,9 @@ async function preCache(root = document, options = {}) {
837
953
  for (const img of imgEls) {
838
954
  const src = img.src;
839
955
  if (!imageCache.has(src)) {
956
+ const crossOrigin = crossOriginFn ? crossOriginFn(src) : "anonymous";
840
957
  promises.push(
841
- fetchImage(src).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
958
+ fetchImage(src, 3e3, crossOrigin).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
842
959
  })
843
960
  );
844
961
  }
@@ -847,8 +964,9 @@ async function preCache(root = document, options = {}) {
847
964
  const bg = getComputedStyle(el).backgroundImage;
848
965
  const url = extractURL(bg);
849
966
  if (url && !bgCache.has(url)) {
967
+ const crossOrigin = crossOriginFn ? crossOriginFn(url) : "anonymous";
850
968
  promises.push(
851
- fetchImage(url).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
969
+ fetchImage(url, 3e3, crossOrigin).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
852
970
  })
853
971
  );
854
972
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zumer/snapdom",
3
- "version": "1.2.5",
4
- "description": "snapDOM captures DOM elements as images with exceptional speed and minimal overhead. Designed to avoid bottlenecks and long tasks.",
3
+ "version": "1.7.1",
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",
7
7
  "scripts": {
@@ -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
  }