@zumer/snapdom 2.0.0-dev.0 → 2.0.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -18
- package/dist/snapdom.js +412 -55
- package/dist/snapdom.min.js +7 -7
- package/dist/snapdom.min.mjs +7 -7
- package/dist/snapdom.mjs +399 -55
- package/package.json +1 -1
- package/types/snapdom.d.ts +3 -3
package/dist/snapdom.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.2.0.0-dev.
|
|
4
|
+
* v.2.0.0-dev.2
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -374,19 +374,6 @@
|
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
// src/utils/image.js
|
|
377
|
-
function createBackground(baseCanvas, backgroundColor) {
|
|
378
|
-
if (!backgroundColor || !baseCanvas.width || !baseCanvas.height) {
|
|
379
|
-
return baseCanvas;
|
|
380
|
-
}
|
|
381
|
-
const temp = document.createElement("canvas");
|
|
382
|
-
temp.width = baseCanvas.width;
|
|
383
|
-
temp.height = baseCanvas.height;
|
|
384
|
-
const ctx = temp.getContext("2d");
|
|
385
|
-
ctx.fillStyle = backgroundColor;
|
|
386
|
-
ctx.fillRect(0, 0, temp.width, temp.height);
|
|
387
|
-
ctx.drawImage(baseCanvas, 0, 0);
|
|
388
|
-
return temp;
|
|
389
|
-
}
|
|
390
377
|
function inlineSingleBackgroundEntry(_0) {
|
|
391
378
|
return __async(this, arguments, function* (entry, options = {}) {
|
|
392
379
|
const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry);
|
|
@@ -664,11 +651,20 @@
|
|
|
664
651
|
}
|
|
665
652
|
}
|
|
666
653
|
function isSafari() {
|
|
667
|
-
|
|
668
|
-
const
|
|
669
|
-
const
|
|
670
|
-
const
|
|
671
|
-
|
|
654
|
+
if (typeof navigator === "undefined") return false;
|
|
655
|
+
const ua = navigator.userAgent || "";
|
|
656
|
+
const uaLower = ua.toLowerCase();
|
|
657
|
+
const isSafariUA = uaLower.includes("safari") && !uaLower.includes("chrome") && !uaLower.includes("crios") && // Chrome on iOS
|
|
658
|
+
!uaLower.includes("fxios") && // Firefox on iOS
|
|
659
|
+
!uaLower.includes("android");
|
|
660
|
+
const isWebKit = /applewebkit/i.test(ua);
|
|
661
|
+
const isMobile = /mobile/i.test(ua);
|
|
662
|
+
const missingSafariToken = !/safari/i.test(ua);
|
|
663
|
+
const isUIWebView = isWebKit && isMobile && missingSafariToken;
|
|
664
|
+
const isWeChatUA = /(micromessenger|wxwork|wecom|windowswechat|macwechat)/i.test(ua);
|
|
665
|
+
const isBaiduUA = /(baiduboxapp|baidubrowser|baidusearch|baiduboxlite)/i.test(uaLower);
|
|
666
|
+
const isIOSWebKit = /ipad|iphone|ipod/.test(uaLower) && isWebKit;
|
|
667
|
+
return isSafariUA || isUIWebView || isWeChatUA || isBaiduUA || isIOSWebKit;
|
|
672
668
|
}
|
|
673
669
|
|
|
674
670
|
// src/modules/styles.js
|
|
@@ -714,6 +710,22 @@
|
|
|
714
710
|
}
|
|
715
711
|
out[prop] = val;
|
|
716
712
|
}
|
|
713
|
+
const EXTRA_TEXT_DECORATION_PROPS = [
|
|
714
|
+
"text-decoration-line",
|
|
715
|
+
"text-decoration-color",
|
|
716
|
+
"text-decoration-style",
|
|
717
|
+
"text-decoration-thickness",
|
|
718
|
+
"text-underline-offset",
|
|
719
|
+
"text-decoration-skip-ink"
|
|
720
|
+
];
|
|
721
|
+
for (const prop of EXTRA_TEXT_DECORATION_PROPS) {
|
|
722
|
+
if (out[prop]) continue;
|
|
723
|
+
try {
|
|
724
|
+
const v = style.getPropertyValue(prop);
|
|
725
|
+
if (v) out[prop] = v;
|
|
726
|
+
} catch (e) {
|
|
727
|
+
}
|
|
728
|
+
}
|
|
717
729
|
if (options.embedFonts) {
|
|
718
730
|
const EXTRA_FONT_PROPS = [
|
|
719
731
|
"font-feature-settings",
|
|
@@ -1482,17 +1494,19 @@
|
|
|
1482
1494
|
/layui/i,
|
|
1483
1495
|
/lucide/i
|
|
1484
1496
|
];
|
|
1497
|
+
var ICON_FONT_URLS = Object.assign({
|
|
1498
|
+
materialIconsFilled: "https://fonts.gstatic.com/s/materialicons/v48/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2",
|
|
1499
|
+
materialIconsOutlined: "https://fonts.gstatic.com/s/materialiconsoutlined/v110/gok-H7zzDkdnRel8-DQ6KAXJ69wP1tGnf4ZGhUcel5euIg.woff2",
|
|
1500
|
+
materialIconsRound: "https://fonts.gstatic.com/s/materialiconsround/v109/LDItaoyNOAY6Uewc665JcIzCKsKc_M9flwmPq_HTTw.woff2",
|
|
1501
|
+
materialIconsSharp: "https://fonts.gstatic.com/s/materialiconssharp/v110/oPWQ_lt5nv4pWNJpghLP75WiFR4kLh3kvmvRImcycg.woff2"
|
|
1502
|
+
}, typeof window !== "undefined" && window.__SNAPDOM_ICON_FONTS__ || {});
|
|
1485
1503
|
var userIconFonts = [];
|
|
1486
1504
|
function extendIconFonts(fonts) {
|
|
1487
1505
|
const list = Array.isArray(fonts) ? fonts : [fonts];
|
|
1488
1506
|
for (const f of list) {
|
|
1489
|
-
if (f instanceof RegExp)
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
userIconFonts.push(new RegExp(f, "i"));
|
|
1493
|
-
} else {
|
|
1494
|
-
console.warn("[snapdom] Ignored invalid iconFont value:", f);
|
|
1495
|
-
}
|
|
1507
|
+
if (f instanceof RegExp) userIconFonts.push(f);
|
|
1508
|
+
else if (typeof f === "string") userIconFonts.push(new RegExp(f, "i"));
|
|
1509
|
+
else console.warn("[snapdom] Ignored invalid iconFont value:", f);
|
|
1496
1510
|
}
|
|
1497
1511
|
}
|
|
1498
1512
|
function isIconFont2(input) {
|
|
@@ -1504,6 +1518,187 @@
|
|
|
1504
1518
|
if (/icon/i.test(text) || /glyph/i.test(text) || /symbols/i.test(text) || /feather/i.test(text) || /fontawesome/i.test(text)) return true;
|
|
1505
1519
|
return false;
|
|
1506
1520
|
}
|
|
1521
|
+
function isMaterialFamily(family = "") {
|
|
1522
|
+
const s = String(family).toLowerCase();
|
|
1523
|
+
return /\bmaterial\s*icons\b/.test(s) || /\bmaterial\s*symbols\b/.test(s);
|
|
1524
|
+
}
|
|
1525
|
+
var loadedCanvasFamilies = /* @__PURE__ */ new Map();
|
|
1526
|
+
function parseAxes(variation = "") {
|
|
1527
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1528
|
+
const v = String(variation || "");
|
|
1529
|
+
const rx = /['"]?\s*([A-Za-z]{3,4})\s*['"]?\s*([+-]?\d+(?:\.\d+)?)\s*/g;
|
|
1530
|
+
let m;
|
|
1531
|
+
while (m = rx.exec(v)) out[m[1].toUpperCase()] = Number(m[2]);
|
|
1532
|
+
return out;
|
|
1533
|
+
}
|
|
1534
|
+
function ensureLigatureCanvasFont(cssFamily, className, axes) {
|
|
1535
|
+
return __async(this, null, function* () {
|
|
1536
|
+
var _a;
|
|
1537
|
+
const fam = String(cssFamily || "");
|
|
1538
|
+
const lowerFam = fam.toLowerCase();
|
|
1539
|
+
const cls = String(className || "").toLowerCase();
|
|
1540
|
+
if (/\bmaterial\s*icons\b/.test(lowerFam) && !/\bsymbols\b/.test(lowerFam)) {
|
|
1541
|
+
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
1542
|
+
}
|
|
1543
|
+
const isSymbols = /\bmaterial\s*symbols\b/.test(lowerFam);
|
|
1544
|
+
if (!isSymbols) {
|
|
1545
|
+
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
1546
|
+
}
|
|
1547
|
+
const FILL = axes && ((_a = axes.FILL) != null ? _a : axes.fill);
|
|
1548
|
+
let style = "outlined";
|
|
1549
|
+
if (/\brounded\b/.test(cls) || /\bround\b/.test(cls)) style = "rounded";
|
|
1550
|
+
else if (/\bsharp\b/.test(cls)) style = "sharp";
|
|
1551
|
+
else if (/\boutlined\b/.test(cls)) style = "outlined";
|
|
1552
|
+
const filled = FILL === 1;
|
|
1553
|
+
let pick = null;
|
|
1554
|
+
if (filled) {
|
|
1555
|
+
if (style === "outlined" && ICON_FONT_URLS.materialIconsFilled) {
|
|
1556
|
+
pick = { url: ICON_FONT_URLS.materialIconsFilled, alias: "snapdom-mi-filled" };
|
|
1557
|
+
} else if (style === "rounded" && ICON_FONT_URLS.materialIconsRound) {
|
|
1558
|
+
pick = { url: ICON_FONT_URLS.materialIconsRound, alias: "snapdom-mi-round" };
|
|
1559
|
+
} else if (style === "sharp" && ICON_FONT_URLS.materialIconsSharp) {
|
|
1560
|
+
pick = { url: ICON_FONT_URLS.materialIconsSharp, alias: "snapdom-mi-sharp" };
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
if (!pick) {
|
|
1564
|
+
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
1565
|
+
}
|
|
1566
|
+
if (!loadedCanvasFamilies.has(pick.alias)) {
|
|
1567
|
+
try {
|
|
1568
|
+
const ff = new FontFace(pick.alias, `url(${pick.url})`, { style: "normal", weight: "400" });
|
|
1569
|
+
document.fonts.add(ff);
|
|
1570
|
+
yield ff.load();
|
|
1571
|
+
loadedCanvasFamilies.set(pick.alias, true);
|
|
1572
|
+
} catch (e) {
|
|
1573
|
+
return { familyForMeasure: fam, familyForCanvas: fam };
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
const quoted = `"${pick.alias}"`;
|
|
1577
|
+
return { familyForMeasure: quoted, familyForCanvas: quoted };
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
function ensureMaterialFontsReady(family = "Material Icons", px = 24) {
|
|
1581
|
+
return __async(this, null, function* () {
|
|
1582
|
+
try {
|
|
1583
|
+
yield Promise.all([
|
|
1584
|
+
document.fonts.load(`400 ${px}px "${String(family).replace(/["']/g, "")}"`),
|
|
1585
|
+
document.fonts.ready
|
|
1586
|
+
]);
|
|
1587
|
+
} catch (e) {
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
function resolvePaintColor(cs) {
|
|
1592
|
+
var _a, _b;
|
|
1593
|
+
let fill = ((_a = cs.getPropertyValue("-webkit-text-fill-color")) == null ? void 0 : _a.trim()) || "";
|
|
1594
|
+
const isTransparent = /^transparent$/i.test(fill) || /rgba?\(\s*0\s*,\s*0\s*,\s*0\s*,\s*0\s*\)/i.test(fill);
|
|
1595
|
+
if (fill && !isTransparent && fill.toLowerCase() !== "currentcolor") return fill;
|
|
1596
|
+
const c = (_b = cs.color) == null ? void 0 : _b.trim();
|
|
1597
|
+
return c && c !== "inherit" ? c : "#000";
|
|
1598
|
+
}
|
|
1599
|
+
function materialIconToImage(_0) {
|
|
1600
|
+
return __async(this, arguments, function* (ligatureText, {
|
|
1601
|
+
family = "Material Icons",
|
|
1602
|
+
weight = "normal",
|
|
1603
|
+
fontSize = 32,
|
|
1604
|
+
color = "#000",
|
|
1605
|
+
variation = "",
|
|
1606
|
+
className = ""
|
|
1607
|
+
} = {}) {
|
|
1608
|
+
const fam = String(family || "").replace(/^['"]+|['"]+$/g, "");
|
|
1609
|
+
const dpr = window.devicePixelRatio || 1;
|
|
1610
|
+
const axes = parseAxes(variation);
|
|
1611
|
+
const { familyForMeasure, familyForCanvas } = yield ensureLigatureCanvasFont(fam, className, axes);
|
|
1612
|
+
yield ensureMaterialFontsReady(familyForCanvas.replace(/^["']+|["']+$/g, ""), fontSize);
|
|
1613
|
+
const span = document.createElement("span");
|
|
1614
|
+
span.textContent = ligatureText;
|
|
1615
|
+
span.style.position = "absolute";
|
|
1616
|
+
span.style.visibility = "hidden";
|
|
1617
|
+
span.style.left = "-99999px";
|
|
1618
|
+
span.style.whiteSpace = "nowrap";
|
|
1619
|
+
span.style.fontFamily = familyForMeasure;
|
|
1620
|
+
span.style.fontWeight = String(weight || "normal");
|
|
1621
|
+
span.style.fontSize = `${fontSize}px`;
|
|
1622
|
+
span.style.lineHeight = "1";
|
|
1623
|
+
span.style.margin = "0";
|
|
1624
|
+
span.style.padding = "0";
|
|
1625
|
+
span.style.fontFeatureSettings = "'liga' 1";
|
|
1626
|
+
span.style.fontVariantLigatures = "normal";
|
|
1627
|
+
span.style.color = color;
|
|
1628
|
+
document.body.appendChild(span);
|
|
1629
|
+
const rect = span.getBoundingClientRect();
|
|
1630
|
+
const width = Math.max(1, Math.ceil(rect.width));
|
|
1631
|
+
const height = Math.max(1, Math.ceil(rect.height));
|
|
1632
|
+
document.body.removeChild(span);
|
|
1633
|
+
const canvas = document.createElement("canvas");
|
|
1634
|
+
canvas.width = width * dpr;
|
|
1635
|
+
canvas.height = height * dpr;
|
|
1636
|
+
const ctx = canvas.getContext("2d");
|
|
1637
|
+
ctx.scale(dpr, dpr);
|
|
1638
|
+
ctx.font = `${weight ? `${weight} ` : ""}${fontSize}px ${familyForCanvas}`;
|
|
1639
|
+
ctx.textAlign = "left";
|
|
1640
|
+
ctx.textBaseline = "top";
|
|
1641
|
+
ctx.fillStyle = color;
|
|
1642
|
+
try {
|
|
1643
|
+
ctx.fontKerning = "normal";
|
|
1644
|
+
} catch (e) {
|
|
1645
|
+
}
|
|
1646
|
+
ctx.fillText(ligatureText, 0, 0);
|
|
1647
|
+
return {
|
|
1648
|
+
dataUrl: canvas.toDataURL(),
|
|
1649
|
+
width,
|
|
1650
|
+
height
|
|
1651
|
+
};
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
function ligatureIconToImage(cloneRoot, sourceRoot) {
|
|
1655
|
+
return __async(this, null, function* () {
|
|
1656
|
+
if (!(cloneRoot instanceof Element)) return 0;
|
|
1657
|
+
const selector = '.material-icons, [class*="material-symbols"]';
|
|
1658
|
+
const cloneNodes = Array.from(
|
|
1659
|
+
cloneRoot.querySelectorAll(selector)
|
|
1660
|
+
).filter((n) => n && n.textContent && n.textContent.trim());
|
|
1661
|
+
if (cloneNodes.length === 0) return 0;
|
|
1662
|
+
const sourceNodes = sourceRoot instanceof Element ? Array.from(sourceRoot.querySelectorAll(selector)).filter((n) => n && n.textContent && n.textContent.trim()) : [];
|
|
1663
|
+
let replaced = 0;
|
|
1664
|
+
for (let i = 0; i < cloneNodes.length; i++) {
|
|
1665
|
+
const el = cloneNodes[i];
|
|
1666
|
+
const src = sourceNodes[i] || null;
|
|
1667
|
+
try {
|
|
1668
|
+
const cs = src ? getComputedStyle(src) : getComputedStyle(el);
|
|
1669
|
+
const family = cs.fontFamily || "Material Icons";
|
|
1670
|
+
if (!isMaterialFamily(family)) continue;
|
|
1671
|
+
const text = (src || el).textContent.trim();
|
|
1672
|
+
if (!text) continue;
|
|
1673
|
+
const size = parseInt(cs.fontSize, 10) || 24;
|
|
1674
|
+
const weight = cs.fontWeight && cs.fontWeight !== "normal" ? cs.fontWeight : "normal";
|
|
1675
|
+
const color = resolvePaintColor(cs);
|
|
1676
|
+
const variation = cs.fontVariationSettings && cs.fontVariationSettings !== "normal" ? cs.fontVariationSettings : "";
|
|
1677
|
+
const className = (src || el).className || "";
|
|
1678
|
+
const { dataUrl, width, height } = yield materialIconToImage(text, {
|
|
1679
|
+
family,
|
|
1680
|
+
weight,
|
|
1681
|
+
fontSize: size,
|
|
1682
|
+
color,
|
|
1683
|
+
variation,
|
|
1684
|
+
className
|
|
1685
|
+
});
|
|
1686
|
+
el.textContent = "";
|
|
1687
|
+
const img = el.ownerDocument.createElement("img");
|
|
1688
|
+
img.src = dataUrl;
|
|
1689
|
+
img.alt = text;
|
|
1690
|
+
img.style.height = `${size}px`;
|
|
1691
|
+
img.style.width = `${Math.max(1, Math.round(width / height * size))}px`;
|
|
1692
|
+
img.style.objectFit = "contain";
|
|
1693
|
+
img.style.verticalAlign = getComputedStyle(el).verticalAlign || "baseline";
|
|
1694
|
+
el.appendChild(img);
|
|
1695
|
+
replaced++;
|
|
1696
|
+
} catch (e) {
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
return replaced;
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1507
1702
|
|
|
1508
1703
|
// src/modules/fonts.js
|
|
1509
1704
|
function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
@@ -1864,7 +2059,7 @@ ${imported}
|
|
|
1864
2059
|
const styleSpec = (((_c = block.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _c[1]) || "normal").trim();
|
|
1865
2060
|
const stretchSpec = (((_d = block.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _d[1]) || "100%").trim();
|
|
1866
2061
|
const urange = (((_e = block.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _e[1]) || "").trim();
|
|
1867
|
-
const srcRaw = (((_f = block.match(/src\s*:\s*([^;]+)
|
|
2062
|
+
const srcRaw = (((_f = block.match(/src\s*:\s*([^;}]+)[;}]/i)) == null ? void 0 : _f[1]) || "").trim();
|
|
1868
2063
|
const urls = extractSrcUrls(srcRaw, location.href);
|
|
1869
2064
|
const srcPart = urls.length ? urls.map((u) => String(u).toLowerCase()).sort().join("|") : srcRaw.toLowerCase();
|
|
1870
2065
|
const key = [
|
|
@@ -2076,7 +2271,7 @@ ${imported}
|
|
|
2076
2271
|
const styleSpec = (((_d = face.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _d[1]) || "normal").trim();
|
|
2077
2272
|
const stretchSpec = (((_e = face.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _e[1]) || "100%").trim();
|
|
2078
2273
|
const urange = (((_f = face.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _f[1]) || "").trim();
|
|
2079
|
-
const srcRaw = (((_g = face.match(/src\s*:\s*([^;]+)
|
|
2274
|
+
const srcRaw = (((_g = face.match(/src\s*:\s*([^;}]+)[;}]/i)) == null ? void 0 : _g[1]) || "").trim();
|
|
2080
2275
|
const srcUrls = extractSrcUrls(srcRaw, link.href);
|
|
2081
2276
|
if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) continue;
|
|
2082
2277
|
const ranges = parseUnicodeRange(urange);
|
|
@@ -3558,13 +3753,58 @@ ${imported}
|
|
|
3558
3753
|
} catch (e) {
|
|
3559
3754
|
}
|
|
3560
3755
|
}
|
|
3756
|
+
function removeAllComments(root) {
|
|
3757
|
+
const it = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
|
|
3758
|
+
const toRemove = [];
|
|
3759
|
+
while (it.nextNode()) toRemove.push(it.currentNode);
|
|
3760
|
+
for (const n of toRemove) n.remove();
|
|
3761
|
+
}
|
|
3762
|
+
function sanitizeAttributesForXHTML(root, opts = {}) {
|
|
3763
|
+
const { stripFrameworkDirectives = true } = opts;
|
|
3764
|
+
const ALLOWED_PREFIXES = /* @__PURE__ */ new Set(["xml", "xlink"]);
|
|
3765
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
3766
|
+
while (walker.nextNode()) {
|
|
3767
|
+
const el = walker.currentNode;
|
|
3768
|
+
for (const attr of Array.from(el.attributes)) {
|
|
3769
|
+
const name = attr.name;
|
|
3770
|
+
if (name.includes("@")) {
|
|
3771
|
+
el.removeAttribute(name);
|
|
3772
|
+
continue;
|
|
3773
|
+
}
|
|
3774
|
+
if (name.includes(":")) {
|
|
3775
|
+
const prefix = name.split(":", 1)[0];
|
|
3776
|
+
if (!ALLOWED_PREFIXES.has(prefix)) {
|
|
3777
|
+
el.removeAttribute(name);
|
|
3778
|
+
continue;
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
if (!stripFrameworkDirectives) continue;
|
|
3782
|
+
if (name.startsWith("x-") || // Alpine
|
|
3783
|
+
name.startsWith("v-") || // Vue
|
|
3784
|
+
name.startsWith(":") || // Vue/Alpine shorthand
|
|
3785
|
+
name.startsWith("on:") || // Svelte
|
|
3786
|
+
name.startsWith("bind:") || // Svelte
|
|
3787
|
+
name.startsWith("let:") || // Svelte
|
|
3788
|
+
name.startsWith("class:")) {
|
|
3789
|
+
el.removeAttribute(name);
|
|
3790
|
+
continue;
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
function sanitizeCloneForXHTML(root, opts = {}) {
|
|
3796
|
+
if (!root) return;
|
|
3797
|
+
sanitizeAttributesForXHTML(root, opts);
|
|
3798
|
+
removeAllComments(root);
|
|
3799
|
+
}
|
|
3561
3800
|
function captureDOM(element, options) {
|
|
3562
3801
|
return __async(this, null, function* () {
|
|
3802
|
+
var _a;
|
|
3563
3803
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
3564
3804
|
applyCachePolicy(options.cache);
|
|
3565
3805
|
const fast = options.fast;
|
|
3566
|
-
const
|
|
3567
|
-
const
|
|
3806
|
+
const outerTransforms = options.outerTransforms !== false;
|
|
3807
|
+
const outerShadows = !!options.outerShadows;
|
|
3568
3808
|
let state = { element, options, plugins: options.plugins };
|
|
3569
3809
|
let clone, classCSS, styleCache;
|
|
3570
3810
|
let fontsCSS = "";
|
|
@@ -3577,17 +3817,29 @@ ${imported}
|
|
|
3577
3817
|
const undoClamp = lineClamp(state.element);
|
|
3578
3818
|
try {
|
|
3579
3819
|
({ clone, classCSS, styleCache } = yield prepareClone(state.element, state.options));
|
|
3580
|
-
if (
|
|
3581
|
-
rootTransform2D = normalizeRootTransforms(state.element,
|
|
3820
|
+
if (!outerTransforms && clone) {
|
|
3821
|
+
rootTransform2D = normalizeRootTransforms(state.element, clone);
|
|
3582
3822
|
}
|
|
3583
|
-
if (
|
|
3584
|
-
stripRootShadows(state.element,
|
|
3823
|
+
if (!outerShadows && clone) {
|
|
3824
|
+
stripRootShadows(state.element, clone);
|
|
3585
3825
|
}
|
|
3586
3826
|
} finally {
|
|
3587
3827
|
undoClamp();
|
|
3588
3828
|
}
|
|
3589
3829
|
state = __spreadValues({ clone, classCSS, styleCache }, state);
|
|
3590
3830
|
yield runHook("afterClone", state);
|
|
3831
|
+
sanitizeCloneForXHTML(state.clone);
|
|
3832
|
+
if (((_a = state.options) == null ? void 0 : _a.excludeMode) === "remove") {
|
|
3833
|
+
try {
|
|
3834
|
+
shrinkAutoSizeBoxes(state.element, state.clone, state.styleCache);
|
|
3835
|
+
} catch (e) {
|
|
3836
|
+
console.warn("[snapdom] shrink pass failed:", e);
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
try {
|
|
3840
|
+
yield ligatureIconToImage(state.clone, state.element);
|
|
3841
|
+
} catch (e) {
|
|
3842
|
+
}
|
|
3591
3843
|
yield new Promise((resolve) => {
|
|
3592
3844
|
idle(() => __async(null, null, function* () {
|
|
3593
3845
|
yield inlineImages(state.clone, state.options);
|
|
@@ -3639,10 +3891,18 @@ ${imported}
|
|
|
3639
3891
|
yield runHook("beforeRender", state);
|
|
3640
3892
|
yield new Promise((resolve) => {
|
|
3641
3893
|
idle(() => {
|
|
3894
|
+
var _a2;
|
|
3642
3895
|
const csEl = getComputedStyle(state.element);
|
|
3643
3896
|
const rect = state.element.getBoundingClientRect();
|
|
3644
|
-
|
|
3645
|
-
|
|
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
|
+
}
|
|
3646
3906
|
const coerceNum = (v, def = NaN) => {
|
|
3647
3907
|
const n = typeof v === "string" ? parseFloat(v) : v;
|
|
3648
3908
|
return Number.isFinite(n) ? n : def;
|
|
@@ -3667,7 +3927,7 @@ ${imported}
|
|
|
3667
3927
|
h = h0;
|
|
3668
3928
|
}
|
|
3669
3929
|
let minX = 0, minY = 0, maxX = w0, maxY = h0;
|
|
3670
|
-
if (
|
|
3930
|
+
if (!outerTransforms && rootTransform2D && Number.isFinite(rootTransform2D.a)) {
|
|
3671
3931
|
const M2 = {
|
|
3672
3932
|
a: rootTransform2D.a,
|
|
3673
3933
|
b: rootTransform2D.b || 0,
|
|
@@ -3682,7 +3942,7 @@ ${imported}
|
|
|
3682
3942
|
maxX = limitDecimals(bb2.maxX);
|
|
3683
3943
|
maxY = limitDecimals(bb2.maxY);
|
|
3684
3944
|
} else {
|
|
3685
|
-
const useTFBBox =
|
|
3945
|
+
const useTFBBox = outerTransforms && hasTFBBox(state.element);
|
|
3686
3946
|
if (useTFBBox) {
|
|
3687
3947
|
const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
|
|
3688
3948
|
const ind2 = readIndividualTransforms(state.element);
|
|
@@ -3705,7 +3965,7 @@ ${imported}
|
|
|
3705
3965
|
const bleedBlur = parseFilterBlur(csEl);
|
|
3706
3966
|
const bleedOutline = parseOutline(csEl);
|
|
3707
3967
|
const drop = parseFilterDropShadows(csEl);
|
|
3708
|
-
const bleed =
|
|
3968
|
+
const bleed = !outerShadows ? { top: 0, right: 0, bottom: 0, left: 0 } : {
|
|
3709
3969
|
top: limitDecimals(bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top),
|
|
3710
3970
|
right: limitDecimals(bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right),
|
|
3711
3971
|
bottom: limitDecimals(bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom),
|
|
@@ -3723,7 +3983,7 @@ ${imported}
|
|
|
3723
3983
|
const outH = Math.max(1, limitDecimals(vbH0 * scaleH));
|
|
3724
3984
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
3725
3985
|
const basePad = isSafari() ? 1 : 0;
|
|
3726
|
-
const extraPad =
|
|
3986
|
+
const extraPad = !outerTransforms ? 1 : 0;
|
|
3727
3987
|
const pad = limitDecimals(basePad + extraPad);
|
|
3728
3988
|
const fo = document.createElementNS(svgNS, "foreignObject");
|
|
3729
3989
|
const vbMinX = limitDecimals(minX);
|
|
@@ -3766,6 +4026,58 @@ ${imported}
|
|
|
3766
4026
|
return state.dataURL;
|
|
3767
4027
|
});
|
|
3768
4028
|
}
|
|
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;
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
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;
|
|
4040
|
+
}
|
|
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;
|
|
4051
|
+
}
|
|
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);
|
|
4080
|
+
}
|
|
3769
4081
|
var limitDecimals = (v, n = 3) => Number.isFinite(v) ? Math.round(v * __pow(10, n)) / __pow(10, n) : v;
|
|
3770
4082
|
function parseFilterDropShadows(cs) {
|
|
3771
4083
|
var _a;
|
|
@@ -4035,6 +4347,52 @@ ${imported}
|
|
|
4035
4347
|
host.removeChild(tmp);
|
|
4036
4348
|
return M;
|
|
4037
4349
|
}
|
|
4350
|
+
function isInNormalFlow(el) {
|
|
4351
|
+
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;
|
|
4357
|
+
}
|
|
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) {
|
|
4363
|
+
try {
|
|
4364
|
+
if (el.matches(sel)) return options.excludeMode === "remove";
|
|
4365
|
+
} catch (e) {
|
|
4366
|
+
}
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
return false;
|
|
4370
|
+
}
|
|
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;
|
|
4395
|
+
}
|
|
4038
4396
|
|
|
4039
4397
|
// src/core/context.js
|
|
4040
4398
|
function normalizeCachePolicy(v) {
|
|
@@ -4085,8 +4443,8 @@ ${imported}
|
|
|
4085
4443
|
backgroundColor: (_q = options.backgroundColor) != null ? _q : ["jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null,
|
|
4086
4444
|
filename: (_r = options.filename) != null ? _r : "snapDOM",
|
|
4087
4445
|
// NEW flags (user-friendly)
|
|
4088
|
-
|
|
4089
|
-
|
|
4446
|
+
outerTransforms: (_s = options.outerTransforms) != null ? _s : true,
|
|
4447
|
+
outerShadows: (_t = options.outerShadows) != null ? _t : false
|
|
4090
4448
|
// Plugins (reservado)
|
|
4091
4449
|
// plugins: normalizePlugins(...),
|
|
4092
4450
|
};
|
|
@@ -4193,7 +4551,7 @@ ${imported}
|
|
|
4193
4551
|
}
|
|
4194
4552
|
function toCanvas(url, options) {
|
|
4195
4553
|
return __async(this, null, function* () {
|
|
4196
|
-
let { width: optW, height: optH, scale = 1, dpr = 1, meta = {} } = options;
|
|
4554
|
+
let { width: optW, height: optH, scale = 1, dpr = 1, meta = {}, backgroundColor } = options;
|
|
4197
4555
|
url = maybeConvertBoxShadowForSafari(url);
|
|
4198
4556
|
const img = new Image();
|
|
4199
4557
|
img.loading = "eager";
|
|
@@ -4232,6 +4590,12 @@ ${imported}
|
|
|
4232
4590
|
canvas.style.height = `${outH}px`;
|
|
4233
4591
|
const ctx = canvas.getContext("2d");
|
|
4234
4592
|
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
4593
|
+
if (backgroundColor) {
|
|
4594
|
+
ctx.save();
|
|
4595
|
+
ctx.fillStyle = backgroundColor;
|
|
4596
|
+
ctx.fillRect(0, 0, outW, outH);
|
|
4597
|
+
ctx.restore();
|
|
4598
|
+
}
|
|
4235
4599
|
ctx.drawImage(img, 0, 0, outW, outH);
|
|
4236
4600
|
return canvas;
|
|
4237
4601
|
});
|
|
@@ -4241,12 +4605,11 @@ ${imported}
|
|
|
4241
4605
|
function rasterize(url, options) {
|
|
4242
4606
|
return __async(this, null, function* () {
|
|
4243
4607
|
const canvas = yield toCanvas(url, options);
|
|
4244
|
-
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
4245
4608
|
const img = new Image();
|
|
4246
|
-
img.src =
|
|
4609
|
+
img.src = canvas.toDataURL(`image/${options.format}`, options.quality);
|
|
4247
4610
|
yield img.decode();
|
|
4248
|
-
img.style.width = `${
|
|
4249
|
-
img.style.height = `${
|
|
4611
|
+
img.style.width = `${canvas.width / options.dpr}px`;
|
|
4612
|
+
img.style.height = `${canvas.height / options.dpr}px`;
|
|
4250
4613
|
return img;
|
|
4251
4614
|
});
|
|
4252
4615
|
}
|
|
@@ -4310,13 +4673,8 @@ ${imported}
|
|
|
4310
4673
|
return new Blob([svgText], { type: "image/svg+xml" });
|
|
4311
4674
|
}
|
|
4312
4675
|
const canvas = yield toCanvas(url, options);
|
|
4313
|
-
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
4314
4676
|
return new Promise(
|
|
4315
|
-
(resolve) =>
|
|
4316
|
-
(blob) => resolve(blob),
|
|
4317
|
-
`image/${type}`,
|
|
4318
|
-
options.quality
|
|
4319
|
-
)
|
|
4677
|
+
(resolve) => canvas.toBlob((blob) => resolve(blob), `image/${type}`, options.quality)
|
|
4320
4678
|
);
|
|
4321
4679
|
});
|
|
4322
4680
|
}
|
|
@@ -4336,9 +4694,8 @@ ${imported}
|
|
|
4336
4694
|
return;
|
|
4337
4695
|
}
|
|
4338
4696
|
const canvas = yield toCanvas(url, options);
|
|
4339
|
-
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
4340
4697
|
const a = document.createElement("a");
|
|
4341
|
-
a.href =
|
|
4698
|
+
a.href = canvas.toDataURL(`image/${options.format}`, options.quality);
|
|
4342
4699
|
a.download = options.filename;
|
|
4343
4700
|
a.click();
|
|
4344
4701
|
});
|