cssstyle 4.3.0 → 4.4.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.
Files changed (94) hide show
  1. package/lib/CSSStyleDeclaration.js +545 -206
  2. package/lib/allExtraProperties.js +42 -60
  3. package/lib/allWebkitProperties.js +107 -187
  4. package/lib/generated/allProperties.js +615 -0
  5. package/lib/{implementedProperties.js → generated/implementedProperties.js} +11 -18
  6. package/lib/generated/properties.js +2574 -0
  7. package/lib/parsers.js +400 -544
  8. package/lib/properties/background.js +40 -13
  9. package/lib/properties/backgroundAttachment.js +23 -15
  10. package/lib/properties/backgroundColor.js +21 -19
  11. package/lib/properties/backgroundImage.js +19 -14
  12. package/lib/properties/backgroundPosition.js +35 -41
  13. package/lib/properties/backgroundRepeat.js +20 -20
  14. package/lib/properties/border.js +34 -25
  15. package/lib/properties/borderBottom.js +34 -11
  16. package/lib/properties/borderBottomColor.js +27 -8
  17. package/lib/properties/borderBottomStyle.js +42 -13
  18. package/lib/properties/borderBottomWidth.js +28 -8
  19. package/lib/properties/borderCollapse.js +16 -16
  20. package/lib/properties/borderColor.js +32 -18
  21. package/lib/properties/borderLeft.js +34 -11
  22. package/lib/properties/borderLeftColor.js +27 -8
  23. package/lib/properties/borderLeftStyle.js +42 -13
  24. package/lib/properties/borderLeftWidth.js +28 -8
  25. package/lib/properties/borderRight.js +34 -11
  26. package/lib/properties/borderRightColor.js +27 -8
  27. package/lib/properties/borderRightStyle.js +42 -13
  28. package/lib/properties/borderRightWidth.js +28 -8
  29. package/lib/properties/borderSpacing.js +31 -27
  30. package/lib/properties/borderStyle.js +43 -27
  31. package/lib/properties/borderTop.js +34 -11
  32. package/lib/properties/borderTopColor.js +27 -8
  33. package/lib/properties/borderTopStyle.js +42 -13
  34. package/lib/properties/borderTopWidth.js +28 -9
  35. package/lib/properties/borderWidth.js +32 -34
  36. package/lib/properties/bottom.js +23 -7
  37. package/lib/properties/clear.js +32 -8
  38. package/lib/properties/clip.js +33 -27
  39. package/lib/properties/color.js +23 -7
  40. package/lib/properties/flex.js +59 -29
  41. package/lib/properties/flexBasis.js +21 -16
  42. package/lib/properties/flexGrow.js +19 -10
  43. package/lib/properties/flexShrink.js +19 -10
  44. package/lib/properties/float.js +21 -6
  45. package/lib/properties/floodColor.js +23 -7
  46. package/lib/properties/font.js +120 -33
  47. package/lib/properties/fontFamily.js +59 -21
  48. package/lib/properties/fontSize.js +37 -28
  49. package/lib/properties/fontStyle.js +23 -9
  50. package/lib/properties/fontVariant.js +26 -8
  51. package/lib/properties/fontWeight.js +26 -23
  52. package/lib/properties/height.js +21 -14
  53. package/lib/properties/left.js +23 -7
  54. package/lib/properties/lightingColor.js +23 -7
  55. package/lib/properties/lineHeight.js +29 -16
  56. package/lib/properties/margin.js +42 -57
  57. package/lib/properties/marginBottom.js +34 -7
  58. package/lib/properties/marginLeft.js +34 -7
  59. package/lib/properties/marginRight.js +34 -7
  60. package/lib/properties/marginTop.js +34 -7
  61. package/lib/properties/opacity.js +39 -7
  62. package/lib/properties/outlineColor.js +23 -7
  63. package/lib/properties/padding.js +43 -51
  64. package/lib/properties/paddingBottom.js +36 -7
  65. package/lib/properties/paddingLeft.js +34 -7
  66. package/lib/properties/paddingRight.js +34 -7
  67. package/lib/properties/paddingTop.js +34 -7
  68. package/lib/properties/right.js +23 -7
  69. package/lib/properties/stopColor.js +23 -7
  70. package/lib/properties/top.js +23 -7
  71. package/lib/properties/webkitBorderAfterColor.js +23 -7
  72. package/lib/properties/webkitBorderBeforeColor.js +23 -7
  73. package/lib/properties/webkitBorderEndColor.js +23 -7
  74. package/lib/properties/webkitBorderStartColor.js +23 -7
  75. package/lib/properties/webkitColumnRuleColor.js +23 -7
  76. package/lib/properties/webkitTapHighlightColor.js +23 -7
  77. package/lib/properties/webkitTextEmphasisColor.js +23 -7
  78. package/lib/properties/webkitTextFillColor.js +23 -7
  79. package/lib/properties/webkitTextStrokeColor.js +23 -7
  80. package/lib/properties/width.js +21 -14
  81. package/lib/utils/camelize.js +35 -0
  82. package/lib/utils/propertyDescriptors.js +16 -0
  83. package/package.json +5 -4
  84. package/lib/allProperties.js +0 -529
  85. package/lib/constants.js +0 -6
  86. package/lib/named_colors.json +0 -152
  87. package/lib/properties/azimuth.js +0 -64
  88. package/lib/properties/cssFloat.js +0 -12
  89. package/lib/properties/textLineThroughColor.js +0 -14
  90. package/lib/properties/textOverlineColor.js +0 -14
  91. package/lib/properties/textUnderlineColor.js +0 -14
  92. package/lib/properties/webkitMatchNearestMailBlockquoteColor.js +0 -14
  93. package/lib/properties.js +0 -1672
  94. package/lib/utils/getBasicPropertyDescriptor.js +0 -14
