cssstyle 5.3.7 → 6.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.
Files changed (93) hide show
  1. package/lib/CSSStyleDeclaration.js +359 -395
  2. package/lib/generated/.gitkeep +0 -0
  3. package/lib/generated/propertyDefinitions.js +3685 -1498
  4. package/lib/generated/propertyDescriptors.js +1705 -0
  5. package/lib/index.js +9 -0
  6. package/lib/normalize.js +127 -253
  7. package/lib/parsers.js +152 -164
  8. package/lib/properties/background.js +201 -202
  9. package/lib/properties/backgroundAttachment.js +35 -33
  10. package/lib/properties/backgroundClip.js +35 -33
  11. package/lib/properties/backgroundColor.js +26 -24
  12. package/lib/properties/backgroundImage.js +36 -34
  13. package/lib/properties/backgroundOrigin.js +35 -33
  14. package/lib/properties/backgroundPosition.js +57 -57
  15. package/lib/properties/backgroundRepeat.js +40 -37
  16. package/lib/properties/backgroundSize.js +38 -34
  17. package/lib/properties/border.js +41 -34
  18. package/lib/properties/{webkitBorderEndColor.js → borderBlockEndColor.js} +26 -22
  19. package/lib/properties/{webkitBorderAfterColor.js → borderBlockStartColor.js} +26 -22
  20. package/lib/properties/borderBottom.js +40 -36
  21. package/lib/properties/borderBottomColor.js +25 -21
  22. package/lib/properties/borderBottomStyle.js +25 -21
  23. package/lib/properties/borderBottomWidth.js +28 -24
  24. package/lib/properties/borderCollapse.js +25 -21
  25. package/lib/properties/borderColor.js +36 -33
  26. package/lib/properties/{webkitBorderStartColor.js → borderInlineEndColor.js} +26 -22
  27. package/lib/properties/borderInlineStartColor.js +49 -0
  28. package/lib/properties/borderLeft.js +40 -36
  29. package/lib/properties/borderLeftColor.js +25 -21
  30. package/lib/properties/borderLeftStyle.js +25 -21
  31. package/lib/properties/borderLeftWidth.js +28 -24
  32. package/lib/properties/borderRight.js +40 -36
  33. package/lib/properties/borderRightColor.js +25 -21
  34. package/lib/properties/borderRightStyle.js +25 -21
  35. package/lib/properties/borderRightWidth.js +28 -24
  36. package/lib/properties/borderSpacing.js +33 -29
  37. package/lib/properties/borderStyle.js +36 -33
  38. package/lib/properties/borderTop.js +40 -36
  39. package/lib/properties/borderTopColor.js +25 -21
  40. package/lib/properties/borderTopStyle.js +25 -21
  41. package/lib/properties/borderTopWidth.js +28 -24
  42. package/lib/properties/borderWidth.js +36 -33
  43. package/lib/properties/bottom.js +27 -23
  44. package/lib/properties/clear.js +25 -21
  45. package/lib/properties/clip.js +37 -31
  46. package/lib/properties/color.js +25 -21
  47. package/lib/properties/display.js +36 -30
  48. package/lib/properties/flex.js +53 -45
  49. package/lib/properties/flexBasis.js +28 -26
  50. package/lib/properties/flexGrow.js +28 -26
  51. package/lib/properties/flexShrink.js +28 -26
  52. package/lib/properties/float.js +25 -21
  53. package/lib/properties/floodColor.js +25 -21
  54. package/lib/properties/font.js +89 -118
  55. package/lib/properties/fontFamily.js +38 -33
  56. package/lib/properties/fontSize.js +29 -27
  57. package/lib/properties/fontStyle.js +38 -34
  58. package/lib/properties/fontVariant.js +35 -33
  59. package/lib/properties/fontWeight.js +33 -31
  60. package/lib/properties/height.js +28 -24
  61. package/lib/properties/left.js +27 -23
  62. package/lib/properties/lightingColor.js +25 -21
  63. package/lib/properties/lineHeight.js +28 -26
  64. package/lib/properties/margin.js +40 -34
  65. package/lib/properties/marginBottom.js +30 -27
  66. package/lib/properties/marginLeft.js +30 -27
  67. package/lib/properties/marginRight.js +30 -27
  68. package/lib/properties/marginTop.js +30 -27
  69. package/lib/properties/opacity.js +27 -23
  70. package/lib/properties/outlineColor.js +25 -21
  71. package/lib/properties/padding.js +40 -34
  72. package/lib/properties/paddingBottom.js +31 -28
  73. package/lib/properties/paddingLeft.js +31 -28
  74. package/lib/properties/paddingRight.js +31 -28
  75. package/lib/properties/paddingTop.js +31 -28
  76. package/lib/properties/right.js +27 -23
  77. package/lib/properties/stopColor.js +25 -21
  78. package/lib/properties/{webkitBorderBeforeColor.js → textEmphasisColor.js} +26 -22
  79. package/lib/properties/top.js +27 -23
  80. package/lib/properties/webkitTextFillColor.js +25 -21
  81. package/lib/properties/webkitTextStrokeColor.js +25 -21
  82. package/lib/properties/width.js +28 -24
  83. package/lib/utils/propertyDescriptors.js +129 -42
  84. package/lib/utils/strings.js +11 -156
  85. package/package.json +11 -21
  86. package/lib/generated/allProperties.js +0 -653
  87. package/lib/generated/implementedProperties.js +0 -1466
  88. package/lib/generated/properties.js +0 -6637
  89. package/lib/properties/webkitColumnRuleColor.js +0 -45
  90. package/lib/properties/webkitTapHighlightColor.js +0 -45
  91. package/lib/properties/webkitTextEmphasisColor.js +0 -45
  92. package/lib/utils/allExtraProperties.js +0 -155
  93. package/lib/utils/camelize.js +0 -37
