@zumer/snapdom 1.9.12-dev.2 → 1.9.12-dev.4
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 +664 -185
- package/dist/snapdom.min.js +5 -5
- package/dist/snapdom.min.mjs +5 -5
- package/dist/snapdom.mjs +658 -181
- package/package.json +1 -1
- package/types/snapdom.d.ts +1 -1
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.4
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -581,17 +581,6 @@ function getStyle(el, pseudo = null) {
|
|
|
581
581
|
}
|
|
582
582
|
return map.get(pseudo);
|
|
583
583
|
}
|
|
584
|
-
function parseContent(content) {
|
|
585
|
-
let clean = content.replace(/^['"]|['"]$/g, "");
|
|
586
|
-
if (clean.startsWith("\\")) {
|
|
587
|
-
try {
|
|
588
|
-
return String.fromCharCode(parseInt(clean.replace("\\", ""), 16));
|
|
589
|
-
} catch {
|
|
590
|
-
return clean;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
return clean;
|
|
594
|
-
}
|
|
595
584
|
function snapshotComputedStyle(style) {
|
|
596
585
|
const snap = {};
|
|
597
586
|
for (let prop of style) {
|
|
@@ -628,8 +617,9 @@ function idle(fn, { fast = false } = {}) {
|
|
|
628
617
|
function isSafari() {
|
|
629
618
|
const ua = typeof navigator !== "undefined" && navigator.userAgent ? navigator.userAgent : "";
|
|
630
619
|
const isSafariUA = /^((?!chrome|android).)*safari/i.test(ua);
|
|
620
|
+
const isUIWebView = /AppleWebKit/i.test(ua) && /Mobile/i.test(ua) && !/Safari/i.test(ua);
|
|
631
621
|
const isWeChatUA = /(MicroMessenger|wxwork|WeCom|WindowsWechat|MacWechat)/i.test(ua);
|
|
632
|
-
return isSafariUA || isWeChatUA;
|
|
622
|
+
return isSafariUA || isUIWebView || isWeChatUA;
|
|
633
623
|
}
|
|
634
624
|
|
|
635
625
|
// src/modules/styles.js
|
|
@@ -2100,10 +2090,20 @@ function buildCounterContext(root) {
|
|
|
2100
2090
|
const empty = /* @__PURE__ */ new Map();
|
|
2101
2091
|
build(rootEl, empty, empty);
|
|
2102
2092
|
return {
|
|
2093
|
+
/**
|
|
2094
|
+
* Get top value for counter name at given node.
|
|
2095
|
+
* @param {Element} node
|
|
2096
|
+
* @param {string} name
|
|
2097
|
+
*/
|
|
2103
2098
|
get(node, name) {
|
|
2104
2099
|
const s = nodeCounters.get(node)?.get(name);
|
|
2105
2100
|
return s && s.length ? s[s.length - 1] : 0;
|
|
2106
2101
|
},
|
|
2102
|
+
/**
|
|
2103
|
+
* Get full stack for counter name at given node.
|
|
2104
|
+
* @param {Element} node
|
|
2105
|
+
* @param {string} name
|
|
2106
|
+
*/
|
|
2107
2107
|
getStack(node, name) {
|
|
2108
2108
|
const s = nodeCounters.get(node)?.get(name);
|
|
2109
2109
|
return s ? s.slice() : [];
|
|
@@ -2139,8 +2139,109 @@ function resolveCountersInContent(raw, node, ctx) {
|
|
|
2139
2139
|
|
|
2140
2140
|
// src/modules/pseudo.js
|
|
2141
2141
|
var counterCtx = null;
|
|
2142
|
+
var __siblingCounters = /* @__PURE__ */ new WeakMap();
|
|
2143
|
+
function unquoteDoubleStrings2(s) {
|
|
2144
|
+
return (s || "").replace(/"([^"]*)"/g, "$1");
|
|
2145
|
+
}
|
|
2146
|
+
function collapseCssContent(raw) {
|
|
2147
|
+
if (!raw) return "";
|
|
2148
|
+
const tokens = [];
|
|
2149
|
+
const rx = /"([^"]*)"/g;
|
|
2150
|
+
let m;
|
|
2151
|
+
while (m = rx.exec(raw)) tokens.push(m[1]);
|
|
2152
|
+
if (tokens.length) return tokens.join("");
|
|
2153
|
+
return unquoteDoubleStrings2(raw);
|
|
2154
|
+
}
|
|
2155
|
+
function withSiblingOverrides(node, base) {
|
|
2156
|
+
const parent = node.parentElement;
|
|
2157
|
+
const map = parent ? __siblingCounters.get(parent) : null;
|
|
2158
|
+
if (!map) return base;
|
|
2159
|
+
return {
|
|
2160
|
+
get(n, name) {
|
|
2161
|
+
const v = base.get(n, name);
|
|
2162
|
+
const ov = map.get(name);
|
|
2163
|
+
return typeof ov === "number" ? Math.max(v, ov) : v;
|
|
2164
|
+
},
|
|
2165
|
+
getStack(n, name) {
|
|
2166
|
+
const s = base.getStack(n, name);
|
|
2167
|
+
if (!s.length) return s;
|
|
2168
|
+
const ov = map.get(name);
|
|
2169
|
+
if (typeof ov === "number") {
|
|
2170
|
+
const out = s.slice();
|
|
2171
|
+
out[out.length - 1] = Math.max(out[out.length - 1], ov);
|
|
2172
|
+
return out;
|
|
2173
|
+
}
|
|
2174
|
+
return s;
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2178
|
+
function deriveCounterCtxForPseudo(node, pseudoStyle, baseCtx) {
|
|
2179
|
+
const modStacks = /* @__PURE__ */ new Map();
|
|
2180
|
+
function parseListDecl(value) {
|
|
2181
|
+
const out = [];
|
|
2182
|
+
if (!value || value === "none") return out;
|
|
2183
|
+
for (const part of String(value).split(",")) {
|
|
2184
|
+
const toks = part.trim().split(/\s+/);
|
|
2185
|
+
const name = toks[0];
|
|
2186
|
+
const num = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : void 0;
|
|
2187
|
+
if (name) out.push({ name, num });
|
|
2188
|
+
}
|
|
2189
|
+
return out;
|
|
2190
|
+
}
|
|
2191
|
+
const resets = parseListDecl(pseudoStyle?.counterReset);
|
|
2192
|
+
const incs = parseListDecl(pseudoStyle?.counterIncrement);
|
|
2193
|
+
function getStackDerived(name) {
|
|
2194
|
+
if (modStacks.has(name)) return modStacks.get(name).slice();
|
|
2195
|
+
let stack = baseCtx.getStack(node, name);
|
|
2196
|
+
stack = stack.length ? stack.slice() : [];
|
|
2197
|
+
const r = resets.find((x) => x.name === name);
|
|
2198
|
+
if (r) {
|
|
2199
|
+
const val = Number.isFinite(r.num) ? r.num : 0;
|
|
2200
|
+
stack = stack.length ? [...stack, val] : [val];
|
|
2201
|
+
}
|
|
2202
|
+
const inc = incs.find((x) => x.name === name);
|
|
2203
|
+
if (inc) {
|
|
2204
|
+
const by = Number.isFinite(inc.num) ? inc.num : 1;
|
|
2205
|
+
if (stack.length === 0) stack = [0];
|
|
2206
|
+
stack[stack.length - 1] += by;
|
|
2207
|
+
}
|
|
2208
|
+
modStacks.set(name, stack.slice());
|
|
2209
|
+
return stack;
|
|
2210
|
+
}
|
|
2211
|
+
return {
|
|
2212
|
+
get(_node, name) {
|
|
2213
|
+
const s = getStackDerived(name);
|
|
2214
|
+
return s.length ? s[s.length - 1] : 0;
|
|
2215
|
+
},
|
|
2216
|
+
getStack(_node, name) {
|
|
2217
|
+
return getStackDerived(name);
|
|
2218
|
+
},
|
|
2219
|
+
/** expone increments del pseudo para que el caller pueda propagar a hermanos */
|
|
2220
|
+
__incs: incs
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
function resolvePseudoContentAndIncs(node, pseudo, baseCtx) {
|
|
2224
|
+
let ps;
|
|
2225
|
+
try {
|
|
2226
|
+
ps = getComputedStyle(node, pseudo);
|
|
2227
|
+
} catch {
|
|
2228
|
+
}
|
|
2229
|
+
const raw = ps?.content;
|
|
2230
|
+
if (!raw || raw === "none" || raw === "normal") return { text: "", incs: [] };
|
|
2231
|
+
const baseWithSiblings = withSiblingOverrides(node, baseCtx);
|
|
2232
|
+
const derived = deriveCounterCtxForPseudo(node, ps, baseWithSiblings);
|
|
2233
|
+
let resolved = hasCounters(raw) ? resolveCountersInContent(raw, node, derived) : raw;
|
|
2234
|
+
const text = collapseCssContent(resolved);
|
|
2235
|
+
return { text, incs: derived.__incs || [] };
|
|
2236
|
+
}
|
|
2142
2237
|
async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
2143
2238
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
2239
|
+
if (!counterCtx) {
|
|
2240
|
+
try {
|
|
2241
|
+
counterCtx = buildCounterContext(source.ownerDocument || document);
|
|
2242
|
+
} catch {
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2144
2245
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
2145
2246
|
try {
|
|
2146
2247
|
const style = getStyle(source, pseudo);
|
|
@@ -2171,12 +2272,8 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2171
2272
|
clone.insertBefore(span, restNode);
|
|
2172
2273
|
continue;
|
|
2173
2274
|
}
|
|
2174
|
-
const
|
|
2175
|
-
|
|
2176
|
-
if (hasCounters(content)) {
|
|
2177
|
-
counterCtx = buildCounterContext(source.ownerDocument || document);
|
|
2178
|
-
cleanContent = resolveCountersInContent(cleanContent, source, counterCtx);
|
|
2179
|
-
}
|
|
2275
|
+
const rawContent = style.content;
|
|
2276
|
+
const { text: cleanContent, incs } = resolvePseudoContentAndIncs(source, pseudo, counterCtx);
|
|
2180
2277
|
const bg = style.backgroundImage;
|
|
2181
2278
|
const bgColor = style.backgroundColor;
|
|
2182
2279
|
const fontFamily = style.fontFamily;
|
|
@@ -2190,28 +2287,42 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2190
2287
|
const borderWidth = parseFloat(style.borderWidth);
|
|
2191
2288
|
const transform = style.transform;
|
|
2192
2289
|
const isIconFont22 = isIconFont2(fontFamily);
|
|
2193
|
-
const hasExplicitContent =
|
|
2290
|
+
const hasExplicitContent = rawContent !== "none" && cleanContent !== "";
|
|
2194
2291
|
const hasBg = bg && bg !== "none";
|
|
2195
2292
|
const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
|
|
2196
2293
|
const hasBox2 = display !== "inline" && (width > 0 || height > 0);
|
|
2197
2294
|
const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
|
|
2198
2295
|
const hasTransform = transform && transform !== "none";
|
|
2199
|
-
const shouldRender = hasExplicitContent || hasBg || hasBgColor ||
|
|
2200
|
-
if (!shouldRender)
|
|
2296
|
+
const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBorder || hasTransform;
|
|
2297
|
+
if (!shouldRender) {
|
|
2298
|
+
if (incs && incs.length && source.parentElement) {
|
|
2299
|
+
const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
|
|
2300
|
+
for (const { name, num } of incs) {
|
|
2301
|
+
if (!name) continue;
|
|
2302
|
+
const baseWithSibs = withSiblingOverrides(source, counterCtx);
|
|
2303
|
+
const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
|
|
2304
|
+
const finalVal = derived.get(source, name);
|
|
2305
|
+
map.set(name, finalVal);
|
|
2306
|
+
}
|
|
2307
|
+
__siblingCounters.set(source.parentElement, map);
|
|
2308
|
+
}
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2201
2311
|
const pseudoEl = document.createElement("span");
|
|
2202
2312
|
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
2203
2313
|
pseudoEl.style.verticalAlign = "middle";
|
|
2314
|
+
pseudoEl.style.pointerEvents = "none";
|
|
2204
2315
|
const snapshot = snapshotComputedStyle(style);
|
|
2205
2316
|
const key = getStyleKey(snapshot, "span");
|
|
2206
2317
|
sessionCache.styleMap.set(pseudoEl, key);
|
|
2207
|
-
if (isIconFont22 && cleanContent.length === 1) {
|
|
2208
|
-
const { dataUrl, width:
|
|
2318
|
+
if (isIconFont22 && cleanContent && cleanContent.length === 1) {
|
|
2319
|
+
const { dataUrl, width: w, height: h } = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
2209
2320
|
const imgEl = document.createElement("img");
|
|
2210
2321
|
imgEl.src = dataUrl;
|
|
2211
|
-
imgEl.style = `height:${fontSize}px;width:${
|
|
2322
|
+
imgEl.style = `height:${fontSize}px;width:${w / h * fontSize}px;object-fit:contain;`;
|
|
2212
2323
|
pseudoEl.appendChild(imgEl);
|
|
2213
2324
|
clone.dataset.snapdomHasIcon = "true";
|
|
2214
|
-
} else if (cleanContent.startsWith("url(")) {
|
|
2325
|
+
} else if (cleanContent && cleanContent.startsWith("url(")) {
|
|
2215
2326
|
const rawUrl = extractURL(cleanContent);
|
|
2216
2327
|
if (rawUrl?.trim()) {
|
|
2217
2328
|
try {
|
|
@@ -2227,6 +2338,10 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2227
2338
|
} else if (!isIconFont22 && hasExplicitContent) {
|
|
2228
2339
|
pseudoEl.textContent = cleanContent;
|
|
2229
2340
|
}
|
|
2341
|
+
pseudoEl.style.background = "none";
|
|
2342
|
+
if ("mask" in pseudoEl.style) {
|
|
2343
|
+
pseudoEl.style.mask = "none";
|
|
2344
|
+
}
|
|
2230
2345
|
if (hasBg) {
|
|
2231
2346
|
try {
|
|
2232
2347
|
const bgSplits = splitBackgroundImage(bg);
|
|
@@ -2238,7 +2353,18 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2238
2353
|
}
|
|
2239
2354
|
if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
|
|
2240
2355
|
const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent?.trim() !== "";
|
|
2241
|
-
const hasVisibleBox = hasContent2 || hasBg || hasBgColor ||
|
|
2356
|
+
const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBorder || hasTransform;
|
|
2357
|
+
if (incs && incs.length && source.parentElement) {
|
|
2358
|
+
const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
|
|
2359
|
+
const baseWithSibs = withSiblingOverrides(source, counterCtx);
|
|
2360
|
+
const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
|
|
2361
|
+
for (const { name } of incs) {
|
|
2362
|
+
if (!name) continue;
|
|
2363
|
+
const finalVal = derived.get(source, name);
|
|
2364
|
+
map.set(name, finalVal);
|
|
2365
|
+
}
|
|
2366
|
+
__siblingCounters.set(source.parentElement, map);
|
|
2367
|
+
}
|
|
2242
2368
|
if (!hasVisibleBox) continue;
|
|
2243
2369
|
if (pseudo === "::before") {
|
|
2244
2370
|
clone.insertBefore(pseudoEl, clone.firstChild);
|
|
@@ -2306,6 +2432,102 @@ function inlineExternalDefsAndSymbols(rootElement) {
|
|
|
2306
2432
|
});
|
|
2307
2433
|
}
|
|
2308
2434
|
|
|
2435
|
+
// src/modules/changeCSS.js
|
|
2436
|
+
function freezeSticky(originalRoot, cloneRoot) {
|
|
2437
|
+
if (!originalRoot || !cloneRoot) return;
|
|
2438
|
+
const scrollTop = originalRoot.scrollTop || 0;
|
|
2439
|
+
if (!scrollTop) return;
|
|
2440
|
+
if (getComputedStyle(cloneRoot).position === "static") {
|
|
2441
|
+
cloneRoot.style.position = "relative";
|
|
2442
|
+
}
|
|
2443
|
+
const rootRect = originalRoot.getBoundingClientRect();
|
|
2444
|
+
const viewportH = originalRoot.clientHeight;
|
|
2445
|
+
const PH_ATTR = "data-snap-ph";
|
|
2446
|
+
const walker = document.createTreeWalker(originalRoot, NodeFilter.SHOW_ELEMENT);
|
|
2447
|
+
while (walker.nextNode()) {
|
|
2448
|
+
const el = (
|
|
2449
|
+
/** @type {HTMLElement} */
|
|
2450
|
+
walker.currentNode
|
|
2451
|
+
);
|
|
2452
|
+
const cs = getComputedStyle(el);
|
|
2453
|
+
const pos = cs.position;
|
|
2454
|
+
if (pos !== "sticky" && pos !== "-webkit-sticky") continue;
|
|
2455
|
+
const topInit = _toPx(cs.top);
|
|
2456
|
+
const bottomInit = _toPx(cs.bottom);
|
|
2457
|
+
if (topInit == null && bottomInit == null) continue;
|
|
2458
|
+
const path = _pathOf(el, originalRoot);
|
|
2459
|
+
const cloneEl = _findByPathIgnoringPlaceholders(cloneRoot, path, PH_ATTR);
|
|
2460
|
+
if (!cloneEl) continue;
|
|
2461
|
+
const elRect = el.getBoundingClientRect();
|
|
2462
|
+
const widthPx = elRect.width;
|
|
2463
|
+
const heightPx = elRect.height;
|
|
2464
|
+
const leftPx = elRect.left - rootRect.left;
|
|
2465
|
+
if (!(widthPx > 0 && heightPx > 0)) continue;
|
|
2466
|
+
if (!Number.isFinite(leftPx)) continue;
|
|
2467
|
+
const topAbsPx = topInit != null ? topInit + scrollTop : scrollTop + (viewportH - heightPx - /** bottomInit non-null */
|
|
2468
|
+
bottomInit);
|
|
2469
|
+
if (!Number.isFinite(topAbsPx)) continue;
|
|
2470
|
+
const zParsed = Number.parseInt(cs.zIndex, 10);
|
|
2471
|
+
const hasZ = Number.isFinite(zParsed);
|
|
2472
|
+
const overlayZ = hasZ ? Math.max(zParsed, 1) + 1 : 2;
|
|
2473
|
+
const placeholderZ = hasZ ? zParsed - 1 : 0;
|
|
2474
|
+
const ph = cloneEl.cloneNode(false);
|
|
2475
|
+
ph.setAttribute(PH_ATTR, "1");
|
|
2476
|
+
ph.style.position = "sticky";
|
|
2477
|
+
ph.style.left = `${leftPx}px`;
|
|
2478
|
+
ph.style.top = `${topAbsPx}px`;
|
|
2479
|
+
ph.style.width = `${widthPx}px`;
|
|
2480
|
+
ph.style.height = `${heightPx}px`;
|
|
2481
|
+
ph.style.visibility = "hidden";
|
|
2482
|
+
ph.style.zIndex = String(placeholderZ);
|
|
2483
|
+
ph.style.overflow = "hidden";
|
|
2484
|
+
ph.style.background = "transparent";
|
|
2485
|
+
ph.style.boxShadow = "none";
|
|
2486
|
+
ph.style.filter = "none";
|
|
2487
|
+
cloneEl.parentElement?.insertBefore(ph, cloneEl);
|
|
2488
|
+
cloneEl.style.position = "absolute";
|
|
2489
|
+
cloneEl.style.left = `${leftPx}px`;
|
|
2490
|
+
cloneEl.style.top = `${topAbsPx}px`;
|
|
2491
|
+
cloneEl.style.bottom = "auto";
|
|
2492
|
+
cloneEl.style.zIndex = String(overlayZ);
|
|
2493
|
+
cloneEl.style.pointerEvents = "none";
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
function _toPx(v) {
|
|
2497
|
+
if (!v || v === "auto") return null;
|
|
2498
|
+
const n = Number.parseFloat(v);
|
|
2499
|
+
return Number.isFinite(n) ? n : null;
|
|
2500
|
+
}
|
|
2501
|
+
function _pathOf(el, root) {
|
|
2502
|
+
const path = [];
|
|
2503
|
+
for (let cur = el; cur && cur !== root; ) {
|
|
2504
|
+
const p = cur.parentElement;
|
|
2505
|
+
if (!p) break;
|
|
2506
|
+
path.push(Array.prototype.indexOf.call(p.children, cur));
|
|
2507
|
+
cur = p;
|
|
2508
|
+
}
|
|
2509
|
+
return path.reverse();
|
|
2510
|
+
}
|
|
2511
|
+
function _findByPathIgnoringPlaceholders(root, path, phAttr) {
|
|
2512
|
+
let cur = root;
|
|
2513
|
+
for (let i = 0; i < path.length; i++) {
|
|
2514
|
+
const kids = _childrenWithoutPlaceholders(cur, phAttr);
|
|
2515
|
+
cur = /** @type {HTMLElement|undefined} */
|
|
2516
|
+
kids[path[i]];
|
|
2517
|
+
if (!cur) return null;
|
|
2518
|
+
}
|
|
2519
|
+
return cur instanceof HTMLElement ? cur : null;
|
|
2520
|
+
}
|
|
2521
|
+
function _childrenWithoutPlaceholders(el, phAttr) {
|
|
2522
|
+
const out = [];
|
|
2523
|
+
const ch = el.children;
|
|
2524
|
+
for (let i = 0; i < ch.length; i++) {
|
|
2525
|
+
const c = ch[i];
|
|
2526
|
+
if (!c.hasAttribute(phAttr)) out.push(c);
|
|
2527
|
+
}
|
|
2528
|
+
return out;
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2309
2531
|
// src/core/prepare.js
|
|
2310
2532
|
async function prepareClone(element, options = {}) {
|
|
2311
2533
|
const sessionCache = {
|
|
@@ -2380,17 +2602,20 @@ async function prepareClone(element, options = {}) {
|
|
|
2380
2602
|
cloneNode.appendChild(inner);
|
|
2381
2603
|
}
|
|
2382
2604
|
}
|
|
2605
|
+
const contentRoot = clone instanceof HTMLElement && clone.firstElementChild instanceof HTMLElement ? clone.firstElementChild : clone;
|
|
2606
|
+
freezeSticky(element, contentRoot);
|
|
2383
2607
|
if (element === sessionCache.nodeMap.get(clone)) {
|
|
2384
2608
|
const computed = sessionCache.styleCache.get(element) || window.getComputedStyle(element);
|
|
2385
2609
|
sessionCache.styleCache.set(element, computed);
|
|
2386
2610
|
const transform = stripTranslate(computed.transform);
|
|
2387
2611
|
clone.style.margin = "0";
|
|
2388
|
-
clone.style.position = "static";
|
|
2389
2612
|
clone.style.top = "auto";
|
|
2390
2613
|
clone.style.left = "auto";
|
|
2391
2614
|
clone.style.right = "auto";
|
|
2392
2615
|
clone.style.bottom = "auto";
|
|
2393
|
-
clone.style.
|
|
2616
|
+
clone.style.animation = "none";
|
|
2617
|
+
clone.style.transition = "none";
|
|
2618
|
+
clone.style.willChange = "auto";
|
|
2394
2619
|
clone.style.float = "none";
|
|
2395
2620
|
clone.style.clear = "none";
|
|
2396
2621
|
clone.style.transform = transform || "";
|
|
@@ -2786,7 +3011,7 @@ async function captureDOM(element, options) {
|
|
|
2786
3011
|
idle(async () => {
|
|
2787
3012
|
const required = collectUsedFontVariants(element);
|
|
2788
3013
|
const usedCodepoints = collectUsedCodepoints(element);
|
|
2789
|
-
if (isSafari) {
|
|
3014
|
+
if (isSafari()) {
|
|
2790
3015
|
const families = new Set(
|
|
2791
3016
|
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2792
3017
|
);
|
|
@@ -2818,101 +3043,186 @@ async function captureDOM(element, options) {
|
|
|
2818
3043
|
}
|
|
2819
3044
|
await new Promise((resolve) => {
|
|
2820
3045
|
idle(() => {
|
|
2821
|
-
const
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
3046
|
+
const csEl = getComputedStyle(element);
|
|
3047
|
+
function parseFilterDropShadows(cs) {
|
|
3048
|
+
const raw = `${cs.filter || ""} ${cs.webkitFilter || ""}`.trim();
|
|
3049
|
+
if (!raw || raw === "none") {
|
|
3050
|
+
return { bleed: { top: 0, right: 0, bottom: 0, left: 0 }, has: false };
|
|
3051
|
+
}
|
|
3052
|
+
const tokens = raw.match(/drop-shadow\((?:[^()]|\([^()]*\))*\)/gi) || [];
|
|
3053
|
+
let t = 0, r = 0, b = 0, l = 0;
|
|
3054
|
+
let found = false;
|
|
3055
|
+
for (const tok of tokens) {
|
|
3056
|
+
found = true;
|
|
3057
|
+
const nums = tok.match(/-?\d+(?:\.\d+)?px/gi)?.map((v) => parseFloat(v)) || [];
|
|
3058
|
+
const [ox = 0, oy = 0, blur = 0] = nums;
|
|
3059
|
+
const extX = Math.abs(ox) + blur;
|
|
3060
|
+
const extY = Math.abs(oy) + blur;
|
|
3061
|
+
r = Math.max(r, extX + Math.max(ox, 0));
|
|
3062
|
+
l = Math.max(l, extX + Math.max(-ox, 0));
|
|
3063
|
+
b = Math.max(b, extY + Math.max(oy, 0));
|
|
3064
|
+
t = Math.max(t, extY + Math.max(-oy, 0));
|
|
3065
|
+
}
|
|
3066
|
+
return {
|
|
3067
|
+
bleed: { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b), left: Math.ceil(l) },
|
|
3068
|
+
has: found
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
function parseBoxShadow(cs) {
|
|
3072
|
+
const v = cs.boxShadow || "";
|
|
3073
|
+
if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
3074
|
+
const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
|
|
3075
|
+
let t = 0, r = 0, b2 = 0, l = 0;
|
|
3076
|
+
for (const part of parts) {
|
|
3077
|
+
const nums = part.match(/-?\d+(\.\d+)?px/g)?.map((n) => parseFloat(n)) || [];
|
|
3078
|
+
if (nums.length < 2) continue;
|
|
3079
|
+
const [ox2, oy2, blur = 0, spread = 0] = nums;
|
|
3080
|
+
const extX = Math.abs(ox2) + blur + spread;
|
|
3081
|
+
const extY = Math.abs(oy2) + blur + spread;
|
|
3082
|
+
r = Math.max(r, extX + Math.max(ox2, 0));
|
|
3083
|
+
l = Math.max(l, extX + Math.max(-ox2, 0));
|
|
3084
|
+
b2 = Math.max(b2, extY + Math.max(oy2, 0));
|
|
3085
|
+
t = Math.max(t, extY + Math.max(-oy2, 0));
|
|
3086
|
+
}
|
|
3087
|
+
return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
|
|
3088
|
+
}
|
|
3089
|
+
function parseFilterBlur(cs) {
|
|
3090
|
+
const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
|
|
3091
|
+
const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
|
|
3092
|
+
return { top: b2, right: b2, bottom: b2, left: b2 };
|
|
3093
|
+
}
|
|
3094
|
+
function parseOutline(cs) {
|
|
3095
|
+
if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
3096
|
+
const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
|
|
3097
|
+
return { top: w2, right: w2, bottom: w2, left: w2 };
|
|
3098
|
+
}
|
|
3099
|
+
function bboxWithOriginFull(w2, h2, M, ox2, oy2) {
|
|
3100
|
+
const a2 = M.a, b2 = M.b, c2 = M.c, d2 = M.d, e2 = M.e || 0, f2 = M.f || 0;
|
|
3101
|
+
function pt(x, y) {
|
|
3102
|
+
let X = x - ox2, Y = y - oy2;
|
|
3103
|
+
let X2 = a2 * X + c2 * Y, Y2 = b2 * X + d2 * Y;
|
|
3104
|
+
X2 += ox2 + e2;
|
|
3105
|
+
Y2 += oy2 + f2;
|
|
3106
|
+
return [X2, Y2];
|
|
3107
|
+
}
|
|
3108
|
+
const P = [pt(0, 0), pt(w2, 0), pt(0, h2), pt(w2, h2)];
|
|
3109
|
+
let minX2 = Infinity, minY2 = Infinity, maxX2 = -Infinity, maxY2 = -Infinity;
|
|
3110
|
+
for (const [X, Y] of P) {
|
|
3111
|
+
if (X < minX2) minX2 = X;
|
|
3112
|
+
if (Y < minY2) minY2 = Y;
|
|
3113
|
+
if (X > maxX2) maxX2 = X;
|
|
3114
|
+
if (Y > maxY2) maxY2 = Y;
|
|
2839
3115
|
}
|
|
3116
|
+
return { minX: minX2, minY: minY2, maxX: maxX2, maxY: maxY2, width: maxX2 - minX2, height: maxY2 - minY2 };
|
|
2840
3117
|
}
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
const
|
|
2845
|
-
const
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
const
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
3118
|
+
function hasTFBBox(el) {
|
|
3119
|
+
return hasBBoxAffectingTransform(el);
|
|
3120
|
+
}
|
|
3121
|
+
const rect = element.getBoundingClientRect();
|
|
3122
|
+
const w0 = Math.max(1, Math.ceil(element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
|
|
3123
|
+
const h0 = Math.max(1, Math.ceil(element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
|
|
3124
|
+
const coerceNum = (v, def = NaN) => {
|
|
3125
|
+
const n = typeof v === "string" ? parseFloat(v) : v;
|
|
3126
|
+
return Number.isFinite(n) ? n : def;
|
|
3127
|
+
};
|
|
3128
|
+
const optW = coerceNum(options.width);
|
|
3129
|
+
const optH = coerceNum(options.height);
|
|
3130
|
+
let w = w0, h = h0;
|
|
3131
|
+
const hasW = Number.isFinite(optW);
|
|
3132
|
+
const hasH = Number.isFinite(optH);
|
|
3133
|
+
const aspect0 = h0 > 0 ? w0 / h0 : 1;
|
|
3134
|
+
if (hasW && hasH) {
|
|
3135
|
+
w = Math.max(1, Math.ceil(optW));
|
|
3136
|
+
h = Math.max(1, Math.ceil(optH));
|
|
3137
|
+
} else if (hasW) {
|
|
3138
|
+
w = Math.max(1, Math.ceil(optW));
|
|
3139
|
+
h = Math.max(1, Math.ceil(w / (aspect0 || 1)));
|
|
3140
|
+
} else if (hasH) {
|
|
3141
|
+
h = Math.max(1, Math.ceil(optH));
|
|
3142
|
+
w = Math.max(1, Math.ceil(h * (aspect0 || 1)));
|
|
2854
3143
|
} else {
|
|
3144
|
+
w = w0;
|
|
3145
|
+
h = h0;
|
|
3146
|
+
}
|
|
3147
|
+
let minX = 0, minY = 0, maxX = w0, maxY = h0;
|
|
3148
|
+
if (hasTFBBox(element)) {
|
|
3149
|
+
const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
|
|
3150
|
+
const ind2 = readIndividualTransforms(element);
|
|
2855
3151
|
const TOTAL = readTotalTransformMatrix({
|
|
2856
|
-
baseTransform,
|
|
2857
|
-
rotate:
|
|
2858
|
-
scale:
|
|
2859
|
-
translate:
|
|
3152
|
+
baseTransform: baseTransform2,
|
|
3153
|
+
rotate: ind2.rotate || "0deg",
|
|
3154
|
+
scale: ind2.scale,
|
|
3155
|
+
translate: ind2.translate
|
|
2860
3156
|
});
|
|
2861
|
-
const
|
|
2862
|
-
const
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
3157
|
+
const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
|
|
3158
|
+
const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
|
|
3159
|
+
const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
|
|
3160
|
+
minX = bb.minX;
|
|
3161
|
+
minY = bb.minY;
|
|
3162
|
+
maxX = bb.maxX;
|
|
3163
|
+
maxY = bb.maxY;
|
|
3164
|
+
}
|
|
3165
|
+
const bleedShadow = parseBoxShadow(csEl);
|
|
3166
|
+
const bleedBlur = parseFilterBlur(csEl);
|
|
3167
|
+
const bleedOutline = parseOutline(csEl);
|
|
3168
|
+
const drop = parseFilterDropShadows(csEl);
|
|
3169
|
+
const bleed = {
|
|
3170
|
+
top: bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top,
|
|
3171
|
+
right: bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right,
|
|
3172
|
+
bottom: bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom,
|
|
3173
|
+
left: bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left
|
|
3174
|
+
};
|
|
3175
|
+
minX -= bleed.left;
|
|
3176
|
+
minY -= bleed.top;
|
|
3177
|
+
maxX += bleed.right;
|
|
3178
|
+
maxY += bleed.bottom;
|
|
3179
|
+
const vbW0 = Math.max(1, Math.ceil(maxX - minX));
|
|
3180
|
+
const vbH0 = Math.max(1, Math.ceil(maxY - minY));
|
|
3181
|
+
const outW = Math.max(1, Math.round(vbW0 * (hasW || hasH ? w / w0 : 1)));
|
|
3182
|
+
const outH = Math.max(1, Math.round(vbH0 * (hasH || hasW ? h / h0 : 1)));
|
|
2872
3183
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
3184
|
+
const pad = isSafari() ? 1 : 0;
|
|
2873
3185
|
const fo = document.createElementNS(svgNS, "foreignObject");
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
fo.setAttribute("x", String(
|
|
2877
|
-
fo.setAttribute("y", String(
|
|
3186
|
+
const vbMinX = Math.floor(minX);
|
|
3187
|
+
const vbMinY = Math.floor(minY);
|
|
3188
|
+
fo.setAttribute("x", String(-(vbMinX - pad)));
|
|
3189
|
+
fo.setAttribute("y", String(-(vbMinY - pad)));
|
|
3190
|
+
fo.setAttribute("width", String(Math.ceil(w0 + pad * 2)));
|
|
3191
|
+
fo.setAttribute("height", String(Math.ceil(h0 + pad * 2)));
|
|
2878
3192
|
fo.style.overflow = "visible";
|
|
2879
3193
|
const styleTag = document.createElement("style");
|
|
2880
3194
|
styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + classCSS;
|
|
2881
3195
|
fo.appendChild(styleTag);
|
|
2882
3196
|
const container = document.createElement("div");
|
|
2883
3197
|
container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
2884
|
-
container.style.width = `${
|
|
2885
|
-
container.style.height = `${
|
|
3198
|
+
container.style.width = `${w0}px`;
|
|
3199
|
+
container.style.height = `${h0}px`;
|
|
2886
3200
|
container.style.overflow = "visible";
|
|
2887
|
-
container.style.transformOrigin = "0 0";
|
|
2888
|
-
const cancels = [];
|
|
2889
|
-
if (cancelTranslateX || cancelTranslateY) {
|
|
2890
|
-
cancels.push(`translate(${cancelTranslateX}px, ${cancelTranslateY}px)`);
|
|
2891
|
-
}
|
|
2892
|
-
if (!hasScale && (hasW || hasH)) {
|
|
2893
|
-
const sxWH = rect.width ? w / rect.width : 1;
|
|
2894
|
-
const syWH = rect.height ? h / rect.height : 1;
|
|
2895
|
-
cancels.push(`scale(${sxWH}, ${syWH})`);
|
|
2896
|
-
} else if (hasScale) {
|
|
2897
|
-
cancels.push(`scale(${scaleOpt})`);
|
|
2898
|
-
}
|
|
2899
|
-
if (cancels.length) container.style.transform = cancels.join(" ");
|
|
2900
3201
|
clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
2901
|
-
clone.style.transformOrigin = "0 0";
|
|
2902
|
-
clone.style.position = "static";
|
|
2903
|
-
clone.style.left = "0";
|
|
2904
|
-
clone.style.top = "0";
|
|
2905
|
-
if (baseTransform) clone.style.transform = baseTransform;
|
|
2906
|
-
clone.style.rotate = ind.rotate || "0deg";
|
|
2907
|
-
clone.style.scale = ind.scale || "1";
|
|
2908
|
-
clone.style.translate = ind.translate || "0 0";
|
|
2909
3202
|
container.appendChild(clone);
|
|
2910
3203
|
fo.appendChild(container);
|
|
2911
3204
|
const serializer = new XMLSerializer();
|
|
2912
3205
|
const foString = serializer.serializeToString(fo);
|
|
2913
|
-
const
|
|
2914
|
-
const
|
|
2915
|
-
const
|
|
3206
|
+
const vbW = vbW0 + pad * 2;
|
|
3207
|
+
const vbH = vbH0 + pad * 2;
|
|
3208
|
+
const wantsSize = hasW || hasH;
|
|
3209
|
+
options.meta = {
|
|
3210
|
+
w0,
|
|
3211
|
+
// ancho natural del elemento
|
|
3212
|
+
h0,
|
|
3213
|
+
// alto natural
|
|
3214
|
+
vbW,
|
|
3215
|
+
// ancho del viewBox
|
|
3216
|
+
vbH,
|
|
3217
|
+
// alto del viewBox
|
|
3218
|
+
targetW: w,
|
|
3219
|
+
// ancho deseado según options.width
|
|
3220
|
+
targetH: h
|
|
3221
|
+
// alto deseado según options.height
|
|
3222
|
+
};
|
|
3223
|
+
const svgOutW = isSafari() && wantsSize ? vbW : outW + pad * 2;
|
|
3224
|
+
const svgOutH = isSafari() && wantsSize ? vbH : outH + pad * 2;
|
|
3225
|
+
const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
|
|
2916
3226
|
const svgFooter = "</svg>";
|
|
2917
3227
|
svgString = svgHeader + foString + svgFooter;
|
|
2918
3228
|
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
@@ -2932,26 +3242,6 @@ function matrixFromComputed(el) {
|
|
|
2932
3242
|
return new WebKitCSSMatrix(tr);
|
|
2933
3243
|
}
|
|
2934
3244
|
}
|
|
2935
|
-
function matrix2DNoTranslate(M) {
|
|
2936
|
-
return new DOMMatrix([M.a, M.b, M.c, M.d, 0, 0]);
|
|
2937
|
-
}
|
|
2938
|
-
function bboxFromMatrix(w, h, M2D) {
|
|
2939
|
-
const pts = [
|
|
2940
|
-
{ x: 0, y: 0 },
|
|
2941
|
-
{ x: w, y: 0 },
|
|
2942
|
-
{ x: 0, y: h },
|
|
2943
|
-
{ x: w, y: h }
|
|
2944
|
-
];
|
|
2945
|
-
const t = pts.map((p) => ({
|
|
2946
|
-
x: M2D.a * p.x + M2D.c * p.y,
|
|
2947
|
-
y: M2D.b * p.x + M2D.d * p.y
|
|
2948
|
-
}));
|
|
2949
|
-
const minX = Math.min(...t.map((p) => p.x));
|
|
2950
|
-
const maxX = Math.max(...t.map((p) => p.x));
|
|
2951
|
-
const minY = Math.min(...t.map((p) => p.y));
|
|
2952
|
-
const maxY = Math.max(...t.map((p) => p.y));
|
|
2953
|
-
return { minX, minY, width: maxX - minX, height: maxY - minY };
|
|
2954
|
-
}
|
|
2955
3245
|
function readIndividualTransforms(el) {
|
|
2956
3246
|
const out = { rotate: "0deg", scale: null, translate: null };
|
|
2957
3247
|
const map = typeof el.computedStyleMap === "function" ? el.computedStyleMap() : null;
|
|
@@ -2967,26 +3257,37 @@ function readIndividualTransforms(el) {
|
|
|
2967
3257
|
};
|
|
2968
3258
|
const rot = safeGet("rotate");
|
|
2969
3259
|
if (rot) {
|
|
2970
|
-
|
|
2971
|
-
|
|
3260
|
+
if (rot.angle) {
|
|
3261
|
+
const ang = rot.angle;
|
|
2972
3262
|
out.rotate = ang.unit === "rad" ? ang.value * 180 / Math.PI + "deg" : ang.value + ang.unit;
|
|
3263
|
+
} else if (rot.unit) {
|
|
3264
|
+
out.rotate = rot.unit === "rad" ? rot.value * 180 / Math.PI + "deg" : rot.value + rot.unit;
|
|
2973
3265
|
} else {
|
|
2974
3266
|
out.rotate = String(rot);
|
|
2975
3267
|
}
|
|
3268
|
+
} else {
|
|
3269
|
+
const cs2 = getComputedStyle(el);
|
|
3270
|
+
out.rotate = cs2.rotate && cs2.rotate !== "none" ? cs2.rotate : "0deg";
|
|
2976
3271
|
}
|
|
2977
3272
|
const sc = safeGet("scale");
|
|
2978
|
-
if (sc
|
|
2979
|
-
const sx = sc[0]?.value
|
|
2980
|
-
const sy = sc[1]?.value
|
|
3273
|
+
if (sc) {
|
|
3274
|
+
const sx = "x" in sc && sc.x?.value != null ? sc.x.value : Array.isArray(sc) ? sc[0]?.value : Number(sc) || 1;
|
|
3275
|
+
const sy = "y" in sc && sc.y?.value != null ? sc.y.value : Array.isArray(sc) ? sc[1]?.value : sx;
|
|
2981
3276
|
out.scale = `${sx} ${sy}`;
|
|
3277
|
+
} else {
|
|
3278
|
+
const cs2 = getComputedStyle(el);
|
|
3279
|
+
out.scale = cs2.scale && cs2.scale !== "none" ? cs2.scale : null;
|
|
2982
3280
|
}
|
|
2983
3281
|
const tr = safeGet("translate");
|
|
2984
|
-
if (tr
|
|
2985
|
-
const tx = tr[0]?.value
|
|
2986
|
-
const ty = tr[1]?.value
|
|
2987
|
-
const ux = tr
|
|
2988
|
-
const uy = tr
|
|
3282
|
+
if (tr) {
|
|
3283
|
+
const tx = "x" in tr && "value" in tr.x ? tr.x.value : Array.isArray(tr) ? tr[0]?.value : 0;
|
|
3284
|
+
const ty = "y" in tr && "value" in tr.y ? tr.y.value : Array.isArray(tr) ? tr[1]?.value : 0;
|
|
3285
|
+
const ux = "x" in tr && tr.x?.unit ? tr.x.unit : "px";
|
|
3286
|
+
const uy = "y" in tr && tr.y?.unit ? tr.y.unit : "px";
|
|
2989
3287
|
out.translate = `${tx}${ux} ${ty}${uy}`;
|
|
3288
|
+
} else {
|
|
3289
|
+
const cs2 = getComputedStyle(el);
|
|
3290
|
+
out.translate = cs2.translate && cs2.translate !== "none" ? cs2.translate : null;
|
|
2990
3291
|
}
|
|
2991
3292
|
return out;
|
|
2992
3293
|
}
|
|
@@ -2997,17 +3298,33 @@ function readIndividualTransforms(el) {
|
|
|
2997
3298
|
return out;
|
|
2998
3299
|
}
|
|
2999
3300
|
function hasBBoxAffectingTransform(el) {
|
|
3000
|
-
const
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
return true;
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
const
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3301
|
+
const cs = getComputedStyle(el);
|
|
3302
|
+
const t = cs.transform || "none";
|
|
3303
|
+
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);
|
|
3304
|
+
if (hasMatrix) return true;
|
|
3305
|
+
const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg";
|
|
3306
|
+
const s = cs.scale && cs.scale !== "none" && cs.scale !== "1";
|
|
3307
|
+
const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px";
|
|
3308
|
+
return Boolean(r || s || tr);
|
|
3309
|
+
}
|
|
3310
|
+
function parseTransformOriginPx(cs, w, h) {
|
|
3311
|
+
const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/);
|
|
3312
|
+
const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"];
|
|
3313
|
+
const toPx = (token, size) => {
|
|
3314
|
+
const t = token.toLowerCase();
|
|
3315
|
+
if (t === "left" || t === "top") return 0;
|
|
3316
|
+
if (t === "center") return size / 2;
|
|
3317
|
+
if (t === "right") return size;
|
|
3318
|
+
if (t === "bottom") return size;
|
|
3319
|
+
if (t.endsWith("px")) return parseFloat(t) || 0;
|
|
3320
|
+
if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100;
|
|
3321
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0;
|
|
3322
|
+
return 0;
|
|
3323
|
+
};
|
|
3324
|
+
return {
|
|
3325
|
+
ox: toPx(oxRaw, w),
|
|
3326
|
+
oy: toPx(oyRaw, h)
|
|
3327
|
+
};
|
|
3011
3328
|
}
|
|
3012
3329
|
var __measureHost = null;
|
|
3013
3330
|
function getMeasureHost() {
|
|
@@ -3095,40 +3412,198 @@ function createContext(options = {}) {
|
|
|
3095
3412
|
};
|
|
3096
3413
|
}
|
|
3097
3414
|
|
|
3098
|
-
// src/exporters/
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3415
|
+
// src/exporters/toCanvas.js
|
|
3416
|
+
function isSvgDataURL(u) {
|
|
3417
|
+
return typeof u === "string" && /^data:image\/svg\+xml/i.test(u);
|
|
3418
|
+
}
|
|
3419
|
+
function decodeSvgFromDataURL(u) {
|
|
3420
|
+
const i = u.indexOf(",");
|
|
3421
|
+
return i >= 0 ? decodeURIComponent(u.slice(i + 1)) : "";
|
|
3422
|
+
}
|
|
3423
|
+
function encodeSvgToDataURL(svgText) {
|
|
3424
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
|
|
3425
|
+
}
|
|
3426
|
+
function splitDecls(s) {
|
|
3427
|
+
let parts = [], buf = "", depth = 0;
|
|
3428
|
+
for (let i = 0; i < s.length; i++) {
|
|
3429
|
+
const ch = s[i];
|
|
3430
|
+
if (ch === "(") depth++;
|
|
3431
|
+
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
3432
|
+
if (ch === ";" && depth === 0) {
|
|
3433
|
+
parts.push(buf);
|
|
3434
|
+
buf = "";
|
|
3435
|
+
} else buf += ch;
|
|
3436
|
+
}
|
|
3437
|
+
if (buf.trim()) parts.push(buf);
|
|
3438
|
+
return parts.map((x) => x.trim()).filter(Boolean);
|
|
3439
|
+
}
|
|
3440
|
+
function boxShadowToDropShadow(value) {
|
|
3441
|
+
const layers = [];
|
|
3442
|
+
let buf = "", depth = 0;
|
|
3443
|
+
for (let i = 0; i < value.length; i++) {
|
|
3444
|
+
const ch = value[i];
|
|
3445
|
+
if (ch === "(") depth++;
|
|
3446
|
+
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
3447
|
+
if (ch === "," && depth === 0) {
|
|
3448
|
+
layers.push(buf.trim());
|
|
3449
|
+
buf = "";
|
|
3450
|
+
} else buf += ch;
|
|
3451
|
+
}
|
|
3452
|
+
if (buf.trim()) layers.push(buf.trim());
|
|
3453
|
+
const fns = [];
|
|
3454
|
+
for (const layer of layers) {
|
|
3455
|
+
if (/\binset\b/i.test(layer)) continue;
|
|
3456
|
+
const nums = layer.match(/-?\d+(?:\.\d+)?px/gi) || [];
|
|
3457
|
+
const [ox = "0px", oy = "0px", blur = "0px"] = nums;
|
|
3458
|
+
let color = layer.replace(/-?\d+(?:\.\d+)?px/gi, "").replace(/\binset\b/ig, "").trim().replace(/\s{2,}/g, " ");
|
|
3459
|
+
const hasColor = !!color && color !== ",";
|
|
3460
|
+
fns.push(`drop-shadow(${ox} ${oy} ${blur}${hasColor ? ` ${color}` : ""})`);
|
|
3461
|
+
}
|
|
3462
|
+
return fns.join(" ");
|
|
3463
|
+
}
|
|
3464
|
+
function rewriteDeclList(list) {
|
|
3465
|
+
const decls = splitDecls(list);
|
|
3466
|
+
let filter = null, wfilter = null, box = null;
|
|
3467
|
+
const rest = [];
|
|
3468
|
+
for (const d of decls) {
|
|
3469
|
+
const idx = d.indexOf(":");
|
|
3470
|
+
if (idx < 0) continue;
|
|
3471
|
+
const prop = d.slice(0, idx).trim().toLowerCase();
|
|
3472
|
+
const val = d.slice(idx + 1).trim();
|
|
3473
|
+
if (prop === "box-shadow") box = val;
|
|
3474
|
+
else if (prop === "filter") filter = val;
|
|
3475
|
+
else if (prop === "-webkit-filter") wfilter = val;
|
|
3476
|
+
else rest.push([prop, val]);
|
|
3477
|
+
}
|
|
3478
|
+
if (box) {
|
|
3479
|
+
const ds = boxShadowToDropShadow(box);
|
|
3480
|
+
if (ds) {
|
|
3481
|
+
filter = filter ? `${filter} ${ds}` : ds;
|
|
3482
|
+
wfilter = wfilter ? `${wfilter} ${ds}` : ds;
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
const out = [...rest];
|
|
3486
|
+
if (filter) out.push(["filter", filter]);
|
|
3487
|
+
if (wfilter) out.push(["-webkit-filter", wfilter]);
|
|
3488
|
+
return out.map(([k, v]) => `${k}:${v}`).join(";");
|
|
3489
|
+
}
|
|
3490
|
+
function rewriteCssBlock(css) {
|
|
3491
|
+
return css.replace(/([^{}]+)\{([^}]*)\}/g, (_m, sel, body) => `${sel}{${rewriteDeclList(body)}}`);
|
|
3492
|
+
}
|
|
3493
|
+
function rewriteSvgBoxShadowToDropShadow(svgText) {
|
|
3494
|
+
svgText = svgText.replace(
|
|
3495
|
+
/<style[^>]*>([\s\S]*?)<\/style>/gi,
|
|
3496
|
+
(m, css) => m.replace(css, rewriteCssBlock(css))
|
|
3497
|
+
);
|
|
3498
|
+
svgText = svgText.replace(
|
|
3499
|
+
/style=(['"])([\s\S]*?)\1/gi,
|
|
3500
|
+
(m, q, body) => `style=${q}${rewriteDeclList(body)}${q}`
|
|
3501
|
+
);
|
|
3502
|
+
return svgText;
|
|
3503
|
+
}
|
|
3504
|
+
function maybeConvertBoxShadowForSafari(url) {
|
|
3505
|
+
if (!isSafari() || !isSvgDataURL(url)) return url;
|
|
3506
|
+
try {
|
|
3507
|
+
const svg = decodeSvgFromDataURL(url);
|
|
3508
|
+
const fixed = rewriteSvgBoxShadowToDropShadow(svg);
|
|
3509
|
+
return encodeSvgToDataURL(fixed);
|
|
3510
|
+
} catch {
|
|
3511
|
+
return url;
|
|
3106
3512
|
}
|
|
3107
|
-
return img;
|
|
3108
3513
|
}
|
|
3109
|
-
|
|
3110
|
-
// src/exporters/toCanvas.js
|
|
3111
3514
|
async function toCanvas(url, options) {
|
|
3112
|
-
|
|
3515
|
+
let { width: optW, height: optH, scale = 1, dpr = 1, meta = {} } = options;
|
|
3516
|
+
url = maybeConvertBoxShadowForSafari(url);
|
|
3113
3517
|
const img = new Image();
|
|
3114
|
-
img.crossOrigin = "anonymous";
|
|
3115
3518
|
img.loading = "eager";
|
|
3116
3519
|
img.decoding = "sync";
|
|
3520
|
+
img.crossOrigin = "anonymous";
|
|
3117
3521
|
img.src = url;
|
|
3118
3522
|
await img.decode();
|
|
3119
|
-
const
|
|
3120
|
-
const
|
|
3523
|
+
const natW = img.naturalWidth;
|
|
3524
|
+
const natH = img.naturalHeight;
|
|
3525
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : natW;
|
|
3526
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : natH;
|
|
3527
|
+
let outW, outH;
|
|
3528
|
+
const hasW = Number.isFinite(optW);
|
|
3529
|
+
const hasH = Number.isFinite(optH);
|
|
3530
|
+
if (hasW && hasH) {
|
|
3531
|
+
outW = Math.max(1, optW);
|
|
3532
|
+
outH = Math.max(1, optH);
|
|
3533
|
+
} else if (hasW) {
|
|
3534
|
+
const k = optW / Math.max(1, refW);
|
|
3535
|
+
outW = optW;
|
|
3536
|
+
outH = Math.round(refH * k);
|
|
3537
|
+
} else if (hasH) {
|
|
3538
|
+
const k = optH / Math.max(1, refH);
|
|
3539
|
+
outH = optH;
|
|
3540
|
+
outW = Math.round(refW * k);
|
|
3541
|
+
} else {
|
|
3542
|
+
outW = natW;
|
|
3543
|
+
outH = natH;
|
|
3544
|
+
}
|
|
3545
|
+
outW = Math.round(outW * scale);
|
|
3546
|
+
outH = Math.round(outH * scale);
|
|
3121
3547
|
const canvas = document.createElement("canvas");
|
|
3122
|
-
canvas.width = Math.ceil(
|
|
3123
|
-
canvas.height = Math.ceil(
|
|
3124
|
-
canvas.style.width = `${
|
|
3125
|
-
canvas.style.height = `${
|
|
3548
|
+
canvas.width = Math.ceil(outW * dpr);
|
|
3549
|
+
canvas.height = Math.ceil(outH * dpr);
|
|
3550
|
+
canvas.style.width = `${outW}px`;
|
|
3551
|
+
canvas.style.height = `${outH}px`;
|
|
3126
3552
|
const ctx = canvas.getContext("2d");
|
|
3127
3553
|
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
3128
|
-
ctx.drawImage(img, 0, 0,
|
|
3554
|
+
ctx.drawImage(img, 0, 0, outW, outH);
|
|
3129
3555
|
return canvas;
|
|
3130
3556
|
}
|
|
3131
3557
|
|
|
3558
|
+
// src/modules/rasterize.js
|
|
3559
|
+
async function rasterize(url, options) {
|
|
3560
|
+
const canvas = await toCanvas(url, options);
|
|
3561
|
+
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
3562
|
+
const img = new Image();
|
|
3563
|
+
img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
3564
|
+
await img.decode();
|
|
3565
|
+
img.style.width = `${finalCanvas.width / options.dpr}px`;
|
|
3566
|
+
img.style.height = `${finalCanvas.height / options.dpr}px`;
|
|
3567
|
+
return img;
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
// src/exporters/toImg.js
|
|
3571
|
+
async function toImg(url, options) {
|
|
3572
|
+
const { scale = 1, width, height, meta = {} } = options;
|
|
3573
|
+
const hasW = Number.isFinite(width);
|
|
3574
|
+
const hasH = Number.isFinite(height);
|
|
3575
|
+
const wantsScale = Number.isFinite(scale) && scale !== 1 || hasW || hasH;
|
|
3576
|
+
if (isSafari() && wantsScale) {
|
|
3577
|
+
const pngUrl = await rasterize(url, { ...options, format: "png", quality: 1, meta });
|
|
3578
|
+
return pngUrl;
|
|
3579
|
+
}
|
|
3580
|
+
const img = new Image();
|
|
3581
|
+
img.decoding = "sync";
|
|
3582
|
+
img.loading = "eager";
|
|
3583
|
+
img.src = url;
|
|
3584
|
+
await img.decode();
|
|
3585
|
+
if (hasW && hasH) {
|
|
3586
|
+
img.style.width = `${width}px`;
|
|
3587
|
+
img.style.height = `${height}px`;
|
|
3588
|
+
} else if (hasW) {
|
|
3589
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
3590
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
3591
|
+
const k = width / Math.max(1, refW);
|
|
3592
|
+
img.style.width = `${width}px`;
|
|
3593
|
+
img.style.height = `${Math.round(refH * k)}px`;
|
|
3594
|
+
} else if (hasH) {
|
|
3595
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
3596
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
3597
|
+
const k = height / Math.max(1, refH);
|
|
3598
|
+
img.style.height = `${height}px`;
|
|
3599
|
+
img.style.width = `${Math.round(refW * k)}px`;
|
|
3600
|
+
} else if (scale !== 1) {
|
|
3601
|
+
img.style.width = `${Math.round(img.naturalWidth * scale)}px`;
|
|
3602
|
+
img.style.height = `${Math.round(img.naturalHeight * scale)}px`;
|
|
3603
|
+
}
|
|
3604
|
+
return img;
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3132
3607
|
// src/exporters/toBlob.js
|
|
3133
3608
|
async function toBlob(url, options) {
|
|
3134
3609
|
const type = options.type;
|
|
@@ -3147,18 +3622,6 @@ async function toBlob(url, options) {
|
|
|
3147
3622
|
);
|
|
3148
3623
|
}
|
|
3149
3624
|
|
|
3150
|
-
// src/modules/rasterize.js
|
|
3151
|
-
async function rasterize(url, options) {
|
|
3152
|
-
const canvas = await toCanvas(url, options);
|
|
3153
|
-
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
3154
|
-
const img = new Image();
|
|
3155
|
-
img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
3156
|
-
await img.decode();
|
|
3157
|
-
img.style.width = `${finalCanvas.width / options.dpr}px`;
|
|
3158
|
-
img.style.height = `${finalCanvas.height / options.dpr}px`;
|
|
3159
|
-
return img;
|
|
3160
|
-
}
|
|
3161
|
-
|
|
3162
3625
|
// src/exporters/download.js
|
|
3163
3626
|
async function download(url, options) {
|
|
3164
3627
|
options.dpr = 1;
|
|
@@ -3185,7 +3648,8 @@ var INTERNAL_TOKEN = Symbol("snapdom.internal");
|
|
|
3185
3648
|
var _safariWarmup = false;
|
|
3186
3649
|
async function snapdom(element, userOptions) {
|
|
3187
3650
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
3188
|
-
|
|
3651
|
+
const context = createContext(userOptions);
|
|
3652
|
+
if (isSafari() && (context.embedFonts === true || hasBackgroundOrMask(element))) {
|
|
3189
3653
|
for (let i = 0; i < 3; i++) {
|
|
3190
3654
|
try {
|
|
3191
3655
|
await safariWarmup(element, userOptions);
|
|
@@ -3195,7 +3659,6 @@ async function snapdom(element, userOptions) {
|
|
|
3195
3659
|
}
|
|
3196
3660
|
}
|
|
3197
3661
|
}
|
|
3198
|
-
const context = createContext(userOptions);
|
|
3199
3662
|
if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
|
|
3200
3663
|
if (!context.snap) {
|
|
3201
3664
|
context.snap = {
|
|
@@ -3275,6 +3738,20 @@ async function safariWarmup(element, baseOptions) {
|
|
|
3275
3738
|
cleanup();
|
|
3276
3739
|
});
|
|
3277
3740
|
}
|
|
3741
|
+
function hasBackgroundOrMask(el) {
|
|
3742
|
+
const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT);
|
|
3743
|
+
while (walker.nextNode()) {
|
|
3744
|
+
const node = (
|
|
3745
|
+
/** @type {Element} */
|
|
3746
|
+
walker.currentNode
|
|
3747
|
+
);
|
|
3748
|
+
const cs = getComputedStyle(node);
|
|
3749
|
+
const bg = cs.backgroundImage && cs.backgroundImage !== "none";
|
|
3750
|
+
const mask = cs.maskImage && cs.maskImage !== "none" || cs.webkitMaskImage && cs.webkitMaskImage !== "none";
|
|
3751
|
+
if (bg || mask) return true;
|
|
3752
|
+
}
|
|
3753
|
+
return false;
|
|
3754
|
+
}
|
|
3278
3755
|
|
|
3279
3756
|
// src/api/preCache.js
|
|
3280
3757
|
async function preCache(root = document, options = {}) {
|