@zumer/snapdom 1.9.6 → 1.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/snapdom.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.9.6
4
+ * v.1.9.8
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -130,7 +130,7 @@
130
130
  if (cache.defaultStyle.has(tagName)) {
131
131
  return cache.defaultStyle.get(tagName);
132
132
  }
133
- const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template"]);
133
+ const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template", "defs", "symbol", "title", "metadata", "desc"]);
134
134
  if (skipTags.has(tagName)) {
135
135
  const empty = {};
136
136
  cache.defaultStyle.set(tagName, empty);
@@ -160,10 +160,14 @@
160
160
  cache.defaultStyle.set(tagName, defaults);
161
161
  return defaults;
162
162
  }
163
+ var IGNORED_PROPS = /* @__PURE__ */ new Set([
164
+ "-webkit-locale"
165
+ ]);
163
166
  function getStyleKey(snapshot, tagName, compress = false) {
164
167
  const entries = [];
165
168
  const defaultStyles = getDefaultStyleForTag(tagName);
166
169
  for (let [prop, value] of Object.entries(snapshot)) {
170
+ if (IGNORED_PROPS.has(prop)) continue;
167
171
  if (!compress) {
168
172
  if (value) {
169
173
  entries.push(`${prop}:${value}`);
@@ -213,6 +217,7 @@
213
217
  const classMap = /* @__PURE__ */ new Map();
214
218
  let counter = 1;
215
219
  for (const key of keySet) {
220
+ if (!key.trim()) continue;
216
221
  classMap.set(key, `c${counter++}`);
217
222
  }
218
223
  return classMap;
@@ -448,6 +453,7 @@
448
453
  // src/modules/styles.js
449
454
  function snapshotComputedStyleFull(style) {
450
455
  const result = {};
456
+ const computedVisibility = style.getPropertyValue("visibility");
451
457
  for (let i = 0; i < style.length; i++) {
452
458
  const prop = style[i];
453
459
  let val = style.getPropertyValue(prop);
@@ -456,6 +462,9 @@
456
462
  }
457
463
  result[prop] = val;
458
464
  }
465
+ if (computedVisibility === "hidden") {
466
+ result.opacity = "0";
467
+ }
459
468
  return result;
460
469
  }
461
470
  function inlineAllStyles(source, clone, compress) {
@@ -574,7 +583,6 @@
574
583
  if (node.checked) clone.setAttribute("checked", "");
575
584
  if (node.indeterminate) clone.indeterminate = node.indeterminate;
576
585
  }
577
- return clone;
578
586
  }
579
587
  if (node instanceof HTMLSelectElement) {
580
588
  pendingSelectValue = node.value;
@@ -679,25 +687,38 @@
679
687
  return __async(this, null, function* () {
680
688
  fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
681
689
  const dpr = window.devicePixelRatio || 1;
682
- const tempCanvas = document.createElement("canvas");
683
- const tempCtx = tempCanvas.getContext("2d");
684
- tempCtx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
685
- const metrics = tempCtx.measureText(unicodeChar);
686
- const ascent = metrics.actualBoundingBoxAscent || fontSize * 0.8;
687
- const descent = metrics.actualBoundingBoxDescent || fontSize * 0.2;
688
- const height = ascent + descent;
689
- const width = metrics.width;
690
+ yield document.fonts.ready;
691
+ const span = document.createElement("span");
692
+ span.textContent = unicodeChar;
693
+ span.style.position = "absolute";
694
+ span.style.visibility = "hidden";
695
+ span.style.fontFamily = `"${fontFamily}"`;
696
+ span.style.fontWeight = fontWeight || "normal";
697
+ span.style.fontSize = `${fontSize}px`;
698
+ span.style.lineHeight = "1";
699
+ span.style.whiteSpace = "nowrap";
700
+ span.style.padding = "0";
701
+ span.style.margin = "0";
702
+ document.body.appendChild(span);
703
+ const rect = span.getBoundingClientRect();
704
+ const width = Math.ceil(rect.width);
705
+ const height = Math.ceil(rect.height);
706
+ document.body.removeChild(span);
690
707
  const canvas = document.createElement("canvas");
691
- canvas.width = Math.ceil(width * dpr);
692
- canvas.height = Math.ceil(height * dpr);
708
+ canvas.width = width * dpr;
709
+ canvas.height = height * dpr;
693
710
  const ctx = canvas.getContext("2d");
694
711
  ctx.scale(dpr, dpr);
695
- ctx.font = tempCtx.font;
712
+ ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
696
713
  ctx.textAlign = "left";
697
- ctx.textBaseline = "alphabetic";
714
+ ctx.textBaseline = "top";
698
715
  ctx.fillStyle = color;
699
- ctx.fillText(unicodeChar, 0, ascent);
700
- return canvas.toDataURL();
716
+ ctx.fillText(unicodeChar, 0, 0);
717
+ return {
718
+ dataUrl: canvas.toDataURL(),
719
+ width,
720
+ height
721
+ };
701
722
  });
702
723
  }
703
724
  function isStylesheetLoaded(href) {
@@ -798,8 +819,8 @@
798
819
  if (!src || isIconFont(family)) continue;
799
820
  const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
800
821
  const localRegex = /local\((["']?)[^)]+?\1\)/g;
801
- const hasURL = urlRegex.test(src);
802
- const hasLocal = localRegex.test(src);
822
+ const hasURL = !!src.match(urlRegex);
823
+ const hasLocal = !!src.match(localRegex);
803
824
  if (!hasURL && hasLocal) {
804
825
  finalCSS += `@font-face{font-family:${family};src:${src};font-style:${rule.style.getPropertyValue("font-style") || "normal"};font-weight:${rule.style.getPropertyValue("font-weight") || "normal"};}`;
805
826
  continue;
@@ -885,19 +906,25 @@
885
906
  }
886
907
 
887
908
  // src/modules/pseudo.js
888
- function inlinePseudoElements(source, clone, compress, embedFonts = false, useProxy) {
909
+ function inlinePseudoElements(source, clone, options) {
889
910
  return __async(this, null, function* () {
911
+ var _a;
890
912
  if (!(source instanceof Element) || !(clone instanceof Element)) return;
891
913
  for (const pseudo of ["::before", "::after", "::first-letter"]) {
892
914
  try {
893
915
  const style = getStyle(source, pseudo);
894
916
  if (!style || typeof style[Symbol.iterator] !== "function") continue;
917
+ const isEmptyPseudo = style.content === "none" && style.backgroundImage === "none" && style.backgroundColor === "transparent" && (style.borderStyle === "none" || parseFloat(style.borderWidth) === 0) && (!style.transform || style.transform === "none") && style.display === "inline";
918
+ if (isEmptyPseudo) continue;
895
919
  if (pseudo === "::first-letter") {
896
920
  const normal = getComputedStyle(source);
897
921
  const isMeaningful = style.color !== normal.color || style.fontSize !== normal.fontSize || style.fontWeight !== normal.fontWeight;
898
922
  if (!isMeaningful) continue;
899
923
  const textNode = Array.from(clone.childNodes).find(
900
- (n) => n.nodeType === Node.TEXT_NODE && n.textContent && n.textContent.trim().length > 0
924
+ (n) => {
925
+ var _a2;
926
+ return n.nodeType === Node.TEXT_NODE && ((_a2 = n.textContent) == null ? void 0 : _a2.trim().length) > 0;
927
+ }
901
928
  );
902
929
  if (!textNode) continue;
903
930
  const text = textNode.textContent;
@@ -908,74 +935,83 @@
908
935
  const span = document.createElement("span");
909
936
  span.textContent = first;
910
937
  span.dataset.snapdomPseudo = "::first-letter";
911
- const snapshot = snapshotComputedStyle(style);
912
- const key = getStyleKey(snapshot, "span", compress);
913
- cache.preStyleMap.set(span, key);
938
+ const snapshot2 = snapshotComputedStyle(style);
939
+ const key2 = getStyleKey(snapshot2, "span", options);
940
+ cache.preStyleMap.set(span, key2);
914
941
  const restNode = document.createTextNode(rest);
915
942
  clone.replaceChild(restNode, textNode);
916
943
  clone.insertBefore(span, restNode);
917
944
  continue;
918
945
  }
919
- const content = style.getPropertyValue("content");
920
- const bg = style.getPropertyValue("background-image");
921
- const bgColor = style.getPropertyValue("background-color");
922
- const hasContent = content !== "none";
946
+ const content = style.content;
947
+ const cleanContent = /counter\s*\(|counters\s*\(/.test(content) ? "- " : parseContent(content);
948
+ const bg = style.backgroundImage;
949
+ const bgColor = style.backgroundColor;
950
+ const fontFamily = style.fontFamily;
951
+ const fontSize = parseInt(style.fontSize) || 32;
952
+ const fontWeight = parseInt(style.fontWeight) || false;
953
+ const color = style.color || "#000";
954
+ const display = style.display;
955
+ const width = parseFloat(style.width);
956
+ const height = parseFloat(style.height);
957
+ const borderStyle = style.borderStyle;
958
+ const borderWidth = parseFloat(style.borderWidth);
959
+ const transform = style.transform;
960
+ const isIconFont2 = isIconFont(fontFamily);
961
+ const hasExplicitContent = content !== "none" && cleanContent !== "";
923
962
  const hasBg = bg && bg !== "none";
924
963
  const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
925
- if (hasContent || hasBg || hasBgColor) {
926
- const fontFamily = style.getPropertyValue("font-family");
927
- const fontSize = parseInt(style.getPropertyValue("font-size")) || 32;
928
- const fontWeight = parseInt(style.getPropertyValue("font-weight")) || false;
929
- const color = style.getPropertyValue("color") || "#000";
930
- const pseudoEl = document.createElement("span");
931
- pseudoEl.dataset.snapdomPseudo = pseudo;
932
- const snapshot = snapshotComputedStyle(style);
933
- const key = getStyleKey(snapshot, "span", compress);
934
- cache.preStyleMap.set(pseudoEl, key);
935
- const isIconFont2 = isIconFont(fontFamily);
936
- let cleanContent;
937
- if (/counter\s*\(|counters\s*\(/.test(content)) {
938
- cleanContent = "- ";
939
- } else {
940
- cleanContent = parseContent(content);
941
- }
942
- if (isIconFont2 && cleanContent.length === 1) {
943
- const imgEl = document.createElement("img");
944
- imgEl.src = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
945
- imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
946
- pseudoEl.appendChild(imgEl);
947
- } else if (cleanContent.startsWith("url(")) {
948
- const rawUrl = extractURL(cleanContent);
949
- if (rawUrl && rawUrl.trim() !== "") {
950
- try {
951
- const imgEl = document.createElement("img");
952
- const dataUrl = yield fetchImage(safeEncodeURI(rawUrl, { useProxy }));
953
- imgEl.src = dataUrl;
954
- imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
955
- pseudoEl.appendChild(imgEl);
956
- } catch (e) {
957
- console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
958
- }
959
- }
960
- } else if (!isIconFont2 && cleanContent && cleanContent !== "none") {
961
- pseudoEl.textContent = cleanContent;
962
- }
963
- if (hasBg) {
964
+ const hasBox = display !== "inline" && (width > 0 || height > 0);
965
+ const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
966
+ const hasTransform = transform && transform !== "none";
967
+ const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox || hasBorder || hasTransform;
968
+ if (!shouldRender) continue;
969
+ const pseudoEl = document.createElement("span");
970
+ pseudoEl.dataset.snapdomPseudo = pseudo;
971
+ pseudoEl.style.verticalAlign = "middle";
972
+ const snapshot = snapshotComputedStyle(style);
973
+ const key = getStyleKey(snapshot, "span", options);
974
+ cache.preStyleMap.set(pseudoEl, key);
975
+ if (isIconFont2 && cleanContent.length === 1) {
976
+ const { dataUrl, width: width2, height: height2 } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
977
+ const imgEl = document.createElement("img");
978
+ imgEl.src = dataUrl;
979
+ imgEl.style = `height:${fontSize}px;width:${width2 / height2 * fontSize}px;object-fit:contain;`;
980
+ pseudoEl.appendChild(imgEl);
981
+ clone.dataset.snapdomHasIcon = "true";
982
+ } else if (cleanContent.startsWith("url(")) {
983
+ const rawUrl = extractURL(cleanContent);
984
+ if (rawUrl == null ? void 0 : rawUrl.trim()) {
964
985
  try {
965
- const bgSplits = splitBackgroundImage(bg);
966
- const newBgParts = yield Promise.all(
967
- bgSplits.map((entry) => inlineSingleBackgroundEntry(entry))
968
- );
969
- pseudoEl.style.backgroundImage = newBgParts.join(", ");
986
+ const imgEl = document.createElement("img");
987
+ const dataUrl = yield fetchImage(safeEncodeURI(rawUrl), options);
988
+ imgEl.src = dataUrl;
989
+ imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`;
990
+ pseudoEl.appendChild(imgEl);
970
991
  } catch (e) {
971
- console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
992
+ console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
972
993
  }
973
994
  }
974
- if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
975
- const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent && pseudoEl.textContent.trim() !== "";
976
- const hasVisibleBox = hasContent2 || hasBg || hasBgColor;
977
- if (!hasVisibleBox) continue;
978
- pseudo === "::before" ? clone.insertBefore(pseudoEl, clone.firstChild) : clone.appendChild(pseudoEl);
995
+ } else if (!isIconFont2 && hasExplicitContent) {
996
+ pseudoEl.textContent = cleanContent;
997
+ }
998
+ if (hasBg) {
999
+ try {
1000
+ const bgSplits = splitBackgroundImage(bg);
1001
+ const newBgParts = yield Promise.all(bgSplits.map(inlineSingleBackgroundEntry));
1002
+ pseudoEl.style.backgroundImage = newBgParts.join(", ");
1003
+ } catch (e) {
1004
+ console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
1005
+ }
1006
+ }
1007
+ if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
1008
+ const hasContent2 = pseudoEl.childNodes.length > 0 || ((_a = pseudoEl.textContent) == null ? void 0 : _a.trim()) !== "";
1009
+ const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox || hasBorder || hasTransform;
1010
+ if (!hasVisibleBox) continue;
1011
+ if (pseudo === "::before") {
1012
+ clone.insertBefore(pseudoEl, clone.firstChild);
1013
+ } else {
1014
+ clone.appendChild(pseudoEl);
979
1015
  }
980
1016
  } catch (e) {
981
1017
  console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e);
@@ -984,53 +1020,73 @@
984
1020
  const sChildren = Array.from(source.children);
985
1021
  const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
986
1022
  for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
987
- yield inlinePseudoElements(
988
- sChildren[i],
989
- cChildren[i],
990
- compress,
991
- embedFonts,
992
- useProxy
993
- );
1023
+ yield inlinePseudoElements(sChildren[i], cChildren[i], options);
994
1024
  }
995
1025
  });
996
1026
  }
997
1027
 
998
1028
  // src/modules/svgDefs.js
999
- function inlineExternalDef(root) {
1000
- if (!root) return;
1001
- const defsSources = document.querySelectorAll("svg > defs");
1002
- if (!defsSources.length) return;
1003
- root.querySelectorAll("svg").forEach((svg) => {
1004
- const uses = svg.querySelectorAll("use");
1005
- if (!uses.length) return;
1006
- const usedIds = /* @__PURE__ */ new Set();
1007
- uses.forEach((use) => {
1008
- const href = use.getAttribute("xlink:href") || use.getAttribute("href");
1009
- if (href && href.startsWith("#")) usedIds.add(href.slice(1));
1010
- });
1011
- if (!usedIds.size) return;
1012
- const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
1013
- for (const id of usedIds) {
1014
- for (const source of defsSources) {
1015
- const def = source.querySelector(`#${CSS.escape(id)}`);
1016
- if (def) {
1017
- defs.appendChild(def.cloneNode(true));
1018
- break;
1029
+ function inlineExternalDefsAndSymbols(rootElement) {
1030
+ if (!rootElement) return;
1031
+ const usedIds = /* @__PURE__ */ new Set();
1032
+ rootElement.querySelectorAll("use").forEach((use) => {
1033
+ const href = use.getAttribute("xlink:href") || use.getAttribute("href");
1034
+ if (href && href.startsWith("#")) {
1035
+ usedIds.add(href.slice(1));
1036
+ }
1037
+ });
1038
+ if (!usedIds.size) return;
1039
+ const allGlobal = Array.from(document.querySelectorAll("svg > symbol, svg > defs"));
1040
+ const globalSymbols = allGlobal.filter((el) => el.tagName.toLowerCase() === "symbol");
1041
+ const globalDefs = allGlobal.filter((el) => el.tagName.toLowerCase() === "defs");
1042
+ let container = rootElement.querySelector("svg.inline-defs-container");
1043
+ if (!container) {
1044
+ container = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1045
+ container.setAttribute("aria-hidden", "true");
1046
+ container.setAttribute("style", "position: absolute; width: 0; height: 0; overflow: hidden;");
1047
+ container.classList.add("inline-defs-container");
1048
+ rootElement.insertBefore(container, rootElement.firstChild);
1049
+ }
1050
+ const existingIds = /* @__PURE__ */ new Set();
1051
+ rootElement.querySelectorAll("symbol[id], defs > *[id]").forEach((el) => {
1052
+ existingIds.add(el.id);
1053
+ });
1054
+ usedIds.forEach((id) => {
1055
+ if (existingIds.has(id)) return;
1056
+ const symbol = globalSymbols.find((sym) => sym.id === id);
1057
+ if (symbol) {
1058
+ container.appendChild(symbol.cloneNode(true));
1059
+ existingIds.add(id);
1060
+ return;
1061
+ }
1062
+ for (const defs of globalDefs) {
1063
+ const defEl = defs.querySelector(`#${CSS.escape(id)}`);
1064
+ if (defEl) {
1065
+ let defsContainer = container.querySelector("defs");
1066
+ if (!defsContainer) {
1067
+ defsContainer = document.createElementNS("http://www.w3.org/2000/svg", "defs");
1068
+ container.appendChild(defsContainer);
1019
1069
  }
1070
+ defsContainer.appendChild(defEl.cloneNode(true));
1071
+ existingIds.add(id);
1072
+ break;
1020
1073
  }
1021
1074
  }
1022
- if (defs.childNodes.length) {
1023
- svg.insertBefore(defs, svg.firstChild);
1024
- }
1025
1075
  });
1026
1076
  }
1027
1077
 
1028
1078
  // src/core/prepare.js
1029
1079
  function prepareClone(_0) {
1030
1080
  return __async(this, arguments, function* (element, compress = false, embedFonts = false, options = {}) {
1031
- var _a;
1081
+ var _a, _b;
1032
1082
  let clone;
1033
1083
  let classCSS = "";
1084
+ stabilizeLayout(element);
1085
+ try {
1086
+ inlineExternalDefsAndSymbols(element);
1087
+ } catch (e) {
1088
+ console.warn("inlineExternal defs or symbol failed:", e);
1089
+ }
1034
1090
  try {
1035
1091
  clone = deepClone(element, compress, options, element);
1036
1092
  } catch (e) {
@@ -1042,21 +1098,24 @@
1042
1098
  } catch (e) {
1043
1099
  console.warn("inlinePseudoElements failed:", e);
1044
1100
  }
1045
- try {
1046
- inlineExternalDef(clone);
1047
- } catch (e) {
1048
- console.warn("inlineExternalDef failed:", e);
1049
- }
1050
1101
  if (compress) {
1051
1102
  const keyToClass = generateCSSClasses();
1052
1103
  classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
1053
1104
  for (const [node, key] of cache.preStyleMap.entries()) {
1054
1105
  if (node.tagName === "STYLE") continue;
1106
+ if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
1107
+ node.setAttribute("style", key.replace(/;/g, "; "));
1108
+ continue;
1109
+ }
1055
1110
  const className = keyToClass.get(key);
1056
1111
  if (className) node.classList.add(className);
1057
1112
  const bgImage = (_a = node.style) == null ? void 0 : _a.backgroundImage;
1058
- node.removeAttribute("style");
1113
+ const hasIcon = (_b = node.dataset) == null ? void 0 : _b.snapdomHasIcon;
1059
1114
  if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
1115
+ if (hasIcon) {
1116
+ node.style.verticalAlign = "middle";
1117
+ node.style.display = "inline";
1118
+ }
1060
1119
  }
1061
1120
  } else {
1062
1121
  for (const [node, key] of cache.preStyleMap.entries()) {
@@ -1107,6 +1166,18 @@
1107
1166
  return { clone, classCSS };
1108
1167
  });
1109
1168
  }
1169
+ function stabilizeLayout(element) {
1170
+ const style = getComputedStyle(element);
1171
+ const outlineStyle = style.outlineStyle;
1172
+ const outlineWidth = style.outlineWidth;
1173
+ const borderStyle = style.borderStyle;
1174
+ const borderWidth = style.borderWidth;
1175
+ const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0;
1176
+ const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0;
1177
+ if (outlineVisible && borderAbsent) {
1178
+ element.style.border = `${outlineWidth} solid transparent`;
1179
+ }
1180
+ }
1110
1181
 
1111
1182
  // src/modules/images.js
1112
1183
  function inlineImages(_0) {
@@ -1145,13 +1216,27 @@
1145
1216
  "mask-source",
1146
1217
  "mask-box-image-source",
1147
1218
  "mask-border-source",
1148
- "-webkit-mask-box-image-source"
1219
+ "-webkit-mask-box-image-source",
1220
+ "border-image",
1221
+ "border-image-source",
1222
+ "border-image-slice",
1223
+ "border-image-width",
1224
+ "border-image-outset",
1225
+ "border-image-repeat"
1149
1226
  ];
1150
1227
  while (queue.length) {
1151
1228
  const [srcNode, cloneNode] = queue.shift();
1152
1229
  const style = cache.preStyle.get(srcNode) || getStyle(srcNode);
1153
1230
  if (!cache.preStyle.has(srcNode)) cache.preStyle.set(srcNode, style);
1231
+ const hasBorderImage = (() => {
1232
+ const bi = style.getPropertyValue("border-image");
1233
+ const bis = style.getPropertyValue("border-image-source");
1234
+ return bi && bi !== "none" || bis && bis !== "none";
1235
+ })();
1154
1236
  for (const prop of imageProps) {
1237
+ if (["border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat"].includes(prop) && !hasBorderImage) {
1238
+ continue;
1239
+ }
1155
1240
  const val = style.getPropertyValue(prop);
1156
1241
  if (!val || val === "none") continue;
1157
1242
  const splits = splitBackgroundImage(val);
@@ -1255,8 +1340,6 @@
1255
1340
  const existingTransform = clone.style.transform || "";
1256
1341
  const scaleTransform = `scale(${scaleX}, ${scaleY})`;
1257
1342
  clone.style.transform = `${scaleTransform} ${existingTransform}`.trim();
1258
- } else if (hasScale && isSafari()) {
1259
- clone.style.scale = `${scale}`;
1260
1343
  }
1261
1344
  const svgNS = "http://www.w3.org/2000/svg";
1262
1345
  const fo = document.createElementNS(svgNS, "foreignObject");
@@ -1282,17 +1365,14 @@
1282
1365
  }
1283
1366
 
1284
1367
  // src/api/snapdom.js
1285
- function toImg(_0, _1) {
1286
- return __async(this, arguments, function* (url, { dpr = 1, scale = 1 }) {
1368
+ function toImg(_0) {
1369
+ return __async(this, arguments, function* (url, { scale = 1 } = {}) {
1287
1370
  const img = new Image();
1288
1371
  img.src = url;
1289
1372
  yield img.decode();
1290
- if (isSafari) {
1291
- img.width = img.width * scale;
1292
- img.height = img.height * scale;
1293
- } else {
1294
- img.width = img.width / scale;
1295
- img.height = img.height / scale;
1373
+ if (scale !== 1) {
1374
+ img.style.width = `${img.naturalWidth * scale}px`;
1375
+ img.style.height = `${img.naturalHeight * scale}px`;
1296
1376
  }
1297
1377
  return img;
1298
1378
  });
@@ -1301,17 +1381,34 @@
1301
1381
  return __async(this, arguments, function* (url, { dpr = 1, scale = 1 } = {}) {
1302
1382
  const img = new Image();
1303
1383
  img.src = url;
1384
+ img.crossOrigin = "anonymous";
1385
+ img.loading = "eager";
1386
+ img.decoding = "sync";
1387
+ const isSafariBrowser = isSafari();
1388
+ let appended = false;
1389
+ if (isSafariBrowser) {
1390
+ document.body.appendChild(img);
1391
+ appended = true;
1392
+ }
1304
1393
  yield img.decode();
1394
+ if (isSafariBrowser) {
1395
+ yield new Promise((resolve) => setTimeout(resolve, 100));
1396
+ }
1397
+ if (img.width === 0 || img.height === 0) {
1398
+ if (appended) img.remove();
1399
+ throw new Error("Image failed to load or has no dimensions");
1400
+ }
1401
+ const width = img.naturalWidth * scale;
1402
+ const height = img.naturalHeight * scale;
1305
1403
  const canvas = document.createElement("canvas");
1306
- const width = img.width * scale;
1307
- const height = img.height * scale;
1308
1404
  canvas.width = Math.ceil(width * dpr);
1309
1405
  canvas.height = Math.ceil(height * dpr);
1406
+ canvas.style.width = `${width}px`;
1407
+ canvas.style.height = `${height}px`;
1310
1408
  const ctx = canvas.getContext("2d");
1311
1409
  ctx.scale(dpr, dpr);
1312
1410
  ctx.drawImage(img, 0, 0, width, height);
1313
- canvas.style.width = `${width}px`;
1314
- canvas.style.height = `${height}px`;
1411
+ if (appended) img.remove();
1315
1412
  return canvas;
1316
1413
  });
1317
1414
  }
@@ -1366,7 +1463,7 @@
1366
1463
  });
1367
1464
  }
1368
1465
  function download(_0) {
1369
- return __async(this, arguments, function* (url, { dpr = 1, scale = 1, backgroundColor = "#fff", format = "png", filename = "capture" } = {}) {
1466
+ return __async(this, arguments, function* (url, { dpr = 1, scale = 1, backgroundColor, format = "png", filename = "snapDOM" } = {}) {
1370
1467
  if (format === "svg") {
1371
1468
  const blob = yield toBlob(url);
1372
1469
  const objectURL = URL.createObjectURL(blob);
@@ -1404,20 +1501,24 @@
1404
1501
  });
1405
1502
  }
1406
1503
  snapdom.capture = (_0, ..._1) => __async(null, [_0, ..._1], function* (el, options = {}) {
1504
+ var _a;
1407
1505
  const url = yield captureDOM(el, options);
1408
- const dpr = window.devicePixelRatio || 1;
1506
+ const dpr = (_a = options.dpr) != null ? _a : window.devicePixelRatio || 1;
1409
1507
  const scale = options.scale || 1;
1410
1508
  return {
1411
1509
  url,
1412
1510
  options,
1413
1511
  toRaw: () => url,
1414
- toImg: () => toImg(url, { dpr, scale }),
1415
- toCanvas: () => toCanvas(url, { dpr, scale }),
1416
- toBlob: (options2) => toBlob(url, __spreadValues({ dpr, scale }, options2)),
1417
- toPng: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "png"),
1418
- toJpg: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "jpeg"),
1419
- toWebp: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "webp"),
1420
- download: ({ format = "png", filename = "capture", backgroundColor } = {}) => download(url, { dpr, scale, backgroundColor, format, filename })
1512
+ toImg: (opts = {}) => toImg(url, __spreadValues({ dpr, scale }, opts)),
1513
+ toCanvas: (opts = {}) => toCanvas(url, __spreadValues({ dpr, scale }, opts)),
1514
+ toBlob: (opts = {}) => toBlob(url, __spreadValues({ dpr, scale }, opts)),
1515
+ toPng: (opts = {}) => toRasterImg(url, __spreadValues({ dpr, scale }, opts), "png"),
1516
+ toJpg: (opts = {}) => toRasterImg(url, __spreadValues({ dpr, scale }, opts), "jpeg"),
1517
+ toWebp: (opts = {}) => toRasterImg(url, __spreadValues({ dpr, scale }, opts), "webp"),
1518
+ download: (_b = {}) => {
1519
+ var _c = _b, { format = "png", filename = "snapDOM", backgroundColor } = _c, opts = __objRest(_c, ["format", "filename", "backgroundColor"]);
1520
+ return download(url, __spreadValues({ dpr, scale, format, filename, backgroundColor }, opts));
1521
+ }
1421
1522
  };
1422
1523
  });
1423
1524
  snapdom.toRaw = (el, options) => __async(null, null, function* () {