@@ -4,9 +4,7 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
- const allProperties = require("./generated/allProperties");
8
- const implementedProperties = require("./generated/implementedProperties");
9
- const generatedProperties = require("./generated/properties");
7
+ const propertyDescriptors = require("./generated/propertyDescriptors");
10
8
  const {
11
9
  borderProperties,
12
10
  getPositionValue,
@@ -15,16 +13,7 @@ const {
15
13
  prepareProperties,
16
14
  shorthandProperties
17
15
  } = require("./normalize");
18
- const {
19
- hasVarFunc,
20
- isGlobalKeyword,
21
- parseCSS,
22
- parsePropertyValue,
23
- prepareValue
24
- } = require("./parsers");
25
- const allExtraProperties = require("./utils/allExtraProperties");
26
- const { dashedToCamelCase } = require("./utils/camelize");
27
- const { getPropertyDescriptor } = require("./utils/propertyDescriptors");
16
+ const { hasVarFunc, isGlobalKeyword, parseCSS, parsePropertyValue, prepareValue } = require("./parsers");
28
17
  const { asciiLowercase } = require("./utils/strings");
29
18
 
30
19
  /**
@@ -32,88 +21,38 @@ const { asciiLowercase } = require("./utils/strings");
32
21
  */
33
22
  class CSSStyleDeclaration {
34
23
  /**
35
- * @param {Function} onChangeCallback
36
- * @param {object} [opt]
37
- * @param {object} [opt.context] - Window, Element or CSSRule.
24
+ * Creates a new CSSStyleDeclaration instance.
25
+ *
26
+ * @param {Function} [onChangeCallback] - Callback triggered when style changes.
27
+ * @param {object} [opt] - Options.
28
+ * @param {object} [opt.context] - The context object (Window, Element, or CSSRule).
38
29
  */
39
- constructor(onChangeCallback, opt = {}) {
40
- // Make constructor and internals non-enumerable.
41
- Object.defineProperties(this, {
42
- constructor: {
43
- enumerable: false,
44
- writable: true
45
- },
46
-
47
- // Window
48
- _global: {
49
- value: globalThis,
50
- enumerable: false,
51
- writable: true
52
- },
53
-
54
- // Element
55
- _ownerNode: {
56
- value: null,
57
- enumerable: false,
58
- writable: true
59
- },
60
-
61
- // CSSRule
62
- _parentNode: {
63
- value: null,
64
- enumerable: false,
65
- writable: true
66
- },
67
-
68
- _onChange: {
69
- value: null,
70
- enumerable: false,
71
- writable: true
72
- },
73
-
74
- _values: {
75
- value: new Map(),
76
- enumerable: false,
77
- writable: true
78
- },
30
+ constructor(onChangeCallback, { context } = {}) {
31
+ // Internals for jsdom
32
+ this._global = globalThis;
33
+ this._onChange = onChangeCallback;
34
+
35
+ // Internals for CSS declaration block
36
+ // @see https://drafts.csswg.org/cssom/#css-declaration-blocks
37
+ this._computed = false;
38
+ this._ownerNode = null;
39
+ this._parentRule = null;
40
+ this._readonly = false;
41
+ this._updating = false;
42
+
43
+ // Other internals
44
+ this._length = 0;
45
+ this._propertyIndices = new Map();
46
+ this._priorities = new Map();
47
+ this._values = new Map();
79
48
 
80
- _priorities: {
81
- value: new Map(),
82
- enumerable: false,
83
- writable: true
84
- },
85
-
86
- _length: {
87
- value: 0,
88
- enumerable: false,
89
- writable: true
90
- },
91
-
92
- _computed: {
93
- value: false,
94
- enumerable: false,
95
- writable: true
96
- },
97
-
98
- _readonly: {
99
- value: false,
100
- enumerable: false,
101
- writable: true
102
- },
103
-
104
- _setInProgress: {
105
- value: false,
106
- enumerable: false,
107
- writable: true
108
- }
109
- });
110
-
111
- const { context } = opt;
112
49
  if (context) {
113
50
  if (typeof context.getComputedStyle === "function") {
114
51
  this._global = context;
115
52
  this._computed = true;
116
- this._readonly = true;
53
+ // FIXME: The `_readonly` flag should initially be `false` to be editable,
54
+ // but should eventually be set to `true`.
55
+ // this._readonly = true;
117
56
  } else if (context.nodeType === 1 && Object.hasOwn(context, "style")) {
118
57
  this._global = context.ownerDocument.defaultView;
119
58
  this._ownerNode = context;
@@ -126,11 +65,13 @@ class CSSStyleDeclaration {
126
65
  }
127
66
  }
128
67
  }
129
- if (typeof onChangeCallback === "function") {
130
- this._onChange = onChangeCallback;
131
- }
132
68
  }
133
69
 
70
+ /**
71
+ * Returns the textual representation of the declaration block.
72
+ *
73
+ * @returns {string} The serialized CSS text.
74
+ */
134
75
  get cssText() {
135
76
  if (this._computed) {
136
77
  return "";
@@ -162,28 +103,27 @@ class CSSStyleDeclaration {
162
103
  return parts.join(" ");
163
104
  }
164
105
 
165
- set cssText(val) {
106
+ /**
107
+ * Sets the textual representation of the declaration block.
108
+ * This clears all existing properties and parses the new CSS text.
109
+ *
110
+ * @param {string} text - The new CSS text.
111
+ */
112
+ set cssText(text) {
166
113
  if (this._readonly) {
167
114
  const msg = "cssText can not be modified.";
168
115
  const name = "NoModificationAllowedError";
169
116
  throw new this._global.DOMException(msg, name);
170
117
  }
171
- Array.prototype.splice.call(this, 0, this._length);
118
+ this._clearIndexedProperties();
172
119
  this._values.clear();
173
120
  this._priorities.clear();
174
- if (this._parentRule || (this._ownerNode && this._setInProgress)) {
121
+ if (this._parentRule || (this._ownerNode && this._updating)) {
175
122
  return;
176
123
  }
177
124
  try {
178
- this._setInProgress = true;
179
- const valueObj = parseCSS(
180
- val,
181
- {
182
- context: "declarationList",
183
- parseValue: false
184
- },
185
- true
186
- );
125
+ this._updating = true;
126
+ const valueObj = parseCSS(text, { context: "declarationList", parseValue: false });
187
127
  if (valueObj?.children) {
188
128
  const properties = new Map();
189
129
  let shouldSkipNext = false;
@@ -217,9 +157,7 @@ class CSSStyleDeclaration {
217
157
  properties.set(property, { property, value, priority });
218
158
  }
219
159
  } else {
220
- const parsedValue = parsePropertyValue(property, value, {
221
- globalObject: this._global
222
- });
160
+ const parsedValue = parsePropertyValue(property, value);
223
161
  if (parsedValue) {
224
162
  if (properties.has(property)) {
225
163
  const { priority: itemPriority } = properties.get(property);
@@ -235,9 +173,7 @@ class CSSStyleDeclaration {
235
173
  }
236
174
  }
237
175
  }
238
- const parsedProperties = prepareProperties(properties, {
239
- globalObject: this._global
240
- });
176
+ const parsedProperties = prepareProperties(properties);
241
177
  for (const [property, item] of parsedProperties) {
242
178
  const { priority, value } = item;
243
179
  this._priorities.set(property, priority);
@@ -247,49 +183,64 @@ class CSSStyleDeclaration {
247
183
  } catch {
248
184
  return;
249
185
  } finally {
250
- this._setInProgress = false;
186
+ this._updating = false;
251
187
  }
252
- if (typeof this._onChange === "function") {
188
+ if (this._onChange) {
253
189
  this._onChange(this.cssText);
254
190
  }
255
191
  }
256
192
 
193
+ /**
194
+ * Returns the number of properties in the declaration block.
195
+ *
196
+ * @returns {number} The property count.
197
+ */
257
198
  get length() {
258
199
  return this._length;
259
200
  }
260
201
 
261
- // This deletes indices if the new length is less then the current length.
262
- // If the new length is more, it does nothing, the new indices will be
263
- // undefined until set.
264
- set length(len) {
265
- for (let i = len; i < this._length; i++) {
266
- delete this[i];
267
- }
268
- this._length = len;
269
- }
270
-
271
- // Readonly
202
+ /**
203
+ * Returns the CSSRule that is the parent of this declaration block.
204
+ *
205
+ * @returns {object|null} The parent CSSRule or null.
206
+ */
272
207
  get parentRule() {
273
208
  return this._parentRule;
274
209
  }
275
210
 
211
+ /**
212
+ * Alias for the "float" property.
213
+ *
214
+ * @returns {string} The value of the "float" property.
215
+ */
276
216
  get cssFloat() {
277
217
  return this.getPropertyValue("float");
278
218
  }
279
219
 
220
+ /**
221
+ * Sets the "float" property.
222
+ *
223
+ * @param {string} value - The new value for "float".
224
+ */
280
225
  set cssFloat(value) {
281
226
  this._setProperty("float", value);
282
227
  }
283
228
 
284
229
  /**
285
- * @param {string} property
230
+ * Returns the priority of the specified property (e.g. "important").
231
+ *
232
+ * @param {string} property - The property name.
233
+ * @returns {string} The priority string, or empty string if not set.
286
234
  */
287
235
  getPropertyPriority(property) {
288
236
  return this._priorities.get(property) || "";
289
237
  }
290
238
 
291
239
  /**
292
- * @param {string} property
240
+ * Returns the value of the specified property.
241
+ *
242
+ * @param {string} property - The property name.
243
+ * @returns {string} The property value, or empty string if not set.
293
244
  */
294
245
  getPropertyValue(property) {
295
246
  if (this._values.has(property)) {
@@ -299,7 +250,10 @@ class CSSStyleDeclaration {
299
250
  }
300
251
 
301
252
  /**
302
- * @param {...number} args
253
+ * Returns the property name at the specified index.
254
+ *
255
+ * @param {...number} args - The index (only the first argument is used).
256
+ * @returns {string} The property name, or empty string if index is invalid.
303
257
  */
304
258
  item(...args) {
305
259
  if (!args.length) {
@@ -315,7 +269,10 @@ class CSSStyleDeclaration {
315
269
  }
316
270
 
317
271
  /**
318
- * @param {string} property
272
+ * Removes the specified property from the declaration block.
273
+ *
274
+ * @param {string} property - The property name to remove.
275
+ * @returns {string} The value of the removed property.
319
276
  */
320
277
  removeProperty(property) {
321
278
  if (this._readonly) {
@@ -329,10 +286,10 @@ class CSSStyleDeclaration {
329
286
  const prevValue = this._values.get(property);
330
287
  this._values.delete(property);
331
288
  this._priorities.delete(property);
332
- const index = Array.prototype.indexOf.call(this, property);
289
+ const index = this._getIndexOf(property);
333
290
  if (index >= 0) {
334
- Array.prototype.splice.call(this, index, 1);
335
- if (typeof this._onChange === "function") {
291
+ this._removeIndexedProperty(index);
292
+ if (this._onChange) {
336
293
  this._onChange(this.cssText);
337
294
  }
338
295
  }
@@ -340,30 +297,34 @@ class CSSStyleDeclaration {
340
297
  }
341
298
 
342
299
  /**
343
- * @param {string} prop
344
- * @param {string} val
345
- * @param {string} prior
300
+ * Sets a property value with an optional priority.
301
+ *
302
+ * @param {string} property - The property name.
303
+ * @param {string} value - The property value.
304
+ * @param {string} [priority=""] - The priority (e.g. "important").
346
305
  */
347
- setProperty(prop, val, prior) {
306
+ setProperty(property, value, priority = "") {
348
307
  if (this._readonly) {
349
- const msg = `Property ${prop} can not be modified.`;
308
+ const msg = `Property ${property} can not be modified.`;
350
309
  const name = "NoModificationAllowedError";
351
310
  throw new this._global.DOMException(msg, name);
352
311
  }
353
- const value = prepareValue(val);
312
+ value = prepareValue(value);
354
313
  if (value === "") {
355
- this[prop] = "";
356
- this.removeProperty(prop);
314
+ if (Object.hasOwn(propertyDescriptors, property)) {
315
+ // TODO: Refactor handlers to not require `.call()`.
316
+ propertyDescriptors[property].set.call(this, value);
317
+ }
318
+ this.removeProperty(property);
357
319
  return;
358
320
  }
359
- const priority = prior === "important" ? "important" : "";
360
- const isCustomProperty = prop.startsWith("--");
361
- if (isCustomProperty) {
362
- this._setProperty(prop, value, priority);
321
+ // Custom property
322
+ if (property.startsWith("--")) {
323
+ this._setProperty(property, value, priority);
363
324
  return;
364
325
  }
365
- const property = asciiLowercase(prop);
366
- if (!allProperties.has(property) && !allExtraProperties.has(property)) {
326
+ property = asciiLowercase(property);
327
+ if (!Object.hasOwn(propertyDescriptors, property)) {
367
328
  return;
368
329
  }
369
330
  if (priority) {
@@ -371,279 +332,282 @@ class CSSStyleDeclaration {
371
332
  } else {
372
333
  this._priorities.delete(property);
373
334
  }
374
- this[property] = value;
335
+ propertyDescriptors[property].set.call(this, value);
375
336
  }
376
- }
377
337
 
378
- // Internal methods
379
- Object.defineProperties(CSSStyleDeclaration.prototype, {
380
- _setProperty: {
381
- /**
382
- * @param {string} property
383
- * @param {string} val
384
- * @param {string} priority
385
- */
386
- value(property, val, priority) {
387
- if (typeof val !== "string") {
388
- return;
389
- }
390
- if (val === "") {
391
- this.removeProperty(property);
392
- return;
393
- }
394
- let originalText = "";
395
- if (typeof this._onChange === "function" && !this._setInProgress) {
396
- originalText = this.cssText;
397
- }
398
- if (this._values.has(property)) {
399
- const index = Array.prototype.indexOf.call(this, property);
400
- // The property already exists but is not indexed into `this` so add it.
401
- if (index < 0) {
402
- this[this._length] = property;
403
- this._length++;
404
- }
405
- } else {
406
- // New property.
407
- this[this._length] = property;
408
- this._length++;
409
- }
410
- if (priority === "important") {
411
- this._priorities.set(property, priority);
412
- } else {
413
- this._priorities.delete(property);
414
- }
415
- this._values.set(property, val);
416
- if (
417
- typeof this._onChange === "function" &&
418
- !this._setInProgress &&
419
- this.cssText !== originalText
420
- ) {
421
- this._onChange(this.cssText);
422
- }
423
- },
424
- enumerable: false
425
- },
338
+ /**
339
+ * Clears all indexed properties, properties indices and resets length to 0.
340
+ *
341
+ * @private
342
+ */
343
+ _clearIndexedProperties() {
344
+ this._propertyIndices.clear();
345
+ for (let i = 0; i < this._length; i++) {
346
+ delete this[i];
347
+ }
348
+ this._length = 0;
349
+ }
426
350
 
427
- _borderSetter: {
428
- /**
429
- * @param {string} prop
430
- * @param {object|Array|string} val
431
- * @param {string} prior
432
- */
433
- value(prop, val, prior) {
434
- const properties = new Map();
435
- if (prop === "border") {
436
- let priority = "";
437
- if (typeof prior === "string") {
438
- priority = prior;
439
- } else {
440
- priority = this._priorities.get(prop) ?? "";
441
- }
442
- properties.set(prop, { propery: prop, value: val, priority });
443
- } else {
444
- for (let i = 0; i < this._length; i++) {
445
- const property = this[i];
446
- if (borderProperties.has(property)) {
447
- const value = this.getPropertyValue(property);
448
- const longhandPriority = this._priorities.get(property) ?? "";
449
- let priority = longhandPriority;
450
- if (prop === property && typeof prior === "string") {
451
- priority = prior;
452
- }
453
- properties.set(property, { property, value, priority });
454
- }
455
- }
456
- }
457
- const parsedProperties = prepareBorderProperties(prop, val, prior, properties, {
458
- globalObject: this._global
459
- });
460
- for (const [property, item] of parsedProperties) {
461
- const { priority, value } = item;
462
- this._setProperty(property, value, priority);
463
- }
464
- },
465
- enumerable: false
466
- },
351
+ /**
352
+ * Removes an indexed property at the specified index, shifts others, and updates indices.
353
+ *
354
+ * @private
355
+ * @param {number} index - The index of the property to remove.
356
+ */
357
+ _removeIndexedProperty(index) {
358
+ this._propertyIndices.delete(this[index]);
359
+ for (let i = index; i < this._length - 1; i++) {
360
+ const property = this[i + 1];
361
+ this[i] = property;
362
+ this._propertyIndices.set(property, i);
363
+ }
364
+ delete this[this._length - 1];
365
+ this._length--;
366
+ }
467
367
 
468
- _flexBoxSetter: {
469
- /**
470
- * @param {string} prop
471
- * @param {string} val
472
- * @param {string} prior
473
- * @param {string} shorthandProperty
474
- */
475
- value(prop, val, prior, shorthandProperty) {
476
- if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) {
477
- return;
478
- }
479
- const shorthandPriority = this._priorities.get(shorthandProperty);
480
- this.removeProperty(shorthandProperty);
481
- let priority = "";
482
- if (typeof prior === "string") {
483
- priority = prior;
484
- } else {
485
- priority = this._priorities.get(prop) ?? "";
486
- }
487
- this.removeProperty(prop);
488
- if (shorthandPriority && priority) {
489
- this._setProperty(prop, val);
490
- } else {
491
- this._setProperty(prop, val, priority);
492
- }
493
- if (val && !hasVarFunc(val)) {
494
- const longhandValues = [];
495
- const shorthandItem = shorthandProperties.get(shorthandProperty);
496
- let hasGlobalKeyword = false;
497
- for (const [longhandProperty] of shorthandItem.shorthandFor) {
498
- if (longhandProperty === prop) {
499
- if (isGlobalKeyword(val)) {
500
- hasGlobalKeyword = true;
501
- }
502
- longhandValues.push(val);
503
- } else {
504
- const longhandValue = this.getPropertyValue(longhandProperty);
505
- const longhandPriority = this._priorities.get(longhandProperty) ?? "";
506
- if (!longhandValue || longhandPriority !== priority) {
507
- break;
508
- }
509
- if (isGlobalKeyword(longhandValue)) {
510
- hasGlobalKeyword = true;
511
- }
512
- longhandValues.push(longhandValue);
513
- }
514
- }
515
- if (longhandValues.length === shorthandItem.shorthandFor.size) {
516
- if (hasGlobalKeyword) {
517
- const [firstValue, ...restValues] = longhandValues;
518
- if (restValues.every((value) => value === firstValue)) {
519
- this._setProperty(shorthandProperty, firstValue, priority);
520
- }
521
- } else {
522
- const parsedValue = shorthandItem.parse(longhandValues.join(" "));
523
- const shorthandValue = Object.values(parsedValue).join(" ");
524
- this._setProperty(shorthandProperty, shorthandValue, priority);
525
- }
526
- }
527
- }
528
- },
529
- enumerable: false
530
- },
368
+ /**
369
+ * Returns the index of the specified property.
370
+ *
371
+ * @private
372
+ * @param {string} property - The property name to search for.
373
+ * @returns {number} The index of the property, or -1 if not found.
374
+ */
375
+ _getIndexOf(property) {
376
+ return this._propertyIndices.get(property) ?? -1;
377
+ }
531
378
 
532
- _positionShorthandSetter: {
533
- /**
534
- * @param {string} prop
535
- * @param {Array|string} val
536
- * @param {string} prior
537
- */
538
- value(prop, val, prior) {
539
- if (!shorthandProperties.has(prop)) {
540
- return;
541
- }
542
- const shorthandValues = [];
543
- if (Array.isArray(val)) {
544
- shorthandValues.push(...val);
545
- } else if (typeof val === "string") {
546
- shorthandValues.push(val);
547
- } else {
548
- return;
549
- }
550
- let priority = "";
551
- if (typeof prior === "string") {
552
- priority = prior;
553
- } else {
554
- priority = this._priorities.get(prop) ?? "";
555
- }
556
- const { position, shorthandFor } = shorthandProperties.get(prop);
557
- let hasPriority = false;
558
- for (const [longhandProperty, longhandItem] of shorthandFor) {
559
- const { position: longhandPosition } = longhandItem;
560
- const longhandValue = getPositionValue(shorthandValues, longhandPosition);
561
- if (priority) {
562
- this._setProperty(longhandProperty, longhandValue, priority);
563
- } else {
564
- const longhandPriority = this._priorities.get(longhandProperty) ?? "";
565
- if (longhandPriority) {
566
- hasPriority = true;
567
- } else {
568
- this._setProperty(longhandProperty, longhandValue, priority);
379
+ /**
380
+ * Sets a property and update indices.
381
+ *
382
+ * @private
383
+ * @param {string} property - The property name.
384
+ * @param {string} value - The property value.
385
+ * @param {string} priority - The priority.
386
+ */
387
+ _setProperty(property, value, priority) {
388
+ if (typeof value !== "string") {
389
+ return;
390
+ }
391
+ if (value === "") {
392
+ this.removeProperty(property);
393
+ return;
394
+ }
395
+ let originalText = "";
396
+ if (this._onChange && !this._updating) {
397
+ originalText = this.cssText;
398
+ }
399
+ if (!this._values.has(property)) {
400
+ // New property.
401
+ this[this._length] = property;
402
+ this._propertyIndices.set(property, this._length);
403
+ this._length++;
404
+ }
405
+ if (priority === "important") {
406
+ this._priorities.set(property, priority);
407
+ } else {
408
+ this._priorities.delete(property);
409
+ }
410
+ this._values.set(property, value);
411
+ if (this._onChange && !this._updating && this.cssText !== originalText) {
412
+ this._onChange(this.cssText);
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Helper to handle border property expansion.
418
+ *
419
+ * @private
420
+ * @param {string} property - The property name (e.g. "border").
421
+ * @param {object|Array|string} value - The value to set.
422
+ * @param {string} priority - The priority.
423
+ */
424
+ _borderSetter(property, value, priority) {
425
+ const properties = new Map();
426
+ if (typeof priority !== "string") {
427
+ priority = this._priorities.get(property) ?? "";
428
+ }
429
+ if (property === "border") {
430
+ properties.set(property, { propery: property, value, priority });
431
+ } else {
432
+ for (let i = 0; i < this._length; i++) {
433
+ const itemProperty = this[i];
434
+ if (borderProperties.has(itemProperty)) {
435
+ const itemValue = this.getPropertyValue(itemProperty);
436
+ const longhandPriority = this._priorities.get(itemProperty) ?? "";
437
+ let itemPriority = longhandPriority;
438
+ if (itemProperty === property) {
439
+ itemPriority = priority;
569
440
  }
441
+ properties.set(itemProperty, {
442
+ property: itemProperty,
443
+ value: itemValue,
444
+ priority: itemPriority
445
+ });
570
446
  }
571
447
  }
572
- if (hasPriority) {
573
- this.removeProperty(prop);
574
- } else {
575
- const shorthandValue = getPositionValue(shorthandValues, position);
576
- this._setProperty(prop, shorthandValue, priority);
577
- }
578
- },
579
- enumerable: false
580
- },
448
+ }
449
+ const parsedProperties = prepareBorderProperties(property, value, priority, properties);
450
+ for (const [itemProperty, item] of parsedProperties) {
451
+ const { priority: itemPriority, value: itemValue } = item;
452
+ this._setProperty(itemProperty, itemValue, itemPriority);
453
+ }
454
+ }
581
455
 
582
- _positionLonghandSetter: {
583
- /**
584
- * @param {string} prop
585
- * @param {string} val
586
- * @param {string} prior
587
- * @param {string} shorthandProperty
588
- */
589
- value(prop, val, prior, shorthandProperty) {
590
- if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) {
591
- return;
592
- }
593
- const shorthandPriority = this._priorities.get(shorthandProperty);
594
- this.removeProperty(shorthandProperty);
595
- let priority = "";
596
- if (typeof prior === "string") {
597
- priority = prior;
598
- } else {
599
- priority = this._priorities.get(prop) ?? "";
600
- }
601
- this.removeProperty(prop);
602
- if (shorthandPriority && priority) {
603
- this._setProperty(prop, val);
604
- } else {
605
- this._setProperty(prop, val, priority);
606
- }
607
- if (val && !hasVarFunc(val)) {
608
- const longhandValues = [];
609
- const { shorthandFor, position: shorthandPosition } =
610
- shorthandProperties.get(shorthandProperty);
611
- for (const [longhandProperty] of shorthandFor) {
456
+ /**
457
+ * Helper to handle flexbox shorthand expansion.
458
+ *
459
+ * @private
460
+ * @param {string} property - The property name.
461
+ * @param {string} value - The property value.
462
+ * @param {string} priority - The priority.
463
+ * @param {string} shorthandProperty - The shorthand property name.
464
+ */
465
+ _flexBoxSetter(property, value, priority, shorthandProperty) {
466
+ if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) {
467
+ return;
468
+ }
469
+ const shorthandPriority = this._priorities.get(shorthandProperty);
470
+ this.removeProperty(shorthandProperty);
471
+ if (typeof priority !== "string") {
472
+ priority = this._priorities.get(property) ?? "";
473
+ }
474
+ this.removeProperty(property);
475
+ if (shorthandPriority && priority) {
476
+ this._setProperty(property, value);
477
+ } else {
478
+ this._setProperty(property, value, priority);
479
+ }
480
+ if (value && !hasVarFunc(value)) {
481
+ const longhandValues = [];
482
+ const shorthandItem = shorthandProperties.get(shorthandProperty);
483
+ let hasGlobalKeyword = false;
484
+ for (const [longhandProperty] of shorthandItem.shorthandFor) {
485
+ if (longhandProperty === property) {
486
+ if (isGlobalKeyword(value)) {
487
+ hasGlobalKeyword = true;
488
+ }
489
+ longhandValues.push(value);
490
+ } else {
612
491
  const longhandValue = this.getPropertyValue(longhandProperty);
613
492
  const longhandPriority = this._priorities.get(longhandProperty) ?? "";
614
493
  if (!longhandValue || longhandPriority !== priority) {
615
- return;
494
+ break;
495
+ }
496
+ if (isGlobalKeyword(longhandValue)) {
497
+ hasGlobalKeyword = true;
616
498
  }
617
499
  longhandValues.push(longhandValue);
618
500
  }
619
- if (longhandValues.length === shorthandFor.size) {
620
- const replacedValue = getPositionValue(longhandValues, shorthandPosition);
621
- this._setProperty(shorthandProperty, replacedValue);
501
+ }
502
+ if (longhandValues.length === shorthandItem.shorthandFor.size) {
503
+ if (hasGlobalKeyword) {
504
+ const [firstValue, ...restValues] = longhandValues;
505
+ if (restValues.every((val) => val === firstValue)) {
506
+ this._setProperty(shorthandProperty, firstValue, priority);
507
+ }
508
+ } else {
509
+ const parsedValue = shorthandItem.parse(longhandValues.join(" "));
510
+ const shorthandValue = Object.values(parsedValue).join(" ");
511
+ this._setProperty(shorthandProperty, shorthandValue, priority);
622
512
  }
623
513
  }
624
- },
625
- enumerable: false
514
+ }
626
515
  }
627
- });
628
516
 
629
- // Properties
630
- Object.defineProperties(CSSStyleDeclaration.prototype, generatedProperties);
517
+ /**
518
+ * Helper to handle position shorthand expansion.
519
+ *
520
+ * @private
521
+ * @param {string} property - The property name.
522
+ * @param {Array|string} value - The property value.
523
+ * @param {string} priority - The priority.
524
+ */
525
+ _positionShorthandSetter(property, value, priority) {
526
+ if (!shorthandProperties.has(property)) {
527
+ return;
528
+ }
529
+ const shorthandValues = [];
530
+ if (Array.isArray(value)) {
531
+ shorthandValues.push(...value);
532
+ } else if (typeof value === "string") {
533
+ shorthandValues.push(value);
534
+ } else {
535
+ return;
536
+ }
537
+ if (typeof priority !== "string") {
538
+ priority = this._priorities.get(property) ?? "";
539
+ }
540
+ const { position, shorthandFor } = shorthandProperties.get(property);
541
+ let hasPriority = false;
542
+ for (const [longhandProperty, longhandItem] of shorthandFor) {
543
+ const { position: longhandPosition } = longhandItem;
544
+ const longhandValue = getPositionValue(shorthandValues, longhandPosition);
545
+ if (priority) {
546
+ this._setProperty(longhandProperty, longhandValue, priority);
547
+ } else {
548
+ const longhandPriority = this._priorities.get(longhandProperty) ?? "";
549
+ if (longhandPriority) {
550
+ hasPriority = true;
551
+ } else {
552
+ this._setProperty(longhandProperty, longhandValue, priority);
553
+ }
554
+ }
555
+ }
556
+ if (hasPriority) {
557
+ this.removeProperty(property);
558
+ } else {
559
+ const shorthandValue = getPositionValue(shorthandValues, position);
560
+ this._setProperty(property, shorthandValue, priority);
561
+ }
562
+ }
631
563
 
632
- // Additional properties
633
- [...allProperties, ...allExtraProperties].forEach((property) => {
634
- if (!implementedProperties.has(property)) {
635
- const declaration = getPropertyDescriptor(property);
636
- Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration);
637
- const camel = dashedToCamelCase(property);
638
- Object.defineProperty(CSSStyleDeclaration.prototype, camel, declaration);
639
- if (/^webkit[A-Z]/.test(camel)) {
640
- const pascal = camel.replace(/^webkit/, "Webkit");
641
- Object.defineProperty(CSSStyleDeclaration.prototype, pascal, declaration);
564
+ /**
565
+ * Helper to handle position longhand updates affecting shorthands.
566
+ *
567
+ * @private
568
+ * @param {string} property - The property name.
569
+ * @param {string} value - The property value.
570
+ * @param {string} priority - The priority.
571
+ * @param {string} shorthandProperty - The shorthand property name.
572
+ */
573
+ _positionLonghandSetter(property, value, priority, shorthandProperty) {
574
+ if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) {
575
+ return;
576
+ }
577
+ const shorthandPriority = this._priorities.get(shorthandProperty);
578
+ this.removeProperty(shorthandProperty);
579
+ if (typeof priority !== "string") {
580
+ priority = this._priorities.get(property) ?? "";
581
+ }
582
+ this.removeProperty(property);
583
+ if (shorthandPriority && priority) {
584
+ this._setProperty(property, value);
585
+ } else {
586
+ this._setProperty(property, value, priority);
587
+ }
588
+ if (value && !hasVarFunc(value)) {
589
+ const longhandValues = [];
590
+ const { shorthandFor, position: shorthandPosition } = shorthandProperties.get(shorthandProperty);
591
+ for (const [longhandProperty] of shorthandFor) {
592
+ const longhandValue = this.getPropertyValue(longhandProperty);
593
+ const longhandPriority = this._priorities.get(longhandProperty) ?? "";
594
+ if (!longhandValue || longhandPriority !== priority) {
595
+ return;
596
+ }
597
+ longhandValues.push(longhandValue);
598
+ }
599
+ if (longhandValues.length === shorthandFor.size) {
600
+ const replacedValue = getPositionValue(longhandValues, shorthandPosition);
601
+ this._setProperty(shorthandProperty, replacedValue);
602
+ }
642
603
  }
643
604
  }
644
- });
605
+ }
606
+
607
+ // TODO: Remove once the CSSStyleDeclaration is fully spec-compliant.
608
+ // @see https://github.com/jsdom/cssstyle/issues/255#issuecomment-3630183207
609
+ Object.defineProperties(CSSStyleDeclaration.prototype, propertyDescriptors);
645
610
 
646
611
  module.exports = {
647
- CSSStyleDeclaration,
648
- propertyList: Object.fromEntries(implementedProperties)
612
+ CSSStyleDeclaration
649
613
  };