@zumer/snapdom 1.9.12-dev.0 → 1.9.12-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -41
- package/dist/snapdom.js +326 -58
- package/dist/snapdom.min.js +5 -5
- package/dist/snapdom.min.mjs +4 -4
- package/dist/snapdom.mjs +313 -50
- package/package.json +1 -1
- package/types/snapdom.d.ts +5 -5
package/dist/snapdom.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.9.12-dev.
|
|
4
|
+
* v.1.9.12-dev.1
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -626,7 +626,10 @@ function idle(fn, { fast = false } = {}) {
|
|
|
626
626
|
}
|
|
627
627
|
}
|
|
628
628
|
function isSafari() {
|
|
629
|
-
|
|
629
|
+
const ua = typeof navigator !== "undefined" && navigator.userAgent ? navigator.userAgent : "";
|
|
630
|
+
const isSafariUA = /^((?!chrome|android).)*safari/i.test(ua);
|
|
631
|
+
const isWeChatUA = /(MicroMessenger|wxwork|WeCom|WindowsWechat|MacWechat)/i.test(ua);
|
|
632
|
+
return isSafariUA || isWeChatUA;
|
|
630
633
|
}
|
|
631
634
|
|
|
632
635
|
// src/modules/styles.js
|
|
@@ -660,7 +663,7 @@ function setupInvalidationOnce(root = document.documentElement) {
|
|
|
660
663
|
} catch {
|
|
661
664
|
}
|
|
662
665
|
}
|
|
663
|
-
function snapshotComputedStyleFull(style) {
|
|
666
|
+
function snapshotComputedStyleFull(style, options = {}) {
|
|
664
667
|
const out = {};
|
|
665
668
|
const vis = style.getPropertyValue("visibility");
|
|
666
669
|
for (let i = 0; i < style.length; i++) {
|
|
@@ -671,6 +674,24 @@ function snapshotComputedStyleFull(style) {
|
|
|
671
674
|
}
|
|
672
675
|
out[prop] = val;
|
|
673
676
|
}
|
|
677
|
+
if (options.embedFonts) {
|
|
678
|
+
const EXTRA_FONT_PROPS = [
|
|
679
|
+
"font-feature-settings",
|
|
680
|
+
"font-variation-settings",
|
|
681
|
+
"font-kerning",
|
|
682
|
+
"font-variant",
|
|
683
|
+
"font-variant-ligatures",
|
|
684
|
+
"font-optical-sizing"
|
|
685
|
+
];
|
|
686
|
+
for (const prop of EXTRA_FONT_PROPS) {
|
|
687
|
+
if (out[prop]) continue;
|
|
688
|
+
try {
|
|
689
|
+
const v = style.getPropertyValue(prop);
|
|
690
|
+
if (v) out[prop] = v;
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
674
695
|
if (vis === "hidden") out.opacity = "0";
|
|
675
696
|
return out;
|
|
676
697
|
}
|
|
@@ -683,11 +704,11 @@ function styleSignature(snap) {
|
|
|
683
704
|
__snapshotSig.set(snap, sig);
|
|
684
705
|
return sig;
|
|
685
706
|
}
|
|
686
|
-
function getSnapshot(el, preStyle = null) {
|
|
707
|
+
function getSnapshot(el, preStyle = null, options = {}) {
|
|
687
708
|
const rec = snapshotCache.get(el);
|
|
688
709
|
if (rec && rec.epoch === __epoch) return rec.snapshot;
|
|
689
710
|
const style = preStyle || getComputedStyle(el);
|
|
690
|
-
const snap = snapshotComputedStyleFull(style);
|
|
711
|
+
const snap = snapshotComputedStyleFull(style, options);
|
|
691
712
|
snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
|
|
692
713
|
return snap;
|
|
693
714
|
}
|
|
@@ -741,7 +762,7 @@ async function inlineAllStyles(source, clone, sessionOrCtx, opts) {
|
|
|
741
762
|
session.styleCache.set(source, getComputedStyle(source));
|
|
742
763
|
}
|
|
743
764
|
const pre = session.styleCache.get(source);
|
|
744
|
-
const snap = getSnapshot(source, pre);
|
|
765
|
+
const snap = getSnapshot(source, pre, ctx.options);
|
|
745
766
|
const sig = styleSignature(snap);
|
|
746
767
|
let key = persist.snapshotKeyCache.get(sig);
|
|
747
768
|
if (!key) {
|
|
@@ -985,7 +1006,7 @@ async function deepClone(node, sessionCache, options) {
|
|
|
985
1006
|
return node.cloneNode(true);
|
|
986
1007
|
}
|
|
987
1008
|
if (node.getAttribute("data-capture") === "exclude") {
|
|
988
|
-
if (options.excludeMode === "
|
|
1009
|
+
if (options.excludeMode === "hide") {
|
|
989
1010
|
const spacer = document.createElement("div");
|
|
990
1011
|
const rect = node.getBoundingClientRect();
|
|
991
1012
|
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
@@ -998,7 +1019,7 @@ async function deepClone(node, sessionCache, options) {
|
|
|
998
1019
|
for (const selector of options.exclude) {
|
|
999
1020
|
try {
|
|
1000
1021
|
if (node.matches?.(selector)) {
|
|
1001
|
-
if (options.excludeMode === "
|
|
1022
|
+
if (options.excludeMode === "hide") {
|
|
1002
1023
|
const spacer = document.createElement("div");
|
|
1003
1024
|
const rect = node.getBoundingClientRect();
|
|
1004
1025
|
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
@@ -1015,7 +1036,7 @@ async function deepClone(node, sessionCache, options) {
|
|
|
1015
1036
|
if (typeof options.filter === "function") {
|
|
1016
1037
|
try {
|
|
1017
1038
|
if (!options.filter(node)) {
|
|
1018
|
-
if (options.filterMode === "
|
|
1039
|
+
if (options.filterMode === "hide") {
|
|
1019
1040
|
const spacer = document.createElement("div");
|
|
1020
1041
|
const rect = node.getBoundingClientRect();
|
|
1021
1042
|
spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`;
|
|
@@ -1898,7 +1919,182 @@ async function ensureFontsReady(families, warmupRepetitions = 2) {
|
|
|
1898
1919
|
}
|
|
1899
1920
|
}
|
|
1900
1921
|
|
|
1922
|
+
// src/modules/counter.js
|
|
1923
|
+
function hasCounters(input) {
|
|
1924
|
+
return /\bcounter\s*\(|\bcounters\s*\(/.test(input || "");
|
|
1925
|
+
}
|
|
1926
|
+
function unquoteDoubleStrings(s) {
|
|
1927
|
+
return (s || "").replace(/"([^"]*)"/g, "$1");
|
|
1928
|
+
}
|
|
1929
|
+
function alpha(n, upper = false) {
|
|
1930
|
+
let s = "", x = Math.max(1, n);
|
|
1931
|
+
while (x > 0) {
|
|
1932
|
+
x--;
|
|
1933
|
+
s = String.fromCharCode(97 + x % 26) + s;
|
|
1934
|
+
x = Math.floor(x / 26);
|
|
1935
|
+
}
|
|
1936
|
+
return upper ? s.toUpperCase() : s;
|
|
1937
|
+
}
|
|
1938
|
+
function roman(n, upper = true) {
|
|
1939
|
+
const map = [[1e3, "M"], [900, "CM"], [500, "D"], [400, "CD"], [100, "C"], [90, "XC"], [50, "L"], [40, "XL"], [10, "X"], [9, "IX"], [5, "V"], [4, "IV"], [1, "I"]];
|
|
1940
|
+
let num = Math.max(1, Math.min(3999, n)), out = "";
|
|
1941
|
+
for (const [v, sym] of map) while (num >= v) {
|
|
1942
|
+
out += sym;
|
|
1943
|
+
num -= v;
|
|
1944
|
+
}
|
|
1945
|
+
return upper ? out : out.toLowerCase();
|
|
1946
|
+
}
|
|
1947
|
+
function formatCounter(value, style) {
|
|
1948
|
+
switch ((style || "decimal").toLowerCase()) {
|
|
1949
|
+
case "decimal":
|
|
1950
|
+
return String(Math.max(0, value));
|
|
1951
|
+
case "decimal-leading-zero":
|
|
1952
|
+
return (value < 10 ? "0" : "") + String(Math.max(0, value));
|
|
1953
|
+
case "lower-alpha":
|
|
1954
|
+
return alpha(value, false);
|
|
1955
|
+
case "upper-alpha":
|
|
1956
|
+
return alpha(value, true);
|
|
1957
|
+
case "lower-roman":
|
|
1958
|
+
return roman(value, false);
|
|
1959
|
+
case "upper-roman":
|
|
1960
|
+
return roman(value, true);
|
|
1961
|
+
default:
|
|
1962
|
+
return String(Math.max(0, value));
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
function buildCounterContext(root) {
|
|
1966
|
+
const nodeCounters = /* @__PURE__ */ new WeakMap();
|
|
1967
|
+
const rootEl = root instanceof Document ? root.documentElement : root;
|
|
1968
|
+
const isLi = (el) => el && el.tagName === "LI";
|
|
1969
|
+
const countPrevLi = (li) => {
|
|
1970
|
+
let c = 0, p = li?.parentElement;
|
|
1971
|
+
if (!p) return 0;
|
|
1972
|
+
for (const sib of p.children) {
|
|
1973
|
+
if (sib === li) break;
|
|
1974
|
+
if (sib.tagName === "LI") c++;
|
|
1975
|
+
}
|
|
1976
|
+
return c;
|
|
1977
|
+
};
|
|
1978
|
+
const cloneMap = (m) => {
|
|
1979
|
+
const out = /* @__PURE__ */ new Map();
|
|
1980
|
+
for (const [k, arr] of m) out.set(k, arr.slice());
|
|
1981
|
+
return out;
|
|
1982
|
+
};
|
|
1983
|
+
const applyTo = (baseMap, parentMap, el) => {
|
|
1984
|
+
const map = cloneMap(baseMap);
|
|
1985
|
+
let reset;
|
|
1986
|
+
try {
|
|
1987
|
+
reset = el.style?.counterReset || getComputedStyle(el).counterReset;
|
|
1988
|
+
} catch {
|
|
1989
|
+
}
|
|
1990
|
+
if (reset && reset !== "none") {
|
|
1991
|
+
for (const part of reset.split(",")) {
|
|
1992
|
+
const toks = part.trim().split(/\s+/);
|
|
1993
|
+
const name = toks[0];
|
|
1994
|
+
const val = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : 0;
|
|
1995
|
+
if (!name) continue;
|
|
1996
|
+
const parentStack = parentMap.get(name);
|
|
1997
|
+
if (parentStack && parentStack.length) {
|
|
1998
|
+
const s = parentStack.slice();
|
|
1999
|
+
s.push(val);
|
|
2000
|
+
map.set(name, s);
|
|
2001
|
+
} else {
|
|
2002
|
+
map.set(name, [val]);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
let inc;
|
|
2007
|
+
try {
|
|
2008
|
+
inc = el.style?.counterIncrement || getComputedStyle(el).counterIncrement;
|
|
2009
|
+
} catch {
|
|
2010
|
+
}
|
|
2011
|
+
if (inc && inc !== "none") {
|
|
2012
|
+
for (const part of inc.split(",")) {
|
|
2013
|
+
const toks = part.trim().split(/\s+/);
|
|
2014
|
+
const name = toks[0];
|
|
2015
|
+
const by = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : 1;
|
|
2016
|
+
if (!name) continue;
|
|
2017
|
+
const stack = map.get(name) || [];
|
|
2018
|
+
if (stack.length === 0) stack.push(0);
|
|
2019
|
+
stack[stack.length - 1] += by;
|
|
2020
|
+
map.set(name, stack);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
try {
|
|
2024
|
+
const cs = getComputedStyle(el);
|
|
2025
|
+
if (cs.display === "list-item" && isLi(el)) {
|
|
2026
|
+
const p = el.parentElement;
|
|
2027
|
+
let idx = 1;
|
|
2028
|
+
if (p && p.tagName === "OL") {
|
|
2029
|
+
const startAttr = p.getAttribute("start");
|
|
2030
|
+
const start = Number.isFinite(Number(startAttr)) ? Number(startAttr) : 1;
|
|
2031
|
+
const prev = countPrevLi(el);
|
|
2032
|
+
const ownAttr = el.getAttribute("value");
|
|
2033
|
+
idx = Number.isFinite(Number(ownAttr)) ? Number(ownAttr) : start + prev;
|
|
2034
|
+
} else {
|
|
2035
|
+
idx = 1 + countPrevLi(el);
|
|
2036
|
+
}
|
|
2037
|
+
const s = map.get("list-item") || [];
|
|
2038
|
+
if (s.length === 0) s.push(0);
|
|
2039
|
+
s[s.length - 1] = idx;
|
|
2040
|
+
map.set("list-item", s);
|
|
2041
|
+
}
|
|
2042
|
+
} catch {
|
|
2043
|
+
}
|
|
2044
|
+
return map;
|
|
2045
|
+
};
|
|
2046
|
+
const build = (el, parentMap, carryMap) => {
|
|
2047
|
+
const curr = applyTo(carryMap, parentMap, el);
|
|
2048
|
+
nodeCounters.set(el, curr);
|
|
2049
|
+
let nextCarry = curr;
|
|
2050
|
+
for (const child of el.children) {
|
|
2051
|
+
const childCarry = build(child, curr, nextCarry);
|
|
2052
|
+
nextCarry = childCarry;
|
|
2053
|
+
}
|
|
2054
|
+
return curr;
|
|
2055
|
+
};
|
|
2056
|
+
const empty = /* @__PURE__ */ new Map();
|
|
2057
|
+
build(rootEl, empty, empty);
|
|
2058
|
+
return {
|
|
2059
|
+
get(node, name) {
|
|
2060
|
+
const s = nodeCounters.get(node)?.get(name);
|
|
2061
|
+
return s && s.length ? s[s.length - 1] : 0;
|
|
2062
|
+
},
|
|
2063
|
+
getStack(node, name) {
|
|
2064
|
+
const s = nodeCounters.get(node)?.get(name);
|
|
2065
|
+
return s ? s.slice() : [];
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
function resolveCountersInContent(raw, node, ctx) {
|
|
2070
|
+
if (!raw || raw === "none") return raw;
|
|
2071
|
+
try {
|
|
2072
|
+
const RX = /\b(counter|counters)\s*\(([^)]+)\)/g;
|
|
2073
|
+
let out = raw.replace(RX, (_, fn, args) => {
|
|
2074
|
+
const parts = String(args).split(",").map((s) => s.trim());
|
|
2075
|
+
if (fn === "counter") {
|
|
2076
|
+
const name = parts[0]?.replace(/^["']|["']$/g, "");
|
|
2077
|
+
const style = (parts[1] || "decimal").toLowerCase();
|
|
2078
|
+
const v = ctx.get(node, name);
|
|
2079
|
+
return formatCounter(v, style);
|
|
2080
|
+
} else {
|
|
2081
|
+
const name = parts[0]?.replace(/^["']|["']$/g, "");
|
|
2082
|
+
const sep = parts[1]?.replace(/^["']|["']$/g, "") ?? "";
|
|
2083
|
+
const style = (parts[2] || "decimal").toLowerCase();
|
|
2084
|
+
const stack = ctx.getStack(node, name);
|
|
2085
|
+
if (!stack.length) return "";
|
|
2086
|
+
const pieces = stack.map((v) => formatCounter(v, style));
|
|
2087
|
+
return pieces.join(sep);
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
return unquoteDoubleStrings(out);
|
|
2091
|
+
} catch {
|
|
2092
|
+
return "- ";
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
|
|
1901
2096
|
// src/modules/pseudo.js
|
|
2097
|
+
var counterCtx = null;
|
|
1902
2098
|
async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
1903
2099
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
1904
2100
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
@@ -1932,7 +2128,11 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
1932
2128
|
continue;
|
|
1933
2129
|
}
|
|
1934
2130
|
const content = style.content;
|
|
1935
|
-
|
|
2131
|
+
let cleanContent = parseContent(content);
|
|
2132
|
+
if (hasCounters(content)) {
|
|
2133
|
+
counterCtx = buildCounterContext(source.ownerDocument || document);
|
|
2134
|
+
cleanContent = resolveCountersInContent(cleanContent, source, counterCtx);
|
|
2135
|
+
}
|
|
1936
2136
|
const bg = style.backgroundImage;
|
|
1937
2137
|
const bgColor = style.backgroundColor;
|
|
1938
2138
|
const fontFamily = style.fontFamily;
|
|
@@ -2317,8 +2517,8 @@ async function inlineImages(clone, options = {}) {
|
|
|
2317
2517
|
if (!img.height) img.height = img.naturalHeight || 100;
|
|
2318
2518
|
return;
|
|
2319
2519
|
}
|
|
2320
|
-
const {
|
|
2321
|
-
if (
|
|
2520
|
+
const { fallbackURL } = options || {};
|
|
2521
|
+
if (fallbackURL) {
|
|
2322
2522
|
try {
|
|
2323
2523
|
const dsW = parseInt(img.dataset?.snapdomWidth || "", 10) || 0;
|
|
2324
2524
|
const dsH = parseInt(img.dataset?.snapdomHeight || "", 10) || 0;
|
|
@@ -2326,14 +2526,14 @@ async function inlineImages(clone, options = {}) {
|
|
|
2326
2526
|
const attrH = parseInt(img.getAttribute("height") || "", 10) || 0;
|
|
2327
2527
|
const styleW = parseFloat(img.style?.width || "") || 0;
|
|
2328
2528
|
const styleH = parseFloat(img.style?.height || "") || 0;
|
|
2329
|
-
const
|
|
2330
|
-
const
|
|
2331
|
-
const fallbackUrl = typeof
|
|
2529
|
+
const fbW = dsW || styleW || attrW || img.width || void 0;
|
|
2530
|
+
const fbH = dsH || styleH || attrH || img.height || void 0;
|
|
2531
|
+
const fallbackUrl = typeof fallbackURL === "function" ? await fallbackURL({ width: fbW, height: fbH, src, element: img }) : fallbackURL;
|
|
2332
2532
|
if (fallbackUrl) {
|
|
2333
2533
|
const fallbackData = await snapFetch(fallbackUrl, { as: "dataURL", useProxy: options.useProxy });
|
|
2334
2534
|
img.src = fallbackData.data;
|
|
2335
|
-
if (!img.width &&
|
|
2336
|
-
if (!img.height &&
|
|
2535
|
+
if (!img.width && fbW) img.width = fbW;
|
|
2536
|
+
if (!img.height && fbH) img.height = fbH;
|
|
2337
2537
|
if (!img.width) img.width = img.naturalWidth || 100;
|
|
2338
2538
|
if (!img.height) img.height = img.naturalHeight || 100;
|
|
2339
2539
|
return;
|
|
@@ -2452,6 +2652,63 @@ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
|
|
|
2452
2652
|
}
|
|
2453
2653
|
}
|
|
2454
2654
|
|
|
2655
|
+
// src/modules/lineClamp.js
|
|
2656
|
+
function lineClamp(el) {
|
|
2657
|
+
if (!el) return () => {
|
|
2658
|
+
};
|
|
2659
|
+
const lines = getClamp(el);
|
|
2660
|
+
if (lines <= 0) return () => {
|
|
2661
|
+
};
|
|
2662
|
+
if (!isPlainTextContainer(el)) return () => {
|
|
2663
|
+
};
|
|
2664
|
+
const cs = getComputedStyle(el);
|
|
2665
|
+
const targetH = Math.round(usedLineHeightPx(cs) * lines + vpad(cs));
|
|
2666
|
+
const original = el.textContent ?? "";
|
|
2667
|
+
const prevText = original;
|
|
2668
|
+
if (el.scrollHeight <= targetH + 0.5) {
|
|
2669
|
+
return () => {
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
let lo = 0, hi = original.length, best = -1;
|
|
2673
|
+
while (lo <= hi) {
|
|
2674
|
+
const mid = lo + hi >> 1;
|
|
2675
|
+
el.textContent = original.slice(0, mid) + "\u2026";
|
|
2676
|
+
if (el.scrollHeight <= targetH + 0.5) {
|
|
2677
|
+
best = mid;
|
|
2678
|
+
lo = mid + 1;
|
|
2679
|
+
} else {
|
|
2680
|
+
hi = mid - 1;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
el.textContent = (best >= 0 ? original.slice(0, best) : "") + "\u2026";
|
|
2684
|
+
return () => {
|
|
2685
|
+
el.textContent = prevText;
|
|
2686
|
+
};
|
|
2687
|
+
}
|
|
2688
|
+
function getClamp(el) {
|
|
2689
|
+
const cs = getComputedStyle(el);
|
|
2690
|
+
let v = cs.getPropertyValue("-webkit-line-clamp") || cs.getPropertyValue("line-clamp");
|
|
2691
|
+
v = (v || "").trim();
|
|
2692
|
+
const n = parseInt(v, 10);
|
|
2693
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
2694
|
+
}
|
|
2695
|
+
function usedLineHeightPx(cs) {
|
|
2696
|
+
const lh = (cs.lineHeight || "").trim();
|
|
2697
|
+
const fs = parseFloat(cs.fontSize) || 16;
|
|
2698
|
+
if (!lh || lh === "normal") return Math.round(fs * 1.2);
|
|
2699
|
+
if (lh.endsWith("px")) return parseFloat(lh);
|
|
2700
|
+
if (/^\d+(\.\d+)?$/.test(lh)) return Math.round(parseFloat(lh) * fs);
|
|
2701
|
+
if (lh.endsWith("%")) return Math.round(parseFloat(lh) / 100 * fs);
|
|
2702
|
+
return Math.round(fs * 1.2);
|
|
2703
|
+
}
|
|
2704
|
+
function vpad(cs) {
|
|
2705
|
+
return (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
|
|
2706
|
+
}
|
|
2707
|
+
function isPlainTextContainer(el) {
|
|
2708
|
+
if (el.childElementCount > 0) return false;
|
|
2709
|
+
return Array.from(el.childNodes).some((n) => n.nodeType === Node.TEXT_NODE);
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2455
2712
|
// src/core/capture.js
|
|
2456
2713
|
async function captureDOM(element, options) {
|
|
2457
2714
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
@@ -2462,7 +2719,12 @@ async function captureDOM(element, options) {
|
|
|
2462
2719
|
let baseCSS = "";
|
|
2463
2720
|
let dataURL;
|
|
2464
2721
|
let svgString;
|
|
2465
|
-
|
|
2722
|
+
const undoClamp = lineClamp(element);
|
|
2723
|
+
try {
|
|
2724
|
+
({ clone, classCSS, styleCache } = await prepareClone(element, options));
|
|
2725
|
+
} finally {
|
|
2726
|
+
undoClamp();
|
|
2727
|
+
}
|
|
2466
2728
|
await new Promise((resolve) => {
|
|
2467
2729
|
idle(async () => {
|
|
2468
2730
|
await inlineImages(clone, options);
|
|
@@ -2512,28 +2774,28 @@ async function captureDOM(element, options) {
|
|
|
2512
2774
|
}
|
|
2513
2775
|
await new Promise((resolve) => {
|
|
2514
2776
|
idle(() => {
|
|
2515
|
-
const rect =
|
|
2516
|
-
let w = rect.width;
|
|
2517
|
-
let h = rect.height;
|
|
2777
|
+
const rect = element.getBoundingClientRect();
|
|
2778
|
+
let w = Number(rect.width) || 0;
|
|
2779
|
+
let h = Number(rect.height) || 0;
|
|
2518
2780
|
const hasW = Number.isFinite(options.width);
|
|
2519
2781
|
const hasH = Number.isFinite(options.height);
|
|
2520
2782
|
const scaleOpt = options.scale ?? 1;
|
|
2521
2783
|
const hasScale = typeof scaleOpt === "number" && scaleOpt !== 1;
|
|
2522
2784
|
if (!hasScale) {
|
|
2523
|
-
const aspect =
|
|
2785
|
+
const aspect = h > 0 ? w / h : 1;
|
|
2524
2786
|
if (hasW && hasH) {
|
|
2525
|
-
w = options.width;
|
|
2526
|
-
h = options.height;
|
|
2787
|
+
w = Number(options.width);
|
|
2788
|
+
h = Number(options.height);
|
|
2527
2789
|
} else if (hasW) {
|
|
2528
|
-
w = options.width;
|
|
2529
|
-
h = w / aspect;
|
|
2790
|
+
w = Number(options.width);
|
|
2791
|
+
h = w / (aspect || 1);
|
|
2530
2792
|
} else if (hasH) {
|
|
2531
|
-
h = options.height;
|
|
2532
|
-
w = h * aspect;
|
|
2793
|
+
h = Number(options.height);
|
|
2794
|
+
w = h * (aspect || 1);
|
|
2533
2795
|
}
|
|
2534
2796
|
}
|
|
2535
|
-
w = Math.max(1, Math.ceil(w));
|
|
2536
|
-
h = Math.max(1, Math.ceil(h));
|
|
2797
|
+
w = Math.max(1, Math.ceil(Number.isFinite(w) ? w : 1));
|
|
2798
|
+
h = Math.max(1, Math.ceil(Number.isFinite(h) ? h : 1));
|
|
2537
2799
|
const csEl = getComputedStyle(element);
|
|
2538
2800
|
const baseTransform = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
|
|
2539
2801
|
const ind = readIndividualTransforms(element);
|
|
@@ -2604,7 +2866,9 @@ async function captureDOM(element, options) {
|
|
|
2604
2866
|
fo.appendChild(container);
|
|
2605
2867
|
const serializer = new XMLSerializer();
|
|
2606
2868
|
const foString = serializer.serializeToString(fo);
|
|
2607
|
-
const
|
|
2869
|
+
const finalW = !hasScale && (hasW || hasH) ? w : outW;
|
|
2870
|
+
const finalH = !hasScale && (hasH || hasW) ? h : outH;
|
|
2871
|
+
const svgHeader = `<svg xmlns="${svgNS}" width="${finalW}" height="${finalH}" viewBox="0 0 ${outW} ${outH}">`;
|
|
2608
2872
|
const svgFooter = "</svg>";
|
|
2609
2873
|
svgString = svgHeader + foString + svgFooter;
|
|
2610
2874
|
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
@@ -2615,17 +2879,6 @@ async function captureDOM(element, options) {
|
|
|
2615
2879
|
if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
|
|
2616
2880
|
return dataURL;
|
|
2617
2881
|
}
|
|
2618
|
-
function getNaturalBorderBoxSize(el) {
|
|
2619
|
-
const cs = getComputedStyle(el);
|
|
2620
|
-
const px = (v) => parseFloat(v) || 0;
|
|
2621
|
-
let w = px(cs.width);
|
|
2622
|
-
let h = px(cs.height);
|
|
2623
|
-
if (cs.boxSizing === "content-box") {
|
|
2624
|
-
w += px(cs.paddingLeft) + px(cs.paddingRight) + px(cs.borderLeftWidth) + px(cs.borderRightWidth);
|
|
2625
|
-
h += px(cs.paddingTop) + px(cs.paddingBottom) + px(cs.borderTopWidth) + px(cs.borderBottomWidth);
|
|
2626
|
-
}
|
|
2627
|
-
return { width: w, height: h };
|
|
2628
|
-
}
|
|
2629
2882
|
function matrixFromComputed(el) {
|
|
2630
2883
|
const tr = getComputedStyle(el).transform;
|
|
2631
2884
|
if (!tr || tr === "none") return new DOMMatrix();
|
|
@@ -2657,24 +2910,33 @@ function bboxFromMatrix(w, h, M2D) {
|
|
|
2657
2910
|
}
|
|
2658
2911
|
function readIndividualTransforms(el) {
|
|
2659
2912
|
const out = { rotate: "0deg", scale: null, translate: null };
|
|
2660
|
-
const map = el.computedStyleMap
|
|
2913
|
+
const map = typeof el.computedStyleMap === "function" ? el.computedStyleMap() : null;
|
|
2661
2914
|
if (map) {
|
|
2662
|
-
const
|
|
2915
|
+
const safeGet = (prop) => {
|
|
2916
|
+
try {
|
|
2917
|
+
if (typeof map.has === "function" && !map.has(prop)) return null;
|
|
2918
|
+
if (typeof map.get !== "function") return null;
|
|
2919
|
+
return map.get(prop);
|
|
2920
|
+
} catch {
|
|
2921
|
+
return null;
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2924
|
+
const rot = safeGet("rotate");
|
|
2663
2925
|
if (rot) {
|
|
2664
2926
|
const ang = rot.angle ?? rot;
|
|
2665
|
-
if (ang && "unit" in ang) {
|
|
2927
|
+
if (ang && typeof ang === "object" && "unit" in ang) {
|
|
2666
2928
|
out.rotate = ang.unit === "rad" ? ang.value * 180 / Math.PI + "deg" : ang.value + ang.unit;
|
|
2667
2929
|
} else {
|
|
2668
2930
|
out.rotate = String(rot);
|
|
2669
2931
|
}
|
|
2670
2932
|
}
|
|
2671
|
-
const sc =
|
|
2933
|
+
const sc = safeGet("scale");
|
|
2672
2934
|
if (sc && sc.length) {
|
|
2673
2935
|
const sx = sc[0]?.value ?? 1;
|
|
2674
2936
|
const sy = sc[1]?.value ?? sx;
|
|
2675
2937
|
out.scale = `${sx} ${sy}`;
|
|
2676
2938
|
}
|
|
2677
|
-
const tr =
|
|
2939
|
+
const tr = safeGet("translate");
|
|
2678
2940
|
if (tr && tr.length) {
|
|
2679
2941
|
const tx = tr[0]?.value ?? 0;
|
|
2680
2942
|
const ty = tr[1]?.value ?? 0;
|
|
@@ -2759,9 +3021,9 @@ function createContext(options = {}) {
|
|
|
2759
3021
|
scale: options.scale ?? 1,
|
|
2760
3022
|
// DOM filters
|
|
2761
3023
|
exclude: options.exclude ?? [],
|
|
2762
|
-
excludeMode: options.excludeMode ?? "
|
|
3024
|
+
excludeMode: options.excludeMode ?? "hide",
|
|
2763
3025
|
filter: options.filter ?? null,
|
|
2764
|
-
filterMode: options.filterMode ?? "
|
|
3026
|
+
filterMode: options.filterMode ?? "hide",
|
|
2765
3027
|
placeholders: options.placeholders !== false,
|
|
2766
3028
|
// default true
|
|
2767
3029
|
// Fuentes
|
|
@@ -2769,7 +3031,7 @@ function createContext(options = {}) {
|
|
|
2769
3031
|
iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
|
|
2770
3032
|
localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
|
|
2771
3033
|
excludeFonts: options.excludeFonts ?? void 0,
|
|
2772
|
-
|
|
3034
|
+
fallbackURL: options.fallbackURL ?? void 0,
|
|
2773
3035
|
/** Cache policy (disabled|full|auto|soft). Default: "soft" */
|
|
2774
3036
|
/** @type {CachePolicy} */
|
|
2775
3037
|
cache: cachePolicy,
|
|
@@ -2855,6 +3117,7 @@ async function rasterize(url, options) {
|
|
|
2855
3117
|
|
|
2856
3118
|
// src/exporters/download.js
|
|
2857
3119
|
async function download(url, options) {
|
|
3120
|
+
options.dpr = 1;
|
|
2858
3121
|
if (options.format === "svg") {
|
|
2859
3122
|
const blob = await toBlob(url, { ...options, type: "svg" });
|
|
2860
3123
|
const objectURL = URL.createObjectURL(blob);
|
package/package.json
CHANGED
package/types/snapdom.d.ts
CHANGED
|
@@ -13,8 +13,8 @@ export type BlobType = "svg" | RasterMime;
|
|
|
13
13
|
export type IconFontMatcher = string | RegExp;
|
|
14
14
|
export type CachePolicy = "disabled" | "full" | "auto" | "soft";
|
|
15
15
|
|
|
16
|
-
export type ExcludeMode = '
|
|
17
|
-
export type FilterMode = '
|
|
16
|
+
export type ExcludeMode = 'hide' | 'remove';
|
|
17
|
+
export type FilterMode = 'hide' | 'remove';
|
|
18
18
|
|
|
19
19
|
export interface LocalFontDescriptor {
|
|
20
20
|
/** CSS font-family name (e.g. "Inter"). */
|
|
@@ -92,7 +92,7 @@ export interface CaptureOptions {
|
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
94
|
* Mode applied to excluded nodes of the cloned tree.
|
|
95
|
-
* Default: "
|
|
95
|
+
* Default: "hide"
|
|
96
96
|
*/
|
|
97
97
|
excludeMode?: ExcludeMode;
|
|
98
98
|
|
|
@@ -104,7 +104,7 @@ export interface CaptureOptions {
|
|
|
104
104
|
|
|
105
105
|
/**
|
|
106
106
|
* Mode applied to filtered nodes of the cloned tree.
|
|
107
|
-
* Default: "
|
|
107
|
+
* Default: "hide"
|
|
108
108
|
*/
|
|
109
109
|
filterMode?: FilterMode;
|
|
110
110
|
|
|
@@ -141,7 +141,7 @@ export interface CaptureOptions {
|
|
|
141
141
|
* - String: use as-is.
|
|
142
142
|
* - Callback: receives measured width/height and original src, returns a URL string.
|
|
143
143
|
*/
|
|
144
|
-
|
|
144
|
+
fallbackURL?:
|
|
145
145
|
| string
|
|
146
146
|
| ((
|
|
147
147
|
args: {
|