package/lib/parsers.js CHANGED
@@ -1,352 +1,446 @@
1
- /*********************************************************************
2
- * These are commonly used parsers for CSS Values they take a string *
3
- * to parse and return a string after it's been converted, if needed *
4
- ********************************************************************/
5
- 'use strict';
1
+ /**
2
+ * These are commonly used parsers for CSS Values they take a string to parse
3
+ * and return a string after it's been converted, if needed
4
+ */
5
+ "use strict";
6
+
7
+ const { resolve: resolveColor, utils } = require("@asamuzakjp/css-color");
6
8
 
7
- const { resolve: resolveColor, utils } = require('@asamuzakjp/css-color');
8
9
  const { cssCalc, isColor, isGradient, splitValue } = utils;
9
10
 
10
- exports.TYPES = {
11
- INTEGER: 1,
11
+ // CSS global values
12
+ // @see https://drafts.csswg.org/css-cascade-5/#defaulting-keywords
13
+ const GLOBAL_VALUE = Object.freeze(["initial", "inherit", "unset", "revert", "revert-layer"]);
14
+
15
+ // Numeric data types
16
+ const NUM_TYPE = Object.freeze({
17
+ UNDEFINED: 0,
18
+ VAR: 1,
12
19
  NUMBER: 2,
13
- LENGTH: 3,
14
20
  PERCENT: 4,
15
- URL: 5,
16
- COLOR: 6,
17
- STRING: 7,
18
- ANGLE: 8,
19
- KEYWORD: 9,
20
- NULL_OR_EMPTY_STR: 10,
21
- CALC: 11,
22
- VAR: 12,
23
- GRADIENT: 13,
24
- };
25
-
26
- // regular expressions
27
- var DIGIT = '(?:0|[1-9]\\d*)';
28
- var NUMBER = `[+-]?(?:${DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${DIGIT})?`;
29
- var integerRegEx = new RegExp(`^[+-]?${DIGIT}$`);
30
- var numberRegEx = new RegExp(`^${NUMBER}$`);
31
- var lengthRegEx = new RegExp(
32
- `^${NUMBER}(?:[cm]m|[dls]?v(?:[bhiw]|max|min)|in|p[ctx]|q|r?(?:[cl]h|cap|e[mx]|ic))$`
33
- );
34
- var percentRegEx = new RegExp(`^${NUMBER}%$`);
35
- var angleRegEx = new RegExp(`^${NUMBER}(?:deg|g?rad|turn)$`);
36
- var urlRegEx = /^url\(\s*([^)]*)\s*\)$/;
37
- var stringRegEx = /^("[^"]*"|'[^']*')$/;
38
- var varRegEx = /^var\(|(?<=[*/\s(])var\(/;
39
- var calcRegEx =
21
+ LENGTH: 8,
22
+ ANGLE: 0x10,
23
+ CALC: 0x20
24
+ });
25
+
26
+ // System colors
27
+ const SYS_COLOR = Object.freeze([
28
+ "accentcolor",
29
+ "accentcolortext",
30
+ "activetext",
31
+ "buttonborder",
32
+ "buttonface",
33
+ "buttontext",
34
+ "canvas",
35
+ "canvastext",
36
+ "field",
37
+ "fieldtext",
38
+ "graytext",
39
+ "highlight",
40
+ "highlighttext",
41
+ "linktext",
42
+ "mark",
43
+ "marktext",
44
+ "visitedtext",
45
+ "activeborder",
46
+ "activecaption",
47
+ "appworkspace",
48
+ "background",
49
+ "buttonhighlight",
50
+ "buttonshadow",
51
+ "captiontext",
52
+ "inactiveborder",
53
+ "inactivecaption",
54
+ "inactivecaptiontext",
55
+ "infobackground",
56
+ "infotext",
57
+ "menu",
58
+ "menutext",
59
+ "scrollbar",
60
+ "threeddarkshadow",
61
+ "threedface",
62
+ "threedhighlight",
63
+ "threedlightshadow",
64
+ "threedshadow",
65
+ "window",
66
+ "windowframe",
67
+ "windowtext"
68
+ ]);
69
+
70
+ // Regular expressions
71
+ const DIGIT = "(?:0|[1-9]\\d*)";
72
+ const NUMBER = `[+-]?(?:${DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${DIGIT})?`;
73
+ const unitRegEx = new RegExp(`^(${NUMBER})([a-z]+|%)?$`, "i");
74
+ const urlRegEx = /^url\(\s*((?:[^)]|\\\))*)\s*\)$/;
75
+ const keywordRegEx = /^[a-z]+(?:-[a-z]+)*$/i;
76
+ const stringRegEx = /^("[^"]*"|'[^']*')$/;
77
+ const varRegEx = /^var\(/;
78
+ const varContainedRegEx = /(?<=[*/\s(])var\(/;
79
+ const calcRegEx =
40
80
  /^(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)\(/;
41
81
 
42
- // This will return one of the above types based on the passed in string
43
- exports.valueType = function valueType(val) {
44
- if (val === '' || val === null) {
45
- return exports.TYPES.NULL_OR_EMPTY_STR;
46
- }
47
- if (typeof val === 'number') {
48
- val = val.toString();
49
- }
50
- if (typeof val !== 'string') {
51
- return undefined;
52
- }
53
- if (integerRegEx.test(val)) {
54
- return exports.TYPES.INTEGER;
55
- }
56
- if (numberRegEx.test(val)) {
57
- return exports.TYPES.NUMBER;
58
- }
59
- if (lengthRegEx.test(val)) {
60
- return exports.TYPES.LENGTH;
61
- }
62
- if (percentRegEx.test(val)) {
63
- return exports.TYPES.PERCENT;
64
- }
65
- if (urlRegEx.test(val)) {
66
- return exports.TYPES.URL;
67
- }
82
+ const getNumericType = function getNumericType(val) {
68
83
  if (varRegEx.test(val)) {
69
- return exports.TYPES.VAR;
84
+ return NUM_TYPE.VAR;
70
85
  }
71
86
  if (calcRegEx.test(val)) {
72
- return exports.TYPES.CALC;
73
- }
74
- if (stringRegEx.test(val)) {
75
- return exports.TYPES.STRING;
76
- }
77
- if (angleRegEx.test(val)) {
78
- return exports.TYPES.ANGLE;
79
- }
80
- if (isColor(val)) {
81
- return exports.TYPES.COLOR;
82
- }
83
- if (isGradient(val)) {
84
- return exports.TYPES.GRADIENT;
87
+ return NUM_TYPE.CALC;
85
88
  }
86
-
87
- switch (val.toLowerCase()) {
88
- // the following are deprecated in CSS3
89
- case 'activeborder':
90
- case 'activecaption':
91
- case 'appworkspace':
92
- case 'background':
93
- case 'buttonface':
94
- case 'buttonhighlight':
95
- case 'buttonshadow':
96
- case 'buttontext':
97
- case 'captiontext':
98
- case 'graytext':
99
- case 'highlight':
100
- case 'highlighttext':
101
- case 'inactiveborder':
102
- case 'inactivecaption':
103
- case 'inactivecaptiontext':
104
- case 'infobackground':
105
- case 'infotext':
106
- case 'menu':
107
- case 'menutext':
108
- case 'scrollbar':
109
- case 'threeddarkshadow':
110
- case 'threedface':
111
- case 'threedhighlight':
112
- case 'threedlightshadow':
113
- case 'threedshadow':
114
- case 'window':
115
- case 'windowframe':
116
- case 'windowtext':
117
- return exports.TYPES.COLOR;
118
- default:
119
- return exports.TYPES.KEYWORD;
89
+ if (unitRegEx.test(val)) {
90
+ const [, , unit] = unitRegEx.exec(val);
91
+ if (!unit) {
92
+ return NUM_TYPE.NUMBER;
93
+ }
94
+ if (unit === "%") {
95
+ return NUM_TYPE.PERCENT;
96
+ }
97
+ if (/^(?:[cm]m|[dls]?v(?:[bhiw]|max|min)|in|p[ctx]|q|r?(?:[cl]h|cap|e[mx]|ic))$/i.test(unit)) {
98
+ return NUM_TYPE.LENGTH;
99
+ }
100
+ if (/^(?:deg|g?rad|turn)$/i.test(unit)) {
101
+ return NUM_TYPE.ANGLE;
102
+ }
120
103
  }
104
+ return NUM_TYPE.UNDEFINED;
121
105
  };
122
106
 
123
- exports.parseInteger = function parseInteger(val) {
124
- var type = exports.valueType(val);
125
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
126
- return val;
127
- }
128
- if (type !== exports.TYPES.INTEGER) {
129
- return undefined;
107
+ // Prepare stringified value.
108
+ exports.prepareValue = function prepareValue(value, globalObject = globalThis) {
109
+ // `null` is converted to an empty string.
110
+ // @see https://webidl.spec.whatwg.org/#LegacyNullToEmptyString
111
+ if (value === null) {
112
+ return "";
113
+ }
114
+ const type = typeof value;
115
+ switch (type) {
116
+ case "string":
117
+ return value.trim();
118
+ case "number":
119
+ return value.toString();
120
+ case "undefined":
121
+ return "undefined";
122
+ case "symbol":
123
+ throw new globalObject.TypeError("Can not convert symbol to string.");
124
+ default: {
125
+ const str = value.toString();
126
+ if (typeof str === "string") {
127
+ return str;
128
+ }
129
+ throw new globalObject.TypeError(`Can not convert ${type} to string.`);
130
+ }
130
131
  }
131
- return String(parseInt(val, 10));
132
132
  };
133
133
 
134
- exports.parseNumber = function parseNumber(val) {
135
- var type = exports.valueType(val);
136
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
137
- return val;
138
- }
139
- if (type !== exports.TYPES.NUMBER && type !== exports.TYPES.INTEGER) {
140
- return undefined;
141
- }
142
- return String(parseFloat(val));
134
+ exports.hasVarFunc = function hasVarFunc(val) {
135
+ return varRegEx.test(val) || varContainedRegEx.test(val);
143
136
  };
144
137
 
145
- exports.parseLength = function parseLength(val) {
146
- if (val === 0 || val === '0') {
147
- return '0px';
148
- }
149
- var type = exports.valueType(val);
150
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
151
- return val;
152
- }
153
- if (type !== exports.TYPES.LENGTH) {
154
- return undefined;
138
+ exports.parseNumber = function parseNumber(val, restrictToPositive = false) {
139
+ if (val === "") {
140
+ return "";
141
+ }
142
+ const type = getNumericType(val);
143
+ switch (type) {
144
+ case NUM_TYPE.VAR:
145
+ return val;
146
+ case NUM_TYPE.CALC:
147
+ return cssCalc(val, {
148
+ format: "specifiedValue"
149
+ });
150
+ case NUM_TYPE.NUMBER: {
151
+ const num = parseFloat(val);
152
+ if (restrictToPositive && num < 0) {
153
+ return;
154
+ }
155
+ return `${num}`;
156
+ }
157
+ default:
158
+ if (varContainedRegEx.test(val)) {
159
+ return val;
160
+ }
155
161
  }
156
- return val;
157
162
  };
158
163
 
159
- exports.parsePercent = function parsePercent(val) {
160
- if (val === 0 || val === '0') {
161
- return '0%';
162
- }
163
- var type = exports.valueType(val);
164
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
165
- return val;
166
- }
167
- if (type !== exports.TYPES.PERCENT) {
168
- return undefined;
164
+ exports.parseLength = function parseLength(val, restrictToPositive = false) {
165
+ if (val === "") {
166
+ return "";
167
+ }
168
+ const type = getNumericType(val);
169
+ switch (type) {
170
+ case NUM_TYPE.VAR:
171
+ return val;
172
+ case NUM_TYPE.CALC:
173
+ return cssCalc(val, {
174
+ format: "specifiedValue"
175
+ });
176
+ case NUM_TYPE.NUMBER:
177
+ if (parseFloat(val) === 0) {
178
+ return "0px";
179
+ }
180
+ return;
181
+ case NUM_TYPE.LENGTH: {
182
+ const [, numVal, unit] = unitRegEx.exec(val);
183
+ const num = parseFloat(numVal);
184
+ if (restrictToPositive && num < 0) {
185
+ return;
186
+ }
187
+ return `${num}${unit.toLowerCase()}`;
188
+ }
189
+ default:
190
+ if (varContainedRegEx.test(val)) {
191
+ return val;
192
+ }
169
193
  }
170
- return val;
171
194
  };
172
195
 
173
- // either a length or a percent
174
- exports.parseMeasurement = function parseMeasurement(val) {
175
- var type = exports.valueType(val);
176
- if (type === exports.TYPES.VAR) {
177
- return val;
178
- }
179
- if (type === exports.TYPES.CALC) {
180
- return cssCalc(val, {
181
- format: 'specifiedValue',
182
- });
196
+ exports.parsePercent = function parsePercent(val, restrictToPositive = false) {
197
+ if (val === "") {
198
+ return "";
199
+ }
200
+ const type = getNumericType(val);
201
+ switch (type) {
202
+ case NUM_TYPE.VAR:
203
+ return val;
204
+ case NUM_TYPE.CALC:
205
+ return cssCalc(val, {
206
+ format: "specifiedValue"
207
+ });
208
+ case NUM_TYPE.NUMBER:
209
+ if (parseFloat(val) === 0) {
210
+ return "0%";
211
+ }
212
+ return;
213
+ case NUM_TYPE.PERCENT: {
214
+ const [, numVal, unit] = unitRegEx.exec(val);
215
+ const num = parseFloat(numVal);
216
+ if (restrictToPositive && num < 0) {
217
+ return;
218
+ }
219
+ return `${num}${unit.toLowerCase()}`;
220
+ }
221
+ default:
222
+ if (varContainedRegEx.test(val)) {
223
+ return val;
224
+ }
183
225
  }
226
+ };
184
227
 
185
- var length = exports.parseLength(val);
186
- if (length !== undefined) {
187
- return length;
228
+ // Either a length or a percent.
229
+ exports.parseMeasurement = function parseMeasurement(val, restrictToPositive = false) {
230
+ if (val === "") {
231
+ return "";
232
+ }
233
+ const type = getNumericType(val);
234
+ switch (type) {
235
+ case NUM_TYPE.VAR:
236
+ return val;
237
+ case NUM_TYPE.CALC:
238
+ return cssCalc(val, {
239
+ format: "specifiedValue"
240
+ });
241
+ case NUM_TYPE.NUMBER:
242
+ if (parseFloat(val) === 0) {
243
+ return "0px";
244
+ }
245
+ return;
246
+ case NUM_TYPE.LENGTH:
247
+ case NUM_TYPE.PERCENT: {
248
+ const [, numVal, unit] = unitRegEx.exec(val);
249
+ const num = parseFloat(numVal);
250
+ if (restrictToPositive && num < 0) {
251
+ return;
252
+ }
253
+ return `${num}${unit.toLowerCase()}`;
254
+ }
255
+ default:
256
+ if (varContainedRegEx.test(val)) {
257
+ return val;
258
+ }
188
259
  }
189
- return exports.parsePercent(val);
190
260
  };
191
261
 
192
- exports.parseInheritingMeasurement = function parseInheritingMeasurement(v) {
193
- if (String(v).toLowerCase() === 'auto') {
194
- return 'auto';
195
- }
196
- if (String(v).toLowerCase() === 'inherit') {
197
- return 'inherit';
262
+ exports.parseAngle = function parseAngle(val, normalizeDeg = false) {
263
+ if (val === "") {
264
+ return "";
265
+ }
266
+ const type = getNumericType(val);
267
+ switch (type) {
268
+ case NUM_TYPE.VAR:
269
+ return val;
270
+ case NUM_TYPE.CALC:
271
+ return cssCalc(val, {
272
+ format: "specifiedValue"
273
+ });
274
+ case NUM_TYPE.NUMBER:
275
+ if (parseFloat(val) === 0) {
276
+ return "0deg";
277
+ }
278
+ return;
279
+ case NUM_TYPE.ANGLE: {
280
+ let [, numVal, unit] = unitRegEx.exec(val);
281
+ numVal = parseFloat(numVal);
282
+ unit = unit.toLowerCase();
283
+ if (unit === "deg") {
284
+ if (normalizeDeg && numVal < 0) {
285
+ while (numVal < 0) {
286
+ numVal += 360;
287
+ }
288
+ }
289
+ numVal %= 360;
290
+ }
291
+ return `${numVal}${unit}`;
292
+ }
293
+ default:
294
+ if (varContainedRegEx.test(val)) {
295
+ return val;
296
+ }
198
297
  }
199
- return exports.parseMeasurement(v);
200
298
  };
201
299
 
202
300
  exports.parseUrl = function parseUrl(val) {
203
- var type = exports.valueType(val);
204
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
301
+ if (val === "") {
205
302
  return val;
206
303
  }
207
- var res = urlRegEx.exec(val);
208
- // does it match the regex?
304
+ const res = urlRegEx.exec(val);
209
305
  if (!res) {
210
- return undefined;
306
+ return;
211
307
  }
212
- var str = res[1];
213
- // if it starts with single or double quotes, does it end with the same?
308
+ let str = res[1];
309
+ // If it starts with single or double quotes, does it end with the same?
214
310
  if ((str[0] === '"' || str[0] === "'") && str[0] !== str[str.length - 1]) {
215
- return undefined;
311
+ return;
216
312
  }
217
313
  if (str[0] === '"' || str[0] === "'") {
218
314
  str = str.substr(1, str.length - 2);
219
315
  }
220
-
221
- var i;
222
- for (i = 0; i < str.length; i++) {
316
+ let urlstr = "";
317
+ let escaped = false;
318
+ for (let i = 0; i < str.length; i++) {
223
319
  switch (str[i]) {
224
- case '(':
225
- case ')':
226
- case ' ':
227
- case '\t':
228
- case '\n':
320
+ case "\\":
321
+ if (escaped) {
322
+ urlstr += "\\\\";
323
+ escaped = false;
324
+ } else {
325
+ escaped = true;
326
+ }
327
+ break;
328
+ case "(":
329
+ case ")":
330
+ case " ":
331
+ case "\t":
332
+ case "\n":
229
333
  case "'":
334
+ if (!escaped) {
335
+ return;
336
+ }
337
+ urlstr += str[i];
338
+ escaped = false;
339
+ break;
230
340
  case '"':
231
- return undefined;
232
- case '\\':
233
- i++;
341
+ if (!escaped) {
342
+ return;
343
+ }
344
+ urlstr += '\\"';
345
+ escaped = false;
234
346
  break;
347
+ default:
348
+ urlstr += str[i];
349
+ escaped = false;
235
350
  }
236
351
  }
237
-
238
- return 'url(' + str + ')';
352
+ return `url("${urlstr}")`;
239
353
  };
240
354
 
241
355
  exports.parseString = function parseString(val) {
242
- var type = exports.valueType(val);
243
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
244
- return val;
356
+ if (val === "") {
357
+ return "";
245
358
  }
246
- if (type !== exports.TYPES.STRING) {
247
- return undefined;
359
+ if (!stringRegEx.test(val)) {
360
+ return;
248
361
  }
249
- var i;
250
- for (i = 1; i < val.length - 1; i++) {
362
+ val = val.substr(1, val.length - 2);
363
+ let str = "";
364
+ let escaped = false;
365
+ for (let i = 0; i < val.length; i++) {
251
366
  switch (val[i]) {
252
- case val[0]:
253
- return undefined;
254
- case '\\':
255
- i++;
256
- while (i < val.length - 1 && /[0-9A-Fa-f]/.test(val[i])) {
257
- i++;
367
+ case "\\":
368
+ if (escaped) {
369
+ str += "\\\\";
370
+ escaped = false;
371
+ } else {
372
+ escaped = true;
258
373
  }
259
374
  break;
375
+ case '"':
376
+ str += '\\"';
377
+ escaped = false;
378
+ break;
379
+ default:
380
+ str += val[i];
381
+ escaped = false;
260
382
  }
261
383
  }
262
- if (i >= val.length) {
263
- return undefined;
264
- }
265
- return val;
384
+ return `"${str}"`;
266
385
  };
267
386
 
268
- exports.parseColor = function parseColor(val) {
269
- var type = exports.valueType(val);
270
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
271
- return val;
387
+ exports.parseKeyword = function parseKeyword(val, validKeywords = []) {
388
+ if (val === "") {
389
+ return "";
272
390
  }
273
- if (/^[a-z]+$/i.test(val) && type === exports.TYPES.COLOR) {
391
+ if (varRegEx.test(val)) {
274
392
  return val;
275
393
  }
276
- var res = resolveColor(val, {
277
- format: 'specifiedValue',
278
- });
279
- if (res) {
280
- return res;
281
- }
282
- return undefined;
283
- };
284
-
285
- exports.parseAngle = function parseAngle(val) {
286
- var type = exports.valueType(val);
287
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
394
+ val = val.toString().toLowerCase();
395
+ if (validKeywords.includes(val) || GLOBAL_VALUE.includes(val)) {
288
396
  return val;
289
397
  }
290
- if (type !== exports.TYPES.ANGLE) {
291
- return undefined;
292
- }
293
- var res = angleRegEx.exec(val);
294
- var flt = parseFloat(res[1]);
295
- if (res[2] === 'rad') {
296
- flt *= 180 / Math.PI;
297
- } else if (res[2] === 'grad') {
298
- flt *= 360 / 400;
299
- }
300
-
301
- while (flt < 0) {
302
- flt += 360;
303
- }
304
- while (flt > 360) {
305
- flt -= 360;
306
- }
307
- return flt + 'deg';
308
398
  };
309
399
 
310
- exports.parseKeyword = function parseKeyword(val, valid_keywords) {
311
- var type = exports.valueType(val);
312
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
400
+ exports.parseColor = function parseColor(val) {
401
+ if (val === "") {
402
+ return "";
403
+ }
404
+ if (varRegEx.test(val)) {
313
405
  return val;
314
406
  }
315
- if (type !== exports.TYPES.KEYWORD) {
316
- return undefined;
407
+ if (/^[a-z]+$/i.test(val) && SYS_COLOR.includes(val.toLowerCase())) {
408
+ return val;
317
409
  }
318
- val = val.toString().toLowerCase();
319
- var i;
320
- for (i = 0; i < valid_keywords.length; i++) {
321
- if (valid_keywords[i].toLowerCase() === val) {
322
- return valid_keywords[i];
323
- }
410
+ const res = resolveColor(val, {
411
+ format: "specifiedValue"
412
+ });
413
+ if (res) {
414
+ return res;
324
415
  }
325
- return undefined;
416
+ return exports.parseKeyword(val);
326
417
  };
327
418
 
328
419
  exports.parseImage = function parseImage(val) {
329
- if (/^(?:none|inherit)$/i.test(val)) {
330
- return val;
420
+ if (val === "") {
421
+ return "";
331
422
  }
332
- var type = exports.valueType(val);
333
- if (type === exports.TYPES.NULL_OR_EMPTY_STR || type === exports.TYPES.VAR) {
423
+ if (varRegEx.test(val)) {
334
424
  return val;
335
425
  }
336
- var values = splitValue(val, ',');
337
- var isImage = !!values.length;
338
- var i;
339
- for (i = 0; i < values.length; i++) {
340
- var image = values[i];
341
- var t = exports.valueType(image);
342
- if (t === exports.TYPES.NULL_OR_EMPTY_STR) {
343
- return image;
426
+ if (keywordRegEx.test(val)) {
427
+ return exports.parseKeyword(val, ["none"]);
428
+ }
429
+ const values = splitValue(val, {
430
+ delimiter: ",",
431
+ preserveComment: varContainedRegEx.test(val)
432
+ });
433
+ let isImage = Boolean(values.length);
434
+ for (let i = 0; i < values.length; i++) {
435
+ const image = values[i];
436
+ if (image === "") {
437
+ return "";
344
438
  }
345
- if (t === exports.TYPES.GRADIENT || /^(?:none|inherit)$/i.test(image)) {
439
+ if (isGradient(image) || /^(?:none|inherit)$/i.test(image)) {
346
440
  continue;
347
441
  }
348
- var imageUrl = exports.parseUrl(image);
349
- if (exports.valueType(imageUrl) === exports.TYPES.URL) {
442
+ const imageUrl = exports.parseUrl(image);
443
+ if (imageUrl) {
350
444
  values[i] = imageUrl;
351
445
  } else {
352
446
  isImage = false;
@@ -354,293 +448,55 @@ exports.parseImage = function parseImage(val) {
354
448
  }
355
449
  }
356
450
  if (isImage) {
357
- return values.join(', ');
451
+ return values.join(", ");
358
452
  }
359
- return undefined;
360
453
  };
361
454
 
362
- // utility to translate from border-width to borderWidth
363
- var dashedToCamelCase = function (dashed) {
364
- var i;
365
- var camel = '';
366
- var nextCap = false;
367
- for (i = 0; i < dashed.length; i++) {
368
- if (dashed[i] !== '-') {
369
- camel += nextCap ? dashed[i].toUpperCase() : dashed[i];
370
- nextCap = false;
371
- } else {
372
- nextCap = true;
455
+ exports.parseShorthand = function parseShorthand(val, shorthandFor, preserve = false) {
456
+ const obj = {};
457
+ if (val === "" || exports.hasVarFunc(val)) {
458
+ for (const [property] of shorthandFor) {
459
+ obj[property] = "";
373
460
  }
374
- }
375
- return camel;
376
- };
377
- exports.dashedToCamelCase = dashedToCamelCase;
378
-
379
- var is_space = /\s/;
380
- var opening_deliminators = ['"', "'", '('];
381
- var closing_deliminators = ['"', "'", ')'];
382
- // this splits on whitespace, but keeps quoted and parened parts together
383
- var getParts = function (str) {
384
- var deliminator_stack = [];
385
- var length = str.length;
386
- var i;
387
- var parts = [];
388
- var current_part = '';
389
- var opening_index;
390
- var closing_index;
391
- for (i = 0; i < length; i++) {
392
- opening_index = opening_deliminators.indexOf(str[i]);
393
- closing_index = closing_deliminators.indexOf(str[i]);
394
- if (is_space.test(str[i])) {
395
- if (deliminator_stack.length === 0) {
396
- if (current_part !== '') {
397
- parts.push(current_part);
398
- }
399
- current_part = '';
400
- } else {
401
- current_part += str[i];
402
- }
403
- } else {
404
- if (str[i] === '\\') {
405
- i++;
406
- current_part += str[i];
407
- } else {
408
- current_part += str[i];
409
- if (
410
- closing_index !== -1 &&
411
- closing_index === deliminator_stack[deliminator_stack.length - 1]
412
- ) {
413
- deliminator_stack.pop();
414
- } else if (opening_index !== -1) {
415
- deliminator_stack.push(opening_index);
416
- }
417
- }
418
- }
419
- }
420
- if (current_part !== '') {
421
- parts.push(current_part);
422
- }
423
- return parts;
424
- };
425
-
426
- /*
427
- * this either returns undefined meaning that it isn't valid
428
- * or returns an object where the keys are dashed short
429
- * hand properties and the values are the values to set
430
- * on them
431
- */
432
- exports.shorthandParser = function parse(v, shorthand_for) {
433
- var obj = {};
434
- var type = exports.valueType(v);
435
- if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
436
- Object.keys(shorthand_for).forEach(function (property) {
437
- obj[property] = '';
438
- });
439
461
  return obj;
440
462
  }
441
-
442
- if (typeof v === 'number') {
443
- v = v.toString();
444
- }
445
-
446
- if (typeof v !== 'string') {
447
- return undefined;
448
- }
449
-
450
- if (v.toLowerCase() === 'inherit') {
451
- return {};
452
- }
453
- var parts = getParts(v);
454
- var valid = true;
455
- parts.forEach(function (part, i) {
456
- var part_valid = false;
457
- Object.keys(shorthand_for).forEach(function (property) {
458
- if (shorthand_for[property].isValid(part, i)) {
459
- part_valid = true;
460
- obj[property] = part;
461
- }
462
- });
463
- valid = valid && part_valid;
464
- });
465
- if (!valid) {
466
- return undefined;
467
- }
468
- return obj;
469
- };
470
-
471
- exports.shorthandSetter = function (property, shorthand_for) {
472
- return function (v) {
473
- var obj = exports.shorthandParser(v, shorthand_for);
474
- if (obj === undefined) {
475
- return;
463
+ const key = exports.parseKeyword(val);
464
+ if (key) {
465
+ if (key === "inherit") {
466
+ return obj;
476
467
  }
477
- //console.log('shorthandSetter for:', property, 'obj:', obj);
478
- Object.keys(obj).forEach(function (subprop) {
479
- // in case subprop is an implicit property, this will clear
480
- // *its* subpropertiesX
481
- var camel = dashedToCamelCase(subprop);
482
- this[camel] = obj[subprop];
483
- // in case it gets translated into something else (0 -> 0px)
484
- obj[subprop] = this[camel];
485
- this.removeProperty(subprop);
486
- // don't add in empty properties
487
- if (obj[subprop] !== '') {
488
- this._values[subprop] = obj[subprop];
489
- }
490
- }, this);
491
- Object.keys(shorthand_for).forEach(function (subprop) {
492
- if (!obj.hasOwnProperty(subprop)) {
493
- this.removeProperty(subprop);
494
- delete this._values[subprop];
468
+ return;
469
+ }
470
+ const parts = splitValue(val);
471
+ const shorthandArr = [...shorthandFor];
472
+ for (const part of parts) {
473
+ let partValid = false;
474
+ for (let i = 0; i < shorthandArr.length; i++) {
475
+ const [property, value] = shorthandArr[i];
476
+ if (value.isValid(part)) {
477
+ partValid = true;
478
+ obj[property] = value.parse(part);
479
+ if (!preserve) {
480
+ shorthandArr.splice(i, 1);
481
+ break;
482
+ }
495
483
  }
496
- }, this);
497
- // in case the value is something like 'none' that removes all values,
498
- // check that the generated one is not empty, first remove the property
499
- // if it already exists, then call the shorthandGetter, if it's an empty
500
- // string, don't set the property
501
- this.removeProperty(property);
502
- var calculated = exports.shorthandGetter(property, shorthand_for).call(this);
503
- if (calculated !== '') {
504
- this._setProperty(property, calculated);
505
- }
506
- };
507
- };
508
-
509
- exports.shorthandGetter = function (property, shorthand_for) {
510
- return function () {
511
- if (this._values[property] !== undefined) {
512
- return this.getPropertyValue(property);
513
- }
514
- return Object.keys(shorthand_for)
515
- .map(function (subprop) {
516
- return this.getPropertyValue(subprop);
517
- }, this)
518
- .filter(function (value) {
519
- return value !== '';
520
- })
521
- .join(' ');
522
- };
523
- };
524
-
525
- // isValid(){1,4} | inherit
526
- // if one, it applies to all
527
- // if two, the first applies to the top and bottom, and the second to left and right
528
- // if three, the first applies to the top, the second to left and right, the third bottom
529
- // if four, top, right, bottom, left
530
- exports.implicitSetter = function (property_before, property_after, isValid, parser) {
531
- property_after = property_after || '';
532
- if (property_after !== '') {
533
- property_after = '-' + property_after;
534
- }
535
- var part_names = ['top', 'right', 'bottom', 'left'];
536
-
537
- return function (v) {
538
- if (typeof v === 'number') {
539
- v = v.toString();
540
- }
541
- if (typeof v !== 'string') {
542
- return undefined;
543
- }
544
- var parts;
545
- if (v.toLowerCase() === 'inherit' || v === '') {
546
- parts = [v];
547
- } else {
548
- parts = getParts(v);
549
- }
550
- if (parts.length < 1 || parts.length > 4) {
551
- return undefined;
552
- }
553
-
554
- if (!parts.every(isValid)) {
555
- return undefined;
556
484
  }
557
-
558
- parts = parts.map(function (part) {
559
- return parser(part);
560
- });
561
- this._setProperty(property_before + property_after, parts.join(' '));
562
- if (parts.length === 1) {
563
- parts[1] = parts[0];
564
- }
565
- if (parts.length === 2) {
566
- parts[2] = parts[0];
567
- }
568
- if (parts.length === 3) {
569
- parts[3] = parts[1];
570
- }
571
-
572
- for (var i = 0; i < 4; i++) {
573
- var property = property_before + '-' + part_names[i] + property_after;
574
- this.removeProperty(property);
575
- if (parts[i] !== '') {
576
- this._values[property] = parts[i];
577
- }
485
+ if (!partValid) {
486
+ return;
578
487
  }
579
- return v;
580
- };
488
+ }
489
+ return obj;
581
490
  };
582
491
 
583
- //
584
- // Companion to implicitSetter, but for the individual parts.
585
- // This sets the individual value, and checks to see if all four
586
- // sub-parts are set. If so, it sets the shorthand version and removes
587
- // the individual parts from the cssText.
588
- //
589
- exports.subImplicitSetter = function (prefix, part, isValid, parser) {
590
- var property = prefix + '-' + part;
591
- var subparts = [prefix + '-top', prefix + '-right', prefix + '-bottom', prefix + '-left'];
592
-
593
- return function (v) {
594
- if (typeof v === 'number') {
595
- v = v.toString();
596
- }
597
- if (v === null) {
598
- v = '';
599
- }
600
- if (typeof v !== 'string') {
601
- return undefined;
602
- }
603
- if (!isValid(v)) {
604
- return undefined;
605
- }
606
- v = parser(v);
607
- this._setProperty(property, v);
608
-
609
- var combinedPriority = this.getPropertyPriority(prefix);
610
- var parts = subparts.map((subpart) => this._values[subpart]);
611
- var priorities = subparts.map((subpart) => this.getPropertyPriority(subpart));
612
- // Combine into a single property if all values are set and have the same priority
613
- if (
614
- parts.every((p) => p !== '' && p != null) &&
615
- priorities.every((p) => p === priorities[0]) &&
616
- priorities[0] === combinedPriority
617
- ) {
618
- for (var i = 0; i < subparts.length; i++) {
619
- this.removeProperty(subparts[i]);
620
- this._values[subparts[i]] = parts[i];
621
- }
622
- this._setProperty(prefix, parts.join(' '), priorities[0]);
623
- } else {
624
- this.removeProperty(prefix);
625
- for (var j = 0; j < subparts.length; j++) {
626
- // The property we're setting won't be important, the rest will either keep their priority or inherit it from the combined property
627
- var priority = subparts[j] === property ? '' : priorities[j] || combinedPriority;
628
- this._setProperty(subparts[j], parts[j], priority);
629
- }
630
- }
631
- return v;
632
- };
492
+ // Returns `false` for global values, e.g. "inherit".
493
+ exports.isValidColor = function isValidColor(val) {
494
+ if (SYS_COLOR.includes(val.toLowerCase())) {
495
+ return true;
496
+ }
497
+ return isColor(val);
633
498
  };
634
499
 
635
- var camel_to_dashed = /[A-Z]/g;
636
- var first_segment = /^\([^-]\)-/;
637
- var vendor_prefixes = ['o', 'moz', 'ms', 'webkit'];
638
- exports.camelToDashed = function (camel_case) {
639
- var match;
640
- var dashed = camel_case.replace(camel_to_dashed, '-$&').toLowerCase();
641
- match = dashed.match(first_segment);
642
- if (match && vendor_prefixes.indexOf(match[1]) !== -1) {
643
- dashed = '-' + dashed;
644
- }
645
- return dashed;
646
- };
500
+ // Splits value into an array.
501
+ // @see https://github.com/asamuzaK/cssColor/blob/main/src/js/util.ts
502
+ exports.splitValue = splitValue;