cssstyle 4.5.0 → 5.0.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/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,8 @@ 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 gradientRegEx = /^(?:repeating-)?(?:conic|linear|radial)-gradient\(/;
87
+ const functionRegEx = /^([a-z][a-z\d]*(?:-[a-z\d]+)*)\(/i;
81
88
 
82
89
  const getNumericType = function getNumericType(val) {
83
90
  if (varRegEx.test(val)) {
@@ -184,7 +191,7 @@ exports.parseLength = function parseLength(val, restrictToPositive = false) {
184
191
  if (restrictToPositive && num < 0) {
185
192
  return;
186
193
  }
187
- return `${num}${unit.toLowerCase()}`;
194
+ return `${num}${asciiLowercase(unit)}`;
188
195
  }
189
196
  default:
190
197
  if (varContainedRegEx.test(val)) {
@@ -216,7 +223,7 @@ exports.parsePercent = function parsePercent(val, restrictToPositive = false) {
216
223
  if (restrictToPositive && num < 0) {
217
224
  return;
218
225
  }
219
- return `${num}${unit.toLowerCase()}`;
226
+ return `${num}${asciiLowercase(unit)}`;
220
227
  }
221
228
  default:
222
229
  if (varContainedRegEx.test(val)) {
@@ -250,7 +257,7 @@ exports.parseMeasurement = function parseMeasurement(val, restrictToPositive = f
250
257
  if (restrictToPositive && num < 0) {
251
258
  return;
252
259
  }
253
- return `${num}${unit.toLowerCase()}`;
260
+ return `${num}${asciiLowercase(unit)}`;
254
261
  }
255
262
  default:
256
263
  if (varContainedRegEx.test(val)) {
@@ -279,7 +286,7 @@ exports.parseAngle = function parseAngle(val, normalizeDeg = false) {
279
286
  case NUM_TYPE.ANGLE: {
280
287
  let [, numVal, unit] = unitRegEx.exec(val);
281
288
  numVal = parseFloat(numVal);
282
- unit = unit.toLowerCase();
289
+ unit = asciiLowercase(unit);
283
290
  if (unit === "deg") {
284
291
  if (normalizeDeg && numVal < 0) {
285
292
  while (numVal < 0) {
@@ -391,7 +398,7 @@ exports.parseKeyword = function parseKeyword(val, validKeywords = []) {
391
398
  if (varRegEx.test(val)) {
392
399
  return val;
393
400
  }
394
- val = val.toString().toLowerCase();
401
+ val = asciiLowercase(val.toString());
395
402
  if (validKeywords.includes(val) || GLOBAL_VALUE.includes(val)) {
396
403
  return val;
397
404
  }
@@ -404,8 +411,11 @@ exports.parseColor = function parseColor(val) {
404
411
  if (varRegEx.test(val)) {
405
412
  return val;
406
413
  }
407
- if (/^[a-z]+$/i.test(val) && SYS_COLOR.includes(val.toLowerCase())) {
408
- return val;
414
+ if (/^[a-z]+$/i.test(val)) {
415
+ const v = asciiLowercase(val);
416
+ if (SYS_COLOR.includes(v)) {
417
+ return v;
418
+ }
409
419
  }
410
420
  const res = resolveColor(val, {
411
421
  format: "specifiedValue"
@@ -420,35 +430,42 @@ exports.parseImage = function parseImage(val) {
420
430
  if (val === "") {
421
431
  return "";
422
432
  }
423
- if (varRegEx.test(val)) {
433
+ if (gradientRegEx.test(val) && varRegEx.test(val)) {
424
434
  return val;
425
435
  }
426
436
  if (keywordRegEx.test(val)) {
427
437
  return exports.parseKeyword(val, ["none"]);
428
438
  }
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 "";
438
- }
439
- if (isGradient(image) || /^(?:none|inherit)$/i.test(image)) {
440
- continue;
441
- }
442
- const imageUrl = exports.parseUrl(image);
443
- if (imageUrl) {
444
- values[i] = imageUrl;
445
- } else {
446
- isImage = false;
447
- break;
448
- }
439
+ // FIXME: need to resolve color values within gradients
440
+ if (isGradient(val)) {
441
+ return val;
449
442
  }
450
- if (isImage) {
451
- return values.join(", ");
443
+ return exports.parseUrl(val);
444
+ };
445
+
446
+ exports.parseFunction = function parseFunction(val) {
447
+ if (val === "") {
448
+ return {
449
+ name: null,
450
+ value: ""
451
+ };
452
+ }
453
+ if (functionRegEx.test(val) && val.endsWith(")")) {
454
+ if (varRegEx.test(val) || varContainedRegEx.test(val)) {
455
+ return {
456
+ name: "var",
457
+ value: val
458
+ };
459
+ }
460
+ const [, name] = functionRegEx.exec(val);
461
+ const value = val
462
+ .replace(new RegExp(`^${name}\\(`), "")
463
+ .replace(/\)$/, "")
464
+ .trim();
465
+ return {
466
+ name,
467
+ value
468
+ };
452
469
  }
453
470
  };
454
471
 
@@ -491,7 +508,7 @@ exports.parseShorthand = function parseShorthand(val, shorthandFor, preserve = f
491
508
 
492
509
  // Returns `false` for global values, e.g. "inherit".
493
510
  exports.isValidColor = function isValidColor(val) {
494
- if (SYS_COLOR.includes(val.toLowerCase())) {
511
+ if (SYS_COLOR.includes(asciiLowercase(val))) {
495
512
  return true;
496
513
  }
497
514
  return isColor(val);
@@ -1,45 +1,382 @@
1
1
  "use strict";
2
- // FIXME:
3
- // * support multiple backgrounds
4
- // * also fix longhands
5
2
 
6
3
  const parsers = require("../parsers");
7
4
  const backgroundImage = require("./backgroundImage");
8
5
  const backgroundPosition = require("./backgroundPosition");
6
+ const backgroundSize = require("./backgroundSize");
9
7
  const backgroundRepeat = require("./backgroundRepeat");
8
+ const backgroundOrigin = require("./backgroundOrigin");
9
+ const backgroundClip = require("./backgroundClip");
10
10
  const backgroundAttachment = require("./backgroundAttachment");
11
11
  const backgroundColor = require("./backgroundColor");
12
12
 
13
- const shorthandFor = new Map([
13
+ module.exports.shorthandFor = new Map([
14
14
  ["background-image", backgroundImage],
15
15
  ["background-position", backgroundPosition],
16
+ ["background-size", backgroundSize],
16
17
  ["background-repeat", backgroundRepeat],
18
+ ["background-origin", backgroundOrigin],
19
+ ["background-clip", backgroundClip],
17
20
  ["background-attachment", backgroundAttachment],
18
21
  ["background-color", backgroundColor]
19
22
  ]);
20
23
 
24
+ const initialValues = new Map([
25
+ ["background-image", "none"],
26
+ ["background-position", "0% 0%"],
27
+ ["background-size", "auto"],
28
+ ["background-repeat", "repeat"],
29
+ ["background-origin", "padding-box"],
30
+ ["background-clip", "border-box"],
31
+ ["background-attachment", "scroll"],
32
+ ["background-color", "transparent"]
33
+ ]);
34
+
35
+ module.exports.parse = function parse(v) {
36
+ const values = parsers.splitValue(v, {
37
+ delimiter: ","
38
+ });
39
+ const bgValues = [];
40
+ const l = values.length;
41
+ for (let i = 0; i < l; i++) {
42
+ let bg = {
43
+ "background-image": initialValues.get("background-image"),
44
+ "background-position": initialValues.get("background-position"),
45
+ "background-size": initialValues.get("background-size"),
46
+ "background-repeat": initialValues.get("background-repeat"),
47
+ "background-origin": initialValues.get("background-origin"),
48
+ "background-clip": initialValues.get("background-clip"),
49
+ "background-attachment": initialValues.get("background-attachment"),
50
+ "background-color": initialValues.get("background-color")
51
+ };
52
+ if (l > 1 && i !== l - 1) {
53
+ bg = {
54
+ "background-image": initialValues.get("background-image"),
55
+ "background-position": initialValues.get("background-position"),
56
+ "background-size": initialValues.get("background-size"),
57
+ "background-repeat": initialValues.get("background-repeat"),
58
+ "background-origin": initialValues.get("background-origin"),
59
+ "background-clip": initialValues.get("background-clip"),
60
+ "background-attachment": initialValues.get("background-attachment")
61
+ };
62
+ }
63
+ const bgPosition = [];
64
+ const bgSize = [];
65
+ const bgRepeat = [];
66
+ const bgBox = [];
67
+ const bgParts = parsers.splitValue(values[i], {
68
+ delimiter: "/"
69
+ });
70
+ if (!bgParts.length || bgParts.length > 2) {
71
+ return;
72
+ }
73
+ const [bgPart1, bgPart2 = ""] = bgParts;
74
+ const parts1 = parsers.splitValue(bgPart1);
75
+ for (const part of parts1) {
76
+ let partValid = false;
77
+ for (const [property, value] of module.exports.shorthandFor) {
78
+ if (value.isValid(part)) {
79
+ partValid = true;
80
+ switch (property) {
81
+ case "background-clip":
82
+ case "background-origin": {
83
+ const parsedValue = value.parse(part);
84
+ if (parsedValue) {
85
+ bgBox.push(parsedValue);
86
+ }
87
+ break;
88
+ }
89
+ case "background-color": {
90
+ if (i !== values.length - 1) {
91
+ return;
92
+ }
93
+ const parsedValue = value.parse(part);
94
+ if (parsedValue) {
95
+ bg[property] = parsedValue;
96
+ }
97
+ break;
98
+ }
99
+ case "background-position": {
100
+ const parsedValue = value.parse(part);
101
+ if (parsedValue) {
102
+ bgPosition.push(parsedValue);
103
+ }
104
+ break;
105
+ }
106
+ case "background-repeat": {
107
+ const parsedValue = value.parse(part);
108
+ if (parsedValue) {
109
+ bgRepeat.push(parsedValue);
110
+ }
111
+ break;
112
+ }
113
+ case "background-size": {
114
+ break;
115
+ }
116
+ default: {
117
+ const parsedValue = value.parse(part);
118
+ if (parsedValue) {
119
+ bg[property] = parsedValue;
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ if (!partValid) {
126
+ return;
127
+ }
128
+ }
129
+ if (bgPart2) {
130
+ const parts2 = parsers.splitValue(bgPart2);
131
+ for (const part of parts2) {
132
+ let partValid = false;
133
+ for (const [property, value] of module.exports.shorthandFor) {
134
+ if (value.isValid(part)) {
135
+ partValid = true;
136
+ switch (property) {
137
+ case "background-clip":
138
+ case "background-origin": {
139
+ const parsedValue = value.parse(part);
140
+ if (parsedValue) {
141
+ bgBox.push(parsedValue);
142
+ }
143
+ break;
144
+ }
145
+ case "background-color": {
146
+ if (i !== l - 1) {
147
+ return;
148
+ }
149
+ const parsedValue = value.parse(part);
150
+ if (parsedValue) {
151
+ bg[property] = parsedValue;
152
+ }
153
+ break;
154
+ }
155
+ case "background-position": {
156
+ break;
157
+ }
158
+ case "background-repeat": {
159
+ const parsedValue = value.parse(part);
160
+ if (parsedValue) {
161
+ bgRepeat.push(parsedValue);
162
+ }
163
+ break;
164
+ }
165
+ case "background-size": {
166
+ const parsedValue = value.parse(part);
167
+ if (parsedValue) {
168
+ bgSize.push(parsedValue);
169
+ }
170
+ break;
171
+ }
172
+ default: {
173
+ const parsedValue = value.parse(part);
174
+ if (parsedValue) {
175
+ bg[property] = parsedValue;
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ if (!partValid) {
182
+ return;
183
+ }
184
+ }
185
+ }
186
+ if (bgPosition.length) {
187
+ const { parse: parser } = module.exports.shorthandFor.get("background-position");
188
+ const value = parser(bgPosition.join(" "));
189
+ if (value) {
190
+ bg["background-position"] = value;
191
+ }
192
+ }
193
+ if (bgSize.length) {
194
+ const { parse: parser } = module.exports.shorthandFor.get("background-size");
195
+ const value = parser(bgSize.join(" "));
196
+ if (value) {
197
+ bg["background-size"] = value;
198
+ }
199
+ }
200
+ if (bgRepeat.length) {
201
+ const { parse: parser } = module.exports.shorthandFor.get("background-repeat");
202
+ const value = parser(bgRepeat.join(" "));
203
+ if (value) {
204
+ bg["background-repeat"] = value;
205
+ }
206
+ }
207
+ if (bgBox.length) {
208
+ switch (bgBox.length) {
209
+ case 1: {
210
+ const [value] = bgBox;
211
+ bg["background-origin"] = value;
212
+ bg["background-clip"] = value;
213
+ break;
214
+ }
215
+ case 2: {
216
+ const [value1, value2] = bgBox;
217
+ bg["background-origin"] = value1;
218
+ bg["background-clip"] = value2;
219
+ break;
220
+ }
221
+ default: {
222
+ return;
223
+ }
224
+ }
225
+ }
226
+ bgValues.push(bg);
227
+ }
228
+ return bgValues;
229
+ };
230
+
21
231
  module.exports.definition = {
22
232
  set(v) {
23
233
  v = parsers.prepareValue(v, this._global);
24
- if (v.toLowerCase() === "none" || parsers.hasVarFunc(v)) {
25
- for (const [key] of shorthandFor) {
234
+ if (v === "" || parsers.hasVarFunc(v)) {
235
+ for (const [key] of module.exports.shorthandFor) {
26
236
  this._setProperty(key, "");
27
237
  }
28
238
  this._setProperty("background", v);
29
239
  } else {
30
- this._shorthandSetter("background", v, shorthandFor);
240
+ const bgValues = module.exports.parse(v);
241
+ if (!Array.isArray(bgValues)) {
242
+ return;
243
+ }
244
+ const bgMap = new Map([
245
+ ["background-image", []],
246
+ ["background-position", []],
247
+ ["background-size", []],
248
+ ["background-repeat", []],
249
+ ["background-origin", []],
250
+ ["background-clip", []],
251
+ ["background-attachment", []],
252
+ ["background-color", []]
253
+ ]);
254
+ const backgrounds = [];
255
+ for (const bgValue of bgValues) {
256
+ const bg = [];
257
+ for (const [property, value] of Object.entries(bgValue)) {
258
+ if (value) {
259
+ const arr = bgMap.get(property);
260
+ arr.push(value);
261
+ bgMap.set(property, arr);
262
+ if (value !== initialValues.get(property)) {
263
+ if (property === "background-size") {
264
+ bg.push(`/ ${value}`);
265
+ } else {
266
+ bg.push(value);
267
+ }
268
+ } else if (property === "background-image") {
269
+ if (v === "none") {
270
+ bg.push(value);
271
+ }
272
+ } else if (property === "background-color") {
273
+ if (v === "transparent") {
274
+ bg.push(value);
275
+ }
276
+ }
277
+ }
278
+ }
279
+ backgrounds.push(bg.join(" "));
280
+ }
281
+ for (const [property, value] of bgMap) {
282
+ this._setProperty(property, value.join(", "));
283
+ }
284
+ this._setProperty("background", backgrounds.join(", "));
31
285
  }
32
286
  },
33
287
  get() {
34
- let val = this.getPropertyValue("background");
35
- if (parsers.hasVarFunc(val)) {
36
- return val;
288
+ const v = this.getPropertyValue("background");
289
+ if (parsers.hasVarFunc(v)) {
290
+ return v;
37
291
  }
38
- val = this._shorthandGetter("background", shorthandFor);
39
- if (parsers.hasVarFunc(val)) {
292
+ const bgMap = new Map();
293
+ let l = 0;
294
+ for (const [property] of module.exports.shorthandFor) {
295
+ const val = this.getPropertyValue(property);
296
+ if (property === "background-image") {
297
+ if (
298
+ val === "none" &&
299
+ v === "none" &&
300
+ this.getPropertyValue("background-color") === "transparent"
301
+ ) {
302
+ return val;
303
+ }
304
+ if (val !== initialValues.get(property)) {
305
+ const imgValues = parsers.splitValue(val, {
306
+ delimiter: ","
307
+ });
308
+ l = imgValues.length;
309
+ bgMap.set(property, imgValues);
310
+ }
311
+ } else if (property === "background-color") {
312
+ if (val !== initialValues.get(property) || v.includes(val)) {
313
+ bgMap.set(property, [val]);
314
+ }
315
+ } else if (val !== initialValues.get(property)) {
316
+ bgMap.set(
317
+ property,
318
+ parsers.splitValue(val, {
319
+ delimiter: ","
320
+ })
321
+ );
322
+ }
323
+ }
324
+ if (l === 0) {
325
+ const [background] = bgMap.get("background-color");
326
+ if (background) {
327
+ return background;
328
+ }
40
329
  return "";
41
330
  }
42
- return val;
331
+ const bgValues = [];
332
+ for (let i = 0; i < l; i++) {
333
+ bgValues[i] = [];
334
+ }
335
+ for (const [property, values] of bgMap) {
336
+ for (let i = 0; i < l; i++) {
337
+ switch (property) {
338
+ case "background-color": {
339
+ if (i === l - 1) {
340
+ const value = values[0];
341
+ if (parsers.hasVarFunc(value)) {
342
+ return "";
343
+ }
344
+ if (value && value !== initialValues.get(property)) {
345
+ const bgValue = bgValues[i];
346
+ bgValue.push(value);
347
+ }
348
+ }
349
+ break;
350
+ }
351
+ case "background-size": {
352
+ const value = values[i];
353
+ if (parsers.hasVarFunc(value)) {
354
+ return "";
355
+ }
356
+ if (value && value !== initialValues.get(property)) {
357
+ const bgValue = bgValues[i];
358
+ bgValue.push(`/ ${value}`);
359
+ }
360
+ break;
361
+ }
362
+ default: {
363
+ const value = values[i];
364
+ if (parsers.hasVarFunc(value)) {
365
+ return "";
366
+ }
367
+ if (value && value !== initialValues.get(property)) {
368
+ const bgValue = bgValues[i];
369
+ bgValue.push(value);
370
+ }
371
+ }
372
+ }
373
+ }
374
+ }
375
+ const backgrounds = [];
376
+ for (const bgValue of bgValues) {
377
+ backgrounds.push(bgValue.join(" "));
378
+ }
379
+ return backgrounds.join(", ");
43
380
  },
44
381
  enumerable: true,
45
382
  configurable: true
@@ -3,8 +3,25 @@
3
3
  const parsers = require("../parsers");
4
4
 
5
5
  module.exports.parse = function parse(v) {
6
+ if (v === "") {
7
+ return v;
8
+ }
9
+ const values = parsers.splitValue(v, {
10
+ delimiter: ","
11
+ });
6
12
  const keywords = ["fixed", "scroll", "local"];
7
- return parsers.parseKeyword(v, keywords);
13
+ const parsedValues = [];
14
+ for (const value of values) {
15
+ const parsedValue = parsers.parseKeyword(value, keywords);
16
+ if (parsedValue) {
17
+ parsedValues.push(parsedValue);
18
+ } else {
19
+ return;
20
+ }
21
+ }
22
+ if (parsedValues.length) {
23
+ return parsedValues.join(", ");
24
+ }
8
25
  };
9
26
 
10
27
  module.exports.isValid = function isValid(v) {
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ const parsers = require("../parsers");
4
+
5
+ module.exports.parse = function parse(v) {
6
+ if (v === "") {
7
+ return v;
8
+ }
9
+ const values = parsers.splitValue(v, {
10
+ delimiter: ","
11
+ });
12
+ const keywords = ["border-box", "padding-box", "content-box"];
13
+ const parsedValues = [];
14
+ for (const value of values) {
15
+ const parsedValue = parsers.parseKeyword(value, keywords);
16
+ if (parsedValue) {
17
+ parsedValues.push(parsedValue);
18
+ } else {
19
+ return;
20
+ }
21
+ }
22
+ if (parsedValues.length) {
23
+ return parsedValues.join(", ");
24
+ }
25
+ };
26
+
27
+ module.exports.isValid = function isValid(v) {
28
+ if (v === "") {
29
+ return true;
30
+ }
31
+ return typeof module.exports.parse(v) === "string";
32
+ };
33
+
34
+ module.exports.definition = {
35
+ set(v) {
36
+ v = parsers.prepareValue(v, this._global);
37
+ if (parsers.hasVarFunc(v)) {
38
+ this._setProperty("background", "");
39
+ this._setProperty("background-clip", v);
40
+ } else {
41
+ this._setProperty("background-clip", module.exports.parse(v));
42
+ }
43
+ },
44
+ get() {
45
+ return this.getPropertyValue("background-clip");
46
+ },
47
+ enumerable: true,
48
+ configurable: true
49
+ };