@zumer/snapdom 1.9.10-dev.1 → 1.9.10-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/snapdom.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.9.10-dev.1
4
+ * v.1.9.10-dev.2
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -82,10 +82,13 @@
82
82
  cache.session.styleMap = /* @__PURE__ */ new Map();
83
83
  cache.session.nodeMap = /* @__PURE__ */ new Map();
84
84
  cache.session.styleCache = /* @__PURE__ */ new WeakMap();
85
- cache.image.clear();
86
- cache.background.clear();
87
- cache.resource.clear();
88
- cache.font.clear();
85
+ cache.computedStyle = /* @__PURE__ */ new WeakMap();
86
+ cache.baseStyle = /* @__PURE__ */ new Map();
87
+ cache.defaultStyle = /* @__PURE__ */ new Map();
88
+ cache.image = /* @__PURE__ */ new Map();
89
+ cache.background = /* @__PURE__ */ new Map();
90
+ cache.resource = /* @__PURE__ */ new Map();
91
+ cache.font = /* @__PURE__ */ new Set();
89
92
  return;
90
93
  }
91
94
  default: {
@@ -687,6 +690,11 @@
687
690
  const ctx = _resolveCtx(sessionOrCtx, opts);
688
691
  const resetMode = ctx.options && ctx.options.cache || "auto";
689
692
  if (resetMode !== "disabled") setupInvalidationOnce(document.documentElement);
693
+ if (resetMode === "disabled" && !ctx.session.__bumpedForDisabled) {
694
+ bumpEpoch();
695
+ snapshotKeyCache.clear();
696
+ ctx.session.__bumpedForDisabled = true;
697
+ }
690
698
  if (NO_DEFAULTS_TAGS.has((_a = source.tagName) == null ? void 0 : _a.toLowerCase())) {
691
699
  const author = (_b = source.getAttribute) == null ? void 0 : _b.call(source, "style");
692
700
  if (author) clone.setAttribute("style", author);
@@ -709,6 +717,31 @@
709
717
  }
710
718
 
711
719
  // src/core/clone.js
720
+ function idleCallback(childList, callback, fast) {
721
+ return Promise.all(childList.map((child) => {
722
+ return new Promise((resolve) => {
723
+ function deal() {
724
+ idle(
725
+ (deadline) => {
726
+ if (deadline instanceof IdleDeadline) {
727
+ if (deadline.timeRemaining()) {
728
+ callback(child, resolve);
729
+ } else {
730
+ deal();
731
+ }
732
+ } else {
733
+ callback(child, resolve);
734
+ }
735
+ },
736
+ {
737
+ fast
738
+ }
739
+ );
740
+ }
741
+ deal();
742
+ });
743
+ }));
744
+ }
712
745
  function addNotSlottedRightmost(sel) {
713
746
  sel = sel.trim();
714
747
  if (!sel) return sel;
@@ -830,185 +863,321 @@
830
863
  root.querySelectorAll("*").forEach((el) => el.setAttribute("data-sd-slotted", ""));
831
864
  }
832
865
  }
