Skip to content

Commit c8afbb5

Browse files
committed
Split into CSSStyleDeclaration and CSSStyleProperties
1 parent c966976 commit c8afbb5

File tree

96 files changed

+2786
-4048
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+2786
-4048
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
node_modules
22
npm-debug.log
3-
lib/generated/implementedProperties.js
43
lib/generated/properties.js
4+
lib/generated/propertyList.js

lib/CSSStyleDeclaration.js

Lines changed: 100 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@
44
*/
55
"use strict";
66

7-
const allProperties = require("./generated/allProperties");
8-
const implementedProperties = require("./generated/implementedProperties");
9-
const generatedProperties = require("./generated/properties");
7+
const propertyList = require("./generated/propertyList");
108
const {
119
borderProperties,
1210
getPositionValue,
13-
normalizeBorderProperties,
11+
normalizeProperties,
1412
prepareBorderProperties,
1513
prepareProperties,
1614
shorthandProperties
@@ -22,28 +20,21 @@ const {
2220
parsePropertyValue,
2321
prepareValue
2422
} = require("./parsers");
25-
const allExtraProperties = require("./utils/allExtraProperties");
26-
const { dashedToCamelCase } = require("./utils/camelize");
27-
const { getPropertyDescriptor } = require("./utils/propertyDescriptors");
2823
const { asciiLowercase } = require("./utils/strings");
2924

3025
/**
3126
* @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface
3227
*/
3328
class CSSStyleDeclaration {
3429
/**
35-
* @param {Function} onChangeCallback
36-
* @param {object} [opt]
37-
* @param {object} [opt.context] - Window, Element or CSSRule.
30+
* @param {object} globalObject - Window
31+
* @param {object} opt - Options
32+
* @param {object} opt.context - Element or CSSStyleRule
33+
* @param {Function} opt.onChange - Callback when cssText change or property removed
3834
*/
39-
constructor(onChangeCallback, opt = {}) {
40-
// Make constructor and internals non-enumerable.
35+
constructor(globalObject = globalThis, opt = {}) {
36+
// Make internals non-enumerable.
4137
Object.defineProperties(this, {
42-
constructor: {
43-
enumerable: false,
44-
writable: true
45-
},
46-
4738
// Window
4839
_global: {
4940
value: globalThis,
@@ -58,8 +49,8 @@ class CSSStyleDeclaration {
5849
writable: true
5950
},
6051

61-
// CSSRule
62-
_parentNode: {
52+
// CSSStyleRule
53+
_parentRule: {
6354
value: null,
6455
enumerable: false,
6556
writable: true
@@ -71,6 +62,12 @@ class CSSStyleDeclaration {
7162
writable: true
7263
},
7364

65+
_options: {
66+
value: null,
67+
enumerable: false,
68+
writable: true
69+
},
70+
7471
_values: {
7572
value: new Map(),
7673
enumerable: false,
@@ -90,7 +87,7 @@ class CSSStyleDeclaration {
9087
},
9188

9289
_computed: {
93-
value: false,
90+
value: undefined,
9491
enumerable: false,
9592
writable: true
9693
},
@@ -101,34 +98,35 @@ class CSSStyleDeclaration {
10198
writable: true
10299
},
103100

104-
_setInProgress: {
101+
_updating: {
105102
value: false,
106103
enumerable: false,
107104
writable: true
108105
}
109106
});
110107

108+
this._global = globalObject;
109+
111110
const { context } = opt;
112111
if (context) {
113-
if (typeof context.getComputedStyle === "function") {
114-
this._global = context;
115-
this._computed = true;
116-
this._readonly = true;
117-
} else if (context.nodeType === 1 && Object.hasOwn(context, "style")) {
118-
this._global = context.ownerDocument.defaultView;
112+
if (context.nodeType === 1) {
119113
this._ownerNode = context;
120114
} else if (Object.hasOwn(context, "parentRule")) {
121115
this._parentRule = context;
122-
// Find Window from the owner node of the StyleSheet.
123-
const window = context?.parentStyleSheet?.ownerNode?.ownerDocument?.defaultView;
124-
if (window) {
125-
this._global = window;
126-
}
127116
}
128117
}
129-
if (typeof onChangeCallback === "function") {
130-
this._onChange = onChangeCallback;
118+
if (typeof opt.onChange === "function") {
119+
this._onChange = opt.onChange;
120+
}
121+
if (opt.format === "computedValue") {
122+
this._computed = true;
123+
} else {
124+
opt.format = "specifiedValue";
125+
}
126+
if (opt.readOnly) {
127+
this._readonly = true;
131128
}
129+
this._options = opt;
132130
}
133131

134132
get cssText() {
@@ -150,7 +148,7 @@ class CSSStyleDeclaration {
150148
}
151149
properties.set(property, { property, value, priority });
152150
}
153-
const normalizedProperties = normalizeBorderProperties(properties);
151+
const normalizedProperties = normalizeProperties(properties);
154152
const parts = [];
155153
for (const { property, value, priority } of normalizedProperties.values()) {
156154
if (priority) {
@@ -171,10 +169,10 @@ class CSSStyleDeclaration {
171169
Array.prototype.splice.call(this, 0, this._length);
172170
this._values.clear();
173171
this._priorities.clear();
174-
if (this._parentRule || (this._ownerNode && this._setInProgress)) {
172+
if (this._parentRule || (this._ownerNode && this._updating)) {
175173
return;
176174
}
177-
this._setInProgress = true;
175+
this._updating = true;
178176
try {
179177
const valueObj = parseCSS(
180178
val,
@@ -206,7 +204,8 @@ class CSSStyleDeclaration {
206204
}
207205
} else {
208206
const parsedValue = parsePropertyValue(property, value, {
209-
globalObject: this._global
207+
globalObject: this._global,
208+
options: this._options
210209
});
211210
if (parsedValue) {
212211
if (properties.has(property)) {
@@ -224,7 +223,8 @@ class CSSStyleDeclaration {
224223
}
225224
}
226225
const parsedProperties = prepareProperties(properties, {
227-
globalObject: this._global
226+
globalObject: this._global,
227+
options: this._options
228228
});
229229
for (const [property, item] of parsedProperties) {
230230
const { priority, value } = item;
@@ -235,7 +235,7 @@ class CSSStyleDeclaration {
235235
} catch {
236236
return;
237237
}
238-
this._setInProgress = false;
238+
this._updating = false;
239239
if (typeof this._onChange === "function") {
240240
this._onChange(this.cssText);
241241
}
@@ -255,36 +255,6 @@ class CSSStyleDeclaration {
255255
this._length = len;
256256
}
257257

258-
// Readonly
259-
get parentRule() {
260-
return this._parentRule;
261-
}
262-
263-
get cssFloat() {
264-
return this.getPropertyValue("float");
265-
}
266-
267-
set cssFloat(value) {
268-
this._setProperty("float", value);
269-
}
270-
271-
/**
272-
* @param {string} property
273-
*/
274-
getPropertyPriority(property) {
275-
return this._priorities.get(property) || "";
276-
}
277-
278-
/**
279-
* @param {string} property
280-
*/
281-
getPropertyValue(property) {
282-
if (this._values.has(property)) {
283-
return this._values.get(property).toString();
284-
}
285-
return "";
286-
}
287-
288258
/**
289259
* @param {...number} args
290260
*/
@@ -304,26 +274,18 @@ class CSSStyleDeclaration {
304274
/**
305275
* @param {string} property
306276
*/
307-
removeProperty(property) {
308-
if (this._readonly) {
309-
const msg = `Property ${property} can not be modified.`;
310-
const name = "NoModificationAllowedError";
311-
throw new this._global.DOMException(msg, name);
312-
}
313-
if (!this._values.has(property)) {
314-
return "";
315-
}
316-
const prevValue = this._values.get(property);
317-
this._values.delete(property);
318-
this._priorities.delete(property);
319-
const index = Array.prototype.indexOf.call(this, property);
320-
if (index >= 0) {
321-
Array.prototype.splice.call(this, index, 1);
322-
if (typeof this._onChange === "function") {
323-
this._onChange(this.cssText);
324-
}
277+
getPropertyValue(property) {
278+
if (this._values.has(property)) {
279+
return this._values.get(property).toString();
325280
}
326-
return prevValue;
281+
return "";
282+
}
283+
284+
/**
285+
* @param {string} property
286+
*/
287+
getPropertyPriority(property) {
288+
return this._priorities.get(property) || "";
327289
}
328290

329291
/**
@@ -350,7 +312,7 @@ class CSSStyleDeclaration {
350312
return;
351313
}
352314
const property = asciiLowercase(prop);
353-
if (!allProperties.has(property) && !allExtraProperties.has(property)) {
315+
if (!propertyList.has(property)) {
354316
return;
355317
}
356318
if (priority) {
@@ -360,6 +322,45 @@ class CSSStyleDeclaration {
360322
}
361323
this[property] = value;
362324
}
325+
326+
/**
327+
* @param {string} property
328+
*/
329+
removeProperty(property) {
330+
if (this._readonly) {
331+
const msg = `Property ${property} can not be modified.`;
332+
const name = "NoModificationAllowedError";
333+
throw new this._global.DOMException(msg, name);
334+
}
335+
if (!this._values.has(property)) {
336+
return "";
337+
}
338+
const prevValue = this._values.get(property);
339+
this._values.delete(property);
340+
this._priorities.delete(property);
341+
const index = Array.prototype.indexOf.call(this, property);
342+
if (index >= 0) {
343+
Array.prototype.splice.call(this, index, 1);
344+
if (typeof this._onChange === "function") {
345+
this._onChange(this.cssText);
346+
}
347+
}
348+
return prevValue;
349+
}
350+
351+
get parentRule() {
352+
return this._parentRule;
353+
}
354+
355+
// Non-standard
356+
setOptions(opt = {}) {
357+
for (const [key, value] of Object.entries(opt)) {
358+
this._options[key] = value;
359+
if (key === "readOnly") {
360+
this._readonly = value;
361+
}
362+
}
363+
}
363364
}
364365

365366
// Internal methods
@@ -403,7 +404,7 @@ Object.defineProperties(CSSStyleDeclaration.prototype, {
403404
if (
404405
typeof this._onChange === "function" &&
405406
this.cssText !== originalText &&
406-
!this._setInProgress
407+
!this._updating
407408
) {
408409
this._onChange(this.cssText);
409410
}
@@ -442,7 +443,8 @@ Object.defineProperties(CSSStyleDeclaration.prototype, {
442443
}
443444
}
444445
const parsedProperties = prepareBorderProperties(prop, val, prior, properties, {
445-
globalObject: this._global
446+
globalObject: this._global,
447+
options: this._options
446448
});
447449
for (const [property, item] of parsedProperties) {
448450
const { priority, value } = item;
@@ -507,8 +509,10 @@ Object.defineProperties(CSSStyleDeclaration.prototype, {
507509
}
508510
} else {
509511
const parsedValue = shorthandItem.parse(longhandValues.join(" "));
510-
const shorthandValue = Object.values(parsedValue).join(" ");
511-
this._setProperty(shorthandProperty, shorthandValue, priority);
512+
if (parsedValue) {
513+
const shorthandValue = Object.values(parsedValue).join(" ");
514+
this._setProperty(shorthandProperty, shorthandValue, priority);
515+
}
512516
}
513517
}
514518
}
@@ -605,32 +609,14 @@ Object.defineProperties(CSSStyleDeclaration.prototype, {
605609
}
606610
if (longhandValues.length === shorthandFor.size) {
607611
const replacedValue = getPositionValue(longhandValues, shorthandPosition);
608-
this._setProperty(shorthandProperty, replacedValue);
612+
this._setProperty(shorthandProperty, replacedValue, priority);
609613
}
610614
}
611615
},
612616
enumerable: false
613617
}
614618
});
615619

616-
// Properties
617-
Object.defineProperties(CSSStyleDeclaration.prototype, generatedProperties);
618-
619-
// Additional properties
620-
[...allProperties, ...allExtraProperties].forEach((property) => {
621-
if (!implementedProperties.has(property)) {
622-
const declaration = getPropertyDescriptor(property);
623-
Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration);
624-
const camel = dashedToCamelCase(property);
625-
Object.defineProperty(CSSStyleDeclaration.prototype, camel, declaration);
626-
if (/^webkit[A-Z]/.test(camel)) {
627-
const pascal = camel.replace(/^webkit/, "Webkit");
628-
Object.defineProperty(CSSStyleDeclaration.prototype, pascal, declaration);
629-
}
630-
}
631-
});
632-
633620
module.exports = {
634-
CSSStyleDeclaration,
635-
propertyList: Object.fromEntries(implementedProperties)
621+
CSSStyleDeclaration
636622
};

0 commit comments

Comments
 (0)