cssstyle 4.4.0 → 4.6.0
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/lib/CSSStyleDeclaration.js +2 -1
- package/lib/generated/implementedProperties.js +1 -1
- package/lib/generated/properties.js +158 -59
- package/lib/parsers.js +54 -19
- package/lib/properties/background.js +7 -1
- package/lib/properties/backgroundAttachment.js +1 -1
- package/lib/properties/backgroundColor.js +1 -1
- package/lib/properties/backgroundImage.js +1 -1
- package/lib/properties/backgroundPosition.js +1 -1
- package/lib/properties/backgroundRepeat.js +1 -1
- package/lib/properties/border.js +1 -1
- package/lib/properties/borderStyle.js +1 -1
- package/lib/properties/clip.js +2 -1
- package/lib/properties/font.js +88 -29
- package/lib/properties/fontFamily.js +33 -9
- package/lib/properties/margin.js +1 -1
- package/lib/properties/marginBottom.js +1 -1
- package/lib/properties/marginLeft.js +1 -1
- package/lib/properties/marginRight.js +1 -1
- package/lib/properties/marginTop.js +1 -1
- package/lib/utils/camelize.js +3 -1
- package/lib/utils/strings.js +167 -0
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ const generatedProperties = require("./generated/properties");
|
|
|
11
11
|
const { hasVarFunc, parseKeyword, parseShorthand, prepareValue, splitValue } = require("./parsers");
|
|
12
12
|
const { dashedToCamelCase } = require("./utils/camelize");
|
|
13
13
|
const { getPropertyDescriptor } = require("./utils/propertyDescriptors");
|
|
14
|
+
const { asciiLowercase } = require("./utils/strings");
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface
|
|
@@ -275,7 +276,7 @@ class CSSStyleDeclaration {
|
|
|
275
276
|
this._setProperty(property, value);
|
|
276
277
|
return;
|
|
277
278
|
}
|
|
278
|
-
property = property
|
|
279
|
+
property = asciiLowercase(property);
|
|
279
280
|
if (!allProperties.has(property) && !allExtraProperties.has(property)) {
|
|
280
281
|
return;
|
|
281
282
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// autogenerated - 2025-06-
|
|
2
|
+
// autogenerated - 2025-06-25
|
|
3
3
|
// https://www.w3.org/Style/CSS/all-properties.en.html
|
|
4
4
|
|
|
5
5
|
var external_dependency_parsers_0 = require("../parsers.js");
|
|
6
|
+
var external_dependency_strings_1 = require("../utils/strings.js");
|
|
6
7
|
var backgroundImage_export_parse, backgroundImage_export_isValid, backgroundImage_export_definition;
|
|
7
8
|
backgroundImage_export_parse = function parse(v) {
|
|
8
9
|
return external_dependency_parsers_0.parseImage(v);
|
|
@@ -18,7 +19,7 @@ backgroundImage_export_definition = {
|
|
|
18
19
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
19
20
|
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
20
21
|
this._setProperty("background", "");
|
|
21
|
-
this._setProperty("
|
|
22
|
+
this._setProperty("background-image", v);
|
|
22
23
|
} else {
|
|
23
24
|
this._setProperty("background-image", backgroundImage_export_parse(v));
|
|
24
25
|
}
|
|
@@ -65,7 +66,7 @@ backgroundPosition_export_definition = {
|
|
|
65
66
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
66
67
|
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
67
68
|
this._setProperty("background", "");
|
|
68
|
-
this._setProperty("
|
|
69
|
+
this._setProperty("background-position", v);
|
|
69
70
|
} else {
|
|
70
71
|
this._setProperty("background-position", backgroundPosition_export_parse(v));
|
|
71
72
|
}
|
|
@@ -92,7 +93,7 @@ backgroundRepeat_export_definition = {
|
|
|
92
93
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
93
94
|
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
94
95
|
this._setProperty("background", "");
|
|
95
|
-
this._setProperty("
|
|
96
|
+
this._setProperty("background-repeat", v);
|
|
96
97
|
} else {
|
|
97
98
|
this._setProperty("background-repeat", backgroundRepeat_export_parse(v));
|
|
98
99
|
}
|
|
@@ -119,7 +120,7 @@ backgroundAttachment_export_definition = {
|
|
|
119
120
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
120
121
|
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
121
122
|
this._setProperty("background", "");
|
|
122
|
-
this._setProperty("
|
|
123
|
+
this._setProperty("background-attachment", v);
|
|
123
124
|
} else {
|
|
124
125
|
this._setProperty("background-attachment", backgroundAttachment_export_parse(v));
|
|
125
126
|
}
|
|
@@ -149,7 +150,7 @@ backgroundColor_export_definition = {
|
|
|
149
150
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
150
151
|
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
151
152
|
this._setProperty("background", "");
|
|
152
|
-
this._setProperty("
|
|
153
|
+
this._setProperty("background-color", v);
|
|
153
154
|
} else {
|
|
154
155
|
this._setProperty("background-color", backgroundColor_export_parse(v));
|
|
155
156
|
}
|
|
@@ -189,7 +190,12 @@ const background_local_var_shorthandFor = new Map([["background-image", {
|
|
|
189
190
|
background_export_definition = {
|
|
190
191
|
set(v) {
|
|
191
192
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
192
|
-
if (
|
|
193
|
+
if (/^none$/i.test(v)) {
|
|
194
|
+
for (const [key] of background_local_var_shorthandFor) {
|
|
195
|
+
this._setProperty(key, "");
|
|
196
|
+
}
|
|
197
|
+
this._setProperty("background", external_dependency_strings_1.asciiLowercase(v));
|
|
198
|
+
} else if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
193
199
|
for (const [key] of background_local_var_shorthandFor) {
|
|
194
200
|
this._setProperty(key, "");
|
|
195
201
|
}
|
|
@@ -258,7 +264,7 @@ borderStyle_export_isValid = function isValid(v) {
|
|
|
258
264
|
borderStyle_export_definition = {
|
|
259
265
|
set(v) {
|
|
260
266
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
261
|
-
if (
|
|
267
|
+
if (/^none$/i.test(v)) {
|
|
262
268
|
v = "";
|
|
263
269
|
}
|
|
264
270
|
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
@@ -323,7 +329,7 @@ const border_local_var_shorthandFor = new Map([["border-width", {
|
|
|
323
329
|
border_export_definition = {
|
|
324
330
|
set(v) {
|
|
325
331
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
326
|
-
if (
|
|
332
|
+
if (/^none$/i.test(v)) {
|
|
327
333
|
v = "";
|
|
328
334
|
}
|
|
329
335
|
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
@@ -1014,7 +1020,7 @@ clip_export_parse = function parse(v) {
|
|
|
1014
1020
|
return val;
|
|
1015
1021
|
}
|
|
1016
1022
|
// parse legacy <shape>
|
|
1017
|
-
v =
|
|
1023
|
+
v = external_dependency_strings_1.asciiLowercase(v);
|
|
1018
1024
|
const matches = v.match(/^rect\(\s*(.*)\s*\)$/);
|
|
1019
1025
|
if (!matches) {
|
|
1020
1026
|
return;
|
|
@@ -1427,7 +1433,8 @@ fontFamily_export_parse = function parse(v) {
|
|
|
1427
1433
|
if (v === "") {
|
|
1428
1434
|
return v;
|
|
1429
1435
|
}
|
|
1430
|
-
const keywords = ["serif", "sans-serif", "system-ui", "
|
|
1436
|
+
const keywords = ["serif", "sans-serif", "cursive", "fantasy", "monospace", "system-ui", "math", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded"];
|
|
1437
|
+
const genericValues = ["fangsong", "kai", "khmer-mul", "nastaliq"];
|
|
1431
1438
|
const val = external_dependency_parsers_0.splitValue(v, {
|
|
1432
1439
|
delimiter: ","
|
|
1433
1440
|
});
|
|
@@ -1446,16 +1453,27 @@ fontFamily_export_parse = function parse(v) {
|
|
|
1446
1453
|
valid = true;
|
|
1447
1454
|
continue;
|
|
1448
1455
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1456
|
+
const obj = external_dependency_parsers_0.parseFunction(i);
|
|
1457
|
+
if (obj) {
|
|
1458
|
+
const {
|
|
1459
|
+
name,
|
|
1460
|
+
value
|
|
1461
|
+
} = obj;
|
|
1462
|
+
if (name === "generic" && genericValues.includes(value)) {
|
|
1463
|
+
font.push(`${name}(${value})`);
|
|
1464
|
+
valid = true;
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
// This implementation does not strictly follow the specification.
|
|
1469
|
+
// The spec does not require the first letter of the font-family to be
|
|
1470
|
+
// capitalized, and unquoted font-family names are not restricted to ASCII.
|
|
1452
1471
|
// However, in the real world, the first letter of the ASCII font-family
|
|
1453
|
-
// names are
|
|
1454
|
-
//
|
|
1455
|
-
//
|
|
1456
|
-
// Therefore, it is unlikely that this implementation will cause problems.
|
|
1472
|
+
// names are capitalized, and unquoted font-family names do not contain
|
|
1473
|
+
// spaces, e.g. `Times`. And non-ASCII font-family names are quoted even
|
|
1474
|
+
// without spaces, e.g. `"メイリオ"`.
|
|
1457
1475
|
// @see https://drafts.csswg.org/css-fonts/#font-family-prop
|
|
1458
|
-
if (
|
|
1476
|
+
if (i !== "undefined" && /^(?:[A-Z][A-Za-z\d-]+(?:\s+[A-Z][A-Za-z\d-]+)*|-?[a-z][a-z-]+)$/.test(i)) {
|
|
1459
1477
|
font.push(i.trim());
|
|
1460
1478
|
valid = true;
|
|
1461
1479
|
continue;
|
|
@@ -1523,50 +1541,131 @@ font_export_parse = function parse(v) {
|
|
|
1523
1541
|
const [fontBlock, ...families] = external_dependency_parsers_0.splitValue(v, {
|
|
1524
1542
|
delimiter: ","
|
|
1525
1543
|
});
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
}
|
|
1534
|
-
const obj = external_dependency_parsers_0.parseShorthand(blockA, font_local_var_shorthandFor, true);
|
|
1535
|
-
if (!obj) {
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
const font = {};
|
|
1544
|
+
const [fontBlockA, fontBlockB] = external_dependency_parsers_0.splitValue(fontBlock, {
|
|
1545
|
+
delimiter: "/"
|
|
1546
|
+
});
|
|
1547
|
+
const font = {
|
|
1548
|
+
"font-style": "normal",
|
|
1549
|
+
"font-variant": "normal",
|
|
1550
|
+
"font-weight": "normal"
|
|
1551
|
+
};
|
|
1539
1552
|
const fontFamilies = new Set();
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1553
|
+
if (fontBlockB) {
|
|
1554
|
+
const [lineB, ...familiesB] = fontBlockB.trim().split(" ");
|
|
1555
|
+
if (!lineB || !{
|
|
1556
|
+
parse: lineHeight_export_parse,
|
|
1557
|
+
isValid: lineHeight_export_isValid,
|
|
1558
|
+
definition: lineHeight_export_definition
|
|
1559
|
+
}.isValid(lineB) || !familiesB.length) {
|
|
1560
|
+
return;
|
|
1547
1561
|
}
|
|
1548
|
-
|
|
1549
|
-
// blockB, if matched, includes line-height and first font-family
|
|
1550
|
-
if (blockB) {
|
|
1551
|
-
const [lineheight, family] = external_dependency_parsers_0.splitValue(blockB);
|
|
1552
|
-
if ({
|
|
1562
|
+
const lineHeightB = {
|
|
1553
1563
|
parse: lineHeight_export_parse,
|
|
1554
1564
|
isValid: lineHeight_export_isValid,
|
|
1555
1565
|
definition: lineHeight_export_definition
|
|
1556
|
-
}.
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1566
|
+
}.parse(lineB);
|
|
1567
|
+
const familyB = familiesB.join(" ");
|
|
1568
|
+
if ({
|
|
1569
|
+
parse: fontFamily_export_parse,
|
|
1570
|
+
isValid: fontFamily_export_isValid,
|
|
1571
|
+
definition: fontFamily_export_definition
|
|
1572
|
+
}.isValid(familyB)) {
|
|
1573
|
+
fontFamilies.add({
|
|
1574
|
+
parse: fontFamily_export_parse,
|
|
1575
|
+
isValid: fontFamily_export_isValid,
|
|
1576
|
+
definition: fontFamily_export_definition
|
|
1577
|
+
}.parse(familyB));
|
|
1562
1578
|
} else {
|
|
1563
1579
|
return;
|
|
1564
1580
|
}
|
|
1565
|
-
|
|
1581
|
+
const parts = external_dependency_parsers_0.splitValue(fontBlockA.trim());
|
|
1582
|
+
const properties = ["font-style", "font-variant", "font-weight", "font-size"];
|
|
1583
|
+
for (const part of parts) {
|
|
1584
|
+
if (part === "normal") {
|
|
1585
|
+
continue;
|
|
1586
|
+
} else {
|
|
1587
|
+
for (const property of properties) {
|
|
1588
|
+
switch (property) {
|
|
1589
|
+
case "font-style":
|
|
1590
|
+
case "font-variant":
|
|
1591
|
+
case "font-weight":
|
|
1592
|
+
case "font-size":
|
|
1593
|
+
{
|
|
1594
|
+
const value = font_local_var_shorthandFor.get(property);
|
|
1595
|
+
if (value.isValid(part)) {
|
|
1596
|
+
font[property] = value.parse(part);
|
|
1597
|
+
}
|
|
1598
|
+
break;
|
|
1599
|
+
}
|
|
1600
|
+
default:
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
if (Object.hasOwn(font, "font-size")) {
|
|
1606
|
+
font["line-height"] = lineHeightB;
|
|
1607
|
+
} else {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
} else {
|
|
1611
|
+
// FIXME: Switch to toReversed() when we can drop Node.js 18 support.
|
|
1612
|
+
const revParts = [...external_dependency_parsers_0.splitValue(fontBlockA.trim())].reverse();
|
|
1613
|
+
const revFontFamily = [];
|
|
1614
|
+
const properties = ["font-style", "font-variant", "font-weight", "line-height"];
|
|
1615
|
+
font["font-style"] = "normal";
|
|
1616
|
+
font["font-variant"] = "normal";
|
|
1617
|
+
font["font-weight"] = "normal";
|
|
1618
|
+
font["line-height"] = "normal";
|
|
1619
|
+
let fontSizeA;
|
|
1620
|
+
for (const part of revParts) {
|
|
1621
|
+
if (fontSizeA) {
|
|
1622
|
+
if (part === "normal") {
|
|
1623
|
+
continue;
|
|
1624
|
+
} else {
|
|
1625
|
+
for (const property of properties) {
|
|
1626
|
+
switch (property) {
|
|
1627
|
+
case "font-style":
|
|
1628
|
+
case "font-variant":
|
|
1629
|
+
case "font-weight":
|
|
1630
|
+
case "line-height":
|
|
1631
|
+
{
|
|
1632
|
+
const value = font_local_var_shorthandFor.get(property);
|
|
1633
|
+
if (value.isValid(part)) {
|
|
1634
|
+
font[property] = value.parse(part);
|
|
1635
|
+
}
|
|
1636
|
+
break;
|
|
1637
|
+
}
|
|
1638
|
+
default:
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
} else if ({
|
|
1643
|
+
parse: fontSize_export_parse,
|
|
1644
|
+
isValid: fontSize_export_isValid,
|
|
1645
|
+
definition: fontSize_export_definition
|
|
1646
|
+
}.isValid(part)) {
|
|
1647
|
+
fontSizeA = {
|
|
1648
|
+
parse: fontSize_export_parse,
|
|
1649
|
+
isValid: fontSize_export_isValid,
|
|
1650
|
+
definition: fontSize_export_definition
|
|
1651
|
+
}.parse(part);
|
|
1652
|
+
} else if ({
|
|
1653
|
+
parse: fontFamily_export_parse,
|
|
1654
|
+
isValid: fontFamily_export_isValid,
|
|
1655
|
+
definition: fontFamily_export_definition
|
|
1656
|
+
}.isValid(part)) {
|
|
1657
|
+
revFontFamily.push(part);
|
|
1658
|
+
} else {
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
const family = revFontFamily.reverse().join(" ");
|
|
1663
|
+
if (fontSizeA && {
|
|
1566
1664
|
parse: fontFamily_export_parse,
|
|
1567
1665
|
isValid: fontFamily_export_isValid,
|
|
1568
1666
|
definition: fontFamily_export_definition
|
|
1569
1667
|
}.isValid(family)) {
|
|
1668
|
+
font["font-size"] = fontSizeA;
|
|
1570
1669
|
fontFamilies.add({
|
|
1571
1670
|
parse: fontFamily_export_parse,
|
|
1572
1671
|
isValid: fontFamily_export_isValid,
|
|
@@ -1597,7 +1696,7 @@ font_export_parse = function parse(v) {
|
|
|
1597
1696
|
font_export_definition = {
|
|
1598
1697
|
set(v) {
|
|
1599
1698
|
v = external_dependency_parsers_0.prepareValue(v, this._global);
|
|
1600
|
-
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
1699
|
+
if (v === "" || external_dependency_parsers_0.hasVarFunc(v)) {
|
|
1601
1700
|
for (const [key] of font_local_var_shorthandFor) {
|
|
1602
1701
|
this._setProperty(key, "");
|
|
1603
1702
|
}
|
|
@@ -1612,7 +1711,7 @@ font_export_definition = {
|
|
|
1612
1711
|
const val = obj[key];
|
|
1613
1712
|
if (typeof val === "string") {
|
|
1614
1713
|
this._setProperty(key, val);
|
|
1615
|
-
if (val && !str.has(val)) {
|
|
1714
|
+
if (val && val !== "normal" && !str.has(val)) {
|
|
1616
1715
|
if (key === "line-height") {
|
|
1617
1716
|
str.add(`/ ${val}`);
|
|
1618
1717
|
} else {
|
|
@@ -1635,7 +1734,7 @@ font_export_definition = {
|
|
|
1635
1734
|
if (external_dependency_parsers_0.hasVarFunc(v)) {
|
|
1636
1735
|
return "";
|
|
1637
1736
|
}
|
|
1638
|
-
if (v && !str.has(v)) {
|
|
1737
|
+
if (v && v !== "normal" && !str.has(v)) {
|
|
1639
1738
|
if (key === "line-height") {
|
|
1640
1739
|
str.add(`/ ${v}`);
|
|
1641
1740
|
} else {
|
|
@@ -1727,7 +1826,7 @@ lightingColor_export_definition = {
|
|
|
1727
1826
|
var margin_export_parse, margin_export_isValid, margin_export_definition;
|
|
1728
1827
|
const margin_local_var_positions = ["top", "right", "bottom", "left"];
|
|
1729
1828
|
margin_export_parse = function parse(v) {
|
|
1730
|
-
const val = external_dependency_parsers_0.parseMeasurement(v
|
|
1829
|
+
const val = external_dependency_parsers_0.parseMeasurement(v);
|
|
1731
1830
|
if (val) {
|
|
1732
1831
|
return val;
|
|
1733
1832
|
}
|
|
@@ -1764,7 +1863,7 @@ margin_export_definition = {
|
|
|
1764
1863
|
};
|
|
1765
1864
|
var marginBottom_export_parse, marginBottom_export_isValid, marginBottom_export_definition;
|
|
1766
1865
|
marginBottom_export_parse = function parse(v) {
|
|
1767
|
-
const val = external_dependency_parsers_0.parseMeasurement(v
|
|
1866
|
+
const val = external_dependency_parsers_0.parseMeasurement(v);
|
|
1768
1867
|
if (val) {
|
|
1769
1868
|
return val;
|
|
1770
1869
|
}
|
|
@@ -1794,7 +1893,7 @@ marginBottom_export_definition = {
|
|
|
1794
1893
|
};
|
|
1795
1894
|
var marginLeft_export_parse, marginLeft_export_isValid, marginLeft_export_definition;
|
|
1796
1895
|
marginLeft_export_parse = function parse(v) {
|
|
1797
|
-
const val = external_dependency_parsers_0.parseMeasurement(v
|
|
1896
|
+
const val = external_dependency_parsers_0.parseMeasurement(v);
|
|
1798
1897
|
if (val) {
|
|
1799
1898
|
return val;
|
|
1800
1899
|
}
|
|
@@ -1824,7 +1923,7 @@ marginLeft_export_definition = {
|
|
|
1824
1923
|
};
|
|
1825
1924
|
var marginRight_export_parse, marginRight_export_isValid, marginRight_export_definition;
|
|
1826
1925
|
marginRight_export_parse = function parse(v) {
|
|
1827
|
-
const val = external_dependency_parsers_0.parseMeasurement(v
|
|
1926
|
+
const val = external_dependency_parsers_0.parseMeasurement(v);
|
|
1828
1927
|
if (val) {
|
|
1829
1928
|
return val;
|
|
1830
1929
|
}
|
|
@@ -1854,7 +1953,7 @@ marginRight_export_definition = {
|
|
|
1854
1953
|
};
|
|
1855
1954
|
var marginTop_export_parse, marginTop_export_isValid, marginTop_export_definition;
|
|
1856
1955
|
marginTop_export_parse = function parse(v) {
|
|
1857
|
-
const val = external_dependency_parsers_0.parseMeasurement(v
|
|
1956
|
+
const val = external_dependency_parsers_0.parseMeasurement(v);
|
|
1858
1957
|
if (val) {
|
|
1859
1958
|
return val;
|
|
1860
1959
|
}
|
package/lib/parsers.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"use strict";
|
|
6
6
|
|
|
7
7
|
const { resolve: resolveColor, utils } = require("@asamuzakjp/css-color");
|
|
8
|
+
const { asciiLowercase } = require("./utils/strings");
|
|
8
9
|
|
|
9
10
|
const { cssCalc, isColor, isGradient, splitValue } = utils;
|
|
10
11
|
|
|
@@ -24,44 +25,48 @@ const NUM_TYPE = Object.freeze({
|
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
// System colors
|
|
28
|
+
// @see https://drafts.csswg.org/css-color/#css-system-colors
|
|
29
|
+
// @see https://drafts.csswg.org/css-color/#deprecated-system-colors
|
|
27
30
|
const SYS_COLOR = Object.freeze([
|
|
28
31
|
"accentcolor",
|
|
29
32
|
"accentcolortext",
|
|
33
|
+
"activeborder",
|
|
34
|
+
"activecaption",
|
|
30
35
|
"activetext",
|
|
36
|
+
"appworkspace",
|
|
37
|
+
"background",
|
|
31
38
|
"buttonborder",
|
|
32
39
|
"buttonface",
|
|
40
|
+
"buttonhighlight",
|
|
41
|
+
"buttonshadow",
|
|
33
42
|
"buttontext",
|
|
34
43
|
"canvas",
|
|
35
44
|
"canvastext",
|
|
45
|
+
"captiontext",
|
|
36
46
|
"field",
|
|
37
47
|
"fieldtext",
|
|
38
48
|
"graytext",
|
|
39
49
|
"highlight",
|
|
40
50
|
"highlighttext",
|
|
41
|
-
"linktext",
|
|
42
|
-
"mark",
|
|
43
|
-
"marktext",
|
|
44
|
-
"visitedtext",
|
|
45
|
-
"activeborder",
|
|
46
|
-
"activecaption",
|
|
47
|
-
"appworkspace",
|
|
48
|
-
"background",
|
|
49
|
-
"buttonhighlight",
|
|
50
|
-
"buttonshadow",
|
|
51
|
-
"captiontext",
|
|
52
51
|
"inactiveborder",
|
|
53
52
|
"inactivecaption",
|
|
54
53
|
"inactivecaptiontext",
|
|
55
54
|
"infobackground",
|
|
56
55
|
"infotext",
|
|
56
|
+
"linktext",
|
|
57
|
+
"mark",
|
|
58
|
+
"marktext",
|
|
57
59
|
"menu",
|
|
58
60
|
"menutext",
|
|
59
61
|
"scrollbar",
|
|
62
|
+
"selecteditem",
|
|
63
|
+
"selecteditemtext",
|
|
60
64
|
"threeddarkshadow",
|
|
61
65
|
"threedface",
|
|
62
66
|
"threedhighlight",
|
|
63
67
|
"threedlightshadow",
|
|
64
68
|
"threedshadow",
|
|
69
|
+
"visitedtext",
|
|
65
70
|
"window",
|
|
66
71
|
"windowframe",
|
|
67
72
|
"windowtext"
|
|
@@ -78,6 +83,7 @@ const varRegEx = /^var\(/;
|
|
|
78
83
|
const varContainedRegEx = /(?<=[*/\s(])var\(/;
|
|
79
84
|
const calcRegEx =
|
|
80
85
|
/^(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)\(/;
|
|
86
|
+
const functionRegEx = /^([a-z][a-z\d]*(?:-[a-z\d]+)*)\(/i;
|
|
81
87
|
|
|
82
88
|
const getNumericType = function getNumericType(val) {
|
|
83
89
|
if (varRegEx.test(val)) {
|
|
@@ -184,7 +190,7 @@ exports.parseLength = function parseLength(val, restrictToPositive = false) {
|
|
|
184
190
|
if (restrictToPositive && num < 0) {
|
|
185
191
|
return;
|
|
186
192
|
}
|
|
187
|
-
return `${num}${unit
|
|
193
|
+
return `${num}${asciiLowercase(unit)}`;
|
|
188
194
|
}
|
|
189
195
|
default:
|
|
190
196
|
if (varContainedRegEx.test(val)) {
|
|
@@ -216,7 +222,7 @@ exports.parsePercent = function parsePercent(val, restrictToPositive = false) {
|
|
|
216
222
|
if (restrictToPositive && num < 0) {
|
|
217
223
|
return;
|
|
218
224
|
}
|
|
219
|
-
return `${num}${unit
|
|
225
|
+
return `${num}${asciiLowercase(unit)}`;
|
|
220
226
|
}
|
|
221
227
|
default:
|
|
222
228
|
if (varContainedRegEx.test(val)) {
|
|
@@ -250,7 +256,7 @@ exports.parseMeasurement = function parseMeasurement(val, restrictToPositive = f
|
|
|
250
256
|
if (restrictToPositive && num < 0) {
|
|
251
257
|
return;
|
|
252
258
|
}
|
|
253
|
-
return `${num}${unit
|
|
259
|
+
return `${num}${asciiLowercase(unit)}`;
|
|
254
260
|
}
|
|
255
261
|
default:
|
|
256
262
|
if (varContainedRegEx.test(val)) {
|
|
@@ -279,7 +285,7 @@ exports.parseAngle = function parseAngle(val, normalizeDeg = false) {
|
|
|
279
285
|
case NUM_TYPE.ANGLE: {
|
|
280
286
|
let [, numVal, unit] = unitRegEx.exec(val);
|
|
281
287
|
numVal = parseFloat(numVal);
|
|
282
|
-
unit = unit
|
|
288
|
+
unit = asciiLowercase(unit);
|
|
283
289
|
if (unit === "deg") {
|
|
284
290
|
if (normalizeDeg && numVal < 0) {
|
|
285
291
|
while (numVal < 0) {
|
|
@@ -391,7 +397,7 @@ exports.parseKeyword = function parseKeyword(val, validKeywords = []) {
|
|
|
391
397
|
if (varRegEx.test(val)) {
|
|
392
398
|
return val;
|
|
393
399
|
}
|
|
394
|
-
val = val.toString()
|
|
400
|
+
val = asciiLowercase(val.toString());
|
|
395
401
|
if (validKeywords.includes(val) || GLOBAL_VALUE.includes(val)) {
|
|
396
402
|
return val;
|
|
397
403
|
}
|
|
@@ -404,8 +410,11 @@ exports.parseColor = function parseColor(val) {
|
|
|
404
410
|
if (varRegEx.test(val)) {
|
|
405
411
|
return val;
|
|
406
412
|
}
|
|
407
|
-
if (/^[a-z]+$/i.test(val)
|
|
408
|
-
|
|
413
|
+
if (/^[a-z]+$/i.test(val)) {
|
|
414
|
+
const v = asciiLowercase(val);
|
|
415
|
+
if (SYS_COLOR.includes(v)) {
|
|
416
|
+
return v;
|
|
417
|
+
}
|
|
409
418
|
}
|
|
410
419
|
const res = resolveColor(val, {
|
|
411
420
|
format: "specifiedValue"
|
|
@@ -452,6 +461,32 @@ exports.parseImage = function parseImage(val) {
|
|
|
452
461
|
}
|
|
453
462
|
};
|
|
454
463
|
|
|
464
|
+
exports.parseFunction = function parseFunction(val) {
|
|
465
|
+
if (val === "") {
|
|
466
|
+
return {
|
|
467
|
+
name: null,
|
|
468
|
+
value: ""
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (functionRegEx.test(val) && val.endsWith(")")) {
|
|
472
|
+
if (varRegEx.test(val) || varContainedRegEx.test(val)) {
|
|
473
|
+
return {
|
|
474
|
+
name: "var",
|
|
475
|
+
value: val
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
const [, name] = functionRegEx.exec(val);
|
|
479
|
+
const value = val
|
|
480
|
+
.replace(new RegExp(`^${name}\\(`), "")
|
|
481
|
+
.replace(/\)$/, "")
|
|
482
|
+
.trim();
|
|
483
|
+
return {
|
|
484
|
+
name,
|
|
485
|
+
value
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
|
|
455
490
|
exports.parseShorthand = function parseShorthand(val, shorthandFor, preserve = false) {
|
|
456
491
|
const obj = {};
|
|
457
492
|
if (val === "" || exports.hasVarFunc(val)) {
|
|
@@ -491,7 +526,7 @@ exports.parseShorthand = function parseShorthand(val, shorthandFor, preserve = f
|
|
|
491
526
|
|
|
492
527
|
// Returns `false` for global values, e.g. "inherit".
|
|
493
528
|
exports.isValidColor = function isValidColor(val) {
|
|
494
|
-
if (SYS_COLOR.includes(val
|
|
529
|
+
if (SYS_COLOR.includes(asciiLowercase(val))) {
|
|
495
530
|
return true;
|
|
496
531
|
}
|
|
497
532
|
return isColor(val);
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// * also fix longhands
|
|
5
5
|
|
|
6
6
|
const parsers = require("../parsers");
|
|
7
|
+
const strings = require("../utils/strings");
|
|
7
8
|
const backgroundImage = require("./backgroundImage");
|
|
8
9
|
const backgroundPosition = require("./backgroundPosition");
|
|
9
10
|
const backgroundRepeat = require("./backgroundRepeat");
|
|
@@ -21,7 +22,12 @@ const shorthandFor = new Map([
|
|
|
21
22
|
module.exports.definition = {
|
|
22
23
|
set(v) {
|
|
23
24
|
v = parsers.prepareValue(v, this._global);
|
|
24
|
-
if (
|
|
25
|
+
if (/^none$/i.test(v)) {
|
|
26
|
+
for (const [key] of shorthandFor) {
|
|
27
|
+
this._setProperty(key, "");
|
|
28
|
+
}
|
|
29
|
+
this._setProperty("background", strings.asciiLowercase(v));
|
|
30
|
+
} else if (parsers.hasVarFunc(v)) {
|
|
25
31
|
for (const [key] of shorthandFor) {
|
|
26
32
|
this._setProperty(key, "");
|
|
27
33
|
}
|
|
@@ -19,7 +19,7 @@ module.exports.definition = {
|
|
|
19
19
|
v = parsers.prepareValue(v, this._global);
|
|
20
20
|
if (parsers.hasVarFunc(v)) {
|
|
21
21
|
this._setProperty("background", "");
|
|
22
|
-
this._setProperty("
|
|
22
|
+
this._setProperty("background-attachment", v);
|
|
23
23
|
} else {
|
|
24
24
|
this._setProperty("background-attachment", module.exports.parse(v));
|
|
25
25
|
}
|
|
@@ -22,7 +22,7 @@ module.exports.definition = {
|
|
|
22
22
|
v = parsers.prepareValue(v, this._global);
|
|
23
23
|
if (parsers.hasVarFunc(v)) {
|
|
24
24
|
this._setProperty("background", "");
|
|
25
|
-
this._setProperty("
|
|
25
|
+
this._setProperty("background-color", v);
|
|
26
26
|
} else {
|
|
27
27
|
this._setProperty("background-color", module.exports.parse(v));
|
|
28
28
|
}
|
|
@@ -18,7 +18,7 @@ module.exports.definition = {
|
|
|
18
18
|
v = parsers.prepareValue(v, this._global);
|
|
19
19
|
if (parsers.hasVarFunc(v)) {
|
|
20
20
|
this._setProperty("background", "");
|
|
21
|
-
this._setProperty("
|
|
21
|
+
this._setProperty("background-image", v);
|
|
22
22
|
} else {
|
|
23
23
|
this._setProperty("background-image", module.exports.parse(v));
|
|
24
24
|
}
|
|
@@ -39,7 +39,7 @@ module.exports.definition = {
|
|
|
39
39
|
v = parsers.prepareValue(v, this._global);
|
|
40
40
|
if (parsers.hasVarFunc(v)) {
|
|
41
41
|
this._setProperty("background", "");
|
|
42
|
-
this._setProperty("
|
|
42
|
+
this._setProperty("background-position", v);
|
|
43
43
|
} else {
|
|
44
44
|
this._setProperty("background-position", module.exports.parse(v));
|
|
45
45
|
}
|
|
@@ -19,7 +19,7 @@ module.exports.definition = {
|
|
|
19
19
|
v = parsers.prepareValue(v, this._global);
|
|
20
20
|
if (parsers.hasVarFunc(v)) {
|
|
21
21
|
this._setProperty("background", "");
|
|
22
|
-
this._setProperty("
|
|
22
|
+
this._setProperty("background-repeat", v);
|
|
23
23
|
} else {
|
|
24
24
|
this._setProperty("background-repeat", module.exports.parse(v));
|
|
25
25
|
}
|
package/lib/properties/border.js
CHANGED
package/lib/properties/clip.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// @see https://drafts.fxtf.org/css-masking/#clip-property
|
|
4
4
|
|
|
5
5
|
const parsers = require("../parsers");
|
|
6
|
+
const strings = require("../utils/strings");
|
|
6
7
|
|
|
7
8
|
module.exports.parse = function parse(v) {
|
|
8
9
|
if (v === "") {
|
|
@@ -13,7 +14,7 @@ module.exports.parse = function parse(v) {
|
|
|
13
14
|
return val;
|
|
14
15
|
}
|
|
15
16
|
// parse legacy <shape>
|
|
16
|
-
v =
|
|
17
|
+
v = strings.asciiLowercase(v);
|
|
17
18
|
const matches = v.match(/^rect\(\s*(.*)\s*\)$/);
|
|
18
19
|
if (!matches) {
|
|
19
20
|
return;
|
package/lib/properties/font.js
CHANGED
|
@@ -26,38 +26,97 @@ module.exports.parse = function parse(v) {
|
|
|
26
26
|
const [fontBlock, ...families] = parsers.splitValue(v, {
|
|
27
27
|
delimiter: ","
|
|
28
28
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
const obj = parsers.parseShorthand(blockA, shorthandFor, true);
|
|
38
|
-
if (!obj) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
const font = {};
|
|
29
|
+
const [fontBlockA, fontBlockB] = parsers.splitValue(fontBlock, {
|
|
30
|
+
delimiter: "/"
|
|
31
|
+
});
|
|
32
|
+
const font = {
|
|
33
|
+
"font-style": "normal",
|
|
34
|
+
"font-variant": "normal",
|
|
35
|
+
"font-weight": "normal"
|
|
36
|
+
};
|
|
42
37
|
const fontFamilies = new Set();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
if (fontBlockB) {
|
|
39
|
+
const [lineB, ...familiesB] = fontBlockB.trim().split(" ");
|
|
40
|
+
if (!lineB || !lineHeight.isValid(lineB) || !familiesB.length) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const lineHeightB = lineHeight.parse(lineB);
|
|
44
|
+
const familyB = familiesB.join(" ");
|
|
45
|
+
if (fontFamily.isValid(familyB)) {
|
|
46
|
+
fontFamilies.add(fontFamily.parse(familyB));
|
|
48
47
|
} else {
|
|
49
|
-
|
|
48
|
+
return;
|
|
50
49
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
const parts = parsers.splitValue(fontBlockA.trim());
|
|
51
|
+
const properties = ["font-style", "font-variant", "font-weight", "font-size"];
|
|
52
|
+
for (const part of parts) {
|
|
53
|
+
if (part === "normal") {
|
|
54
|
+
continue;
|
|
55
|
+
} else {
|
|
56
|
+
for (const property of properties) {
|
|
57
|
+
switch (property) {
|
|
58
|
+
case "font-style":
|
|
59
|
+
case "font-variant":
|
|
60
|
+
case "font-weight":
|
|
61
|
+
case "font-size": {
|
|
62
|
+
const value = shorthandFor.get(property);
|
|
63
|
+
if (value.isValid(part)) {
|
|
64
|
+
font[property] = value.parse(part);
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
default:
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (Object.hasOwn(font, "font-size")) {
|
|
74
|
+
font["line-height"] = lineHeightB;
|
|
57
75
|
} else {
|
|
58
76
|
return;
|
|
59
77
|
}
|
|
60
|
-
|
|
78
|
+
} else {
|
|
79
|
+
// FIXME: Switch to toReversed() when we can drop Node.js 18 support.
|
|
80
|
+
const revParts = [...parsers.splitValue(fontBlockA.trim())].reverse();
|
|
81
|
+
const revFontFamily = [];
|
|
82
|
+
const properties = ["font-style", "font-variant", "font-weight", "line-height"];
|
|
83
|
+
font["font-style"] = "normal";
|
|
84
|
+
font["font-variant"] = "normal";
|
|
85
|
+
font["font-weight"] = "normal";
|
|
86
|
+
font["line-height"] = "normal";
|
|
87
|
+
let fontSizeA;
|
|
88
|
+
for (const part of revParts) {
|
|
89
|
+
if (fontSizeA) {
|
|
90
|
+
if (part === "normal") {
|
|
91
|
+
continue;
|
|
92
|
+
} else {
|
|
93
|
+
for (const property of properties) {
|
|
94
|
+
switch (property) {
|
|
95
|
+
case "font-style":
|
|
96
|
+
case "font-variant":
|
|
97
|
+
case "font-weight":
|
|
98
|
+
case "line-height": {
|
|
99
|
+
const value = shorthandFor.get(property);
|
|
100
|
+
if (value.isValid(part)) {
|
|
101
|
+
font[property] = value.parse(part);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
default:
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} else if (fontSize.isValid(part)) {
|
|
110
|
+
fontSizeA = fontSize.parse(part);
|
|
111
|
+
} else if (fontFamily.isValid(part)) {
|
|
112
|
+
revFontFamily.push(part);
|
|
113
|
+
} else {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const family = revFontFamily.reverse().join(" ");
|
|
118
|
+
if (fontSizeA && fontFamily.isValid(family)) {
|
|
119
|
+
font["font-size"] = fontSizeA;
|
|
61
120
|
fontFamilies.add(fontFamily.parse(family));
|
|
62
121
|
} else {
|
|
63
122
|
return;
|
|
@@ -77,7 +136,7 @@ module.exports.parse = function parse(v) {
|
|
|
77
136
|
module.exports.definition = {
|
|
78
137
|
set(v) {
|
|
79
138
|
v = parsers.prepareValue(v, this._global);
|
|
80
|
-
if (parsers.hasVarFunc(v)) {
|
|
139
|
+
if (v === "" || parsers.hasVarFunc(v)) {
|
|
81
140
|
for (const [key] of shorthandFor) {
|
|
82
141
|
this._setProperty(key, "");
|
|
83
142
|
}
|
|
@@ -92,7 +151,7 @@ module.exports.definition = {
|
|
|
92
151
|
const val = obj[key];
|
|
93
152
|
if (typeof val === "string") {
|
|
94
153
|
this._setProperty(key, val);
|
|
95
|
-
if (val && !str.has(val)) {
|
|
154
|
+
if (val && val !== "normal" && !str.has(val)) {
|
|
96
155
|
if (key === "line-height") {
|
|
97
156
|
str.add(`/ ${val}`);
|
|
98
157
|
} else {
|
|
@@ -115,7 +174,7 @@ module.exports.definition = {
|
|
|
115
174
|
if (parsers.hasVarFunc(v)) {
|
|
116
175
|
return "";
|
|
117
176
|
}
|
|
118
|
-
if (v && !str.has(v)) {
|
|
177
|
+
if (v && v !== "normal" && !str.has(v)) {
|
|
119
178
|
if (key === "line-height") {
|
|
120
179
|
str.add(`/ ${v}`);
|
|
121
180
|
} else {
|
|
@@ -6,7 +6,20 @@ module.exports.parse = function parse(v) {
|
|
|
6
6
|
if (v === "") {
|
|
7
7
|
return v;
|
|
8
8
|
}
|
|
9
|
-
const keywords = [
|
|
9
|
+
const keywords = [
|
|
10
|
+
"serif",
|
|
11
|
+
"sans-serif",
|
|
12
|
+
"cursive",
|
|
13
|
+
"fantasy",
|
|
14
|
+
"monospace",
|
|
15
|
+
"system-ui",
|
|
16
|
+
"math",
|
|
17
|
+
"ui-serif",
|
|
18
|
+
"ui-sans-serif",
|
|
19
|
+
"ui-monospace",
|
|
20
|
+
"ui-rounded"
|
|
21
|
+
];
|
|
22
|
+
const genericValues = ["fangsong", "kai", "khmer-mul", "nastaliq"];
|
|
10
23
|
const val = parsers.splitValue(v, {
|
|
11
24
|
delimiter: ","
|
|
12
25
|
});
|
|
@@ -25,16 +38,27 @@ module.exports.parse = function parse(v) {
|
|
|
25
38
|
valid = true;
|
|
26
39
|
continue;
|
|
27
40
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
const obj = parsers.parseFunction(i);
|
|
42
|
+
if (obj) {
|
|
43
|
+
const { name, value } = obj;
|
|
44
|
+
if (name === "generic" && genericValues.includes(value)) {
|
|
45
|
+
font.push(`${name}(${value})`);
|
|
46
|
+
valid = true;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// This implementation does not strictly follow the specification.
|
|
51
|
+
// The spec does not require the first letter of the font-family to be
|
|
52
|
+
// capitalized, and unquoted font-family names are not restricted to ASCII.
|
|
31
53
|
// However, in the real world, the first letter of the ASCII font-family
|
|
32
|
-
// names are
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// Therefore, it is unlikely that this implementation will cause problems.
|
|
54
|
+
// names are capitalized, and unquoted font-family names do not contain
|
|
55
|
+
// spaces, e.g. `Times`. And non-ASCII font-family names are quoted even
|
|
56
|
+
// without spaces, e.g. `"メイリオ"`.
|
|
36
57
|
// @see https://drafts.csswg.org/css-fonts/#font-family-prop
|
|
37
|
-
if (
|
|
58
|
+
if (
|
|
59
|
+
i !== "undefined" &&
|
|
60
|
+
/^(?:[A-Z][A-Za-z\d-]+(?:\s+[A-Z][A-Za-z\d-]+)*|-?[a-z][a-z-]+)$/.test(i)
|
|
61
|
+
) {
|
|
38
62
|
font.push(i.trim());
|
|
39
63
|
valid = true;
|
|
40
64
|
continue;
|
package/lib/properties/margin.js
CHANGED
|
@@ -5,7 +5,7 @@ const parsers = require("../parsers");
|
|
|
5
5
|
const positions = ["top", "right", "bottom", "left"];
|
|
6
6
|
|
|
7
7
|
module.exports.parse = function parse(v) {
|
|
8
|
-
const val = parsers.parseMeasurement(v
|
|
8
|
+
const val = parsers.parseMeasurement(v);
|
|
9
9
|
if (val) {
|
|
10
10
|
return val;
|
|
11
11
|
}
|
package/lib/utils/camelize.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const { asciiLowercase } = require("./strings");
|
|
4
|
+
|
|
3
5
|
// Utility to translate from `border-width` to `borderWidth`.
|
|
4
6
|
// NOTE: For values prefixed with webkit, e.g. `-webkit-foo`, we need to provide
|
|
5
7
|
// both `webkitFoo` and `WebkitFoo`. Here we only return `webkitFoo`.
|
|
@@ -27,7 +29,7 @@ exports.camelCaseToDashed = function (camelCase) {
|
|
|
27
29
|
if (camelCase.startsWith("--")) {
|
|
28
30
|
return camelCase;
|
|
29
31
|
}
|
|
30
|
-
const dashed = camelCase.replace(/(?<=[a-z])[A-Z]/g, "-$&")
|
|
32
|
+
const dashed = asciiLowercase(camelCase.replace(/(?<=[a-z])[A-Z]/g, "-$&"));
|
|
31
33
|
if (/^webkit-/.test(dashed)) {
|
|
32
34
|
return `-${dashed}`;
|
|
33
35
|
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// Forked from https://github.com/jsdom/jsdom/blob/main/lib/jsdom/living/helpers/strings.js
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
// https://infra.spec.whatwg.org/#ascii-whitespace
|
|
6
|
+
const asciiWhitespaceRe = /^[\t\n\f\r ]$/;
|
|
7
|
+
exports.asciiWhitespaceRe = asciiWhitespaceRe;
|
|
8
|
+
|
|
9
|
+
// https://infra.spec.whatwg.org/#ascii-lowercase
|
|
10
|
+
exports.asciiLowercase = (s) => {
|
|
11
|
+
const len = s.length;
|
|
12
|
+
const out = new Array(len);
|
|
13
|
+
for (let i = 0; i < len; i++) {
|
|
14
|
+
const code = s.charCodeAt(i);
|
|
15
|
+
// If the character is between 'A' (65) and 'Z' (90), convert using bitwise OR with 32
|
|
16
|
+
out[i] = code >= 65 && code <= 90 ? String.fromCharCode(code | 32) : s[i];
|
|
17
|
+
}
|
|
18
|
+
return out.join("");
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// https://infra.spec.whatwg.org/#ascii-uppercase
|
|
22
|
+
exports.asciiUppercase = (s) => {
|
|
23
|
+
const len = s.length;
|
|
24
|
+
const out = new Array(len);
|
|
25
|
+
for (let i = 0; i < len; i++) {
|
|
26
|
+
const code = s.charCodeAt(i);
|
|
27
|
+
// If the character is between 'a' (97) and 'z' (122), convert using bitwise AND with ~32
|
|
28
|
+
out[i] = code >= 97 && code <= 122 ? String.fromCharCode(code & ~32) : s[i];
|
|
29
|
+
}
|
|
30
|
+
return out.join("");
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// https://infra.spec.whatwg.org/#strip-newlines
|
|
34
|
+
exports.stripNewlines = (s) => {
|
|
35
|
+
return s.replace(/[\n\r]+/g, "");
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
|
|
39
|
+
exports.stripLeadingAndTrailingASCIIWhitespace = (s) => {
|
|
40
|
+
return s.replace(/^[ \t\n\f\r]+/, "").replace(/[ \t\n\f\r]+$/, "");
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
|
|
44
|
+
exports.stripAndCollapseASCIIWhitespace = (s) => {
|
|
45
|
+
return s
|
|
46
|
+
.replace(/[ \t\n\f\r]+/g, " ")
|
|
47
|
+
.replace(/^[ \t\n\f\r]+/, "")
|
|
48
|
+
.replace(/[ \t\n\f\r]+$/, "");
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// https://html.spec.whatwg.org/multipage/infrastructure.html#valid-simple-colour
|
|
52
|
+
exports.isValidSimpleColor = (s) => {
|
|
53
|
+
return /^#[a-fA-F\d]{6}$/.test(s);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// https://infra.spec.whatwg.org/#ascii-case-insensitive
|
|
57
|
+
exports.asciiCaseInsensitiveMatch = (a, b) => {
|
|
58
|
+
if (a.length !== b.length) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < a.length; ++i) {
|
|
63
|
+
if ((a.charCodeAt(i) | 32) !== (b.charCodeAt(i) | 32)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return true;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers
|
|
72
|
+
// Error is represented as null.
|
|
73
|
+
const parseInteger = (exports.parseInteger = (input) => {
|
|
74
|
+
// The implementation here is slightly different from the spec's. We want to use parseInt(), but parseInt() trims
|
|
75
|
+
// Unicode whitespace in addition to just ASCII ones, so we make sure that the trimmed prefix contains only ASCII
|
|
76
|
+
// whitespace ourselves.
|
|
77
|
+
const numWhitespace = input.length - input.trimStart().length;
|
|
78
|
+
if (/[^\t\n\f\r ]/.test(input.slice(0, numWhitespace))) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
// We don't allow hexadecimal numbers here.
|
|
82
|
+
// eslint-disable-next-line radix
|
|
83
|
+
const value = parseInt(input, 10);
|
|
84
|
+
if (Number.isNaN(value)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
// parseInt() returns -0 for "-0". Normalize that here.
|
|
88
|
+
return value === 0 ? 0 : value;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-non-negative-integers
|
|
92
|
+
// Error is represented as null.
|
|
93
|
+
exports.parseNonNegativeInteger = (input) => {
|
|
94
|
+
const value = parseInteger(input);
|
|
95
|
+
if (value === null) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
if (value < 0) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-floating-point-number
|
|
105
|
+
const floatingPointNumRe = /^-?(?:\d+|\d*\.\d+)(?:[eE][-+]?\d+)?$/;
|
|
106
|
+
exports.isValidFloatingPointNumber = (str) => floatingPointNumRe.test(str);
|
|
107
|
+
|
|
108
|
+
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-floating-point-number-values
|
|
109
|
+
// Error is represented as null.
|
|
110
|
+
exports.parseFloatingPointNumber = (str) => {
|
|
111
|
+
// The implementation here is slightly different from the spec's. We need to use parseFloat() in order to retain
|
|
112
|
+
// accuracy, but parseFloat() trims Unicode whitespace in addition to just ASCII ones, so we make sure that the
|
|
113
|
+
// trimmed prefix contains only ASCII whitespace ourselves.
|
|
114
|
+
const numWhitespace = str.length - str.trimStart().length;
|
|
115
|
+
if (/[^\t\n\f\r ]/.test(str.slice(0, numWhitespace))) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const parsed = parseFloat(str);
|
|
119
|
+
return isFinite(parsed) ? parsed : null;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// https://infra.spec.whatwg.org/#split-on-ascii-whitespace
|
|
123
|
+
exports.splitOnASCIIWhitespace = (str) => {
|
|
124
|
+
let position = 0;
|
|
125
|
+
const tokens = [];
|
|
126
|
+
while (position < str.length && asciiWhitespaceRe.test(str[position])) {
|
|
127
|
+
position++;
|
|
128
|
+
}
|
|
129
|
+
if (position === str.length) {
|
|
130
|
+
return tokens;
|
|
131
|
+
}
|
|
132
|
+
while (position < str.length) {
|
|
133
|
+
const start = position;
|
|
134
|
+
while (position < str.length && !asciiWhitespaceRe.test(str[position])) {
|
|
135
|
+
position++;
|
|
136
|
+
}
|
|
137
|
+
tokens.push(str.slice(start, position));
|
|
138
|
+
while (position < str.length && asciiWhitespaceRe.test(str[position])) {
|
|
139
|
+
position++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return tokens;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// https://infra.spec.whatwg.org/#split-on-commas
|
|
146
|
+
exports.splitOnCommas = (str) => {
|
|
147
|
+
let position = 0;
|
|
148
|
+
const tokens = [];
|
|
149
|
+
while (position < str.length) {
|
|
150
|
+
let start = position;
|
|
151
|
+
while (position < str.length && str[position] !== ",") {
|
|
152
|
+
position++;
|
|
153
|
+
}
|
|
154
|
+
let end = position;
|
|
155
|
+
while (start < str.length && asciiWhitespaceRe.test(str[start])) {
|
|
156
|
+
start++;
|
|
157
|
+
}
|
|
158
|
+
while (end > start && asciiWhitespaceRe.test(str[end - 1])) {
|
|
159
|
+
end--;
|
|
160
|
+
}
|
|
161
|
+
tokens.push(str.slice(start, end));
|
|
162
|
+
if (position < str.length) {
|
|
163
|
+
position++;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return tokens;
|
|
167
|
+
};
|