866
+ function getAccessibleIframeDocument(iframe, attempts = 3) {
867
+ return __async(this, null, function* () {
868
+ const probe = () => {
869
+ var _a;
870
+ try {
871
+ return iframe.contentDocument || ((_a = iframe.contentWindow) == null ? void 0 : _a.document) || null;
872
+ } catch (e) {
873
+ return null;
874
+ }
875
+ };
876
+ let doc = probe();
877
+ let i = 0;
878
+ while (i < attempts && (!doc || !doc.body && !doc.documentElement)) {
879
+ yield new Promise((r) => setTimeout(r, 0));
880
+ doc = probe();
881
+ i++;
882
+ }
883
+ return doc && (doc.body || doc.documentElement) ? doc : null;
884
+ });
885
+ }
886
+ function measureContentBox(el) {
887
+ const rect = el.getBoundingClientRect();
888
+ let bl = 0, br = 0, bt = 0, bb = 0;
889
+ try {
890
+ const cs = getComputedStyle(el);
891
+ bl = parseFloat(cs.borderLeftWidth) || 0;
892
+ br = parseFloat(cs.borderRightWidth) || 0;
893
+ bt = parseFloat(cs.borderTopWidth) || 0;
894
+ bb = parseFloat(cs.borderBottomWidth) || 0;
895
+ } catch (e) {
896
+ }
897
+ const contentWidth = Math.max(0, Math.round(rect.width - (bl + br)));
898
+ const contentHeight = Math.max(0, Math.round(rect.height - (bt + bb)));
899
+ return { contentWidth, contentHeight, rect };
900
+ }
901
+ function pinIframeViewport(doc, w, h) {
902
+ const style = doc.createElement("style");
903
+ style.setAttribute("data-sd-iframe-pin", "");
904
+ style.textContent = `html, body {margin: 0 !important;padding: 0 !important;width: ${w}px !important;height: ${h}px !important;min-width: ${w}px !important;min-height: ${h}px !important;box-sizing: border-box !important;overflow: hidden !important;background-clip: border-box !important;}`;
905
+ (doc.head || doc.documentElement).appendChild(style);
906
+ return () => {
907
+ try {
908
+ style.remove();
909
+ } catch (e) {
910
+ }
911
+ };
912
+ }
913
+ function rasterizeIframe(iframe, sessionCache, options) {
914
+ return __async(this, null, function* () {
915
+ const doc = yield getAccessibleIframeDocument(iframe, 3);
916
+ if (!doc) throw new Error("iframe document not accessible/ready");
917
+ const { contentWidth, contentHeight, rect } = measureContentBox(iframe);
918
+ const snap = options == null ? void 0 : options.snap;
919
+ if (!snap || typeof snap.toPng !== "function") {
920
+ throw new Error("snapdom.toPng not available in iframe or window");
921
+ }
922
+ const nested = __spreadProps(__spreadValues({}, options), { scale: 1 });
923
+ const unpin = pinIframeViewport(doc, contentWidth, contentHeight);
924
+ let imgEl;
925
+ try {
926
+ imgEl = yield snap.toPng(doc.documentElement, nested);
927
+ } finally {
928
+ unpin();
929
+ }
930
+ imgEl.style.display = "block";
931
+ imgEl.style.width = `${contentWidth}px`;
932
+ imgEl.style.height = `${contentHeight}px`;
933
+ const wrapper = document.createElement("div");
934
+ sessionCache.nodeMap.set(wrapper, iframe);
935
+ inlineAllStyles(iframe, wrapper, sessionCache, options);
936
+ wrapper.style.overflow = "hidden";
937
+ wrapper.style.display = "block";
938
+ if (!wrapper.style.width) wrapper.style.width = `${Math.round(rect.width)}px`;
939
+ if (!wrapper.style.height) wrapper.style.height = `${Math.round(rect.height)}px`;
940
+ wrapper.appendChild(imgEl);
941
+ return wrapper;
942
+ });
943
+ }
833
944
  function deepClone(node, sessionCache, options) {
834
- var _a, _b, _c, _d, _e;
835
- if (!node) throw new Error("Invalid node");
836
- const clonedAssignedNodes = /* @__PURE__ */ new Set();
837
- let pendingSelectValue = null;
838
- if (node.nodeType === Node.ELEMENT_NODE) {
839
- const tag = (node.localName || node.tagName || "").toLowerCase();
840
- if (node.id === "snapdom-sandbox" || node.hasAttribute("data-snapdom-sandbox")) {
841
- return null;
945
+ return __async(this, null, function* () {
946
+ var _a, _b, _c, _d, _e, _f;
947
+ if (!node) throw new Error("Invalid node");
948
+ const clonedAssignedNodes = /* @__PURE__ */ new Set();
949
+ let pendingSelectValue = null;
950
+ let pendingTextAreaValue = null;
951
+ if (node.nodeType === Node.ELEMENT_NODE) {
952
+ const tag = (node.localName || node.tagName || "").toLowerCase();
953
+ if (node.id === "snapdom-sandbox" || node.hasAttribute("data-snapdom-sandbox")) {
954
+ return null;
955
+ }
956
+ if (NO_CAPTURE_TAGS.has(tag)) {
957
+ return null;
958
+ }
842
959
  }
843
- if (NO_CAPTURE_TAGS.has(tag)) {
844
- return null;
960
+ if (node.nodeType === Node.TEXT_NODE) {
961
+ return node.cloneNode(true);
845
962
  }
846
- }
847
- if (node.nodeType === Node.TEXT_NODE) {
848
- return node.cloneNode(true);
849
- }
850
- if (node.nodeType !== Node.ELEMENT_NODE) {
851
- return node.cloneNode(true);
852
- }
853
- if (node.getAttribute("data-capture") === "exclude") {
854
- const spacer = document.createElement("div");
855
- const rect = node.getBoundingClientRect();
856
- spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
857
- return spacer;
858
- }
859
- if (options.exclude && Array.isArray(options.exclude)) {
860
- for (const selector of options.exclude) {
963
+ if (node.nodeType !== Node.ELEMENT_NODE) {
964
+ return node.cloneNode(true);
965
+ }
966
+ if (node.getAttribute("data-capture") === "exclude") {
967
+ const spacer = document.createElement("div");
968
+ const rect = node.getBoundingClientRect();
969
+ spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
970
+ return spacer;
971
+ }
972
+ if (options.exclude && Array.isArray(options.exclude)) {
973
+ for (const selector of options.exclude) {
974
+ try {
975
+ if ((_a = node.matches) == null ? void 0 : _a.call(node, selector)) {
976
+ const spacer = document.createElement("div");
977
+ const rect = node.getBoundingClientRect();
978
+ spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
979
+ return spacer;
980
+ }
981
+ } catch (err) {
982
+ console.warn(`Invalid selector in exclude option: ${selector}`, err);
983
+ }
984
+ }
985
+ }
986
+ if (typeof options.filter === "function") {
861
987
  try {
862
- if ((_a = node.matches) == null ? void 0 : _a.call(node, selector)) {
988
+ if (!options.filter(node)) {
863
989
  const spacer = document.createElement("div");
864
990
  const rect = node.getBoundingClientRect();
865
991
  spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
866
992
  return spacer;
867
993
  }
868
994
  } catch (err) {
869
- console.warn(`Invalid selector in exclude option: ${selector}`, err);
995
+ console.warn("Error in filter function:", err);
870
996
  }
871
997
  }
872
- }
873
- if (typeof options.filter === "function") {
874
- try {
875
- if (!options.filter(node)) {
876
- const spacer = document.createElement("div");
998
+ if (node.tagName === "IFRAME") {
999
+ let sameOrigin = false;
1000
+ try {
1001
+ sameOrigin = !!(node.contentDocument || ((_b = node.contentWindow) == null ? void 0 : _b.document));
1002
+ } catch (e) {
1003
+ sameOrigin = false;
1004
+ }
1005
+ if (sameOrigin) {
1006
+ try {
1007
+ const wrapper = yield rasterizeIframe(node, sessionCache, options);
1008
+ return wrapper;
1009
+ } catch (err) {
1010
+ console.warn("[SnapDOM] iframe rasterization failed, fallback:", err);
1011
+ }
1012
+ }
1013
+ if (options.placeholders) {
1014
+ const fallback = document.createElement("div");
1015
+ fallback.style.cssText = `width:${node.offsetWidth}px;height:${node.offsetHeight}px;background-image: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;`;
1016
+ inlineAllStyles(node, fallback, sessionCache, options);
1017
+ return fallback;
1018
+ } else {
877
1019
  const rect = node.getBoundingClientRect();
1020
+ const spacer = document.createElement("div");
878
1021
  spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
1022
+ inlineAllStyles(node, spacer, sessionCache, options);
879
1023
  return spacer;
880
1024
  }
881
- } catch (err) {
882
- console.warn("Error in filter function:", err);
883
1025
  }
884
- }
885
- if (node.tagName === "IFRAME") {
886
- if (options.placeholders) {
887
- const fallback = document.createElement("div");
888
- fallback.style.cssText = `width:${node.offsetWidth}px;height:${node.offsetHeight}px;background-image: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;`;
889
- inlineAllStyles(node, fallback, sessionCache, options);
890
- return fallback;
891
- } else {
892
- const rect = node.getBoundingClientRect();
893
- const spacer = document.createElement("div");
894
- spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
895
- inlineAllStyles(node, spacer, sessionCache, options);
896
- return spacer;
1026
+ if (node.getAttribute("data-capture") === "placeholder") {
1027
+ const clone2 = node.cloneNode(false);
1028
+ sessionCache.nodeMap.set(clone2, node);
1029
+ inlineAllStyles(node, clone2, sessionCache, options);
1030
+ const placeholder = document.createElement("div");
1031
+ placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
1032
+ placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`;
1033
+ clone2.appendChild(placeholder);
1034
+ return clone2;
1035
+ }
1036
+ if (node.tagName === "CANVAS") {
1037
+ const dataURL = node.toDataURL();
1038
+ const img = document.createElement("img");
1039
+ img.src = dataURL;
1040
+ img.width = node.width;
1041
+ img.height = node.height;
1042
+ sessionCache.nodeMap.set(img, node);
1043
+ inlineAllStyles(node, img, sessionCache, options);
1044
+ return img;
897
1045
  }
898
- }
899
- if (node.getAttribute("data-capture") === "placeholder") {
900
- const clone2 = node.cloneNode(false);
901
- sessionCache.nodeMap.set(clone2, node);
902
- inlineAllStyles(node, clone2, sessionCache, options);
903
- const placeholder = document.createElement("div");
904
- placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
905
- placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`;
906
- clone2.appendChild(placeholder);
907
- return clone2;
908
- }
909
- if (node.tagName === "CANVAS") {
910
- const dataURL = node.toDataURL();
911
- const img = document.createElement("img");
912
- img.src = dataURL;
913
- img.width = node.width;
914
- img.height = node.height;
915
- sessionCache.nodeMap.set(img, node);
916
- inlineAllStyles(node, img, sessionCache, options);
917
- return img;
918
- }
919
- let clone;
920
- try {
921
- clone = node.cloneNode(false);
922
- sessionCache.nodeMap.set(clone, node);
923
- if (node.tagName === "IMG") {
924
- freezeImgSrcset(node, clone);
925
- }
926
- } catch (err) {
927
- console.error("[Snapdom] Failed to clone node:", node, err);
928
- throw err;
929
- }
930
- if (node instanceof HTMLTextAreaElement) {
931
- clone.textContent = node.value;
932
- clone.value = node.value;
933
- const rect = node.getBoundingClientRect();
934
- clone.style.width = `${rect.width}px`;
935
- clone.style.height = `${rect.height}px`;
936
- }
937
- if (node instanceof HTMLInputElement) {
938
- clone.value = node.value;
939
- clone.setAttribute("value", node.value);
940
- if (node.checked !== void 0) {
941
- clone.checked = node.checked;
942
- if (node.checked) clone.setAttribute("checked", "");
943
- if (node.indeterminate) clone.indeterminate = node.indeterminate;
944
- }
945
- }
946
- if (node instanceof HTMLSelectElement) {
947
- pendingSelectValue = node.value;
948
- }
949
- inlineAllStyles(node, clone, sessionCache, options);
950
- if (node.shadowRoot) {
1046
+ let clone;
951
1047
  try {
952
- const slots = node.shadowRoot.querySelectorAll("slot");
953
- for (const s of slots) {
954
- let assigned = [];
1048
+ clone = node.cloneNode(false);
1049
+ sessionCache.nodeMap.set(clone, node);
1050
+ if (node.tagName === "IMG") {
1051
+ freezeImgSrcset(node, clone);
955
1052
  try {
956
- assigned = ((_b = s.assignedNodes) == null ? void 0 : _b.call(s, { flatten: true })) || ((_c = s.assignedNodes) == null ? void 0 : _c.call(s)) || [];
1053
+ const rect = node.getBoundingClientRect();
1054
+ let w = Math.round(rect.width || 0);
1055
+ let h = Math.round(rect.height || 0);
1056
+ if (!w || !h) {
1057
+ const computed = window.getComputedStyle(node);
1058
+ const cssW = parseFloat(computed.width) || 0;
1059
+ const cssH = parseFloat(computed.height) || 0;
1060
+ const attrW = parseInt(node.getAttribute("width") || "", 10) || 0;
1061
+ const attrH = parseInt(node.getAttribute("height") || "", 10) || 0;
1062
+ const propW = node.width || node.naturalWidth || 0;
1063
+ const propH = node.height || node.naturalHeight || 0;
1064
+ w = Math.round(w || cssW || attrW || propW || 0);
1065
+ h = Math.round(h || cssH || attrH || propH || 0);
1066
+ }
1067
+ if (w) clone.dataset.snapdomWidth = String(w);
1068
+ if (h) clone.dataset.snapdomHeight = String(h);
957
1069
  } catch (e) {
958
- assigned = ((_d = s.assignedNodes) == null ? void 0 : _d.call(s)) || [];
959
1070
  }
960
- for (const an of assigned) clonedAssignedNodes.add(an);
961
1071
  }
962
- } catch (e) {
1072
+ } catch (err) {
1073
+ console.error("[Snapdom] Failed to clone node:", node, err);
1074
+ throw err;
963
1075
  }
964
- const scopeId = nextShadowScopeId(sessionCache);
965
- const scopeSelector = `[data-sd="${scopeId}"]`;
966
- try {
967
- clone.setAttribute("data-sd", scopeId);
968
- } catch (e) {
1076
+ if (node instanceof HTMLTextAreaElement) {
1077
+ const rect = node.getBoundingClientRect();
1078
+ clone.style.width = `${rect.width}px`;
1079
+ clone.style.height = `${rect.height}px`;
1080
+ }
1081
+ if (node instanceof HTMLInputElement) {
1082
+ clone.value = node.value;
1083
+ clone.setAttribute("value", node.value);
1084
+ if (node.checked !== void 0) {
1085
+ clone.checked = node.checked;
1086
+ if (node.checked) clone.setAttribute("checked", "");
1087
+ if (node.indeterminate) clone.indeterminate = node.indeterminate;
1088
+ }
969
1089
  }
970
- const rawCSS = extractShadowCSS(node.shadowRoot);
971
- const rewritten = rewriteShadowCSS(rawCSS, scopeSelector);
972
- const neededVars = collectCustomPropsFromCSS(rawCSS);
973
- const seed = buildSeedCustomPropsRule(node, neededVars, scopeSelector);
974
- injectScopedStyle(clone, seed + rewritten, scopeId);
975
- const shadowFrag = document.createDocumentFragment();
976
- for (const child of node.shadowRoot.childNodes) {
977
- if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") continue;
978
- const clonedChild = deepClone(child, sessionCache, options);
979
- if (clonedChild) shadowFrag.appendChild(clonedChild);
980
- }
981
- clone.appendChild(shadowFrag);
982
- }
983
- if (node.tagName === "SLOT") {
984
- const assigned = ((_e = node.assignedNodes) == null ? void 0 : _e.call(node, { flatten: true })) || [];
985
- const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
986
- const fragment = document.createDocumentFragment();
987
- for (const child of nodesToClone) {
988
- const clonedChild = deepClone(child, sessionCache, options);
989
- if (clonedChild) {
990
- markSlottedSubtree(clonedChild);
991
- fragment.appendChild(clonedChild);
1090
+ if (node instanceof HTMLSelectElement) {
1091
+ pendingSelectValue = node.value;
1092
+ }
1093
+ if (node instanceof HTMLTextAreaElement) {
1094
+ pendingTextAreaValue = node.value;
1095
+ }
1096
+ inlineAllStyles(node, clone, sessionCache, options);
1097
+ if (node.shadowRoot) {
1098
+ let callback2 = function(child, resolve) {
1099
+ if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") {
1100
+ return resolve(null);
1101
+ } else {
1102
+ deepClone(child, sessionCache, options).then((clonedChild) => {
1103
+ resolve(clonedChild || null);
1104
+ }).catch((e) => {
1105
+ resolve(null);
1106
+ });
1107
+ }
1108
+ };
1109
+ try {
1110
+ const slots = node.shadowRoot.querySelectorAll("slot");
1111
+ for (const s of slots) {
1112
+ let assigned = [];
1113
+ try {
1114
+ assigned = ((_c = s.assignedNodes) == null ? void 0 : _c.call(s, { flatten: true })) || ((_d = s.assignedNodes) == null ? void 0 : _d.call(s)) || [];
1115
+ } catch (e) {
1116
+ assigned = ((_e = s.assignedNodes) == null ? void 0 : _e.call(s)) || [];
1117
+ }
1118
+ for (const an of assigned) clonedAssignedNodes.add(an);
1119
+ }
1120
+ } catch (e) {
992
1121
  }
1122
+ const scopeId = nextShadowScopeId(sessionCache);
1123
+ const scopeSelector = `[data-sd="${scopeId}"]`;
1124
+ try {
1125
+ clone.setAttribute("data-sd", scopeId);
1126
+ } catch (e) {
1127
+ }
1128
+ const rawCSS = extractShadowCSS(node.shadowRoot);
1129
+ const rewritten = rewriteShadowCSS(rawCSS, scopeSelector);
1130
+ const neededVars = collectCustomPropsFromCSS(rawCSS);
1131
+ const seed = buildSeedCustomPropsRule(node, neededVars, scopeSelector);
1132
+ injectScopedStyle(clone, seed + rewritten, scopeId);
1133
+ const shadowFrag = document.createDocumentFragment();
1134
+ const cloneList2 = yield idleCallback(Array.from(node.shadowRoot.childNodes), callback2, options.fast);
1135
+ shadowFrag.append(...cloneList2.filter((clonedChild) => !!clonedChild));
1136
+ clone.appendChild(shadowFrag);
1137
+ }
1138
+ if (node.tagName === "SLOT") {
1139
+ let callback2 = function(child, resolve) {
1140
+ deepClone(child, sessionCache, options).then((clonedChild) => {
1141
+ if (clonedChild) {
1142
+ markSlottedSubtree(clonedChild);
1143
+ }
1144
+ resolve(clonedChild || null);
1145
+ }).catch((e) => {
1146
+ resolve(null);
1147
+ });
1148
+ };
1149
+ const assigned = ((_f = node.assignedNodes) == null ? void 0 : _f.call(node, { flatten: true })) || [];
1150
+ const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
1151
+ const fragment = document.createDocumentFragment();
1152
+ const cloneList2 = yield idleCallback(Array.from(nodesToClone), callback2, options.fast);
1153
+ fragment.append(...cloneList2.filter((clonedChild) => !!clonedChild));
1154
+ return fragment;
1155
+ }
1156
+ function callback(child, resolve) {
1157
+ if (clonedAssignedNodes.has(child)) return resolve(null);
1158
+ deepClone(child, sessionCache, options).then((clonedChild) => {
1159
+ resolve(clonedChild || null);
1160
+ }).catch((e) => {
1161
+ resolve(null);
1162
+ });
993
1163
  }
994
- return fragment;
995
- }
996
- for (const child of node.childNodes) {
997
- if (clonedAssignedNodes.has(child)) continue;
998
- const clonedChild = deepClone(child, sessionCache, options);
999
- if (clonedChild) clone.appendChild(clonedChild);
1000
- }
1001
- if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
1002
- clone.value = pendingSelectValue;
1003
- for (const opt of clone.options) {
1004
- if (opt.value === pendingSelectValue) {
1005
- opt.setAttribute("selected", "");
1006
- } else {
1007
- opt.removeAttribute("selected");
1164
+ const cloneList = yield idleCallback(Array.from(node.childNodes), callback, options.fast);
1165
+ clone.append(...cloneList.filter((clonedChild) => !!clonedChild));
1166
+ if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) {
1167
+ clone.value = pendingSelectValue;
1168
+ for (const opt of clone.options) {
1169
+ if (opt.value === pendingSelectValue) {
1170
+ opt.setAttribute("selected", "");
1171
+ } else {
1172
+ opt.removeAttribute("selected");
1173
+ }
1008
1174
  }
1009
1175
  }
1010
- }
1011
- return clone;
1176
+ if (pendingTextAreaValue !== null && clone instanceof HTMLTextAreaElement) {
1177
+ clone.textContent = pendingTextAreaValue;
1178
+ }
1179
+ return clone;
1180
+ });
1012
1181
  }
1013
1182
 
1014
1183
  // src/modules/iconFonts.js
@@ -1898,7 +2067,7 @@
1898
2067
  console.warn("inlineExternal defs or symbol failed:", e);
1899
2068
  }
1900
2069
  try {
1901
- clone = deepClone(element, sessionCache, options, element);
2070
+ clone = yield deepClone(element, sessionCache, options, element);
1902
2071
  } catch (e) {
1903
2072
  console.warn("deepClone failed:", e);
1904
2073
  throw e;
@@ -2132,6 +2301,7 @@
2132
2301
  return __async(this, arguments, function* (clone, options = {}) {
2133
2302
  const imgs = Array.from(clone.querySelectorAll("img"));
2134
2303
  const processImg = (img) => __async(null, null, function* () {
2304
+ var _a, _b, _c, _d;
2135
2305
  if (!img.getAttribute("src")) {
2136
2306
  const eff = img.currentSrc || img.src || "";
2137
2307
  if (eff) img.setAttribute("src", eff);
@@ -2147,6 +2317,30 @@
2147
2317
  if (!img.height) img.height = img.naturalHeight || 100;
2148
2318
  return;
2149
2319
  }
2320
+ const { defaultImageUrl } = options || {};
2321
+ if (defaultImageUrl) {
2322
+ try {
2323
+ const dsW = parseInt(((_a = img.dataset) == null ? void 0 : _a.snapdomWidth) || "", 10) || 0;
2324
+ const dsH = parseInt(((_b = img.dataset) == null ? void 0 : _b.snapdomHeight) || "", 10) || 0;
2325
+ const attrW = parseInt(img.getAttribute("width") || "", 10) || 0;
2326
+ const attrH = parseInt(img.getAttribute("height") || "", 10) || 0;
2327
+ const styleW = parseFloat(((_c = img.style) == null ? void 0 : _c.width) || "") || 0;
2328
+ const styleH = parseFloat(((_d = img.style) == null ? void 0 : _d.height) || "") || 0;
2329
+ const width = dsW || styleW || attrW || img.width || void 0;
2330
+ const height = dsH || styleH || attrH || img.height || void 0;
2331
+ const fallbackUrl = typeof defaultImageUrl === "function" ? yield defaultImageUrl({ width, height, src, element: img }) : defaultImageUrl;
2332
+ if (fallbackUrl) {
2333
+ const fallbackData = yield snapFetch(fallbackUrl, { as: "dataURL", useProxy: options.useProxy });
2334
+ img.src = fallbackData.data;
2335
+ if (!img.width && width) img.width = width;
2336
+ if (!img.height && height) img.height = height;
2337
+ if (!img.width) img.width = img.naturalWidth || 100;
2338
+ if (!img.height) img.height = img.naturalHeight || 100;
2339
+ return;
2340
+ }
2341
+ } catch (e) {
2342
+ }
2343
+ }
2150
2344
  const w = img.width || img.naturalWidth || 100;
2151
2345
  const h = img.height || img.naturalHeight || 100;
2152
2346
  if (options.placeholders !== false) {
@@ -2264,7 +2458,13 @@
2264
2458
  );
2265
2459
  yield ensureFontsReady(families, 2);
2266
2460
  }
2267
- fontsCSS = yield embedCustomFonts({ required, usedCodepoints, preCached: false, exclude: options.excludeFonts, useProxy: options.useProxy });
2461
+ fontsCSS = yield embedCustomFonts({
2462
+ required,
2463
+ usedCodepoints,
2464
+ preCached: false,
2465
+ exclude: options.excludeFonts,
2466
+ useProxy: options.useProxy
2467
+ });
2268
2468
  resolve();
2269
2469
  }), { fast });
2270
2470
  });
@@ -2284,12 +2484,14 @@
2284
2484
  }
2285
2485
  yield new Promise((resolve) => {
2286
2486
  idle(() => {
2287
- const rect = element.getBoundingClientRect();
2487
+ var _a, _b, _c, _d, _e;
2488
+ const rect = getNaturalBorderBoxSize(element);
2288
2489
  let w = rect.width;
2289
2490
  let h = rect.height;
2290
2491
  const hasW = Number.isFinite(options.width);
2291
2492
  const hasH = Number.isFinite(options.height);
2292
- const hasScale = typeof scale === "number" && scale !== 1;
2493
+ const scaleOpt = (_a = options.scale) != null ? _a : 1;
2494
+ const hasScale = typeof scaleOpt === "number" && scaleOpt !== 1;
2293
2495
  if (!hasScale) {
2294
2496
  const aspect = rect.width / rect.height;
2295
2497
  if (hasW && hasH) {
@@ -2303,30 +2505,79 @@
2303
2505
  w = h * aspect;
2304
2506
  }
2305
2507
  }
2306
- w = Math.ceil(w);
2307
- h = Math.ceil(h);
2308
- clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
2309
- clone.style.transformOrigin = "top left";
2310
- if (!hasScale && (hasW || hasH)) {
2311
- const originalW = rect.width;
2312
- const originalH = rect.height;
2313
- const scaleX = w / originalW;
2314
- const scaleY = h / originalH;
2315
- const existingTransform = clone.style.transform || "";
2316
- const scaleTransform = `scale(${scaleX}, ${scaleY})`;
2317
- clone.style.transform = `${scaleTransform} ${existingTransform}`.trim();
2508
+ w = Math.max(1, Math.ceil(w));
2509
+ h = Math.max(1, Math.ceil(h));
2510
+ const csEl = getComputedStyle(element);
2511
+ const baseTransform = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
2512
+ const ind = readIndividualTransforms(element);
2513
+ let outW, outH, tx, ty, cancelTranslateX = 0, cancelTranslateY = 0;
2514
+ if (!hasBBoxAffectingTransform(element)) {
2515
+ const r = element.getBoundingClientRect();
2516
+ const vp = inflateRect(r, { top: 0, right: 0, bottom: 0, left: 0 });
2517
+ outW = Math.max(1, vp.width);
2518
+ outH = Math.max(1, vp.height);
2519
+ tx = -(vp.x - Math.floor(r.left));
2520
+ ty = -(vp.y - Math.floor(r.top));
2521
+ } else {
2522
+ const TOTAL = readTotalTransformMatrix({
2523
+ baseTransform,
2524
+ rotate: ind.rotate || "0deg",
2525
+ scale: ind.scale,
2526
+ translate: ind.translate
2527
+ });
2528
+ const e = (_c = (_b = TOTAL.e) != null ? _b : TOTAL.m41) != null ? _c : 0;
2529
+ const f = (_e = (_d = TOTAL.f) != null ? _d : TOTAL.m42) != null ? _e : 0;
2530
+ cancelTranslateX = -e;
2531
+ cancelTranslateY = -f;
2532
+ const M2D = TOTAL.is2D ? matrix2DNoTranslate(TOTAL) : matrix2DNoTranslate(new DOMMatrix(TOTAL.toString()));
2533
+ const bb = bboxFromMatrix(w, h, M2D);
2534
+ outW = Math.max(1, Math.ceil(bb.width));
2535
+ outH = Math.max(1, Math.ceil(bb.height));
2536
+ tx = -bb.minX;
2537
+ ty = -bb.minY;
2318
2538
  }
2319
2539
  const svgNS = "http://www.w3.org/2000/svg";
2320
2540
  const fo = document.createElementNS(svgNS, "foreignObject");
2321
2541
  fo.setAttribute("width", "100%");
2322
2542
  fo.setAttribute("height", "100%");
2543
+ fo.setAttribute("x", String(tx));
2544
+ fo.setAttribute("y", String(ty));
2545
+ fo.style.overflow = "visible";
2323
2546
  const styleTag = document.createElement("style");
2324
- styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;}" + classCSS;
2547
+ styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + classCSS;
2325
2548
  fo.appendChild(styleTag);
2326
- fo.appendChild(clone);
2549
+ const container = document.createElement("div");
2550
+ container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
2551
+ container.style.width = `${w}px`;
2552
+ container.style.height = `${h}px`;
2553
+ container.style.overflow = "visible";
2554
+ container.style.transformOrigin = "0 0";
2555
+ const cancels = [];
2556
+ if (cancelTranslateX || cancelTranslateY) {
2557
+ cancels.push(`translate(${cancelTranslateX}px, ${cancelTranslateY}px)`);
2558
+ }
2559
+ if (!hasScale && (hasW || hasH)) {
2560
+ const sxWH = rect.width ? w / rect.width : 1;
2561
+ const syWH = rect.height ? h / rect.height : 1;
2562
+ cancels.push(`scale(${sxWH}, ${syWH})`);
2563
+ } else if (hasScale) {
2564
+ cancels.push(`scale(${scaleOpt})`);
2565
+ }
2566
+ if (cancels.length) container.style.transform = cancels.join(" ");
2567
+ clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
2568
+ clone.style.transformOrigin = "0 0";
2569
+ clone.style.position = "static";
2570
+ clone.style.left = "0";
2571
+ clone.style.top = "0";
2572
+ if (baseTransform) clone.style.transform = baseTransform;
2573
+ clone.style.rotate = ind.rotate || "0deg";
2574
+ clone.style.scale = ind.scale || "1";
2575
+ clone.style.translate = ind.translate || "0 0";
2576
+ container.appendChild(clone);
2577
+ fo.appendChild(container);
2327
2578
  const serializer = new XMLSerializer();
2328
2579
  const foString = serializer.serializeToString(fo);
2329
- const svgHeader = `<svg xmlns="${svgNS}" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`;
2580
+ const svgHeader = `<svg xmlns="${svgNS}" width="${outW}" height="${outH}" viewBox="0 0 ${outW} ${outH}">`;
2330
2581
  const svgFooter = "</svg>";
2331
2582
  svgString = svgHeader + foString + svgFooter;
2332
2583
  dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
@@ -2338,6 +2589,129 @@
2338
2589
  return dataURL;
2339
2590
  });
2340
2591
  }
2592
+ function getNaturalBorderBoxSize(el) {
2593
+ const cs = getComputedStyle(el);
2594
+ const px = (v) => parseFloat(v) || 0;
2595
+ let w = px(cs.width);
2596
+ let h = px(cs.height);
2597
+ if (cs.boxSizing === "content-box") {
2598
+ w += px(cs.paddingLeft) + px(cs.paddingRight) + px(cs.borderLeftWidth) + px(cs.borderRightWidth);
2599
+ h += px(cs.paddingTop) + px(cs.paddingBottom) + px(cs.borderTopWidth) + px(cs.borderBottomWidth);
2600
+ }
2601
+ return { width: w, height: h };
2602
+ }
2603
+ function matrixFromComputed(el) {
2604
+ const tr = getComputedStyle(el).transform;
2605
+ if (!tr || tr === "none") return new DOMMatrix();
2606
+ try {
2607
+ return new DOMMatrix(tr);
2608
+ } catch (e) {
2609
+ return new WebKitCSSMatrix(tr);
2610
+ }
2611
+ }
2612
+ function matrix2DNoTranslate(M) {
2613
+ return new DOMMatrix([M.a, M.b, M.c, M.d, 0, 0]);
2614
+ }
2615
+ function bboxFromMatrix(w, h, M2D) {
2616
+ const pts = [
2617
+ { x: 0, y: 0 },
2618
+ { x: w, y: 0 },
2619
+ { x: 0, y: h },
2620
+ { x: w, y: h }
2621
+ ];
2622
+ const t = pts.map((p) => ({
2623
+ x: M2D.a * p.x + M2D.c * p.y,
2624
+ y: M2D.b * p.x + M2D.d * p.y
2625
+ }));
2626
+ const minX = Math.min(...t.map((p) => p.x));
2627
+ const maxX = Math.max(...t.map((p) => p.x));
2628
+ const minY = Math.min(...t.map((p) => p.y));
2629
+ const maxY = Math.max(...t.map((p) => p.y));
2630
+ return { minX, minY, width: maxX - minX, height: maxY - minY };
2631
+ }
2632
+ function readIndividualTransforms(el) {
2633
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
2634
+ const out = { rotate: "0deg", scale: null, translate: null };
2635
+ const map = (_a = el.computedStyleMap) == null ? void 0 : _a.call(el);
2636
+ if (map) {
2637
+ const rot = map.get("rotate");
2638
+ if (rot) {
2639
+ const ang = (_b = rot.angle) != null ? _b : rot;
2640
+ if (ang && "unit" in ang) {
2641
+ out.rotate = ang.unit === "rad" ? ang.value * 180 / Math.PI + "deg" : ang.value + ang.unit;
2642
+ } else {
2643
+ out.rotate = String(rot);
2644
+ }
2645
+ }
2646
+ const sc = map.get("scale");
2647
+ if (sc && sc.length) {
2648
+ const sx = (_d = (_c = sc[0]) == null ? void 0 : _c.value) != null ? _d : 1;
2649
+ const sy = (_f = (_e = sc[1]) == null ? void 0 : _e.value) != null ? _f : sx;
2650
+ out.scale = `${sx} ${sy}`;
2651
+ }
2652
+ const tr = map.get("translate");
2653
+ if (tr && tr.length) {
2654
+ const tx = (_h = (_g = tr[0]) == null ? void 0 : _g.value) != null ? _h : 0;
2655
+ const ty = (_j = (_i = tr[1]) == null ? void 0 : _i.value) != null ? _j : 0;
2656
+ const ux = (_l = (_k = tr[0]) == null ? void 0 : _k.unit) != null ? _l : "px";
2657
+ const uy = (_n = (_m = tr[1]) == null ? void 0 : _m.unit) != null ? _n : "px";
2658
+ out.translate = `${tx}${ux} ${ty}${uy}`;
2659
+ }
2660
+ return out;
2661
+ }
2662
+ const cs = getComputedStyle(el);
2663
+ out.rotate = cs.rotate && cs.rotate !== "none" ? cs.rotate : "0deg";
2664
+ out.scale = cs.scale && cs.scale !== "none" ? cs.scale : null;
2665
+ out.translate = cs.translate && cs.translate !== "none" ? cs.translate : null;
2666
+ return out;
2667
+ }
2668
+ function hasBBoxAffectingTransform(el) {
2669
+ const t = getComputedStyle(el).transform || "none";
2670
+ if (t === "none" || t === "matrix(1, 0, 0, 1, 0, 0)") return false;
2671
+ if (/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,/i.test(t)) return false;
2672
+ return true;
2673
+ }
2674
+ function inflateRect(r, p) {
2675
+ const x = Math.floor(r.left - p.left);
2676
+ const y = Math.floor(r.top - p.top);
2677
+ const w = Math.ceil(r.width + p.left + p.right);
2678
+ const h = Math.ceil(r.height + p.top + p.bottom);
2679
+ return { x, y, width: w, height: h };
2680
+ }
2681
+ var __measureHost = null;
2682
+ function getMeasureHost() {
2683
+ if (__measureHost) return __measureHost;
2684
+ const n = document.createElement("div");
2685
+ n.id = "snapdom-measure-slot";
2686
+ n.setAttribute("aria-hidden", "true");
2687
+ Object.assign(n.style, {
2688
+ position: "absolute",
2689
+ left: "-99999px",
2690
+ top: "0px",
2691
+ width: "0px",
2692
+ height: "0px",
2693
+ overflow: "hidden",
2694
+ opacity: "0",
2695
+ pointerEvents: "none",
2696
+ contain: "size layout style"
2697
+ });
2698
+ document.documentElement.appendChild(n);
2699
+ __measureHost = n;
2700
+ return n;
2701
+ }
2702
+ function readTotalTransformMatrix(t) {
2703
+ const host = getMeasureHost();
2704
+ const tmp = document.createElement("div");
2705
+ tmp.style.transformOrigin = "0 0";
2706
+ if (t.baseTransform) tmp.style.transform = t.baseTransform;
2707
+ if (t.rotate) tmp.style.rotate = t.rotate;
2708
+ if (t.scale) tmp.style.scale = t.scale;
2709
+ if (t.translate) tmp.style.translate = t.translate;
2710
+ host.appendChild(tmp);
2711
+ const M = matrixFromComputed(tmp);
2712
+ host.removeChild(tmp);
2713
+ return M;
2714
+ }
2341
2715
 
2342
2716
  // src/core/context.js
2343
2717
  function normalizeCachePolicy(v) {
@@ -2353,7 +2727,7 @@
2353
2727
  return "soft";
2354
2728
  }
2355
2729
  function createContext(options = {}) {
2356
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
2730
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
2357
2731
  const resolvedFormat = (_a = options.format) != null ? _a : "png";
2358
2732
  const cachePolicy = normalizeCachePolicy(options.cache);
2359
2733
  return {
@@ -2371,20 +2745,21 @@
2371
2745
  iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
2372
2746
  localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
2373
2747
  excludeFonts: (_h = options.excludeFonts) != null ? _h : void 0,
2748
+ defaultImageUrl: (_i = options.defaultImageUrl) != null ? _i : void 0,
2374
2749
  /** Cache policy (disabled|full|auto|soft). Default: "soft" */
2375
2750
  /** @type {CachePolicy} */
2376
2751
  cache: cachePolicy,
2377
2752
  // Red
2378
2753
  useProxy: typeof options.useProxy === "string" ? options.useProxy : "",
2379
2754
  // Salida
2380
- width: (_i = options.width) != null ? _i : null,
2381
- height: (_j = options.height) != null ? _j : null,
2755
+ width: (_j = options.width) != null ? _j : null,
2756
+ height: (_k = options.height) != null ? _k : null,
2382
2757
  format: resolvedFormat,
2383
- type: (_k = options.type) != null ? _k : "svg",
2384
- quality: (_l = options.quality) != null ? _l : 0.92,
2385
- dpr: (_m = options.dpr) != null ? _m : window.devicePixelRatio || 1,
2386
- backgroundColor: (_n = options.backgroundColor) != null ? _n : ["jpg", "jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null,
2387
- filename: (_o = options.filename) != null ? _o : "snapDOM"
2758
+ type: (_l = options.type) != null ? _l : "svg",
2759
+ quality: (_m = options.quality) != null ? _m : 0.92,
2760
+ dpr: (_n = options.dpr) != null ? _n : window.devicePixelRatio || 1,
2761
+ backgroundColor: (_o = options.backgroundColor) != null ? _o : ["jpg", "jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null,
2762
+ filename: (_p = options.filename) != null ? _p : "snapDOM"
2388
2763
  // Plugins (reservado para futura activación)
2389
2764
  // plugins: normalizePlugins(...),
2390
2765
  };
@@ -2407,7 +2782,7 @@
2407
2782
  // src/exporters/toCanvas.js
2408
2783
  function toCanvas(url, options) {
2409
2784
  return __async(this, null, function* () {
2410
- const { scale: scale2, dpr } = options;
2785
+ const { scale, dpr } = options;
2411
2786
  const safari = isSafari();
2412
2787
  const img = new Image();
2413
2788
  img.crossOrigin = "anonymous";
@@ -2426,8 +2801,8 @@
2426
2801
  if (safari) {
2427
2802
  yield new Promise((r) => setTimeout(r, 100));
2428
2803
  }
2429
- const width = img.naturalWidth * scale2;
2430
- const height = img.naturalHeight * scale2;
2804
+ const width = img.naturalWidth * scale;
2805
+ const height = img.naturalHeight * scale;
2431
2806
  const canvas = document.createElement("canvas");
2432
2807
  canvas.width = Math.ceil(width * dpr);
2433
2808
  canvas.height = Math.ceil(height * dpr);
@@ -2509,6 +2884,14 @@
2509
2884
  if (!element) throw new Error("Element cannot be null or undefined");
2510
2885
  const context = createContext(userOptions);
2511
2886
  if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
2887
+ if (!context.snap) {
2888
+ context.snap = {
2889
+ /** Delegates to this module’s PNG path (tu build devuelve <img>). */
2890
+ toPng: (el, opts) => snapdom.toPng(el, opts),
2891
+ /** Útil si en algún flujo necesitás vector o <img> directo. */
2892
+ toImg: (el, opts) => snapdom.toImg(el, opts)
2893
+ };
2894
+ }
2512
2895
  return snapdom.capture(element, context, INTERNAL_TOKEN);
2513
2896
  });
2514
2897
  }