@zumer/snapdom 2.0.0-dev.2 → 2.0.0-dev.3

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.2.0.0-dev.2
4
+ * v.2.0.0-dev.3
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -972,7 +972,7 @@
972
972
  }
973
973
  }
974
974
 
975
- // src/core/clone.js
975
+ // src/utils/clone.helpers.js
976
976
  function idleCallback(childList, callback, fast) {
977
977
  return Promise.all(childList.map((child) => {
978
978
  return new Promise((resolve) => {
@@ -1189,6 +1189,143 @@
1189
1189
  return wrapper;
1190
1190
  });
1191
1191
  }
1192
+ var _blobToDataUrlCache = /* @__PURE__ */ new Map();
1193
+ function blobUrlToDataUrl(blobUrl) {
1194
+ return __async(this, null, function* () {
1195
+ var _a;
1196
+ if ((_a = cache.resource) == null ? void 0 : _a.has(blobUrl)) return cache.resource.get(blobUrl);
1197
+ if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl);
1198
+ const p = (() => __async(null, null, function* () {
1199
+ var _a2;
1200
+ const r = yield snapFetch(blobUrl, { as: "dataURL", silent: true });
1201
+ if (!r.ok || typeof r.data !== "string") {
1202
+ throw new Error(`[snapDOM] Failed to read blob URL: ${blobUrl}`);
1203
+ }
1204
+ (_a2 = cache.resource) == null ? void 0 : _a2.set(blobUrl, r.data);
1205
+ return r.data;
1206
+ }))();
1207
+ _blobToDataUrlCache.set(blobUrl, p);
1208
+ try {
1209
+ const data = yield p;
1210
+ _blobToDataUrlCache.set(blobUrl, data);
1211
+ return data;
1212
+ } catch (e) {
1213
+ _blobToDataUrlCache.delete(blobUrl);
1214
+ throw e;
1215
+ }
1216
+ });
1217
+ }
1218
+ var BLOB_URL_RE = /\bblob:[^)"'\s]+/g;
1219
+ function replaceBlobUrlsInCssText(cssText) {
1220
+ return __async(this, null, function* () {
1221
+ if (!cssText || cssText.indexOf("blob:") === -1) return cssText;
1222
+ const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || []));
1223
+ if (uniques.length === 0) return cssText;
1224
+ let out = cssText;
1225
+ for (const u of uniques) {
1226
+ try {
1227
+ const d = yield blobUrlToDataUrl(u);
1228
+ out = out.split(u).join(d);
1229
+ } catch (e) {
1230
+ }
1231
+ }
1232
+ return out;
1233
+ });
1234
+ }
1235
+ function isBlobUrl(u) {
1236
+ return typeof u === "string" && u.startsWith("blob:");
1237
+ }
1238
+ function parseSrcset(srcset) {
1239
+ return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => {
1240
+ const m = item.match(/^(\S+)(\s+.+)?$/);
1241
+ return m ? { url: m[1], desc: m[2] || "" } : null;
1242
+ }).filter(Boolean);
1243
+ }
1244
+ function stringifySrcset(parts) {
1245
+ return parts.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", ");
1246
+ }
1247
+ function resolveBlobUrlsInTree(root) {
1248
+ return __async(this, null, function* () {
1249
+ var _a, _b;
1250
+ if (!root) return;
1251
+ const imgs = root.querySelectorAll ? root.querySelectorAll("img") : [];
1252
+ for (const img of imgs) {
1253
+ try {
1254
+ const srcAttr = img.getAttribute("src");
1255
+ const effective = srcAttr || img.currentSrc || "";
1256
+ if (isBlobUrl(effective)) {
1257
+ const data = yield blobUrlToDataUrl(effective);
1258
+ img.setAttribute("src", data);
1259
+ }
1260
+ const srcset = img.getAttribute("srcset");
1261
+ if (srcset && srcset.includes("blob:")) {
1262
+ const parts = parseSrcset(srcset);
1263
+ let changed = false;
1264
+ for (const p of parts) {
1265
+ if (isBlobUrl(p.url)) {
1266
+ try {
1267
+ p.url = yield blobUrlToDataUrl(p.url);
1268
+ changed = true;
1269
+ } catch (e) {
1270
+ }
1271
+ }
1272
+ }
1273
+ if (changed) img.setAttribute("srcset", stringifySrcset(parts));
1274
+ }
1275
+ } catch (e) {
1276
+ }
1277
+ }
1278
+ const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : [];
1279
+ for (const node of svgImages) {
1280
+ try {
1281
+ const XLINK_NS = "http://www.w3.org/1999/xlink";
1282
+ const href = node.getAttribute("href") || ((_a = node.getAttributeNS) == null ? void 0 : _a.call(node, XLINK_NS, "href"));
1283
+ if (isBlobUrl(href)) {
1284
+ const d = yield blobUrlToDataUrl(href);
1285
+ node.setAttribute("href", d);
1286
+ (_b = node.removeAttributeNS) == null ? void 0 : _b.call(node, XLINK_NS, "href");
1287
+ }
1288
+ } catch (e) {
1289
+ }
1290
+ }
1291
+ const styled = root.querySelectorAll ? root.querySelectorAll("[style*='blob:']") : [];
1292
+ for (const el of styled) {
1293
+ try {
1294
+ const styleText = el.getAttribute("style");
1295
+ if (styleText && styleText.includes("blob:")) {
1296
+ const replaced = yield replaceBlobUrlsInCssText(styleText);
1297
+ el.setAttribute("style", replaced);
1298
+ }
1299
+ } catch (e) {
1300
+ }
1301
+ }
1302
+ const styleTags = root.querySelectorAll ? root.querySelectorAll("style") : [];
1303
+ for (const s of styleTags) {
1304
+ try {
1305
+ const css = s.textContent || "";
1306
+ if (css.includes("blob:")) {
1307
+ s.textContent = yield replaceBlobUrlsInCssText(css);
1308
+ }
1309
+ } catch (e) {
1310
+ }
1311
+ }
1312
+ const urlAttrs = ["poster"];
1313
+ for (const attr of urlAttrs) {
1314
+ const nodes = root.querySelectorAll ? root.querySelectorAll(`[${attr}^='blob:']`) : [];
1315
+ for (const n of nodes) {
1316
+ try {
1317
+ const u = n.getAttribute(attr);
1318
+ if (isBlobUrl(u)) {
1319
+ n.setAttribute(attr, yield blobUrlToDataUrl(u));
1320
+ }
1321
+ } catch (e) {
1322
+ }
1323
+ }
1324
+ }
1325
+ });
1326
+ }
1327
+
1328
+ // src/core/clone.js
1192
1329
  function deepClone(node, sessionCache, options) {
1193
1330
  return __async(this, null, function* () {
1194
1331
  var _a, _b, _c, _d, _e, _f;
@@ -2691,6 +2828,152 @@ ${imported}
2691
2828
  }
2692
2829
 
2693
2830
  // src/modules/pseudo.js
2831
+ var __preflightMemo = /* @__PURE__ */ new WeakMap();
2832
+ var CSS_RULE_SCAN_BUDGET = 300;
2833
+ function preflightWithFp(doc, sessionCache) {
2834
+ const fp = styleFingerprint(doc);
2835
+ if (!sessionCache) return shouldProcessPseudos(doc);
2836
+ if (sessionCache.__pseudoPreflightFp !== fp) {
2837
+ sessionCache.__pseudoPreflight = shouldProcessPseudos(doc);
2838
+ sessionCache.__pseudoPreflightFp = fp;
2839
+ }
2840
+ return !!sessionCache.__pseudoPreflight;
2841
+ }
2842
+ function safeRules(sheet) {
2843
+ try {
2844
+ return sheet && sheet.cssRules ? sheet.cssRules : null;
2845
+ } catch (e) {
2846
+ return null;
2847
+ }
2848
+ }
2849
+ function styleFingerprint(doc) {
2850
+ const nodes = doc.querySelectorAll('style,link[rel~="stylesheet"]');
2851
+ let fp = `n:${nodes.length}|`;
2852
+ let totalRules = 0;
2853
+ for (let i = 0; i < nodes.length; i++) {
2854
+ const n = nodes[i];
2855
+ if (n.tagName === "STYLE") {
2856
+ const len = n.textContent ? n.textContent.length : 0;
2857
+ fp += `S${len}|`;
2858
+ const sheet = (
2859
+ /** @type {HTMLStyleElement} */
2860
+ n.sheet
2861
+ );
2862
+ const rules = sheet ? safeRules(sheet) : null;
2863
+ if (rules) totalRules += rules.length;
2864
+ } else {
2865
+ const href = n.getAttribute("href") || "";
2866
+ const media = n.getAttribute("media") || "all";
2867
+ fp += `L${href}|m:${media}|`;
2868
+ const sheet = (
2869
+ /** @type {HTMLLinkElement} */
2870
+ n.sheet
2871
+ );
2872
+ const rules = sheet ? safeRules(sheet) : null;
2873
+ if (rules) totalRules += rules.length;
2874
+ }
2875
+ }
2876
+ const ass = (
2877
+ /** @type {any} */
2878
+ doc.adoptedStyleSheets
2879
+ );
2880
+ fp += `ass:${Array.isArray(ass) ? ass.length : 0}|tr:${totalRules}`;
2881
+ return fp;
2882
+ }
2883
+ function sheetHasNeedles(sheet, needles, state) {
2884
+ const rules = safeRules(sheet);
2885
+ if (!rules) return false;
2886
+ for (let i = 0; i < rules.length; i++) {
2887
+ if (state.budget <= 0) return false;
2888
+ const rule = rules[i];
2889
+ const css = rule && rule.cssText ? rule.cssText : "";
2890
+ state.budget--;
2891
+ for (const k of needles) {
2892
+ if (css.includes(k)) return true;
2893
+ }
2894
+ if (rule && rule.cssRules && rule.cssRules.length) {
2895
+ for (let j = 0; j < rule.cssRules.length && state.budget > 0; j++) {
2896
+ const inner = rule.cssRules[j];
2897
+ const innerCss = inner && inner.cssText ? inner.cssText : "";
2898
+ state.budget--;
2899
+ for (const k of needles) {
2900
+ if (innerCss.includes(k)) return true;
2901
+ }
2902
+ }
2903
+ }
2904
+ if (state.budget <= 0) return false;
2905
+ }
2906
+ return false;
2907
+ }
2908
+ function shouldProcessPseudos(doc = document) {
2909
+ const fp = styleFingerprint(doc);
2910
+ const memo = __preflightMemo.get(doc);
2911
+ if (memo && memo.fingerprint === fp) return memo.result;
2912
+ const NEEDLES = [
2913
+ // double-colon
2914
+ "::before",
2915
+ "::after",
2916
+ "::first-letter",
2917
+ // single-colon robustness
2918
+ ":before",
2919
+ ":after",
2920
+ ":first-letter",
2921
+ // counters
2922
+ "counter(",
2923
+ "counters(",
2924
+ "counter-increment",
2925
+ "counter-reset"
2926
+ ];
2927
+ const styleEls = doc.querySelectorAll("style");
2928
+ for (let i = 0; i < styleEls.length; i++) {
2929
+ const t = styleEls[i].textContent || "";
2930
+ for (const k of NEEDLES) if (t.includes(k)) {
2931
+ __preflightMemo.set(doc, { fingerprint: fp, result: true });
2932
+ return true;
2933
+ }
2934
+ }
2935
+ const ass = (
2936
+ /** @type {any} */
2937
+ doc.adoptedStyleSheets
2938
+ );
2939
+ if (Array.isArray(ass) && ass.length) {
2940
+ const state = { budget: CSS_RULE_SCAN_BUDGET };
2941
+ try {
2942
+ for (const sheet of ass) {
2943
+ if (sheetHasNeedles(sheet, NEEDLES, state)) {
2944
+ __preflightMemo.set(doc, { fingerprint: fp, result: true });
2945
+ return true;
2946
+ }
2947
+ }
2948
+ } catch (e) {
2949
+ }
2950
+ }
2951
+ {
2952
+ const nodes = doc.querySelectorAll('style,link[rel~="stylesheet"]');
2953
+ const state = { budget: CSS_RULE_SCAN_BUDGET };
2954
+ for (let i = 0; i < nodes.length && state.budget > 0; i++) {
2955
+ const n = nodes[i];
2956
+ let sheet = null;
2957
+ if (n.tagName === "STYLE") {
2958
+ sheet = /** @type {HTMLStyleElement} */
2959
+ n.sheet || null;
2960
+ } else {
2961
+ sheet = /** @type {HTMLLinkElement} */
2962
+ n.sheet || null;
2963
+ }
2964
+ if (sheet && sheetHasNeedles(sheet, NEEDLES, state)) {
2965
+ __preflightMemo.set(doc, { fingerprint: fp, result: true });
2966
+ return true;
2967
+ }
2968
+ }
2969
+ }
2970
+ if (doc.querySelector('[style*="counter("], [style*="counters("]')) {
2971
+ __preflightMemo.set(doc, { fingerprint: fp, result: true });
2972
+ return true;
2973
+ }
2974
+ __preflightMemo.set(doc, { fingerprint: fp, result: false });
2975
+ return false;
2976
+ }
2694
2977
  var __siblingCounters = /* @__PURE__ */ new WeakMap();
2695
2978
  var __pseudoEpoch = -1;
2696
2979
  function unquoteDoubleStrings2(s) {
@@ -2789,8 +3072,12 @@ ${imported}
2789
3072
  }
2790
3073
  function inlinePseudoElements(source, clone, sessionCache, options) {
2791
3074
  return __async(this, null, function* () {
2792
- var _a, _b, _c, _d;
3075
+ var _a, _b, _c, _d, _e;
2793
3076
  if (!(source instanceof Element) || !(clone instanceof Element)) return;
3077
+ const doc = source.ownerDocument || document;
3078
+ if (!preflightWithFp(doc, sessionCache)) {
3079
+ return;
3080
+ }
2794
3081
  const epoch = (_c = (_b = (_a = cache) == null ? void 0 : _a.session) == null ? void 0 : _b.__counterEpoch) != null ? _c : 0;
2795
3082
  if (__pseudoEpoch !== epoch) {
2796
3083
  __siblingCounters = /* @__PURE__ */ new WeakMap();
@@ -2807,7 +3094,7 @@ ${imported}
2807
3094
  for (const pseudo of ["::before", "::after", "::first-letter"]) {
2808
3095
  try {
2809
3096
  const style = getStyle(source, pseudo);
2810
- if (!style || typeof style[Symbol.iterator] !== "function") continue;
3097
+ if (!style) continue;
2811
3098
  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";
2812
3099
  if (isEmptyPseudo) continue;
2813
3100
  if (pseudo === "::first-letter") {
@@ -2837,7 +3124,8 @@ ${imported}
2837
3124
  clone.insertBefore(span, restNode);
2838
3125
  continue;
2839
3126
  }
2840
- const rawContent = style.content;
3127
+ const rawContent = (_d = style.content) != null ? _d : "";
3128
+ const isNoExplicitContent = rawContent === "" || rawContent === "none" || rawContent === "normal";
2841
3129
  const { text: cleanContent, incs } = resolvePseudoContentAndIncs(source, pseudo, counterCtx);
2842
3130
  const bg = style.backgroundImage;
2843
3131
  const bgColor = style.backgroundColor;
@@ -2849,7 +3137,7 @@ ${imported}
2849
3137
  const borderWidth = parseFloat(style.borderWidth);
2850
3138
  const transform = style.transform;
2851
3139
  const isIconFont22 = isIconFont2(fontFamily);
2852
- const hasExplicitContent = rawContent !== "none" && cleanContent !== "";
3140
+ const hasExplicitContent = !isNoExplicitContent && cleanContent !== "";
2853
3141
  const hasBg = bg && bg !== "none";
2854
3142
  const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
2855
3143
  const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
@@ -2871,8 +3159,6 @@ ${imported}
2871
3159
  }
2872
3160
  const pseudoEl = document.createElement("span");
2873
3161
  pseudoEl.dataset.snapdomPseudo = pseudo;
2874
- pseudoEl.style.display = "inline";
2875
- pseudoEl.style.verticalAlign = "baseline";
2876
3162
  pseudoEl.style.pointerEvents = "none";
2877
3163
  const snapshot = snapshotComputedStyle(style);
2878
3164
  const key = getStyleKey(snapshot, "span");
@@ -2928,7 +3214,7 @@ ${imported}
2928
3214
  }
2929
3215
  }
2930
3216
  if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
2931
- const hasContent2 = pseudoEl.childNodes.length > 0 || ((_d = pseudoEl.textContent) == null ? void 0 : _d.trim()) !== "";
3217
+ const hasContent2 = pseudoEl.childNodes.length > 0 || ((_e = pseudoEl.textContent) == null ? void 0 : _e.trim()) !== "";
2932
3218
  const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBorder || hasTransform;
2933
3219
  if (incs && incs.length && source.parentElement) {
2934
3220
  const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
@@ -3167,6 +3453,20 @@ ${imported}
3167
3453
  return out;
3168
3454
  }
3169
3455
 
3456
+ // src/utils/prepare.helpers.js
3457
+ function stabilizeLayout(element) {
3458
+ const style = getComputedStyle(element);
3459
+ const outlineStyle = style.outlineStyle;
3460
+ const outlineWidth = style.outlineWidth;
3461
+ const borderStyle = style.borderStyle;
3462
+ const borderWidth = style.borderWidth;
3463
+ const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0;
3464
+ const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0;
3465
+ if (outlineVisible && borderAbsent) {
3466
+ element.style.border = `${outlineWidth} solid transparent`;
3467
+ }
3468
+ }
3469
+
3170
3470
  // src/core/prepare.js
3171
3471
  function prepareClone(_0) {
3172
3472
  return __async(this, arguments, function* (element, options = {}) {
@@ -3270,163 +3570,27 @@ ${imported}
3270
3570
  return { clone, classCSS, styleCache: sessionCache.styleCache };
3271
3571
  });
3272
3572
  }
3273
- function stabilizeLayout(element) {
3274
- const style = getComputedStyle(element);
3275
- const outlineStyle = style.outlineStyle;
3276
- const outlineWidth = style.outlineWidth;
3277
- const borderStyle = style.borderStyle;
3278
- const borderWidth = style.borderWidth;
3279
- const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0;
3280
- const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0;
3281
- if (outlineVisible && borderAbsent) {
3282
- element.style.border = `${outlineWidth} solid transparent`;
3283
- }
3284
- }
3285
- var _blobToDataUrlCache = /* @__PURE__ */ new Map();
3286
- function blobUrlToDataUrl(blobUrl) {
3287
- return __async(this, null, function* () {
3288
- var _a;
3289
- if ((_a = cache.resource) == null ? void 0 : _a.has(blobUrl)) return cache.resource.get(blobUrl);
3290
- if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl);
3291
- const p = (() => __async(null, null, function* () {
3292
- var _a2;
3293
- const r = yield snapFetch(blobUrl, { as: "dataURL", silent: true });
3294
- if (!r.ok || typeof r.data !== "string") {
3295
- throw new Error(`[snapDOM] Failed to read blob URL: ${blobUrl}`);
3296
- }
3297
- (_a2 = cache.resource) == null ? void 0 : _a2.set(blobUrl, r.data);
3298
- return r.data;
3299
- }))();
3300
- _blobToDataUrlCache.set(blobUrl, p);
3301
- try {
3302
- const data = yield p;
3303
- _blobToDataUrlCache.set(blobUrl, data);
3304
- return data;
3305
- } catch (e) {
3306
- _blobToDataUrlCache.delete(blobUrl);
3307
- throw e;
3308
- }
3309
- });
3573
+
3574
+ // src/modules/images.js
3575
+ function extractImageDimensions(img) {
3576
+ var _a, _b, _c, _d;
3577
+ const dsW = parseInt(((_a = img.dataset) == null ? void 0 : _a.snapdomWidth) || "", 10) || 0;
3578
+ const dsH = parseInt(((_b = img.dataset) == null ? void 0 : _b.snapdomHeight) || "", 10) || 0;
3579
+ const attrW = parseInt(img.getAttribute("width") || "", 10) || 0;
3580
+ const attrH = parseInt(img.getAttribute("height") || "", 10) || 0;
3581
+ const styleW = parseFloat(((_c = img.style) == null ? void 0 : _c.width) || "") || 0;
3582
+ const styleH = parseFloat(((_d = img.style) == null ? void 0 : _d.height) || "") || 0;
3583
+ const w = dsW || styleW || attrW || img.width || img.naturalWidth || 100;
3584
+ const h = dsH || styleH || attrH || img.height || img.naturalHeight || 100;
3585
+ return { width: w, height: h };
3310
3586
  }
3311
- var BLOB_URL_RE = /\bblob:[^)"'\s]+/g;
3312
- function replaceBlobUrlsInCssText(cssText) {
3313
- return __async(this, null, function* () {
3314
- if (!cssText || cssText.indexOf("blob:") === -1) return cssText;
3315
- const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || []));
3316
- if (uniques.length === 0) return cssText;
3317
- let out = cssText;
3318
- for (const u of uniques) {
3319
- try {
3320
- const d = yield blobUrlToDataUrl(u);
3321
- out = out.split(u).join(d);
3322
- } catch (e) {
3323
- }
3324
- }
3325
- return out;
3326
- });
3327
- }
3328
- function isBlobUrl(u) {
3329
- return typeof u === "string" && u.startsWith("blob:");
3330
- }
3331
- function parseSrcset(srcset) {
3332
- return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => {
3333
- const m = item.match(/^(\S+)(\s+.+)?$/);
3334
- return m ? { url: m[1], desc: m[2] || "" } : null;
3335
- }).filter(Boolean);
3336
- }
3337
- function stringifySrcset(parts) {
3338
- return parts.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", ");
3339
- }
3340
- function resolveBlobUrlsInTree(root) {
3341
- return __async(this, null, function* () {
3342
- var _a, _b;
3343
- if (!root) return;
3344
- const imgs = root.querySelectorAll ? root.querySelectorAll("img") : [];
3345
- for (const img of imgs) {
3346
- try {
3347
- const srcAttr = img.getAttribute("src");
3348
- const effective = srcAttr || img.currentSrc || "";
3349
- if (isBlobUrl(effective)) {
3350
- const data = yield blobUrlToDataUrl(effective);
3351
- img.setAttribute("src", data);
3352
- }
3353
- const srcset = img.getAttribute("srcset");
3354
- if (srcset && srcset.includes("blob:")) {
3355
- const parts = parseSrcset(srcset);
3356
- let changed = false;
3357
- for (const p of parts) {
3358
- if (isBlobUrl(p.url)) {
3359
- try {
3360
- p.url = yield blobUrlToDataUrl(p.url);
3361
- changed = true;
3362
- } catch (e) {
3363
- }
3364
- }
3365
- }
3366
- if (changed) img.setAttribute("srcset", stringifySrcset(parts));
3367
- }
3368
- } catch (e) {
3369
- }
3370
- }
3371
- const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : [];
3372
- for (const node of svgImages) {
3373
- try {
3374
- const XLINK_NS = "http://www.w3.org/1999/xlink";
3375
- const href = node.getAttribute("href") || ((_a = node.getAttributeNS) == null ? void 0 : _a.call(node, XLINK_NS, "href"));
3376
- if (isBlobUrl(href)) {
3377
- const d = yield blobUrlToDataUrl(href);
3378
- node.setAttribute("href", d);
3379
- (_b = node.removeAttributeNS) == null ? void 0 : _b.call(node, XLINK_NS, "href");
3380
- }
3381
- } catch (e) {
3382
- }
3383
- }
3384
- const styled = root.querySelectorAll ? root.querySelectorAll("[style*='blob:']") : [];
3385
- for (const el of styled) {
3386
- try {
3387
- const styleText = el.getAttribute("style");
3388
- if (styleText && styleText.includes("blob:")) {
3389
- const replaced = yield replaceBlobUrlsInCssText(styleText);
3390
- el.setAttribute("style", replaced);
3391
- }
3392
- } catch (e) {
3393
- }
3394
- }
3395
- const styleTags = root.querySelectorAll ? root.querySelectorAll("style") : [];
3396
- for (const s of styleTags) {
3397
- try {
3398
- const css = s.textContent || "";
3399
- if (css.includes("blob:")) {
3400
- s.textContent = yield replaceBlobUrlsInCssText(css);
3401
- }
3402
- } catch (e) {
3403
- }
3404
- }
3405
- const urlAttrs = ["poster"];
3406
- for (const attr of urlAttrs) {
3407
- const nodes = root.querySelectorAll ? root.querySelectorAll(`[${attr}^='blob:']`) : [];
3408
- for (const n of nodes) {
3409
- try {
3410
- const u = n.getAttribute(attr);
3411
- if (isBlobUrl(u)) {
3412
- n.setAttribute(attr, yield blobUrlToDataUrl(u));
3413
- }
3414
- } catch (e) {
3415
- }
3416
- }
3417
- }
3418
- });
3419
- }
3420
-
3421
- // src/modules/images.js
3422
- function inlineImages(_0) {
3423
- return __async(this, arguments, function* (clone, options = {}) {
3424
- const imgs = Array.from(clone.querySelectorAll("img"));
3425
- const processImg = (img) => __async(null, null, function* () {
3426
- var _a, _b, _c, _d;
3427
- if (!img.getAttribute("src")) {
3428
- const eff = img.currentSrc || img.src || "";
3429
- if (eff) img.setAttribute("src", eff);
3587
+ function inlineImages(_0) {
3588
+ return __async(this, arguments, function* (clone, options = {}) {
3589
+ const imgs = Array.from(clone.querySelectorAll("img"));
3590
+ const processImg = (img) => __async(null, null, function* () {
3591
+ if (!img.getAttribute("src")) {
3592
+ const eff = img.currentSrc || img.src || "";
3593
+ if (eff) img.setAttribute("src", eff);
3430
3594
  }
3431
3595
  img.removeAttribute("srcset");
3432
3596
  img.removeAttribute("sizes");
@@ -3439,41 +3603,30 @@ ${imported}
3439
3603
  if (!img.height) img.height = img.naturalHeight || 100;
3440
3604
  return;
3441
3605
  }
3606
+ const { width: fbW, height: fbH } = extractImageDimensions(img);
3442
3607
  const { fallbackURL } = options || {};
3443
3608
  if (fallbackURL) {
3444
3609
  try {
3445
- const dsW = parseInt(((_a = img.dataset) == null ? void 0 : _a.snapdomWidth) || "", 10) || 0;
3446
- const dsH = parseInt(((_b = img.dataset) == null ? void 0 : _b.snapdomHeight) || "", 10) || 0;
3447
- const attrW = parseInt(img.getAttribute("width") || "", 10) || 0;
3448
- const attrH = parseInt(img.getAttribute("height") || "", 10) || 0;
3449
- const styleW = parseFloat(((_c = img.style) == null ? void 0 : _c.width) || "") || 0;
3450
- const styleH = parseFloat(((_d = img.style) == null ? void 0 : _d.height) || "") || 0;
3451
- const fbW = dsW || styleW || attrW || img.width || void 0;
3452
- const fbH = dsH || styleH || attrH || img.height || void 0;
3453
3610
  const fallbackUrl = typeof fallbackURL === "function" ? yield fallbackURL({ width: fbW, height: fbH, src, element: img }) : fallbackURL;
3454
3611
  if (fallbackUrl) {
3455
3612
  const fallbackData = yield snapFetch(fallbackUrl, { as: "dataURL", useProxy: options.useProxy });
3456
3613
  img.src = fallbackData.data;
3457
- if (!img.width && fbW) img.width = fbW;
3458
- if (!img.height && fbH) img.height = fbH;
3459
- if (!img.width) img.width = img.naturalWidth || 100;
3460
- if (!img.height) img.height = img.naturalHeight || 100;
3614
+ if (!img.width) img.width = fbW;
3615
+ if (!img.height) img.height = fbH;
3461
3616
  return;
3462
3617
  }
3463
3618
  } catch (e) {
3464
3619
  }
3465
3620
  }
3466
- const w = img.width || img.naturalWidth || 100;
3467
- const h = img.height || img.naturalHeight || 100;
3468
3621
  if (options.placeholders !== false) {
3469
3622
  const fallback = document.createElement("div");
3470
3623
  fallback.style.cssText = [
3471
- `width:${w}px`,
3472
- `height:${h}px`,
3624
+ `width:${fbW}px`,
3625
+ `height:${fbH}px`,
3473
3626
  "background:#ccc",
3474
3627
  "display:inline-block",
3475
3628
  "text-align:center",
3476
- `line-height:${h}px`,
3629
+ `line-height:${fbH}px`,
3477
3630
  "color:#666",
3478
3631
  "font-size:12px",
3479
3632
  "overflow:hidden"
@@ -3482,7 +3635,7 @@ ${imported}
3482
3635
  img.replaceWith(fallback);
3483
3636
  } else {
3484
3637
  const spacer = document.createElement("div");
3485
- spacer.style.cssText = `display:inline-block;width:${w}px;height:${h}px;visibility:hidden;`;
3638
+ spacer.style.cssText = `display:inline-block;width:${fbW}px;height:${fbH}px;visibility:hidden;`;
3486
3639
  img.replaceWith(spacer);
3487
3640
  }
3488
3641
  });
@@ -3730,7 +3883,7 @@ ${imported}
3730
3883
  return context;
3731
3884
  }
3732
3885
 
3733
- // src/core/capture.js
3886
+ // src/utils/capture.helpers.js
3734
3887
  function stripRootShadows(originalEl, cloneRoot) {
3735
3888
  if (!originalEl || !cloneRoot || !cloneRoot.style) return;
3736
3889
  const cs = getComputedStyle(originalEl);
@@ -3797,288 +3950,136 @@ ${imported}
3797
3950
  sanitizeAttributesForXHTML(root, opts);
3798
3951
  removeAllComments(root);
3799
3952
  }
3800
- function captureDOM(element, options) {
3801
- return __async(this, null, function* () {
3802
- var _a;
3803
- if (!element) throw new Error("Element cannot be null or undefined");
3804
- applyCachePolicy(options.cache);
3805
- const fast = options.fast;
3806
- const outerTransforms = options.outerTransforms !== false;
3807
- const outerShadows = !!options.outerShadows;
3808
- let state = { element, options, plugins: options.plugins };
3809
- let clone, classCSS, styleCache;
3810
- let fontsCSS = "";
3811
- let baseCSS = "";
3812
- let dataURL;
3813
- let svgString;
3814
- let rootTransform2D = null;
3815
- yield runHook("beforeSnap", state);
3816
- yield runHook("beforeClone", state);
3817
- const undoClamp = lineClamp(state.element);
3818
- try {
3819
- ({ clone, classCSS, styleCache } = yield prepareClone(state.element, state.options));
3820
- if (!outerTransforms && clone) {
3821
- rootTransform2D = normalizeRootTransforms(state.element, clone);
3822
- }
3823
- if (!outerShadows && clone) {
3824
- stripRootShadows(state.element, clone);
3953
+ function authorHasExplicitSize(el) {
3954
+ var _a;
3955
+ try {
3956
+ const s = ((_a = el.getAttribute) == null ? void 0 : _a.call(el, "style")) || "";
3957
+ return /\b(height|width|block-size|inline-size)\s*:/.test(s);
3958
+ } catch (e) {
3959
+ return false;
3960
+ }
3961
+ }
3962
+ function isReplacedElement(el) {
3963
+ return el instanceof HTMLImageElement || el instanceof HTMLCanvasElement || el instanceof HTMLVideoElement || el instanceof HTMLIFrameElement || el instanceof SVGElement || el instanceof HTMLObjectElement || el instanceof HTMLEmbedElement;
3964
+ }
3965
+ function shouldShrinkBox(srcEl, cs) {
3966
+ if (!(srcEl instanceof Element)) return false;
3967
+ if (authorHasExplicitSize(srcEl)) return false;
3968
+ if (isReplacedElement(srcEl)) return false;
3969
+ const pos = cs.position;
3970
+ if (pos === "absolute" || pos === "fixed" || pos === "sticky") return false;
3971
+ const disp = cs.display || "";
3972
+ if (disp.includes("flex") || disp.includes("grid") || disp.startsWith("table")) return false;
3973
+ if (cs.transform && cs.transform !== "none") return false;
3974
+ return true;
3975
+ }
3976
+ function shrinkAutoSizeBoxes(sourceRoot, cloneRoot, styleCache = /* @__PURE__ */ new Map()) {
3977
+ function walk(src, cln) {
3978
+ if (!(src instanceof Element) || !(cln instanceof Element)) return;
3979
+ const lostKids = src.childElementCount > cln.childElementCount;
3980
+ const cs = styleCache.get(src) || getComputedStyle(src);
3981
+ if (!styleCache.has(src)) styleCache.set(src, cs);
3982
+ if (lostKids && shouldShrinkBox(src, cs)) {
3983
+ if (!cln.style.height) cln.style.height = "auto";
3984
+ if (!cln.style.width) cln.style.width = "auto";
3985
+ cln.style.removeProperty("block-size");
3986
+ cln.style.removeProperty("inline-size");
3987
+ if (!cln.style.minHeight) cln.style.minHeight = "0";
3988
+ if (!cln.style.minWidth) cln.style.minWidth = "0";
3989
+ if (!cln.style.maxHeight) cln.style.maxHeight = "none";
3990
+ if (!cln.style.maxWidth) cln.style.maxWidth = "none";
3991
+ const oy = cs.overflowY || cs.overflowBlock || "visible";
3992
+ const ox = cs.overflowX || cs.overflowInline || "visible";
3993
+ if (oy !== "visible" || ox !== "visible") {
3994
+ cln.style.overflow = "visible";
3825
3995
  }
3826
- } finally {
3827
- undoClamp();
3828
3996
  }
3829
- state = __spreadValues({ clone, classCSS, styleCache }, state);
3830
- yield runHook("afterClone", state);
3831
- sanitizeCloneForXHTML(state.clone);
3832
- if (((_a = state.options) == null ? void 0 : _a.excludeMode) === "remove") {
3997
+ const sKids = Array.from(src.children);
3998
+ const cKids = Array.from(cln.children);
3999
+ for (let i = 0; i < Math.min(sKids.length, cKids.length); i++) {
4000
+ walk(sKids[i], cKids[i]);
4001
+ }
4002
+ }
4003
+ walk(sourceRoot, cloneRoot);
4004
+ }
4005
+ function isInNormalFlow(el) {
4006
+ const cs = getComputedStyle(el);
4007
+ if (cs.display === "none") return false;
4008
+ if (cs.position === "absolute" || cs.position === "fixed" || cs.position === "sticky") return false;
4009
+ if ((cs.cssFloat || cs.float || "none") !== "none") return false;
4010
+ if (cs.transform && cs.transform !== "none") return false;
4011
+ return true;
4012
+ }
4013
+ function willBeExcluded(el, options) {
4014
+ if (!(el instanceof Element)) return false;
4015
+ if (el.getAttribute("data-capture") === "exclude" && (options == null ? void 0 : options.excludeMode) === "remove") return true;
4016
+ if (Array.isArray(options == null ? void 0 : options.exclude)) {
4017
+ for (const sel of options.exclude) {
3833
4018
  try {
3834
- shrinkAutoSizeBoxes(state.element, state.clone, state.styleCache);
4019
+ if (el.matches(sel)) return options.excludeMode === "remove";
3835
4020
  } catch (e) {
3836
- console.warn("[snapdom] shrink pass failed:", e);
3837
4021
  }
3838
4022
  }
3839
- try {
3840
- yield ligatureIconToImage(state.clone, state.element);
3841
- } catch (e) {
3842
- }
3843
- yield new Promise((resolve) => {
3844
- idle(() => __async(null, null, function* () {
3845
- yield inlineImages(state.clone, state.options);
3846
- resolve();
3847
- }), { fast });
3848
- });
3849
- yield new Promise((resolve) => {
3850
- idle(() => __async(null, null, function* () {
3851
- yield inlineBackgroundImages(state.element, state.clone, state.styleCache, state.options);
3852
- resolve();
3853
- }), { fast });
3854
- });
3855
- if (options.embedFonts) {
3856
- yield new Promise((resolve) => {
3857
- idle(() => __async(null, null, function* () {
3858
- const required = collectUsedFontVariants(state.element);
3859
- const usedCodepoints = collectUsedCodepoints(state.element);
3860
- if (isSafari()) {
3861
- const families = new Set(
3862
- Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
3863
- );
3864
- yield ensureFontsReady(families, 1);
3865
- }
3866
- fontsCSS = yield embedCustomFonts({
3867
- required,
3868
- usedCodepoints,
3869
- preCached: false,
3870
- exclude: state.options.excludeFonts,
3871
- useProxy: state.options.useProxy
3872
- });
3873
- resolve();
3874
- }), { fast });
3875
- });
3876
- }
3877
- const usedTags = collectUsedTagNames(state.clone).sort();
3878
- const tagKey = usedTags.join(",");
3879
- if (cache.baseStyle.has(tagKey)) {
3880
- baseCSS = cache.baseStyle.get(tagKey);
3881
- } else {
3882
- yield new Promise((resolve) => {
3883
- idle(() => {
3884
- baseCSS = generateDedupedBaseCSS(usedTags);
3885
- cache.baseStyle.set(tagKey, baseCSS);
3886
- resolve();
3887
- }, { fast });
3888
- });
3889
- }
3890
- state = __spreadValues({ fontsCSS, baseCSS }, state);
3891
- yield runHook("beforeRender", state);
3892
- yield new Promise((resolve) => {
3893
- idle(() => {
3894
- var _a2;
3895
- const csEl = getComputedStyle(state.element);
3896
- const rect = state.element.getBoundingClientRect();
3897
- let w0 = Math.max(1, limitDecimals(state.element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
3898
- let h0 = Math.max(1, limitDecimals(state.element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
3899
- if (((_a2 = state.options) == null ? void 0 : _a2.excludeMode) === "remove") {
3900
- const hEst = estimateKeptHeight(state.element, state.options);
3901
- const EPS = 1;
3902
- if (Number.isFinite(hEst) && hEst > 0) {
3903
- h0 = Math.max(1, Math.min(h0, limitDecimals(hEst + EPS)));
3904
- }
3905
- }
3906
- const coerceNum = (v, def = NaN) => {
3907
- const n = typeof v === "string" ? parseFloat(v) : v;
3908
- return Number.isFinite(n) ? n : def;
3909
- };
3910
- const optW = coerceNum(state.options.width);
3911
- const optH = coerceNum(state.options.height);
3912
- let w = w0, h = h0;
3913
- const hasW = Number.isFinite(optW);
3914
- const hasH = Number.isFinite(optH);
3915
- const aspect0 = h0 > 0 ? w0 / h0 : 1;
3916
- if (hasW && hasH) {
3917
- w = Math.max(1, limitDecimals(optW));
3918
- h = Math.max(1, limitDecimals(optH));
3919
- } else if (hasW) {
3920
- w = Math.max(1, limitDecimals(optW));
3921
- h = Math.max(1, limitDecimals(w / (aspect0 || 1)));
3922
- } else if (hasH) {
3923
- h = Math.max(1, limitDecimals(optH));
3924
- w = Math.max(1, limitDecimals(h * (aspect0 || 1)));
3925
- } else {
3926
- w = w0;
3927
- h = h0;
3928
- }
3929
- let minX = 0, minY = 0, maxX = w0, maxY = h0;
3930
- if (!outerTransforms && rootTransform2D && Number.isFinite(rootTransform2D.a)) {
3931
- const M2 = {
3932
- a: rootTransform2D.a,
3933
- b: rootTransform2D.b || 0,
3934
- c: rootTransform2D.c || 0,
3935
- d: rootTransform2D.d || 1,
3936
- e: 0,
3937
- f: 0
3938
- };
3939
- const bb2 = bboxWithOriginFull(w0, h0, M2, 0, 0);
3940
- minX = limitDecimals(bb2.minX);
3941
- minY = limitDecimals(bb2.minY);
3942
- maxX = limitDecimals(bb2.maxX);
3943
- maxY = limitDecimals(bb2.maxY);
3944
- } else {
3945
- const useTFBBox = outerTransforms && hasTFBBox(state.element);
3946
- if (useTFBBox) {
3947
- const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
3948
- const ind2 = readIndividualTransforms(state.element);
3949
- const TOTAL = readTotalTransformMatrix({
3950
- baseTransform: baseTransform2,
3951
- rotate: ind2.rotate || "0deg",
3952
- scale: ind2.scale,
3953
- translate: ind2.translate
3954
- });
3955
- const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
3956
- const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
3957
- const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
3958
- minX = limitDecimals(bb.minX);
3959
- minY = limitDecimals(bb.minY);
3960
- maxX = limitDecimals(bb.maxX);
3961
- maxY = limitDecimals(bb.maxY);
3962
- }
3963
- }
3964
- const bleedShadow = parseBoxShadow(csEl);
3965
- const bleedBlur = parseFilterBlur(csEl);
3966
- const bleedOutline = parseOutline(csEl);
3967
- const drop = parseFilterDropShadows(csEl);
3968
- const bleed = !outerShadows ? { top: 0, right: 0, bottom: 0, left: 0 } : {
3969
- top: limitDecimals(bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top),
3970
- right: limitDecimals(bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right),
3971
- bottom: limitDecimals(bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom),
3972
- left: limitDecimals(bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left)
3973
- };
3974
- minX = limitDecimals(minX - bleed.left);
3975
- minY = limitDecimals(minY - bleed.top);
3976
- maxX = limitDecimals(maxX + bleed.right);
3977
- maxY = limitDecimals(maxY + bleed.bottom);
3978
- const vbW0 = Math.max(1, limitDecimals(maxX - minX));
3979
- const vbH0 = Math.max(1, limitDecimals(maxY - minY));
3980
- const scaleW = hasW || hasH ? limitDecimals(w / w0) : 1;
3981
- const scaleH = hasH || hasW ? limitDecimals(h / h0) : 1;
3982
- const outW = Math.max(1, limitDecimals(vbW0 * scaleW));
3983
- const outH = Math.max(1, limitDecimals(vbH0 * scaleH));
3984
- const svgNS = "http://www.w3.org/2000/svg";
3985
- const basePad = isSafari() ? 1 : 0;
3986
- const extraPad = !outerTransforms ? 1 : 0;
3987
- const pad = limitDecimals(basePad + extraPad);
3988
- const fo = document.createElementNS(svgNS, "foreignObject");
3989
- const vbMinX = limitDecimals(minX);
3990
- const vbMinY = limitDecimals(minY);
3991
- fo.setAttribute("x", String(limitDecimals(-(vbMinX - pad))));
3992
- fo.setAttribute("y", String(limitDecimals(-(vbMinY - pad))));
3993
- fo.setAttribute("width", String(limitDecimals(w0 + pad * 2)));
3994
- fo.setAttribute("height", String(limitDecimals(h0 + pad * 2)));
3995
- fo.style.overflow = "visible";
3996
- const styleTag = document.createElement("style");
3997
- styleTag.textContent = state.baseCSS + state.fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + state.classCSS;
3998
- fo.appendChild(styleTag);
3999
- const container = document.createElement("div");
4000
- container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
4001
- container.style.width = `${limitDecimals(w0)}px`;
4002
- container.style.height = `${limitDecimals(h0)}px`;
4003
- container.style.overflow = "visible";
4004
- state.clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
4005
- container.appendChild(state.clone);
4006
- fo.appendChild(container);
4007
- const serializer = new XMLSerializer();
4008
- const foString = serializer.serializeToString(fo);
4009
- const vbW = limitDecimals(vbW0 + pad * 2);
4010
- const vbH = limitDecimals(vbH0 + pad * 2);
4011
- const wantsSize = hasW || hasH;
4012
- options.meta = { w0, h0, vbW, vbH, targetW: w, targetH: h };
4013
- const svgOutW = isSafari() && wantsSize ? vbW : limitDecimals(outW + pad * 2);
4014
- const svgOutH = isSafari() && wantsSize ? vbH : limitDecimals(outH + pad * 2);
4015
- const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
4016
- const svgFooter = "</svg>";
4017
- svgString = svgHeader + foString + svgFooter;
4018
- dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
4019
- state = __spreadValues({ svgString, dataURL }, state);
4020
- resolve();
4021
- }, { fast });
4022
- });
4023
- yield runHook("afterRender", state);
4024
- const sandbox = document.getElementById("snapdom-sandbox");
4025
- if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
4026
- return state.dataURL;
4027
- });
4023
+ }
4024
+ return false;
4028
4025
  }
4029
- function authorHasExplicitSize(el) {
4030
- var _a;
4031
- try {
4032
- const s = ((_a = el.getAttribute) == null ? void 0 : _a.call(el, "style")) || "";
4033
- return /\b(height|width|block-size|inline-size)\s*:/.test(s);
4034
- } catch (e) {
4035
- return false;
4026
+ function estimateKeptHeight(container, options) {
4027
+ const csC = getComputedStyle(container);
4028
+ const rC = container.getBoundingClientRect();
4029
+ let minTop = Infinity;
4030
+ let maxBottom = -Infinity;
4031
+ let found = false;
4032
+ const kids = Array.from(container.children);
4033
+ for (const k of kids) {
4034
+ if (willBeExcluded(k, options)) continue;
4035
+ if (!isInNormalFlow(k)) continue;
4036
+ const rk = k.getBoundingClientRect();
4037
+ const top = rk.top - rC.top;
4038
+ const bottom = rk.bottom - rC.top;
4039
+ if (bottom <= top) continue;
4040
+ if (top < minTop) minTop = top;
4041
+ if (bottom > maxBottom) maxBottom = bottom;
4042
+ found = true;
4036
4043
  }
4044
+ const contentSpan = found ? Math.max(0, maxBottom - minTop) : 0;
4045
+ const bt = parseFloat(csC.borderTopWidth) || 0;
4046
+ const bb = parseFloat(csC.borderBottomWidth) || 0;
4047
+ const pt = parseFloat(csC.paddingTop) || 0;
4048
+ const pb = parseFloat(csC.paddingBottom) || 0;
4049
+ return bt + bb + pt + pb + contentSpan;
4037
4050
  }
4038
- function isReplacedElement(el) {
4039
- return el instanceof HTMLImageElement || el instanceof HTMLCanvasElement || el instanceof HTMLVideoElement || el instanceof HTMLIFrameElement || el instanceof SVGElement || el instanceof HTMLObjectElement || el instanceof HTMLEmbedElement;
4051
+ var limitDecimals = (v, n = 3) => Number.isFinite(v) ? Math.round(v * __pow(10, n)) / __pow(10, n) : v;
4052
+
4053
+ // src/utils/transforms.helpers.js
4054
+ function parseBoxShadow(cs) {
4055
+ var _a;
4056
+ const v = cs.boxShadow || "";
4057
+ if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
4058
+ const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
4059
+ let t = 0, r = 0, b2 = 0, l = 0;
4060
+ for (const part of parts) {
4061
+ const nums = ((_a = part.match(/-?\d+(\.\d+)?px/g)) == null ? void 0 : _a.map((n) => parseFloat(n))) || [];
4062
+ if (nums.length < 2) continue;
4063
+ const [ox2, oy2, blur = 0, spread = 0] = nums;
4064
+ const extX = Math.abs(ox2) + blur + spread;
4065
+ const extY = Math.abs(oy2) + blur + spread;
4066
+ r = Math.max(r, extX + Math.max(ox2, 0));
4067
+ l = Math.max(l, extX + Math.max(-ox2, 0));
4068
+ b2 = Math.max(b2, extY + Math.max(oy2, 0));
4069
+ t = Math.max(t, extY + Math.max(-oy2, 0));
4070
+ }
4071
+ return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
4040
4072
  }
4041
- function shouldShrinkBox(srcEl, cs) {
4042
- if (!(srcEl instanceof Element)) return false;
4043
- if (authorHasExplicitSize(srcEl)) return false;
4044
- if (isReplacedElement(srcEl)) return false;
4045
- const pos = cs.position;
4046
- if (pos === "absolute" || pos === "fixed" || pos === "sticky") return false;
4047
- const disp = cs.display || "";
4048
- if (disp.includes("flex") || disp.includes("grid") || disp.startsWith("table")) return false;
4049
- if (cs.transform && cs.transform !== "none") return false;
4050
- return true;
4073
+ function parseFilterBlur(cs) {
4074
+ const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
4075
+ const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
4076
+ return { top: b2, right: b2, bottom: b2, left: b2 };
4051
4077
  }
4052
- function shrinkAutoSizeBoxes(sourceRoot, cloneRoot, styleCache = /* @__PURE__ */ new Map()) {
4053
- function walk(src, cln) {
4054
- if (!(src instanceof Element) || !(cln instanceof Element)) return;
4055
- const lostKids = src.childElementCount > cln.childElementCount;
4056
- const cs = styleCache.get(src) || getComputedStyle(src);
4057
- if (!styleCache.has(src)) styleCache.set(src, cs);
4058
- if (lostKids && shouldShrinkBox(src, cs)) {
4059
- if (!cln.style.height) cln.style.height = "auto";
4060
- if (!cln.style.width) cln.style.width = "auto";
4061
- cln.style.removeProperty("block-size");
4062
- cln.style.removeProperty("inline-size");
4063
- if (!cln.style.minHeight) cln.style.minHeight = "0";
4064
- if (!cln.style.minWidth) cln.style.minWidth = "0";
4065
- if (!cln.style.maxHeight) cln.style.maxHeight = "none";
4066
- if (!cln.style.maxWidth) cln.style.maxWidth = "none";
4067
- const oy = cs.overflowY || cs.overflowBlock || "visible";
4068
- const ox = cs.overflowX || cs.overflowInline || "visible";
4069
- if (oy !== "visible" || ox !== "visible") {
4070
- cln.style.overflow = "visible";
4071
- }
4072
- }
4073
- const sKids = Array.from(src.children);
4074
- const cKids = Array.from(cln.children);
4075
- for (let i = 0; i < Math.min(sKids.length, cKids.length); i++) {
4076
- walk(sKids[i], cKids[i]);
4077
- }
4078
- }
4079
- walk(sourceRoot, cloneRoot);
4078
+ function parseOutline(cs) {
4079
+ if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
4080
+ const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
4081
+ return { top: w2, right: w2, bottom: w2, left: w2 };
4080
4082
  }
4081
- var limitDecimals = (v, n = 3) => Number.isFinite(v) ? Math.round(v * __pow(10, n)) / __pow(10, n) : v;
4082
4083
  function parseFilterDropShadows(cs) {
4083
4084
  var _a;
4084
4085
  const raw = `${cs.filter || ""} ${cs.webkitFilter || ""}`.trim();
@@ -4168,35 +4169,6 @@ ${imported}
4168
4169
  return null;
4169
4170
  }
4170
4171
  }
4171
- function parseBoxShadow(cs) {
4172
- var _a;
4173
- const v = cs.boxShadow || "";
4174
- if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
4175
- const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
4176
- let t = 0, r = 0, b2 = 0, l = 0;
4177
- for (const part of parts) {
4178
- const nums = ((_a = part.match(/-?\d+(\.\d+)?px/g)) == null ? void 0 : _a.map((n) => parseFloat(n))) || [];
4179
- if (nums.length < 2) continue;
4180
- const [ox2, oy2, blur = 0, spread = 0] = nums;
4181
- const extX = Math.abs(ox2) + blur + spread;
4182
- const extY = Math.abs(oy2) + blur + spread;
4183
- r = Math.max(r, extX + Math.max(ox2, 0));
4184
- l = Math.max(l, extX + Math.max(-ox2, 0));
4185
- b2 = Math.max(b2, extY + Math.max(oy2, 0));
4186
- t = Math.max(t, extY + Math.max(-oy2, 0));
4187
- }
4188
- return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
4189
- }
4190
- function parseFilterBlur(cs) {
4191
- const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
4192
- const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
4193
- return { top: b2, right: b2, bottom: b2, left: b2 };
4194
- }
4195
- function parseOutline(cs) {
4196
- if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
4197
- const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
4198
- return { top: w2, right: w2, bottom: w2, left: w2 };
4199
- }
4200
4172
  function bboxWithOriginFull(w2, h2, M, ox2, oy2) {
4201
4173
  const a2 = M.a, b2 = M.b, c2 = M.c, d2 = M.d, e2 = M.e || 0, f2 = M.f || 0;
4202
4174
  function pt(x, y) {
@@ -4216,17 +4188,24 @@ ${imported}
4216
4188
  }
4217
4189
  return { minX: minX2, minY: minY2, maxX: maxX2, maxY: maxY2, width: maxX2 - minX2, height: maxY2 - minY2 };
4218
4190
  }
4219
- function hasTFBBox(el) {
4220
- return hasBBoxAffectingTransform(el);
4221
- }
4222
- function matrixFromComputed(el) {
4223
- const tr = getComputedStyle(el).transform;
4224
- if (!tr || tr === "none") return new DOMMatrix();
4225
- try {
4226
- return new DOMMatrix(tr);
4227
- } catch (e) {
4228
- return new WebKitCSSMatrix(tr);
4229
- }
4191
+ function parseTransformOriginPx(cs, w, h) {
4192
+ const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/);
4193
+ const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"];
4194
+ const toPx = (token, size) => {
4195
+ const t = token.toLowerCase();
4196
+ if (t === "left" || t === "top") return 0;
4197
+ if (t === "center") return size / 2;
4198
+ if (t === "right") return size;
4199
+ if (t === "bottom") return size;
4200
+ if (t.endsWith("px")) return parseFloat(t) || 0;
4201
+ if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100;
4202
+ if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0;
4203
+ return 0;
4204
+ };
4205
+ return {
4206
+ ox: toPx(oxRaw, w),
4207
+ oy: toPx(oyRaw, h)
4208
+ };
4230
4209
  }
4231
4210
  function readIndividualTransforms(el) {
4232
4211
  var _a, _b, _c, _d, _e, _f, _g, _h;
@@ -4284,35 +4263,6 @@ ${imported}
4284
4263
  out.translate = cs.translate && cs.translate !== "none" ? cs.translate : null;
4285
4264
  return out;
4286
4265
  }
4287
- function hasBBoxAffectingTransform(el) {
4288
- const cs = getComputedStyle(el);
4289
- const t = cs.transform || "none";
4290
- const hasMatrix = t !== "none" && !/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,\s*0\s*,\s*0\s*\)$/i.test(t);
4291
- if (hasMatrix) return true;
4292
- const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg";
4293
- const s = cs.scale && cs.scale !== "none" && cs.scale !== "1";
4294
- const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px";
4295
- return Boolean(r || s || tr);
4296
- }
4297
- function parseTransformOriginPx(cs, w, h) {
4298
- const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/);
4299
- const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"];
4300
- const toPx = (token, size) => {
4301
- const t = token.toLowerCase();
4302
- if (t === "left" || t === "top") return 0;
4303
- if (t === "center") return size / 2;
4304
- if (t === "right") return size;
4305
- if (t === "bottom") return size;
4306
- if (t.endsWith("px")) return parseFloat(t) || 0;
4307
- if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100;
4308
- if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0;
4309
- return 0;
4310
- };
4311
- return {
4312
- ox: toPx(oxRaw, w),
4313
- oy: toPx(oyRaw, h)
4314
- };
4315
- }
4316
4266
  var __measureHost = null;
4317
4267
  function getMeasureHost() {
4318
4268
  if (__measureHost) return __measureHost;
@@ -4347,51 +4297,258 @@ ${imported}
4347
4297
  host.removeChild(tmp);
4348
4298
  return M;
4349
4299
  }
4350
- function isInNormalFlow(el) {
4300
+ function hasBBoxAffectingTransform(el) {
4351
4301
  const cs = getComputedStyle(el);
4352
- if (cs.display === "none") return false;
4353
- if (cs.position === "absolute" || cs.position === "fixed" || cs.position === "sticky") return false;
4354
- if ((cs.cssFloat || cs.float || "none") !== "none") return false;
4355
- if (cs.transform && cs.transform !== "none") return false;
4356
- return true;
4302
+ const t = cs.transform || "none";
4303
+ const hasMatrix = t !== "none" && !/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,\s*0\s*,\s*0\s*\)$/i.test(t);
4304
+ if (hasMatrix) return true;
4305
+ const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg";
4306
+ const s = cs.scale && cs.scale !== "none" && cs.scale !== "1";
4307
+ const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px";
4308
+ return Boolean(r || s || tr);
4357
4309
  }
4358
- function willBeExcluded(el, options) {
4359
- if (!(el instanceof Element)) return false;
4360
- if (el.getAttribute("data-capture") === "exclude" && (options == null ? void 0 : options.excludeMode) === "remove") return true;
4361
- if (Array.isArray(options == null ? void 0 : options.exclude)) {
4362
- for (const sel of options.exclude) {
4310
+ function matrixFromComputed(el) {
4311
+ const tr = getComputedStyle(el).transform;
4312
+ if (!tr || tr === "none") return new DOMMatrix();
4313
+ try {
4314
+ return new DOMMatrix(tr);
4315
+ } catch (e) {
4316
+ return new WebKitCSSMatrix(tr);
4317
+ }
4318
+ }
4319
+
4320
+ // src/core/capture.js
4321
+ function captureDOM(element, options) {
4322
+ return __async(this, null, function* () {
4323
+ var _a;
4324
+ if (!element) throw new Error("Element cannot be null or undefined");
4325
+ applyCachePolicy(options.cache);
4326
+ const fast = options.fast;
4327
+ const outerTransforms = options.outerTransforms !== false;
4328
+ const outerShadows = !!options.outerShadows;
4329
+ let state = { element, options, plugins: options.plugins };
4330
+ let clone, classCSS, styleCache;
4331
+ let fontsCSS = "";
4332
+ let baseCSS = "";
4333
+ let dataURL;
4334
+ let svgString;
4335
+ let rootTransform2D = null;
4336
+ yield runHook("beforeSnap", state);
4337
+ yield runHook("beforeClone", state);
4338
+ const undoClamp = lineClamp(state.element);
4339
+ try {
4340
+ ({ clone, classCSS, styleCache } = yield prepareClone(state.element, state.options));
4341
+ if (!outerTransforms && clone) {
4342
+ rootTransform2D = normalizeRootTransforms(state.element, clone);
4343
+ }
4344
+ if (!outerShadows && clone) {
4345
+ stripRootShadows(state.element, clone);
4346
+ }
4347
+ } finally {
4348
+ undoClamp();
4349
+ }
4350
+ state = __spreadValues({ clone, classCSS, styleCache }, state);
4351
+ yield runHook("afterClone", state);
4352
+ sanitizeCloneForXHTML(state.clone);
4353
+ if (((_a = state.options) == null ? void 0 : _a.excludeMode) === "remove") {
4363
4354
  try {
4364
- if (el.matches(sel)) return options.excludeMode === "remove";
4355
+ shrinkAutoSizeBoxes(state.element, state.clone, state.styleCache);
4365
4356
  } catch (e) {
4357
+ console.warn("[snapdom] shrink pass failed:", e);
4366
4358
  }
4367
4359
  }
4368
- }
4369
- return false;
4360
+ try {
4361
+ yield ligatureIconToImage(state.clone, state.element);
4362
+ } catch (e) {
4363
+ }
4364
+ yield new Promise((resolve) => {
4365
+ idle(() => __async(null, null, function* () {
4366
+ yield inlineImages(state.clone, state.options);
4367
+ resolve();
4368
+ }), { fast });
4369
+ });
4370
+ yield new Promise((resolve) => {
4371
+ idle(() => __async(null, null, function* () {
4372
+ yield inlineBackgroundImages(state.element, state.clone, state.styleCache, state.options);
4373
+ resolve();
4374
+ }), { fast });
4375
+ });
4376
+ if (options.embedFonts) {
4377
+ yield new Promise((resolve) => {
4378
+ idle(() => __async(null, null, function* () {
4379
+ const required = collectUsedFontVariants(state.element);
4380
+ const usedCodepoints = collectUsedCodepoints(state.element);
4381
+ if (isSafari()) {
4382
+ const families = new Set(
4383
+ Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
4384
+ );
4385
+ yield ensureFontsReady(families, 1);
4386
+ }
4387
+ fontsCSS = yield embedCustomFonts({
4388
+ required,
4389
+ usedCodepoints,
4390
+ preCached: false,
4391
+ exclude: state.options.excludeFonts,
4392
+ useProxy: state.options.useProxy
4393
+ });
4394
+ resolve();
4395
+ }), { fast });
4396
+ });
4397
+ }
4398
+ const usedTags = collectUsedTagNames(state.clone).sort();
4399
+ const tagKey = usedTags.join(",");
4400
+ if (cache.baseStyle.has(tagKey)) {
4401
+ baseCSS = cache.baseStyle.get(tagKey);
4402
+ } else {
4403
+ yield new Promise((resolve) => {
4404
+ idle(() => {
4405
+ baseCSS = generateDedupedBaseCSS(usedTags);
4406
+ cache.baseStyle.set(tagKey, baseCSS);
4407
+ resolve();
4408
+ }, { fast });
4409
+ });
4410
+ }
4411
+ state = __spreadValues({ fontsCSS, baseCSS }, state);
4412
+ yield runHook("beforeRender", state);
4413
+ yield new Promise((resolve) => {
4414
+ idle(() => {
4415
+ var _a2;
4416
+ const csEl = getComputedStyle(state.element);
4417
+ const rect = state.element.getBoundingClientRect();
4418
+ let w0 = Math.max(1, limitDecimals(state.element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
4419
+ let h0 = Math.max(1, limitDecimals(state.element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
4420
+ if (((_a2 = state.options) == null ? void 0 : _a2.excludeMode) === "remove") {
4421
+ const hEst = estimateKeptHeight(state.element, state.options);
4422
+ const EPS = 1;
4423
+ if (Number.isFinite(hEst) && hEst > 0) {
4424
+ h0 = Math.max(1, Math.min(h0, limitDecimals(hEst + EPS)));
4425
+ }
4426
+ }
4427
+ const coerceNum = (v, def = NaN) => {
4428
+ const n = typeof v === "string" ? parseFloat(v) : v;
4429
+ return Number.isFinite(n) ? n : def;
4430
+ };
4431
+ const optW = coerceNum(state.options.width);
4432
+ const optH = coerceNum(state.options.height);
4433
+ let w = w0, h = h0;
4434
+ const hasW = Number.isFinite(optW);
4435
+ const hasH = Number.isFinite(optH);
4436
+ const aspect0 = h0 > 0 ? w0 / h0 : 1;
4437
+ if (hasW && hasH) {
4438
+ w = Math.max(1, limitDecimals(optW));
4439
+ h = Math.max(1, limitDecimals(optH));
4440
+ } else if (hasW) {
4441
+ w = Math.max(1, limitDecimals(optW));
4442
+ h = Math.max(1, limitDecimals(w / (aspect0 || 1)));
4443
+ } else if (hasH) {
4444
+ h = Math.max(1, limitDecimals(optH));
4445
+ w = Math.max(1, limitDecimals(h * (aspect0 || 1)));
4446
+ } else {
4447
+ w = w0;
4448
+ h = h0;
4449
+ }
4450
+ let minX = 0, minY = 0, maxX = w0, maxY = h0;
4451
+ if (!outerTransforms && rootTransform2D && Number.isFinite(rootTransform2D.a)) {
4452
+ const M2 = {
4453
+ a: rootTransform2D.a,
4454
+ b: rootTransform2D.b || 0,
4455
+ c: rootTransform2D.c || 0,
4456
+ d: rootTransform2D.d || 1,
4457
+ e: 0,
4458
+ f: 0
4459
+ };
4460
+ const bb2 = bboxWithOriginFull(w0, h0, M2, 0, 0);
4461
+ minX = limitDecimals(bb2.minX);
4462
+ minY = limitDecimals(bb2.minY);
4463
+ maxX = limitDecimals(bb2.maxX);
4464
+ maxY = limitDecimals(bb2.maxY);
4465
+ } else {
4466
+ const useTFBBox = outerTransforms && hasTFBBox(state.element);
4467
+ if (useTFBBox) {
4468
+ const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
4469
+ const ind2 = readIndividualTransforms(state.element);
4470
+ const TOTAL = readTotalTransformMatrix({
4471
+ baseTransform: baseTransform2,
4472
+ rotate: ind2.rotate || "0deg",
4473
+ scale: ind2.scale,
4474
+ translate: ind2.translate
4475
+ });
4476
+ const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
4477
+ const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
4478
+ const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
4479
+ minX = limitDecimals(bb.minX);
4480
+ minY = limitDecimals(bb.minY);
4481
+ maxX = limitDecimals(bb.maxX);
4482
+ maxY = limitDecimals(bb.maxY);
4483
+ }
4484
+ }
4485
+ const bleedShadow = parseBoxShadow(csEl);
4486
+ const bleedBlur = parseFilterBlur(csEl);
4487
+ const bleedOutline = parseOutline(csEl);
4488
+ const drop = parseFilterDropShadows(csEl);
4489
+ const bleed = !outerShadows ? { top: 0, right: 0, bottom: 0, left: 0 } : {
4490
+ top: limitDecimals(bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top),
4491
+ right: limitDecimals(bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right),
4492
+ bottom: limitDecimals(bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom),
4493
+ left: limitDecimals(bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left)
4494
+ };
4495
+ minX = limitDecimals(minX - bleed.left);
4496
+ minY = limitDecimals(minY - bleed.top);
4497
+ maxX = limitDecimals(maxX + bleed.right);
4498
+ maxY = limitDecimals(maxY + bleed.bottom);
4499
+ const vbW0 = Math.max(1, limitDecimals(maxX - minX));
4500
+ const vbH0 = Math.max(1, limitDecimals(maxY - minY));
4501
+ const scaleW = hasW || hasH ? limitDecimals(w / w0) : 1;
4502
+ const scaleH = hasH || hasW ? limitDecimals(h / h0) : 1;
4503
+ const outW = Math.max(1, limitDecimals(vbW0 * scaleW));
4504
+ const outH = Math.max(1, limitDecimals(vbH0 * scaleH));
4505
+ const svgNS = "http://www.w3.org/2000/svg";
4506
+ const basePad = isSafari() ? 1 : 0;
4507
+ const extraPad = !outerTransforms ? 1 : 0;
4508
+ const pad = limitDecimals(basePad + extraPad);
4509
+ const fo = document.createElementNS(svgNS, "foreignObject");
4510
+ const vbMinX = limitDecimals(minX);
4511
+ const vbMinY = limitDecimals(minY);
4512
+ fo.setAttribute("x", String(limitDecimals(-(vbMinX - pad))));
4513
+ fo.setAttribute("y", String(limitDecimals(-(vbMinY - pad))));
4514
+ fo.setAttribute("width", String(limitDecimals(w0 + pad * 2)));
4515
+ fo.setAttribute("height", String(limitDecimals(h0 + pad * 2)));
4516
+ fo.style.overflow = "visible";
4517
+ const styleTag = document.createElement("style");
4518
+ styleTag.textContent = state.baseCSS + state.fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + state.classCSS;
4519
+ fo.appendChild(styleTag);
4520
+ const container = document.createElement("div");
4521
+ container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
4522
+ container.style.width = `${limitDecimals(w0)}px`;
4523
+ container.style.height = `${limitDecimals(h0)}px`;
4524
+ container.style.overflow = "visible";
4525
+ state.clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
4526
+ container.appendChild(state.clone);
4527
+ fo.appendChild(container);
4528
+ const serializer = new XMLSerializer();
4529
+ const foString = serializer.serializeToString(fo);
4530
+ const vbW = limitDecimals(vbW0 + pad * 2);
4531
+ const vbH = limitDecimals(vbH0 + pad * 2);
4532
+ const wantsSize = hasW || hasH;
4533
+ options.meta = { w0, h0, vbW, vbH, targetW: w, targetH: h };
4534
+ const svgOutW = isSafari() && wantsSize ? vbW : limitDecimals(outW + pad * 2);
4535
+ const svgOutH = isSafari() && wantsSize ? vbH : limitDecimals(outH + pad * 2);
4536
+ const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
4537
+ const svgFooter = "</svg>";
4538
+ svgString = svgHeader + foString + svgFooter;
4539
+ dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
4540
+ state = __spreadValues({ svgString, dataURL }, state);
4541
+ resolve();
4542
+ }, { fast });
4543
+ });
4544
+ yield runHook("afterRender", state);
4545
+ const sandbox = document.getElementById("snapdom-sandbox");
4546
+ if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
4547
+ return state.dataURL;
4548
+ });
4370
4549
  }
4371
- function estimateKeptHeight(container, options) {
4372
- const csC = getComputedStyle(container);
4373
- const rC = container.getBoundingClientRect();
4374
- let minTop = Infinity;
4375
- let maxBottom = -Infinity;
4376
- let found = false;
4377
- const kids = Array.from(container.children);
4378
- for (const k of kids) {
4379
- if (willBeExcluded(k, options)) continue;
4380
- if (!isInNormalFlow(k)) continue;
4381
- const rk = k.getBoundingClientRect();
4382
- const top = rk.top - rC.top;
4383
- const bottom = rk.bottom - rC.top;
4384
- if (bottom <= top) continue;
4385
- if (top < minTop) minTop = top;
4386
- if (bottom > maxBottom) maxBottom = bottom;
4387
- found = true;
4388
- }
4389
- const contentSpan = found ? Math.max(0, maxBottom - minTop) : 0;
4390
- const bt = parseFloat(csC.borderTopWidth) || 0;
4391
- const bb = parseFloat(csC.borderBottomWidth) || 0;
4392
- const pt = parseFloat(csC.paddingTop) || 0;
4393
- const pb = parseFloat(csC.paddingBottom) || 0;
4394
- return bt + bb + pt + pb + contentSpan;
4550
+ function hasTFBBox(el) {
4551
+ return hasBBoxAffectingTransform(el);
4395
4552
  }
4396
4553
 
4397
4554
  // src/core/context.js