feat: initial commit of Railtrack Pro prototype with complete test suite

This commit is contained in:
Railtrack Pro Dev
2026-03-13 14:26:16 +00:00
commit 40500bb503
7790 changed files with 986332 additions and 0 deletions
+613
View File
@@ -0,0 +1,613 @@
/**
* This is a fork from the CSS Style Declaration part of
* https://github.com/NV/CSSOM
*/
"use strict";
const propertyDescriptors = require("./generated/propertyDescriptors");
const {
borderProperties,
getPositionValue,
normalizeProperties,
prepareBorderProperties,
prepareProperties,
shorthandProperties
} = require("./normalize");
const { hasVarFunc, isGlobalKeyword, parseCSS, parsePropertyValue, prepareValue } = require("./parsers");
const { asciiLowercase } = require("./utils/strings");
/**
* @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface
*/
class CSSStyleDeclaration {
/**
* Creates a new CSSStyleDeclaration instance.
*
* @param {Function} [onChangeCallback] - Callback triggered when style changes.
* @param {object} [opt] - Options.
* @param {object} [opt.context] - The context object (Window, Element, or CSSRule).
*/
constructor(onChangeCallback, { context } = {}) {
// Internals for jsdom
this._global = globalThis;
this._onChange = onChangeCallback;
// Internals for CSS declaration block
// @see https://drafts.csswg.org/cssom/#css-declaration-blocks
this._computed = false;
this._ownerNode = null;
this._parentRule = null;
this._readonly = false;
this._updating = false;
// Other internals
this._length = 0;
this._propertyIndices = new Map();
this._priorities = new Map();
this._values = new Map();
if (context) {
if (typeof context.getComputedStyle === "function") {
this._global = context;
this._computed = true;
// FIXME: The `_readonly` flag should initially be `false` to be editable,
// but should eventually be set to `true`.
// this._readonly = true;
} else if (context.nodeType === 1 && Object.hasOwn(context, "style")) {
this._global = context.ownerDocument.defaultView;
this._ownerNode = context;
} else if (Object.hasOwn(context, "parentRule")) {
this._parentRule = context;
// Find Window from the owner node of the StyleSheet.
const window = context?.parentStyleSheet?.ownerNode?.ownerDocument?.defaultView;
if (window) {
this._global = window;
}
}
}
}
/**
* Returns the textual representation of the declaration block.
*
* @returns {string} The serialized CSS text.
*/
get cssText() {
if (this._computed) {
return "";
}
const properties = new Map();
for (let i = 0; i < this._length; i++) {
const property = this[i];
const value = this.getPropertyValue(property);
const priority = this._priorities.get(property) ?? "";
if (shorthandProperties.has(property)) {
const { shorthandFor } = shorthandProperties.get(property);
for (const [longhand] of shorthandFor) {
if (priority || !this._priorities.get(longhand)) {
properties.delete(longhand);
}
}
}
properties.set(property, { property, value, priority });
}
const normalizedProperties = normalizeProperties(properties);
const parts = [];
for (const { property, value, priority } of normalizedProperties.values()) {
if (priority) {
parts.push(`${property}: ${value} !${priority};`);
} else {
parts.push(`${property}: ${value};`);
}
}
return parts.join(" ");
}
/**
* Sets the textual representation of the declaration block.
* This clears all existing properties and parses the new CSS text.
*
* @param {string} text - The new CSS text.
*/
set cssText(text) {
if (this._readonly) {
const msg = "cssText can not be modified.";
const name = "NoModificationAllowedError";
throw new this._global.DOMException(msg, name);
}
this._clearIndexedProperties();
this._values.clear();
this._priorities.clear();
if (this._parentRule || (this._ownerNode && this._updating)) {
return;
}
try {
this._updating = true;
const valueObj = parseCSS(text, { context: "declarationList", parseValue: false });
if (valueObj?.children) {
const properties = new Map();
let shouldSkipNext = false;
for (const item of valueObj.children) {
if (item.type === "Atrule") {
continue;
}
if (item.type === "Rule") {
shouldSkipNext = true;
continue;
}
if (shouldSkipNext === true) {
shouldSkipNext = false;
continue;
}
const {
important,
property,
value: { value }
} = item;
if (typeof property === "string" && typeof value === "string") {
const priority = important ? "important" : "";
const isCustomProperty = property.startsWith("--");
if (isCustomProperty || hasVarFunc(value)) {
if (properties.has(property)) {
const { priority: itemPriority } = properties.get(property);
if (!itemPriority) {
properties.set(property, { property, value, priority });
}
} else {
properties.set(property, { property, value, priority });
}
} else {
const parsedValue = parsePropertyValue(property, value);
if (parsedValue) {
if (properties.has(property)) {
const { priority: itemPriority } = properties.get(property);
if (!itemPriority) {
properties.set(property, { property, value, priority });
}
} else {
properties.set(property, { property, value, priority });
}
} else {
this.removeProperty(property);
}
}
}
}
const parsedProperties = prepareProperties(properties);
for (const [property, item] of parsedProperties) {
const { priority, value } = item;
this._priorities.set(property, priority);
this.setProperty(property, value, priority);
}
}
} catch {
return;
} finally {
this._updating = false;
}
if (this._onChange) {
this._onChange(this.cssText);
}
}
/**
* Returns the number of properties in the declaration block.
*
* @returns {number} The property count.
*/
get length() {
return this._length;
}
/**
* Returns the CSSRule that is the parent of this declaration block.
*
* @returns {object|null} The parent CSSRule or null.
*/
get parentRule() {
return this._parentRule;
}
/**
* Alias for the "float" property.
*
* @returns {string} The value of the "float" property.
*/
get cssFloat() {
return this.getPropertyValue("float");
}
/**
* Sets the "float" property.
*
* @param {string} value - The new value for "float".
*/
set cssFloat(value) {
this._setProperty("float", value);
}
/**
* Returns the priority of the specified property (e.g. "important").
*
* @param {string} property - The property name.
* @returns {string} The priority string, or empty string if not set.
*/
getPropertyPriority(property) {
return this._priorities.get(property) || "";
}
/**
* Returns the value of the specified property.
*
* @param {string} property - The property name.
* @returns {string} The property value, or empty string if not set.
*/
getPropertyValue(property) {
if (this._values.has(property)) {
return this._values.get(property).toString();
}
return "";
}
/**
* Returns the property name at the specified index.
*
* @param {...number} args - The index (only the first argument is used).
* @returns {string} The property name, or empty string if index is invalid.
*/
item(...args) {
if (!args.length) {
const msg = "1 argument required, but only 0 present.";
throw new this._global.TypeError(msg);
}
const [value] = args;
const index = parseInt(value);
if (Number.isNaN(index) || index < 0 || index >= this._length) {
return "";
}
return this[index];
}
/**
* Removes the specified property from the declaration block.
*
* @param {string} property - The property name to remove.
* @returns {string} The value of the removed property.
*/
removeProperty(property) {
if (this._readonly) {
const msg = `Property ${property} can not be modified.`;
const name = "NoModificationAllowedError";
throw new this._global.DOMException(msg, name);
}
if (!this._values.has(property)) {
return "";
}
const prevValue = this._values.get(property);
this._values.delete(property);
this._priorities.delete(property);
const index = this._getIndexOf(property);
if (index >= 0) {
this._removeIndexedProperty(index);
if (this._onChange) {
this._onChange(this.cssText);
}
}
return prevValue;
}
/**
* Sets a property value with an optional priority.
*
* @param {string} property - The property name.
* @param {string} value - The property value.
* @param {string} [priority=""] - The priority (e.g. "important").
*/
setProperty(property, value, priority = "") {
if (this._readonly) {
const msg = `Property ${property} can not be modified.`;
const name = "NoModificationAllowedError";
throw new this._global.DOMException(msg, name);
}
value = prepareValue(value);
if (value === "") {
if (Object.hasOwn(propertyDescriptors, property)) {
// TODO: Refactor handlers to not require `.call()`.
propertyDescriptors[property].set.call(this, value);
}
this.removeProperty(property);
return;
}
// Custom property
if (property.startsWith("--")) {
this._setProperty(property, value, priority);
return;
}
property = asciiLowercase(property);
if (!Object.hasOwn(propertyDescriptors, property)) {
return;
}
if (priority) {
this._priorities.set(property, priority);
} else {
this._priorities.delete(property);
}
propertyDescriptors[property].set.call(this, value);
}
/**
* Clears all indexed properties, properties indices and resets length to 0.
*
* @private
*/
_clearIndexedProperties() {
this._propertyIndices.clear();
for (let i = 0; i < this._length; i++) {
delete this[i];
}
this._length = 0;
}
/**
* Removes an indexed property at the specified index, shifts others, and updates indices.
*
* @private
* @param {number} index - The index of the property to remove.
*/
_removeIndexedProperty(index) {
this._propertyIndices.delete(this[index]);
for (let i = index; i < this._length - 1; i++) {
const property = this[i + 1];
this[i] = property;
this._propertyIndices.set(property, i);
}
delete this[this._length - 1];
this._length--;
}
/**
* Returns the index of the specified property.
*
* @private
* @param {string} property - The property name to search for.
* @returns {number} The index of the property, or -1 if not found.
*/
_getIndexOf(property) {
return this._propertyIndices.get(property) ?? -1;
}
/**
* Sets a property and update indices.
*
* @private
* @param {string} property - The property name.
* @param {string} value - The property value.
* @param {string} priority - The priority.
*/
_setProperty(property, value, priority) {
if (typeof value !== "string") {
return;
}
if (value === "") {
this.removeProperty(property);
return;
}
let originalText = "";
if (this._onChange && !this._updating) {
originalText = this.cssText;
}
if (!this._values.has(property)) {
// New property.
this[this._length] = property;
this._propertyIndices.set(property, this._length);
this._length++;
}
if (priority === "important") {
this._priorities.set(property, priority);
} else {
this._priorities.delete(property);
}
this._values.set(property, value);
if (this._onChange && !this._updating && this.cssText !== originalText) {
this._onChange(this.cssText);
}
}
/**
* Helper to handle border property expansion.
*
* @private
* @param {string} property - The property name (e.g. "border").
* @param {object|Array|string} value - The value to set.
* @param {string} priority - The priority.
*/
_borderSetter(property, value, priority) {
const properties = new Map();
if (typeof priority !== "string") {
priority = this._priorities.get(property) ?? "";
}
if (property === "border") {
properties.set(property, { propery: property, value, priority });
} else {
for (let i = 0; i < this._length; i++) {
const itemProperty = this[i];
if (borderProperties.has(itemProperty)) {
const itemValue = this.getPropertyValue(itemProperty);
const longhandPriority = this._priorities.get(itemProperty) ?? "";
let itemPriority = longhandPriority;
if (itemProperty === property) {
itemPriority = priority;
}
properties.set(itemProperty, {
property: itemProperty,
value: itemValue,
priority: itemPriority
});
}
}
}
const parsedProperties = prepareBorderProperties(property, value, priority, properties);
for (const [itemProperty, item] of parsedProperties) {
const { priority: itemPriority, value: itemValue } = item;
this._setProperty(itemProperty, itemValue, itemPriority);
}
}
/**
* Helper to handle flexbox shorthand expansion.
*
* @private
* @param {string} property - The property name.
* @param {string} value - The property value.
* @param {string} priority - The priority.
* @param {string} shorthandProperty - The shorthand property name.
*/
_flexBoxSetter(property, value, priority, shorthandProperty) {
if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) {
return;
}
const shorthandPriority = this._priorities.get(shorthandProperty);
this.removeProperty(shorthandProperty);
if (typeof priority !== "string") {
priority = this._priorities.get(property) ?? "";
}
this.removeProperty(property);
if (shorthandPriority && priority) {
this._setProperty(property, value);
} else {
this._setProperty(property, value, priority);
}
if (value && !hasVarFunc(value)) {
const longhandValues = [];
const shorthandItem = shorthandProperties.get(shorthandProperty);
let hasGlobalKeyword = false;
for (const [longhandProperty] of shorthandItem.shorthandFor) {
if (longhandProperty === property) {
if (isGlobalKeyword(value)) {
hasGlobalKeyword = true;
}
longhandValues.push(value);
} else {
const longhandValue = this.getPropertyValue(longhandProperty);
const longhandPriority = this._priorities.get(longhandProperty) ?? "";
if (!longhandValue || longhandPriority !== priority) {
break;
}
if (isGlobalKeyword(longhandValue)) {
hasGlobalKeyword = true;
}
longhandValues.push(longhandValue);
}
}
if (longhandValues.length === shorthandItem.shorthandFor.size) {
if (hasGlobalKeyword) {
const [firstValue, ...restValues] = longhandValues;
if (restValues.every((val) => val === firstValue)) {
this._setProperty(shorthandProperty, firstValue, priority);
}
} else {
const parsedValue = shorthandItem.parse(longhandValues.join(" "));
const shorthandValue = Object.values(parsedValue).join(" ");
this._setProperty(shorthandProperty, shorthandValue, priority);
}
}
}
}
/**
* Helper to handle position shorthand expansion.
*
* @private
* @param {string} property - The property name.
* @param {Array|string} value - The property value.
* @param {string} priority - The priority.
*/
_positionShorthandSetter(property, value, priority) {
if (!shorthandProperties.has(property)) {
return;
}
const shorthandValues = [];
if (Array.isArray(value)) {
shorthandValues.push(...value);
} else if (typeof value === "string") {
shorthandValues.push(value);
} else {
return;
}
if (typeof priority !== "string") {
priority = this._priorities.get(property) ?? "";
}
const { position, shorthandFor } = shorthandProperties.get(property);
let hasPriority = false;
for (const [longhandProperty, longhandItem] of shorthandFor) {
const { position: longhandPosition } = longhandItem;
const longhandValue = getPositionValue(shorthandValues, longhandPosition);
if (priority) {
this._setProperty(longhandProperty, longhandValue, priority);
} else {
const longhandPriority = this._priorities.get(longhandProperty) ?? "";
if (longhandPriority) {
hasPriority = true;
} else {
this._setProperty(longhandProperty, longhandValue, priority);
}
}
}
if (hasPriority) {
this.removeProperty(property);
} else {
const shorthandValue = getPositionValue(shorthandValues, position);
this._setProperty(property, shorthandValue, priority);
}
}
/**
* Helper to handle position longhand updates affecting shorthands.
*
* @private
* @param {string} property - The property name.
* @param {string} value - The property value.
* @param {string} priority - The priority.
* @param {string} shorthandProperty - The shorthand property name.
*/
_positionLonghandSetter(property, value, priority, shorthandProperty) {
if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) {
return;
}
const shorthandPriority = this._priorities.get(shorthandProperty);
this.removeProperty(shorthandProperty);
if (typeof priority !== "string") {
priority = this._priorities.get(property) ?? "";
}
this.removeProperty(property);
if (shorthandPriority && priority) {
this._setProperty(property, value);
} else {
this._setProperty(property, value, priority);
}
if (value && !hasVarFunc(value)) {
const longhandValues = [];
const { shorthandFor, position: shorthandPosition } = shorthandProperties.get(shorthandProperty);
for (const [longhandProperty] of shorthandFor) {
const longhandValue = this.getPropertyValue(longhandProperty);
const longhandPriority = this._priorities.get(longhandProperty) ?? "";
if (!longhandValue || longhandPriority !== priority) {
return;
}
longhandValues.push(longhandValue);
}
if (longhandValues.length === shorthandFor.size) {
const replacedValue = getPositionValue(longhandValues, shorthandPosition);
this._setProperty(shorthandProperty, replacedValue);
}
}
}
}
// TODO: Remove once the CSSStyleDeclaration is fully spec-compliant.
// @see https://github.com/jsdom/cssstyle/issues/255#issuecomment-3630183207
Object.defineProperties(CSSStyleDeclaration.prototype, propertyDescriptors);
module.exports = {
CSSStyleDeclaration
};
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
"use strict";
const { CSSStyleDeclaration } = require("./CSSStyleDeclaration");
const propertyDefinitions = require("./generated/propertyDefinitions");
const { parsePropertyValue } = require("./parsers");
module.exports = {
CSSStyleDeclaration,
parsePropertyValue,
propertyDefinitions
};
+1448
View File
File diff suppressed because it is too large Load Diff
+851
View File
@@ -0,0 +1,851 @@
"use strict";
const {
resolve: resolveColor,
utils: { cssCalc, resolveGradient, splitValue }
} = require("@asamuzakjp/css-color");
const { next: syntaxes } = require("@csstools/css-syntax-patches-for-csstree");
const csstree = require("css-tree");
const { LRUCache } = require("lru-cache");
const propertyDefinitions = require("./generated/propertyDefinitions");
const { asciiLowercase } = require("./utils/strings");
// Constants
const CALC_FUNC_NAMES = "(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)";
// CSS global keywords
// @see https://drafts.csswg.org/css-cascade-5/#defaulting-keywords
const GLOBAL_KEYS = new Set(["initial", "inherit", "unset", "revert", "revert-layer"]);
// System colors
// @see https://drafts.csswg.org/css-color/#css-system-colors
// @see https://drafts.csswg.org/css-color/#deprecated-system-colors
const SYS_COLORS = new Set([
"accentcolor",
"accentcolortext",
"activeborder",
"activecaption",
"activetext",
"appworkspace",
"background",
"buttonborder",
"buttonface",
"buttonhighlight",
"buttonshadow",
"buttontext",
"canvas",
"canvastext",
"captiontext",
"field",
"fieldtext",
"graytext",
"highlight",
"highlighttext",
"inactiveborder",
"inactivecaption",
"inactivecaptiontext",
"infobackground",
"infotext",
"linktext",
"mark",
"marktext",
"menu",
"menutext",
"scrollbar",
"selecteditem",
"selecteditemtext",
"threeddarkshadow",
"threedface",
"threedhighlight",
"threedlightshadow",
"threedshadow",
"visitedtext",
"window",
"windowframe",
"windowtext"
]);
// AST node types
const AST_TYPES = Object.freeze({
CALC: "Calc",
DIMENSION: "Dimension",
FUNCTION: "Function",
GLOBAL_KEYWORD: "GlobalKeyword",
HASH: "Hash",
IDENTIFIER: "Identifier",
NUMBER: "Number",
PERCENTAGE: "Percentage",
STRING: "String",
URL: "Url"
});
// Regular expressions
const calcRegEx = new RegExp(`^${CALC_FUNC_NAMES}\\(`);
const calcContainedRegEx = new RegExp(`(?<=[*/\\s(])${CALC_FUNC_NAMES}\\(`);
const calcNameRegEx = new RegExp(`^${CALC_FUNC_NAMES}$`);
const varRegEx = /^var\(/;
const varContainedRegEx = /(?<=[*/\s(])var\(/;
// Patched css-tree
const cssTree = csstree.fork(syntaxes);
// Instance of the LRU Cache. Stores up to 4096 items.
const lruCache = new LRUCache({
max: 4096
});
/**
* Prepares a stringified value.
*
* @param {string|number|null|undefined} value - The value to prepare.
* @returns {string} The prepared value.
*/
function prepareValue(value) {
// `null` is converted to an empty string.
// @see https://webidl.spec.whatwg.org/#LegacyNullToEmptyString
if (value === null) {
return "";
}
return `${value}`.trim();
}
/**
* Checks if the value is a global keyword.
*
* @param {string} val - The value to check.
* @returns {boolean} True if the value is a global keyword, false otherwise.
*/
function isGlobalKeyword(val) {
return GLOBAL_KEYS.has(asciiLowercase(val));
}
/**
* Checks if the value starts with or contains a CSS var() function.
*
* @param {string} val - The value to check.
* @returns {boolean} True if the value contains a var() function, false otherwise.
*/
function hasVarFunc(val) {
return varRegEx.test(val) || varContainedRegEx.test(val);
}
/**
* Checks if the value starts with or contains CSS calc() or math functions.
*
* @param {string} val - The value to check.
* @returns {boolean} True if the value contains calc() or math functions, false otherwise.
*/
function hasCalcFunc(val) {
return calcRegEx.test(val) || calcContainedRegEx.test(val);
}
/**
* Parses a CSS string into an AST.
*
* @param {string} val - The CSS string to parse.
* @param {object} opt - The options for parsing.
* @returns {object} The AST.
*/
function parseCSS(val, opt) {
return cssTree.parse(prepareValue(val), opt);
}
/**
* Checks if the value is a valid property value.
* Returns false for custom properties or values containing var().
*
* @param {string} prop - The property name.
* @param {string} val - The property value.
* @returns {boolean} True if the value is valid, false otherwise.
*/
function isValidPropertyValue(prop, val) {
if (!propertyDefinitions.has(prop)) {
return false;
}
val = prepareValue(val);
if (val === "") {
return true;
}
const cacheKey = `isValidPropertyValue_${prop}_${val}`;
const cachedValue = lruCache.get(cacheKey);
if (typeof cachedValue === "boolean") {
return cachedValue;
}
let result;
try {
const ast = parseCSS(val, {
context: "value"
});
const { error, matched } = cssTree.lexer.matchProperty(prop, ast);
result = error === null && matched !== null;
} catch {
result = false;
}
lruCache.set(cacheKey, result);
return result;
}
/**
* Resolves CSS math functions.
*
* @param {string} val - The value to resolve.
* @param {object} [opt={ format: "specifiedValue" }] - The options for resolving.
* @returns {string|undefined} The resolved value.
*/
function resolveCalc(val, opt = { format: "specifiedValue" }) {
val = prepareValue(val);
if (val === "" || hasVarFunc(val) || !hasCalcFunc(val)) {
return val;
}
const cacheKey = `resolveCalc_${val}`;
const cachedValue = lruCache.get(cacheKey);
if (typeof cachedValue === "string") {
return cachedValue;
}
const ast = parseCSS(val, { context: "value" });
if (!ast?.children) {
return;
}
const values = [];
for (const item of ast.children) {
const { type: itemType, name: itemName, value: itemValue } = item;
if (itemType === AST_TYPES.FUNCTION) {
const value = cssTree
.generate(item)
.replace(/\)(?!\)|\s|,)/g, ") ")
.trim();
if (calcNameRegEx.test(itemName)) {
const newValue = cssCalc(value, opt);
values.push(newValue);
} else {
values.push(value);
}
} else if (itemType === AST_TYPES.STRING) {
values.push(`"${itemValue}"`);
} else {
values.push(itemName ?? itemValue);
}
}
const resolvedValue = values.join(" ");
lruCache.set(cacheKey, resolvedValue);
return resolvedValue;
}
/**
* Parses a property value.
* Returns a string or an array of parsed objects.
*
* @param {string} prop - The property name.
* @param {string} val - The property value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|Array<object>|undefined} The parsed value.
*/
function parsePropertyValue(prop, val, opt = {}) {
if (!propertyDefinitions.has(prop)) {
return;
}
const { caseSensitive } = opt;
val = prepareValue(val);
if (val === "" || hasVarFunc(val)) {
return val;
} else if (hasCalcFunc(val)) {
const calculatedValue = resolveCalc(val, {
format: "specifiedValue"
});
if (typeof calculatedValue !== "string") {
return;
}
val = calculatedValue;
}
const cacheKey = `parsePropertyValue_${prop}_${val}_${caseSensitive}`;
const cachedValue = lruCache.get(cacheKey);
if (cachedValue === false) {
return;
} else if (cachedValue !== undefined) {
return cachedValue;
}
let parsedValue;
const lowerCasedValue = asciiLowercase(val);
if (GLOBAL_KEYS.has(lowerCasedValue)) {
parsedValue = [
{
type: AST_TYPES.GLOBAL_KEYWORD,
name: lowerCasedValue
}
];
} else {
try {
const ast = parseCSS(val, {
context: "value"
});
const { error, matched } = cssTree.lexer.matchProperty(prop, ast);
if (error || !matched) {
parsedValue = false;
} else {
const items = ast.children;
const itemCount = items.size;
const values = [];
for (const item of items) {
const { children, name, type, value, unit } = item;
switch (type) {
case AST_TYPES.DIMENSION: {
values.push({
type,
value,
unit: asciiLowercase(unit)
});
break;
}
case AST_TYPES.FUNCTION: {
const raw = itemCount === 1 ? val : cssTree.generate(item).replace(/\)(?!\)|\s|,)/g, ") ");
// Remove "${name}(" from the start and ")" from the end
const itemValue = raw.trim().slice(name.length + 1, -1);
if (name === "calc") {
if (children.size === 1) {
const child = children.first;
if (child.type === AST_TYPES.NUMBER) {
values.push({
type: AST_TYPES.CALC,
name,
isNumber: true,
value: `${parseFloat(child.value)}`
});
} else {
values.push({
type: AST_TYPES.CALC,
name,
isNumber: false,
value: asciiLowercase(itemValue)
});
}
} else {
values.push({
type: AST_TYPES.CALC,
name,
isNumber: false,
value: asciiLowercase(itemValue)
});
}
} else {
values.push({
type,
name,
value: caseSensitive ? itemValue : asciiLowercase(itemValue)
});
}
break;
}
case AST_TYPES.IDENTIFIER: {
if (caseSensitive) {
values.push(item);
} else {
values.push({
type,
name: asciiLowercase(name)
});
}
break;
}
default: {
values.push(item);
}
}
}
parsedValue = values;
}
} catch {
parsedValue = false;
}
}
lruCache.set(cacheKey, parsedValue);
if (parsedValue === false) {
return;
}
return parsedValue;
}
/**
* Parses a numeric value (number, dimension, percentage).
* Helper function for serializeNumber, serializeLength, etc.
*
* @param {Array<object>} val - The AST value.
* @param {object} [opt={}] - The options for parsing.
* @param {Function} validateType - Function to validate the node type.
* @returns {object|undefined} The parsed result containing num and unit, or undefined.
*/
function parseNumericValue(val, opt, validateType) {
const [item] = val;
const { type, value, unit } = item ?? {};
if (!validateType(type, value, unit)) {
return;
}
const { clamp } = opt || {};
const max = opt?.max ?? Number.INFINITY;
const min = opt?.min ?? Number.NEGATIVE_INFINITY;
let num = parseFloat(value);
if (clamp) {
if (num > max) {
num = max;
} else if (num < min) {
num = min;
}
} else if (num > max || num < min) {
return;
}
return {
num,
unit: unit ? asciiLowercase(unit) : null,
type
};
}
/**
* Serializes a <number> value.
*
* @param {Array<object>} val - The AST value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The parsed number.
*/
function serializeNumber(val, opt = {}) {
const res = parseNumericValue(val, opt, (type) => type === AST_TYPES.NUMBER);
if (!res) {
return;
}
return `${res.num}`;
}
/**
* Serializes an <angle> value.
*
* @param {Array<object>} val - The AST value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The serialized angle.
*/
function serializeAngle(val, opt = {}) {
const res = parseNumericValue(
val,
opt,
(type, value) => type === AST_TYPES.DIMENSION || (type === AST_TYPES.NUMBER && value === "0")
);
if (!res) {
return;
}
const { num, unit } = res;
if (unit) {
if (!/^(?:deg|g?rad|turn)$/i.test(unit)) {
return;
}
return `${num}${unit}`;
} else if (num === 0) {
return `${num}deg`;
}
}
/**
* Serializes a <length> value.
*
* @param {Array<object>} val - The AST value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The serialized length.
*/
function serializeLength(val, opt = {}) {
const res = parseNumericValue(
val,
opt,
(type, value) => type === AST_TYPES.DIMENSION || (type === AST_TYPES.NUMBER && value === "0")
);
if (!res) {
return;
}
const { num, unit } = res;
if (num === 0 && !unit) {
return `${num}px`;
} else if (unit) {
return `${num}${unit}`;
}
}
/**
* Serializes a <dimension> value, e.g. <frequency>, <time> and <resolution>.
*
* @param {Array<object>} val - The AST value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The serialized dimension.
*/
function serializeDimension(val, opt = {}) {
const res = parseNumericValue(val, opt, (type) => type === AST_TYPES.DIMENSION);
if (!res) {
return;
}
const { num, unit } = res;
if (unit) {
return `${num}${unit}`;
}
}
/**
* Serializes a <percentage> value.
*
* @param {Array<object>} val - The AST value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The serialized percentage.
*/
function serializePercentage(val, opt = {}) {
const res = parseNumericValue(
val,
opt,
(type, value) => type === AST_TYPES.PERCENTAGE || (type === AST_TYPES.NUMBER && value === "0")
);
if (!res) {
return;
}
const { num } = res;
return `${num}%`;
}
/**
* Serializes a <url> value.
*
* @param {Array<object>} val - The AST value.
* @returns {string|undefined} The serialized url.
*/
function serializeURL(val) {
const [item] = val;
const { type, value } = item ?? {};
if (type !== AST_TYPES.URL) {
return;
}
const str = value.replace(/\\\\/g, "\\").replaceAll('"', '\\"');
return `url("${str}")`;
}
/**
* Serializes a <string> value.
*
* @param {Array<object>} val - The AST value.
* @returns {string|undefined} The serialized string.
*/
function serializeString(val) {
const [item] = val;
const { type, value } = item ?? {};
if (type !== AST_TYPES.STRING) {
return;
}
const str = value.replace(/\\\\/g, "\\").replaceAll('"', '\\"');
return `"${str}"`;
}
/**
* Serializes a <color> value.
*
* @param {Array<object>} val - The AST value.
* @returns {string|undefined} The serialized color.
*/
function serializeColor(val) {
const [item] = val;
const { name, type, value } = item ?? {};
switch (type) {
case AST_TYPES.FUNCTION: {
const res = resolveColor(`${name}(${value})`, {
format: "specifiedValue"
});
if (res) {
return res;
}
break;
}
case AST_TYPES.HASH: {
const res = resolveColor(`#${value}`, {
format: "specifiedValue"
});
if (res) {
return res;
}
break;
}
case AST_TYPES.IDENTIFIER: {
if (SYS_COLORS.has(name)) {
return name;
}
const res = resolveColor(name, {
format: "specifiedValue"
});
if (res) {
return res;
}
break;
}
default:
}
}
/**
* Serializes a <gradient> value.
*
* @param {Array<object>} val - The AST value.
* @returns {string|undefined} The serialized gradient.
*/
function serializeGradient(val) {
const [item] = val;
const { name, type, value } = item ?? {};
if (type !== AST_TYPES.FUNCTION) {
return;
}
const res = resolveGradient(`${name}(${value})`, {
format: "specifiedValue"
});
if (res) {
return res;
}
}
/**
* Resolves a keyword value.
*
* @param {Array<object>} value - The AST node array containing the keyword value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The resolved keyword or undefined.
*/
function resolveKeywordValue(value, opt = {}) {
const [{ name, type }] = value;
const { length } = opt;
switch (type) {
case AST_TYPES.GLOBAL_KEYWORD: {
if (length > 1) {
return;
}
return name;
}
case AST_TYPES.IDENTIFIER: {
return name;
}
default:
}
}
/**
* Resolves a function value.
*
* @param {Array<object>} value - The AST node array containing the function value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The resolved function or undefined.
*/
function resolveFunctionValue(value, opt = {}) {
const [{ name, type, value: itemValue }] = value;
const { length } = opt;
switch (type) {
case AST_TYPES.FUNCTION: {
return `${name}(${itemValue})`;
}
case AST_TYPES.GLOBAL_KEYWORD: {
if (length > 1) {
return;
}
return name;
}
case AST_TYPES.IDENTIFIER: {
return name;
}
default:
}
}
/**
* Resolves a numeric value.
*
* @param {Array<object>} value - The AST node array containing the numeric value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The resolved length/percentage/number or undefined.
*/
function resolveNumericValue(value, opt = {}) {
const [{ name, type: itemType, value: itemValue }] = value;
const { length, type } = opt;
switch (itemType) {
case AST_TYPES.CALC: {
return `${name}(${itemValue})`;
}
case AST_TYPES.DIMENSION: {
if (type === "angle") {
return serializeAngle(value, opt);
} else if (type === "length") {
return serializeLength(value, opt);
}
return serializeDimension(value, opt);
}
case AST_TYPES.GLOBAL_KEYWORD: {
if (length > 1) {
return;
}
return name;
}
case AST_TYPES.IDENTIFIER: {
return name;
}
case AST_TYPES.NUMBER: {
switch (type) {
case "angle": {
return serializeAngle(value, opt);
}
case "length": {
return serializeLength(value, opt);
}
case "percentage": {
return serializePercentage(value, opt);
}
default: {
return serializeNumber(value, opt);
}
}
}
case AST_TYPES.PERCENTAGE: {
return serializePercentage(value, opt);
}
default:
}
}
/**
* Resolves a color value.
*
* @param {Array<object>} value - The AST node array containing the color value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The resolved color or undefined.
*/
function resolveColorValue(value, opt = {}) {
const [{ name, type }] = value;
const { length } = opt;
switch (type) {
case AST_TYPES.GLOBAL_KEYWORD: {
if (length > 1) {
return;
}
return name;
}
default: {
return serializeColor(value, opt);
}
}
}
/**
* Resolves an image value.
*
* @param {Array<object>} value - The AST node array containing the image value.
* @param {object} [opt={}] - The options for parsing.
* @returns {string|undefined} The resolved gradient/url or undefined.
*/
function resolveImageValue(value, opt = {}) {
const [{ name, type }] = value;
const { length } = opt;
switch (type) {
case AST_TYPES.GLOBAL_KEYWORD: {
if (length > 1) {
return;
}
return name;
}
case AST_TYPES.IDENTIFIER: {
return name;
}
case AST_TYPES.URL: {
return serializeURL(value, opt);
}
default: {
return serializeGradient(value, opt);
}
}
}
/**
* Resolves a border shorthand value.
*
* @param {Array<object>} value - The AST node array containing the shorthand value.
* @param {object} subProps - The sub properties object.
* @param {Map} parsedValues - The Map of parsed values.
* @returns {Array|string|undefined} - The resolved [prop, value] pair, keyword or undefined.
*/
function resolveBorderShorthandValue(value, subProps, parsedValues) {
const [{ isNumber, name, type, value: itemValue }] = value;
const { color: colorProp, style: styleProp, width: widthProp } = subProps;
switch (type) {
case AST_TYPES.CALC: {
if (isNumber || parsedValues.has(widthProp)) {
return;
}
return [widthProp, `${name}(${itemValue}`];
}
case AST_TYPES.DIMENSION:
case AST_TYPES.NUMBER: {
if (parsedValues.has(widthProp)) {
return;
}
const parsedValue = serializeLength(value, { min: 0 });
if (!parsedValue) {
return;
}
return [widthProp, parsedValue];
}
case AST_TYPES.FUNCTION:
case AST_TYPES.HASH: {
if (parsedValues.has(colorProp)) {
return;
}
const parsedValue = serializeColor(value);
if (!parsedValue) {
return;
}
return [colorProp, parsedValue];
}
case AST_TYPES.GLOBAL_KEYWORD: {
return name;
}
case AST_TYPES.IDENTIFIER: {
if (isValidPropertyValue(widthProp, name)) {
if (parsedValues.has(widthProp)) {
return;
}
return [widthProp, name];
} else if (isValidPropertyValue(styleProp, name)) {
if (parsedValues.has(styleProp)) {
return;
}
return [styleProp, name];
} else if (isValidPropertyValue(colorProp, name)) {
if (parsedValues.has(colorProp)) {
return;
}
return [colorProp, name];
}
break;
}
default:
}
}
module.exports = {
AST_TYPES,
hasCalcFunc,
hasVarFunc,
isGlobalKeyword,
isValidPropertyValue,
parseCSS,
parsePropertyValue,
prepareValue,
resolveBorderShorthandValue,
resolveCalc,
resolveColorValue,
resolveFunctionValue,
resolveImageValue,
resolveKeywordValue,
resolveNumericValue,
serializeAngle,
serializeColor,
serializeDimension,
serializeGradient,
serializeLength,
serializeNumber,
serializePercentage,
serializeString,
serializeURL,
splitValue
};
+405
View File
@@ -0,0 +1,405 @@
"use strict";
const parsers = require("../parsers");
const backgroundImage = require("./backgroundImage");
const backgroundPosition = require("./backgroundPosition");
const backgroundSize = require("./backgroundSize");
const backgroundRepeat = require("./backgroundRepeat");
const backgroundOrigin = require("./backgroundOrigin");
const backgroundClip = require("./backgroundClip");
const backgroundAttachment = require("./backgroundAttachment");
const backgroundColor = require("./backgroundColor");
const property = "background";
const initialValues = new Map([
[backgroundImage.property, "none"],
[backgroundPosition.property, "0% 0%"],
[backgroundSize.property, "auto"],
[backgroundRepeat.property, "repeat"],
[backgroundOrigin.property, "padding-box"],
[backgroundClip.property, "border-box"],
[backgroundAttachment.property, "scroll"],
[backgroundColor.property, "transparent"]
]);
const shorthandFor = new Map([
[backgroundImage.property, backgroundImage],
[backgroundPosition.property, backgroundPosition],
[backgroundSize.property, backgroundSize],
[backgroundRepeat.property, backgroundRepeat],
[backgroundOrigin.property, backgroundOrigin],
[backgroundClip.property, backgroundClip],
[backgroundAttachment.property, backgroundAttachment],
[backgroundColor.property, backgroundColor]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (v === "" || parsers.hasVarFunc(v)) {
for (const [key] of shorthandFor) {
this._setProperty(key, "");
}
this._setProperty(property, v);
} else {
const bgValues = parse(v);
if (!Array.isArray(bgValues)) {
return;
}
const bgMap = new Map([
[backgroundImage.property, []],
[backgroundPosition.property, []],
[backgroundSize.property, []],
[backgroundRepeat.property, []],
[backgroundOrigin.property, []],
[backgroundClip.property, []],
[backgroundAttachment.property, []],
[backgroundColor.property, []]
]);
const backgrounds = [];
for (const bgValue of bgValues) {
const bg = [];
for (const [longhand, value] of Object.entries(bgValue)) {
if (value) {
const arr = bgMap.get(longhand);
arr.push(value);
bgMap.set(longhand, arr);
if (value !== initialValues.get(longhand)) {
if (longhand === backgroundSize.property) {
bg.push(`/ ${value}`);
} else {
bg.push(value);
}
} else if (longhand === backgroundImage.property) {
if (v === "none") {
bg.push(value);
}
} else if (longhand === backgroundColor.property) {
if (v === "transparent") {
bg.push(value);
}
}
}
}
backgrounds.push(bg.join(" "));
}
const priority = this._priorities.get(property) ?? "";
for (const [longhand, value] of bgMap) {
this._setProperty(longhand, value.join(", "), priority);
}
this._setProperty(property, backgrounds.join(", "), priority);
}
},
get() {
const v = this.getPropertyValue(property);
if (parsers.hasVarFunc(v)) {
return v;
}
const bgMap = new Map();
let l = 0;
for (const [longhand] of shorthandFor) {
const val = this.getPropertyValue(longhand);
if (longhand === backgroundImage.property) {
if (val === "none" && v === "none" && this.getPropertyValue(backgroundColor.property) === "transparent") {
return val;
}
if (val !== initialValues.get(longhand)) {
const imgValues = parsers.splitValue(val, {
delimiter: ","
});
l = imgValues.length;
bgMap.set(longhand, imgValues);
}
} else if (longhand === backgroundColor.property) {
if (val !== initialValues.get(longhand) || v.includes(val)) {
bgMap.set(longhand, [val]);
}
} else if (val !== initialValues.get(longhand)) {
bgMap.set(
longhand,
parsers.splitValue(val, {
delimiter: ","
})
);
}
}
if (l === 0) {
const bgColArr = bgMap.get(backgroundColor.property);
const background = bgColArr ? bgColArr[0] : null;
if (background) {
return background;
}
return "";
}
const bgValues = [];
for (let i = 0; i < l; i++) {
bgValues[i] = [];
}
for (const [longhand, values] of bgMap) {
for (let i = 0; i < l; i++) {
switch (longhand) {
case backgroundColor.property: {
if (i === l - 1) {
const value = values[0];
if (parsers.hasVarFunc(value)) {
return "";
}
if (value && value !== initialValues.get(longhand)) {
const bgValue = bgValues[i];
bgValue.push(value);
}
}
break;
}
case backgroundSize.property: {
const value = values[i];
if (parsers.hasVarFunc(value)) {
return "";
}
if (value && value !== initialValues.get(longhand)) {
const bgValue = bgValues[i];
bgValue.push(`/ ${value}`);
}
break;
}
default: {
const value = values[i];
if (parsers.hasVarFunc(value)) {
return "";
}
if (value && value !== initialValues.get(longhand)) {
const bgValue = bgValues[i];
bgValue.push(value);
}
}
}
}
}
const backgrounds = [];
for (const bgValue of bgValues) {
backgrounds.push(bgValue.join(" "));
}
return backgrounds.join(", ");
},
enumerable: true,
configurable: true
};
/**
* Parses the background property value.
*
* @param {string} v - The value to parse.
* @returns {Array<object>|undefined} The parsed background values or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
} else if (parsers.hasCalcFunc(v)) {
v = parsers.resolveCalc(v);
}
if (!parsers.isValidPropertyValue(property, v)) {
return;
}
const values = parsers.splitValue(v, {
delimiter: ","
});
const bgValues = [];
const l = values.length;
for (let i = 0; i < l; i++) {
let bg = {
[backgroundImage.property]: initialValues.get(backgroundImage.property),
[backgroundPosition.property]: initialValues.get(backgroundPosition.property),
[backgroundSize.property]: initialValues.get(backgroundSize.property),
[backgroundRepeat.property]: initialValues.get(backgroundRepeat.property),
[backgroundOrigin.property]: initialValues.get(backgroundOrigin.property),
[backgroundClip.property]: initialValues.get(backgroundClip.property),
[backgroundAttachment.property]: initialValues.get(backgroundAttachment.property),
[backgroundColor.property]: initialValues.get(backgroundColor.property)
};
if (l > 1 && i !== l - 1) {
bg = {
[backgroundImage.property]: initialValues.get(backgroundImage.property),
[backgroundPosition.property]: initialValues.get(backgroundPosition.property),
[backgroundSize.property]: initialValues.get(backgroundSize.property),
[backgroundRepeat.property]: initialValues.get(backgroundRepeat.property),
[backgroundOrigin.property]: initialValues.get(backgroundOrigin.property),
[backgroundClip.property]: initialValues.get(backgroundClip.property),
[backgroundAttachment.property]: initialValues.get(backgroundAttachment.property)
};
}
const bgPosition = [];
const bgSize = [];
const bgRepeat = [];
const bgBox = [];
const bgParts = parsers.splitValue(values[i], {
delimiter: "/"
});
if (!bgParts.length || bgParts.length > 2) {
return;
}
const [bgPart1, bgPart2 = ""] = bgParts;
const parts1 = parsers.splitValue(bgPart1);
for (const part of parts1) {
let partValid = false;
for (const [longhand, value] of shorthandFor) {
if (parsers.isValidPropertyValue(longhand, part)) {
partValid = true;
switch (longhand) {
case backgroundClip.property:
case backgroundOrigin.property: {
const parsedValue = value.parse(part);
if (parsedValue) {
bgBox.push(parsedValue);
}
break;
}
case backgroundColor.property: {
if (i !== values.length - 1) {
return;
}
const parsedValue = value.parse(part);
if (parsedValue) {
bg[longhand] = parsedValue;
}
break;
}
case backgroundPosition.property: {
const parsedValue = value.parse(part);
if (parsedValue) {
bgPosition.push(parsedValue);
}
break;
}
case backgroundRepeat.property: {
const parsedValue = value.parse(part);
if (parsedValue) {
bgRepeat.push(parsedValue);
}
break;
}
case backgroundSize.property: {
break;
}
default: {
const parsedValue = value.parse(part);
if (parsedValue) {
bg[longhand] = parsedValue;
}
}
}
}
}
if (!partValid) {
return;
}
}
if (bgPart2) {
const parts2 = parsers.splitValue(bgPart2);
for (const part of parts2) {
let partValid = false;
for (const [longhand, value] of shorthandFor) {
if (parsers.isValidPropertyValue(longhand, part)) {
partValid = true;
switch (longhand) {
case backgroundClip.property:
case backgroundOrigin.property: {
const parsedValue = value.parse(part);
if (parsedValue) {
bgBox.push(parsedValue);
}
break;
}
case backgroundColor.property: {
if (i !== l - 1) {
return;
}
const parsedValue = value.parse(part);
if (parsedValue) {
bg[longhand] = parsedValue;
}
break;
}
case backgroundPosition.property: {
break;
}
case backgroundRepeat.property: {
const parsedValue = value.parse(part);
if (parsedValue) {
bgRepeat.push(parsedValue);
}
break;
}
case backgroundSize.property: {
const parsedValue = value.parse(part);
if (parsedValue) {
bgSize.push(parsedValue);
}
break;
}
default: {
const parsedValue = value.parse(part);
if (parsedValue) {
bg[longhand] = parsedValue;
}
}
}
}
}
if (!partValid) {
return;
}
}
}
if (bgPosition.length) {
const { parse: parser } = shorthandFor.get(backgroundPosition.property);
const value = parser(bgPosition.join(" "));
if (value) {
bg[backgroundPosition.property] = value;
}
}
if (bgSize.length) {
const { parse: parser } = shorthandFor.get(backgroundSize.property);
const value = parser(bgSize.join(" "));
if (value) {
bg[backgroundSize.property] = value;
}
}
if (bgRepeat.length) {
const { parse: parser } = shorthandFor.get(backgroundRepeat.property);
const value = parser(bgRepeat.join(" "));
if (value) {
bg[backgroundRepeat.property] = value;
}
}
if (bgBox.length) {
switch (bgBox.length) {
case 1: {
const [value] = bgBox;
bg[backgroundOrigin.property] = value;
bg[backgroundClip.property] = value;
break;
}
case 2: {
const [value1, value2] = bgBox;
bg[backgroundOrigin.property] = value1;
bg[backgroundClip.property] = value2;
break;
}
default: {
return;
}
}
}
bgValues.push(bg);
}
return bgValues;
}
module.exports = {
descriptor,
initialValues,
parse,
property,
shorthandFor
};
+63
View File
@@ -0,0 +1,63 @@
"use strict";
const parsers = require("../parsers");
const property = "background-attachment";
const shorthand = "background";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-attachment property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v, { delimiter: "," });
const parsedValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveKeywordValue(value);
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
} else if (typeof value === "string") {
parsedValues.push(value);
}
}
if (parsedValues.length) {
return parsedValues.join(", ");
}
}
module.exports = {
descriptor,
parse,
property
};
+63
View File
@@ -0,0 +1,63 @@
"use strict";
const parsers = require("../parsers");
const property = "background-clip";
const shorthand = "background";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-clip property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v, { delimiter: "," });
const parsedValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveKeywordValue(value);
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
} else if (typeof value === "string") {
parsedValues.push(value);
}
}
if (parsedValues.length) {
return parsedValues.join(", ");
}
}
module.exports = {
descriptor,
parse,
property
};
+52
View File
@@ -0,0 +1,52 @@
"use strict";
const parsers = require("../parsers");
const property = "background-color";
const shorthand = "background";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+65
View File
@@ -0,0 +1,65 @@
"use strict";
const parsers = require("../parsers");
const property = "background-image";
const shorthand = "background";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-image property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v, { delimiter: "," });
const parsedValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveImageValue(value);
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
} else if (typeof value === "string") {
parsedValues.push(value);
} else {
return;
}
}
if (parsedValues.length) {
return parsedValues.join(", ");
}
}
module.exports = {
descriptor,
parse,
property
};
+63
View File
@@ -0,0 +1,63 @@
"use strict";
const parsers = require("../parsers");
const property = "background-origin";
const shorthand = "background";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-origin property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v, { delimiter: "," });
const parsedValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveKeywordValue(value);
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
} else if (typeof value === "string") {
parsedValues.push(value);
}
}
if (parsedValues.length) {
return parsedValues.join(", ");
}
}
module.exports = {
descriptor,
parse,
property
};
+204
View File
@@ -0,0 +1,204 @@
"use strict";
const parsers = require("../parsers");
// Constants
const { AST_TYPES } = parsers;
const property = "background-position";
const shorthand = "background";
const keyX = ["left", "right"];
const keyY = ["top", "bottom"];
const keywordsX = ["center", ...keyX];
const keywordsY = ["center", ...keyY];
const keywords = ["center", ...keyX, ...keyY];
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-position property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v, {
delimiter: ","
});
const parsedValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length) {
const [part1, part2, part3, part4] = value;
let parsedValue = "";
switch (value.length) {
case 1: {
if (part1.type === AST_TYPES.GLOBAL_KEYWORD) {
parsedValue = part1.name;
} else {
const val1 =
part1.type === AST_TYPES.IDENTIFIER
? part1.name
: parsers.resolveNumericValue([part1], { type: "length" });
if (val1) {
if (val1 === "center") {
parsedValue = `${val1} ${val1}`;
} else if (val1 === "top" || val1 === "bottom") {
parsedValue = `center ${val1}`;
} else {
parsedValue = `${val1} center`;
}
}
}
break;
}
case 2: {
const val1 =
part1.type === AST_TYPES.IDENTIFIER ? part1.name : parsers.resolveNumericValue([part1], { type: "length" });
const val2 =
part2.type === AST_TYPES.IDENTIFIER ? part2.name : parsers.resolveNumericValue([part2], { type: "length" });
if (val1 && val2) {
if (keywordsX.includes(val1) && keywordsY.includes(val2)) {
parsedValue = `${val1} ${val2}`;
} else if (keywordsY.includes(val1) && keywordsX.includes(val2)) {
parsedValue = `${val2} ${val1}`;
} else if (keywordsX.includes(val1)) {
if (val2 === "center" || !keywordsX.includes(val2)) {
parsedValue = `${val1} ${val2}`;
}
} else if (keywordsY.includes(val2)) {
if (!keywordsY.includes(val1)) {
parsedValue = `${val1} ${val2}`;
}
} else if (!keywordsY.includes(val1) && !keywordsX.includes(val2)) {
parsedValue = `${val1} ${val2}`;
}
}
break;
}
case 3: {
const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name;
const val2 =
part2.type === AST_TYPES.IDENTIFIER ? part2.name : parsers.resolveNumericValue([part2], { type: "length" });
const val3 =
part3.type === AST_TYPES.IDENTIFIER ? part3.name : parsers.resolveNumericValue([part3], { type: "length" });
if (val1 && val2 && val3) {
let posX = "";
let offX = "";
let posY = "";
let offY = "";
if (keywordsX.includes(val1)) {
if (keyY.includes(val2)) {
if (!keywords.includes(val3)) {
posX = val1;
posY = val2;
offY = val3;
}
} else if (keyY.includes(val3)) {
if (!keywords.includes(val2)) {
posX = val1;
offX = val2;
posY = val3;
}
}
} else if (keywordsY.includes(val1)) {
if (keyX.includes(val2)) {
if (!keywords.includes(val3)) {
posX = val2;
offX = val3;
posY = val1;
}
} else if (keyX.includes(val3)) {
if (!keywords.includes(val2)) {
posX = val3;
posY = val1;
offY = val2;
}
}
}
if (posX && posY) {
if (offX) {
parsedValue = `${posX} ${offX} ${posY}`;
} else if (offY) {
parsedValue = `${posX} ${posY} ${offY}`;
}
}
}
break;
}
case 4: {
const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name;
const val2 = parsers.resolveNumericValue([part2], { type: "length" });
const val3 = part3.type === AST_TYPES.IDENTIFIER && part3.name;
const val4 = parsers.resolveNumericValue([part4], { type: "length" });
if (val1 && val2 && val3 && val4) {
let posX = "";
let offX = "";
let posY = "";
let offY = "";
if (keywordsX.includes(val1) && keyY.includes(val3)) {
posX = val1;
offX = val2;
posY = val3;
offY = val4;
} else if (keyX.includes(val1) && keywordsY.includes(val3)) {
posX = val1;
offX = val2;
posY = val3;
offY = val4;
} else if (keyY.includes(val1) && keywordsX.includes(val3)) {
posX = val3;
offX = val4;
posY = val1;
offY = val2;
}
if (posX && offX && posY && offY) {
parsedValue = `${posX} ${offX} ${posY} ${offY}`;
}
}
break;
}
default:
}
if (parsedValue) {
parsedValues.push(parsedValue);
} else {
return;
}
} else if (typeof value === "string") {
parsedValues.push(value);
}
}
if (parsedValues.length) {
return parsedValues.join(", ");
}
}
module.exports = {
descriptor,
parse,
property
};
+96
View File
@@ -0,0 +1,96 @@
"use strict";
const parsers = require("../parsers");
// Constants
const { AST_TYPES } = parsers;
const property = "background-repeat";
const shorthand = "background";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-repeat property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v, {
delimiter: ","
});
const parsedValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length) {
let parsedValue = "";
switch (value.length) {
case 1: {
const [part1] = value;
if (part1.type === AST_TYPES.GLOBAL_KEYWORD || part1.type === AST_TYPES.IDENTIFIER) {
parsedValue = part1.name;
}
break;
}
case 2: {
const [part1, part2] = value;
const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name;
const val2 = part2.type === AST_TYPES.IDENTIFIER && part2.name;
if (val1 && val2) {
if (val1 === "repeat" && val2 === "no-repeat") {
parsedValue = "repeat-x";
} else if (val1 === "no-repeat" && val2 === "repeat") {
parsedValue = "repeat-y";
} else if (val1 === val2) {
parsedValue = val1;
} else {
parsedValue = `${val1} ${val2}`;
}
}
break;
}
default:
}
if (parsedValue) {
parsedValues.push(parsedValue);
} else {
return;
}
} else if (typeof value === "string") {
parsedValues.push(value);
}
}
if (parsedValues.length) {
return parsedValues.join(", ");
}
}
module.exports = {
descriptor,
parse,
property
};
+130
View File
@@ -0,0 +1,130 @@
"use strict";
const parsers = require("../parsers");
// Constants
const { AST_TYPES } = parsers;
const property = "background-size";
const shorthand = "background";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-size property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v, {
delimiter: ","
});
const parsedValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length) {
if (value.length === 1) {
const [{ isNumber, name, type, value: itemValue }] = value;
switch (type) {
case AST_TYPES.CALC: {
if (isNumber) {
return;
}
parsedValues.push(`${name}(${itemValue})`);
break;
}
case AST_TYPES.GLOBAL_KEYWORD:
case AST_TYPES.IDENTIFIER: {
parsedValues.push(name);
break;
}
default: {
const parsedValue = parsers.resolveNumericValue(value, {
type: "length"
});
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
}
}
} else {
const [val1, val2] = value;
const parts = [];
if (val1.type === AST_TYPES.CALC && !val1.isNumber) {
parts.push(`${val1.name}(${val1.value})`);
} else if (val1.type === AST_TYPES.IDENTIFIER) {
parts.push(val1.name);
} else if (val1.type === AST_TYPES.DIMENSION) {
parts.push(`${val1.value}${val1.unit}`);
} else if (val1.type === AST_TYPES.PERCENTAGE) {
parts.push(`${val1.value}%`);
} else {
return;
}
switch (val2.type) {
case AST_TYPES.CALC: {
if (val2.isNumber) {
return;
}
parts.push(`${val2.name}(${val2.value})`);
break;
}
case AST_TYPES.DIMENSION: {
parts.push(`${val2.value}${val2.unit}`);
break;
}
case AST_TYPES.IDENTIFIER: {
if (val2.name !== "auto") {
parts.push(val2.name);
}
break;
}
case AST_TYPES.PERCENTAGE: {
parts.push(`${val2.value}%`);
break;
}
default: {
return;
}
}
parsedValues.push(parts.join(" "));
}
} else if (typeof value === "string") {
parsedValues.push(value);
}
}
if (parsedValues.length) {
return parsedValues.join(", ");
}
}
module.exports = {
descriptor,
parse,
property
};
+114
View File
@@ -0,0 +1,114 @@
"use strict";
const parsers = require("../parsers");
const borderWidth = require("./borderWidth");
const borderStyle = require("./borderStyle");
const borderColor = require("./borderColor");
const borderTop = require("./borderTop");
const borderRight = require("./borderRight");
const borderBottom = require("./borderBottom");
const borderLeft = require("./borderLeft");
const property = "border";
const subProps = {
width: borderWidth.property,
style: borderStyle.property,
color: borderColor.property
};
const initialValues = new Map([
[borderWidth.property, "medium"],
[borderStyle.property, "none"],
[borderColor.property, "currentcolor"]
]);
const shorthandFor = new Map([
[borderWidth.property, borderWidth],
[borderStyle.property, borderStyle],
[borderColor.property, borderColor]
]);
const positionShorthandFor = new Map([
[borderTop.property, borderTop],
[borderRight.property, borderRight],
[borderBottom.property, borderBottom],
[borderLeft.property, borderLeft]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (val || typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border property value.
*
* @param {string} v - The value to parse.
* @returns {object|string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "" || parsers.hasVarFunc(v)) {
return v;
}
const values = parsers.splitValue(v);
const parsedValues = new Map();
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveBorderShorthandValue(value, subProps, parsedValues);
if (typeof parsedValue === "string") {
return parsedValue;
} else if (Array.isArray(parsedValue)) {
const [key, resolvedVal] = parsedValue;
parsedValues.set(key, resolvedVal);
} else {
return;
}
} else {
return;
}
}
if (parsedValues.size) {
const keys = shorthandFor.keys();
const obj = {
[borderWidth.property]: "medium"
};
for (const key of keys) {
if (parsedValues.has(key)) {
const parsedValue = parsedValues.get(key);
if (parsedValue !== initialValues.get(key)) {
obj[key] = parsedValues.get(key);
if (obj[borderWidth.property] && obj[borderWidth.property] === "medium") {
delete obj[borderWidth.property];
}
}
}
}
return obj;
}
}
module.exports = {
descriptor,
initialValues,
parse,
positionShorthandFor,
property,
shorthandFor
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "border-block-end-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-block-end-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "border-block-start-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-block-start-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+104
View File
@@ -0,0 +1,104 @@
"use strict";
const parsers = require("../parsers");
const borderBottomWidth = require("./borderBottomWidth");
const borderBottomStyle = require("./borderBottomStyle");
const borderBottomColor = require("./borderBottomColor");
const property = "border-bottom";
const shorthand = "border";
const subProps = {
width: borderBottomWidth.property,
style: borderBottomStyle.property,
color: borderBottomColor.property
};
const initialValues = new Map([
[borderBottomWidth.property, "medium"],
[borderBottomStyle.property, "none"],
[borderBottomColor.property, "currentcolor"]
]);
const shorthandFor = new Map([
[borderBottomWidth.property, borderBottomWidth],
[borderBottomStyle.property, borderBottomStyle],
[borderBottomColor.property, borderBottomColor]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (val || typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-bottom property value.
*
* @param {string} v - The value to parse.
* @returns {object|string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v);
const parsedValues = new Map();
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveBorderShorthandValue(value, subProps, parsedValues);
if (typeof parsedValue === "string") {
return parsedValue;
} else if (Array.isArray(parsedValue)) {
const [key, resolvedVal] = parsedValue;
parsedValues.set(key, resolvedVal);
} else {
return;
}
} else {
return;
}
}
if (parsedValues.size) {
const keys = shorthandFor.keys();
const obj = {
[borderBottomWidth.property]: "medium"
};
for (const key of keys) {
if (parsedValues.has(key)) {
const parsedValue = parsedValues.get(key);
if (parsedValue !== initialValues.get(key)) {
obj[key] = parsedValues.get(key);
if (obj[borderBottomWidth.property] && obj[borderBottomWidth.property] === "medium") {
delete obj[borderBottomWidth.property];
}
}
}
}
return obj;
}
}
module.exports = {
descriptor,
initialValues,
parse,
property,
shorthandFor
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "border-bottom-color";
const lineShorthand = "border-color";
const positionShorthand = "border-bottom";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-bottom-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "border-bottom-style";
const lineShorthand = "border-style";
const positionShorthand = "border-bottom";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-bottom-style property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveKeywordValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+61
View File
@@ -0,0 +1,61 @@
"use strict";
const parsers = require("../parsers");
const property = "border-bottom-width";
const lineShorthand = "border-width";
const positionShorthand = "border-bottom";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-bottom-width property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "border-collapse";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-collapse property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveKeywordValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+113
View File
@@ -0,0 +1,113 @@
"use strict";
const parsers = require("../parsers");
const borderTopColor = require("./borderTopColor");
const borderRightColor = require("./borderRightColor");
const borderBottomColor = require("./borderBottomColor");
const borderLeftColor = require("./borderLeftColor");
const property = "border-color";
const shorthand = "border";
const shorthandFor = new Map([
[borderTopColor.property, borderTopColor],
[borderRightColor.property, borderRightColor],
[borderBottomColor.property, borderBottomColor],
[borderLeftColor.property, borderLeftColor]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (Array.isArray(val) || typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-color property value.
*
* @param {string} v - The value to parse.
* @returns {Array<string>|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.parsePropertyValue(property, v);
const parsedValues = [];
if (Array.isArray(values) && values.length) {
if (values.length > 4) {
return;
}
for (const value of values) {
const parsedValue = parsers.resolveColorValue([value], {
length: values.length
});
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
}
} else if (typeof values === "string") {
parsedValues.push(values);
}
if (parsedValues.length) {
switch (parsedValues.length) {
case 1: {
return parsedValues;
}
case 2: {
const [val1, val2] = parsedValues;
if (val1 === val2) {
return [val1];
}
return parsedValues;
}
case 3: {
const [val1, val2, val3] = parsedValues;
if (val1 === val3) {
if (val1 === val2) {
return [val1];
}
return [val1, val2];
}
return parsedValues;
}
case 4: {
const [val1, val2, val3, val4] = parsedValues;
if (val2 === val4) {
if (val1 === val3) {
if (val1 === val2) {
return [val1];
}
return [val1, val2];
}
return [val1, val2, val3];
}
return parsedValues;
}
default:
}
}
}
module.exports = {
descriptor,
parse,
property,
shorthandFor
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "border-inline-end-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-inline-end-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "border-inline-start-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-inline-start-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+104
View File
@@ -0,0 +1,104 @@
"use strict";
const parsers = require("../parsers");
const borderLeftWidth = require("./borderLeftWidth");
const borderLeftStyle = require("./borderLeftStyle");
const borderLeftColor = require("./borderLeftColor");
const property = "border-left";
const shorthand = "border";
const subProps = {
width: borderLeftWidth.property,
style: borderLeftStyle.property,
color: borderLeftColor.property
};
const initialValues = new Map([
[borderLeftWidth.property, "medium"],
[borderLeftStyle.property, "none"],
[borderLeftColor.property, "currentcolor"]
]);
const shorthandFor = new Map([
[borderLeftWidth.property, borderLeftWidth],
[borderLeftStyle.property, borderLeftStyle],
[borderLeftColor.property, borderLeftColor]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (val || typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-left property value.
*
* @param {string} v - The value to parse.
* @returns {object|string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v);
const parsedValues = new Map();
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveBorderShorthandValue(value, subProps, parsedValues);
if (typeof parsedValue === "string") {
return parsedValue;
} else if (Array.isArray(parsedValue)) {
const [key, resolvedVal] = parsedValue;
parsedValues.set(key, resolvedVal);
} else {
return;
}
} else {
return;
}
}
if (parsedValues.size) {
const keys = shorthandFor.keys();
const obj = {
[borderLeftWidth.property]: "medium"
};
for (const key of keys) {
if (parsedValues.has(key)) {
const parsedValue = parsedValues.get(key);
if (parsedValue !== initialValues.get(key)) {
obj[key] = parsedValues.get(key);
if (obj[borderLeftWidth.property] && obj[borderLeftWidth.property] === "medium") {
delete obj[borderLeftWidth.property];
}
}
}
}
return obj;
}
}
module.exports = {
descriptor,
initialValues,
parse,
property,
shorthandFor
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "border-left-color";
const lineShorthand = "border-color";
const positionShorthand = "border-left";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-left-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "border-left-style";
const lineShorthand = "border-style";
const positionShorthand = "border-left";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-left-style property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveKeywordValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+61
View File
@@ -0,0 +1,61 @@
"use strict";
const parsers = require("../parsers");
const property = "border-left-width";
const lineShorthand = "border-width";
const positionShorthand = "border-left";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-left-width property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+104
View File
@@ -0,0 +1,104 @@
"use strict";
const parsers = require("../parsers");
const borderRightWidth = require("./borderRightWidth");
const borderRightStyle = require("./borderRightStyle");
const borderRightColor = require("./borderRightColor");
const property = "border-right";
const shorthand = "border";
const subProps = {
width: borderRightWidth.property,
style: borderRightStyle.property,
color: borderRightColor.property
};
const initialValues = new Map([
[borderRightWidth.property, "medium"],
[borderRightStyle.property, "none"],
[borderRightColor.property, "currentcolor"]
]);
const shorthandFor = new Map([
[borderRightWidth.property, borderRightWidth],
[borderRightStyle.property, borderRightStyle],
[borderRightColor.property, borderRightColor]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (val || typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-right property value.
*
* @param {string} v - The value to parse.
* @returns {object|string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v);
const parsedValues = new Map();
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveBorderShorthandValue(value, subProps, parsedValues);
if (typeof parsedValue === "string") {
return parsedValue;
} else if (Array.isArray(parsedValue)) {
const [key, resolvedVal] = parsedValue;
parsedValues.set(key, resolvedVal);
} else {
return;
}
} else {
return;
}
}
if (parsedValues.size) {
const keys = shorthandFor.keys();
const obj = {
[borderRightWidth.property]: "medium"
};
for (const key of keys) {
if (parsedValues.has(key)) {
const parsedValue = parsedValues.get(key);
if (parsedValue !== initialValues.get(key)) {
obj[key] = parsedValues.get(key);
if (obj[borderRightWidth.property] && obj[borderRightWidth.property] === "medium") {
delete obj[borderRightWidth.property];
}
}
}
}
return obj;
}
}
module.exports = {
descriptor,
initialValues,
parse,
property,
shorthandFor
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "border-right-color";
const lineShorthand = "border-color";
const positionShorthand = "border-right";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-right-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "border-right-style";
const lineShorthand = "border-style";
const positionShorthand = "border-right";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-right-style property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveKeywordValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+61
View File
@@ -0,0 +1,61 @@
"use strict";
const parsers = require("../parsers");
const property = "border-right-width";
const lineShorthand = "border-width";
const positionShorthand = "border-right";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-right-width property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+69
View File
@@ -0,0 +1,69 @@
"use strict";
const parsers = require("../parsers");
const property = "border-spacing";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-spacing property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length) {
switch (value.length) {
case 1: {
return parsers.resolveNumericValue(value, {
type: "length"
});
}
case 2: {
const [part1, part2] = value;
const val1 = parsers.resolveNumericValue([part1], {
type: "length"
});
const val2 = parsers.resolveNumericValue([part2], {
type: "length"
});
if (val1 && val2) {
return `${val1} ${val2}`;
}
break;
}
default:
}
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+113
View File
@@ -0,0 +1,113 @@
"use strict";
const parsers = require("../parsers");
const borderTopStyle = require("./borderTopStyle");
const borderRightStyle = require("./borderRightStyle");
const borderBottomStyle = require("./borderBottomStyle");
const borderLeftStyle = require("./borderLeftStyle");
const property = "border-style";
const shorthand = "border";
const shorthandFor = new Map([
[borderTopStyle.property, borderTopStyle],
[borderRightStyle.property, borderRightStyle],
[borderBottomStyle.property, borderBottomStyle],
[borderLeftStyle.property, borderLeftStyle]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (Array.isArray(val) || typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-style property value.
*
* @param {string} v - The value to parse.
* @returns {Array<string>|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.parsePropertyValue(property, v);
const parsedValues = [];
if (Array.isArray(values) && values.length) {
if (values.length > 4) {
return;
}
for (const value of values) {
const parsedValue = parsers.resolveKeywordValue([value], {
length: values.length
});
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
}
} else if (typeof values === "string") {
parsedValues.push(values);
}
if (parsedValues.length) {
switch (parsedValues.length) {
case 1: {
return parsedValues;
}
case 2: {
const [val1, val2] = parsedValues;
if (val1 === val2) {
return [val1];
}
return parsedValues;
}
case 3: {
const [val1, val2, val3] = parsedValues;
if (val1 === val3) {
if (val1 === val2) {
return [val1];
}
return [val1, val2];
}
return parsedValues;
}
case 4: {
const [val1, val2, val3, val4] = parsedValues;
if (val2 === val4) {
if (val1 === val3) {
if (val1 === val2) {
return [val1];
}
return [val1, val2];
}
return [val1, val2, val3];
}
return parsedValues;
}
default:
}
}
}
module.exports = {
descriptor,
parse,
property,
shorthandFor
};
+104
View File
@@ -0,0 +1,104 @@
"use strict";
const parsers = require("../parsers");
const borderTopWidth = require("./borderTopWidth");
const borderTopStyle = require("./borderTopStyle");
const borderTopColor = require("./borderTopColor");
const property = "border-top";
const shorthand = "border";
const subProps = {
width: borderTopWidth.property,
style: borderTopStyle.property,
color: borderTopColor.property
};
const initialValues = new Map([
[borderTopWidth.property, "medium"],
[borderTopStyle.property, "none"],
[borderTopColor.property, "currentcolor"]
]);
const shorthandFor = new Map([
[borderTopWidth.property, borderTopWidth],
[borderTopStyle.property, borderTopStyle],
[borderTopColor.property, borderTopColor]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (val || typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-top property value.
*
* @param {string} v - The value to parse.
* @returns {object|string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v);
const parsedValues = new Map();
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveBorderShorthandValue(value, subProps, parsedValues);
if (typeof parsedValue === "string") {
return parsedValue;
} else if (Array.isArray(parsedValue)) {
const [key, resolvedVal] = parsedValue;
parsedValues.set(key, resolvedVal);
} else {
return;
}
} else {
return;
}
}
if (parsedValues.size) {
const keys = shorthandFor.keys();
const obj = {
[borderTopWidth.property]: "medium"
};
for (const key of keys) {
if (parsedValues.has(key)) {
const parsedValue = parsedValues.get(key);
if (parsedValue !== initialValues.get(key)) {
obj[key] = parsedValues.get(key);
if (obj[borderTopWidth.property] && obj[borderTopWidth.property] === "medium") {
delete obj[borderTopWidth.property];
}
}
}
}
return obj;
}
}
module.exports = {
descriptor,
initialValues,
parse,
property,
shorthandFor
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "border-top-color";
const lineShorthand = "border-color";
const positionShorthand = "border-top";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-top-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "border-top-style";
const lineShorthand = "border-style";
const positionShorthand = "border-top";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-top-style property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveKeywordValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+61
View File
@@ -0,0 +1,61 @@
"use strict";
const parsers = require("../parsers");
const property = "border-top-width";
const lineShorthand = "border-width";
const positionShorthand = "border-top";
const shorthand = "border";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (typeof val === "string") {
const shorthandPriority = this._priorities.get(shorthand);
const linePriority = this._priorities.get(lineShorthand);
const positionPriority = this._priorities.get(positionShorthand);
const priority =
!(shorthandPriority || linePriority || positionPriority) && this._priorities.has(property)
? this._priorities.get(property)
: "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-top-width property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+114
View File
@@ -0,0 +1,114 @@
"use strict";
const parsers = require("../parsers");
const borderTopWidth = require("./borderTopWidth");
const borderRightWidth = require("./borderRightWidth");
const borderBottomWidth = require("./borderBottomWidth");
const borderLeftWidth = require("./borderLeftWidth");
const property = "border-width";
const shorthand = "border";
const shorthandFor = new Map([
[borderTopWidth.property, borderTopWidth],
[borderRightWidth.property, borderRightWidth],
[borderBottomWidth.property, borderBottomWidth],
[borderLeftWidth.property, borderLeftWidth]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._borderSetter(property, v, "");
} else {
const val = parse(v);
if (Array.isArray(val) || typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._borderSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the border-width property value.
*
* @param {string} v - The value to parse.
* @returns {Array<string>|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.parsePropertyValue(property, v);
const parsedValues = [];
if (Array.isArray(values) && values.length) {
if (values.length > 4) {
return;
}
for (const value of values) {
const parsedValue = parsers.resolveNumericValue([value], {
length: values.length,
type: "length"
});
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
}
} else if (typeof values === "string") {
parsedValues.push(values);
}
if (parsedValues.length) {
switch (parsedValues.length) {
case 1: {
return parsedValues;
}
case 2: {
const [val1, val2] = parsedValues;
if (val1 === val2) {
return [val1];
}
return parsedValues;
}
case 3: {
const [val1, val2, val3] = parsedValues;
if (val1 === val3) {
if (val1 === val2) {
return [val1];
}
return [val1, val2];
}
return parsedValues;
}
case 4: {
const [val1, val2, val3, val4] = parsedValues;
if (val2 === val4) {
if (val1 === val3) {
if (val1 === val2) {
return [val1];
}
return [val1, val2];
}
return [val1, val2, val3];
}
return parsedValues;
}
default:
}
}
}
module.exports = {
descriptor,
parse,
property,
shorthandFor
};
+51
View File
@@ -0,0 +1,51 @@
"use strict";
const parsers = require("../parsers");
const property = "bottom";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the bottom property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "clear";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the clear property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveKeywordValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+79
View File
@@ -0,0 +1,79 @@
"use strict";
// deprecated
// @see https://drafts.csswg.org/css-masking-1/#clip-property
const parsers = require("../parsers");
// Constants
const { AST_TYPES } = parsers;
const property = "clip";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the clip property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
const [{ name, type, value: itemValue }] = value;
switch (type) {
case AST_TYPES.FUNCTION: {
const values = parsers.splitValue(itemValue, {
delimiter: ","
});
const parsedValues = [];
for (const item of values) {
const parsedValue = parsers.parseCSS(item, { context: "value" });
const val = parsers.resolveNumericValue(parsedValue.children, {
type: "length"
});
if (val) {
parsedValues.push(val);
} else {
return;
}
}
return `${name}(${parsedValues.join(", ")})`;
}
case AST_TYPES.GLOBAL_KEYWORD:
case AST_TYPES.IDENTIFIER: {
return name;
}
default:
}
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+216
View File
@@ -0,0 +1,216 @@
"use strict";
const parsers = require("../parsers");
// Constants
const { AST_TYPES } = parsers;
const property = "display";
/* keywords */
const displayOutside = ["block", "inline", "run-in"];
const displayFlow = ["flow", "flow-root"];
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the display property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length) {
switch (value.length) {
case 1: {
const [{ name, type }] = value;
switch (type) {
case AST_TYPES.GLOBAL_KEYWORD: {
return name;
}
case AST_TYPES.IDENTIFIER: {
if (name === "flow") {
return "block";
}
return name;
}
default:
}
break;
}
case 2: {
const [part1, part2] = value;
const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name;
const val2 = part2.type === AST_TYPES.IDENTIFIER && part2.name;
if (val1 && val2) {
let outerValue = "";
let innerValue = "";
if (val1 === "list-item") {
outerValue = val2;
innerValue = val1;
} else if (val2 === "list-item") {
outerValue = val1;
innerValue = val2;
} else if (displayOutside.includes(val1)) {
outerValue = val1;
innerValue = val2;
} else if (displayOutside.includes(val2)) {
outerValue = val2;
innerValue = val1;
}
if (innerValue === "list-item") {
switch (outerValue) {
case "block":
case "flow": {
return innerValue;
}
case "flow-root":
case "inline":
case "run-in": {
return `${outerValue} ${innerValue}`;
}
default:
}
} else if (outerValue === "block") {
switch (innerValue) {
case "flow": {
return outerValue;
}
case "flow-root":
case "flex":
case "grid":
case "table": {
return innerValue;
}
case "ruby": {
return `${outerValue} ${innerValue}`;
}
default:
}
} else if (outerValue === "inline") {
switch (innerValue) {
case "flow": {
return outerValue;
}
case "flow-root": {
return `${outerValue}-block`;
}
case "flex":
case "grid":
case "table": {
return `${outerValue}-${innerValue}`;
}
case "ruby": {
return innerValue;
}
default:
}
} else if (outerValue === "run-in") {
switch (innerValue) {
case "flow": {
return outerValue;
}
case "flow-root":
case "flex":
case "grid":
case "table":
case "ruby": {
return `${outerValue} ${innerValue}`;
}
default:
}
}
}
break;
}
case 3: {
const [part1, part2, part3] = value;
const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name;
const val2 = part2.type === AST_TYPES.IDENTIFIER && part2.name;
const val3 = part3.type === AST_TYPES.IDENTIFIER && part3.name;
if (val1 && val2 && part3) {
let outerValue = "";
let flowValue = "";
let listItemValue = "";
if (val1 === "list-item") {
listItemValue = val1;
if (displayFlow.includes(val2)) {
flowValue = val2;
outerValue = val3;
} else if (displayFlow.includes(val3)) {
flowValue = val3;
outerValue = val2;
}
} else if (val2 === "list-item") {
listItemValue = val2;
if (displayFlow.includes(val1)) {
flowValue = val1;
outerValue = val3;
} else if (displayFlow.includes(val3)) {
flowValue = val3;
outerValue = val1;
}
} else if (val3 === "list-item") {
listItemValue = val3;
if (displayFlow.includes(val1)) {
flowValue = val1;
outerValue = val2;
} else if (displayFlow.includes(val2)) {
flowValue = val2;
outerValue = val1;
}
}
if (outerValue && flowValue && listItemValue) {
switch (outerValue) {
case "block": {
if (flowValue === "flow") {
return listItemValue;
}
return `${flowValue} ${listItemValue}`;
}
case "inline":
case "run-in": {
if (flowValue === "flow") {
return `${outerValue} ${listItemValue}`;
}
return `${outerValue} ${flowValue} ${listItemValue}`;
}
}
}
}
break;
}
default:
}
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+186
View File
@@ -0,0 +1,186 @@
"use strict";
const parsers = require("../parsers");
const flexGrow = require("./flexGrow");
const flexShrink = require("./flexShrink");
const flexBasis = require("./flexBasis");
// Constants
const { AST_TYPES } = parsers;
const property = "flex";
const initialValues = new Map([
[flexGrow.property, "0"],
[flexShrink.property, "1"],
[flexBasis.property, "auto"]
]);
const shorthandFor = new Map([
[flexGrow.property, flexGrow],
[flexShrink.property, flexShrink],
[flexBasis.property, flexBasis]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
for (const [longhand] of shorthandFor) {
this._setProperty(longhand, "");
}
this._setProperty(property, v);
} else {
const val = parse(v);
const priority = this._priorities.get(property) ?? "";
if (typeof val === "string") {
for (const [longhand] of shorthandFor) {
this._setProperty(longhand, val, priority);
}
this._setProperty(property, val, priority);
} else if (val) {
const values = [];
for (const [longhand, value] of Object.entries(val)) {
values.push(value);
this._setProperty(longhand, value, priority);
}
this._setProperty(property, values.join(" "), priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the flex property value.
*
* @param {string} v - The value to parse.
* @returns {object|string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length) {
const flex = {
[flexGrow.property]: "1",
[flexShrink.property]: "1",
[flexBasis.property]: "0%"
};
if (value.length === 1) {
const [{ isNumber, name, type, unit, value: itemValue }] = value;
switch (type) {
case AST_TYPES.CALC: {
if (isNumber) {
flex[flexGrow.property] = `${name}(${itemValue})`;
return flex;
}
flex[flexBasis.property] = `${name}(${itemValue})`;
return flex;
}
case AST_TYPES.DIMENSION: {
flex[flexBasis.property] = `${itemValue}${unit}`;
return flex;
}
case AST_TYPES.GLOBAL_KEYWORD: {
return name;
}
case AST_TYPES.IDENTIFIER: {
if (name === "none") {
return {
[flexGrow.property]: "0",
[flexShrink.property]: "0",
[flexBasis.property]: "auto"
};
}
flex[flexBasis.property] = name;
return flex;
}
case AST_TYPES.NUMBER: {
flex[flexGrow.property] = itemValue;
return flex;
}
case AST_TYPES.PERCENTAGE: {
flex[flexBasis.property] = `${itemValue}%`;
return flex;
}
default:
}
} else {
const [val1, val2, val3] = value;
if (val1.type === AST_TYPES.CALC && val1.isNumber) {
flex[flexGrow.property] = `${val1.name}(${val1.value})`;
} else if (val1.type === AST_TYPES.NUMBER) {
flex[flexGrow.property] = val1.value;
} else {
return;
}
if (val3) {
if (val2.type === AST_TYPES.CALC && val2.isNumber) {
flex[flexShrink.property] = `${val2.name}(${val2.value})`;
} else if (val2.type === AST_TYPES.NUMBER) {
flex[flexShrink.property] = val2.value;
} else {
return;
}
if (val3.type === AST_TYPES.GLOBAL_KEYWORD || val3.type === AST_TYPES.IDENTIFIER) {
flex[flexBasis.property] = val3.name;
} else if (val3.type === AST_TYPES.CALC && !val3.isNumber) {
flex[flexBasis.property] = `${val3.name}(${val3.value})`;
} else if (val3.type === AST_TYPES.DIMENSION) {
flex[flexBasis.property] = `${val3.value}${val3.unit}`;
} else if (val3.type === AST_TYPES.PERCENTAGE) {
flex[flexBasis.property] = `${val3.value}%`;
} else {
return;
}
} else {
switch (val2.type) {
case AST_TYPES.CALC: {
if (val2.isNumber) {
flex[flexShrink.property] = `${val2.name}(${val2.value})`;
} else {
flex[flexBasis.property] = `${val2.name}(${val2.value})`;
}
break;
}
case AST_TYPES.DIMENSION: {
flex[flexBasis.property] = `${val2.value}${val2.unit}`;
break;
}
case AST_TYPES.NUMBER: {
flex[flexShrink.property] = val2.value;
break;
}
case AST_TYPES.PERCENTAGE: {
flex[flexBasis.property] = `${val2.value}%`;
break;
}
case AST_TYPES.IDENTIFIER: {
flex[flexBasis.property] = val2.name;
break;
}
default: {
return;
}
}
}
return flex;
}
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
initialValues,
parse,
property,
shorthandFor
};
+54
View File
@@ -0,0 +1,54 @@
"use strict";
const parsers = require("../parsers");
const property = "flex-basis";
const shorthand = "flex";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._flexBoxSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the flex-basis property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+54
View File
@@ -0,0 +1,54 @@
"use strict";
const parsers = require("../parsers");
const property = "flex-grow";
const shorthand = "flex";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._flexBoxSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the flex-grow property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue("flex-grow", v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+54
View File
@@ -0,0 +1,54 @@
"use strict";
const parsers = require("../parsers");
const property = "flex-shrink";
const shorthand = "flex";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._flexBoxSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the flex-shrink property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "float";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the float property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveKeywordValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "flood-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the flood-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+273
View File
@@ -0,0 +1,273 @@
"use strict";
const parsers = require("../parsers");
const fontStyle = require("./fontStyle");
const fontVariant = require("./fontVariant");
const fontWeight = require("./fontWeight");
const fontSize = require("./fontSize");
const lineHeight = require("./lineHeight");
const fontFamily = require("./fontFamily");
// Constants
const { AST_TYPES } = parsers;
const property = "font";
const shorthandFor = new Map([
[fontStyle.property, fontStyle],
[fontVariant.property, fontVariant],
[fontWeight.property, fontWeight],
[fontSize.property, fontSize],
[lineHeight.property, lineHeight],
[fontFamily.property, fontFamily]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (v === "" || parsers.hasVarFunc(v)) {
for (const [key] of shorthandFor) {
this._setProperty(key, "");
}
this._setProperty(property, v);
} else {
const obj = parse(v);
if (!obj) {
return;
}
const priority = this._priorities.get(property) ?? "";
const str = new Set();
for (const [key] of shorthandFor) {
const val = obj[key];
if (typeof val === "string") {
this._setProperty(key, val, priority);
if (val && val !== "normal" && !str.has(val)) {
if (key === lineHeight.property) {
str.add(`/ ${val}`);
} else {
str.add(val);
}
}
}
}
this._setProperty(property, [...str].join(" "), priority);
}
},
get() {
const val = this.getPropertyValue(property);
if (parsers.hasVarFunc(val)) {
return val;
}
const str = new Set();
for (const [key] of shorthandFor) {
const v = this.getPropertyValue(key);
if (parsers.hasVarFunc(v)) {
return "";
}
if (v && v !== "normal" && !str.has(v)) {
if (key === lineHeight.property) {
str.add(`/ ${v}`);
} else {
str.add(`${v}`);
}
}
}
return [...str].join(" ");
},
enumerable: true,
configurable: true
};
/**
* Parses the font property value.
*
* @param {string} v - The value to parse.
* @returns {object|undefined} The parsed value object or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
} else if (parsers.hasCalcFunc(v)) {
v = parsers.resolveCalc(v);
}
if (!parsers.isValidPropertyValue(property, v)) {
return;
}
const [fontBlock, ...families] = parsers.splitValue(v, {
delimiter: ","
});
const [fontBlockA, fontBlockB] = parsers.splitValue(fontBlock, {
delimiter: "/"
});
const font = {
[fontStyle.property]: "normal",
[fontVariant.property]: "normal",
[fontWeight.property]: "normal"
};
const fontFamilies = new Set();
if (fontBlockB) {
const [lineB, ...familiesB] = fontBlockB.trim().split(" ");
if (!lineB || !familiesB.length) {
return;
}
const lineHeightB = lineHeight.parse(lineB);
if (typeof lineHeightB !== "string") {
return;
}
const familyB = fontFamily.parse(familiesB.join(" "));
if (typeof familyB === "string") {
fontFamilies.add(familyB);
} else {
return;
}
const parts = parsers.splitValue(fontBlockA.trim());
const properties = [fontStyle.property, fontVariant.property, fontWeight.property, fontSize.property];
for (const part of parts) {
if (part === "normal") {
continue;
} else {
for (const longhand of properties) {
switch (longhand) {
case fontSize.property: {
const parsedValue = fontSize.parse(part);
if (typeof parsedValue === "string") {
font[longhand] = parsedValue;
}
break;
}
case fontStyle.property:
case fontWeight.property: {
if (font[longhand] === "normal") {
const longhandItem = shorthandFor.get(longhand);
const parsedValue = longhandItem.parse(part);
if (typeof parsedValue === "string") {
font[longhand] = parsedValue;
}
}
break;
}
case fontVariant.property: {
if (font[longhand] === "normal") {
const parsedValue = fontVariant.parse(part);
if (typeof parsedValue === "string") {
if (parsedValue === "small-cap") {
font[longhand] = parsedValue;
} else if (parsedValue !== "normal") {
return;
}
}
}
break;
}
default:
}
}
}
}
if (Object.hasOwn(font, fontSize.property)) {
font[lineHeight.property] = lineHeightB;
} else {
return;
}
} else {
const revParts = parsers.splitValue(fontBlockA.trim()).toReversed();
if (revParts.length === 1) {
const [part] = revParts;
const value = parsers.parsePropertyValue(property, part);
if (Array.isArray(value) && value.length === 1) {
const [{ name, type }] = value;
if (type === AST_TYPES.GLOBAL_KEYWORD) {
return {
[fontStyle.property]: name,
[fontVariant.property]: name,
[fontWeight.property]: name,
[fontSize.property]: name,
[lineHeight.property]: name,
[fontFamily.property]: name
};
}
}
return;
}
const properties = [fontStyle.property, fontVariant.property, fontWeight.property, lineHeight.property];
for (const longhand of properties) {
font[longhand] = "normal";
}
const revFontFamily = [];
let fontSizeA;
for (const part of revParts) {
if (fontSizeA) {
if (/^normal$/i.test(part)) {
continue;
} else {
for (const longhand of properties) {
switch (longhand) {
case fontStyle.property:
case fontWeight.property:
case lineHeight.property: {
if (font[longhand] === "normal") {
const longhandItem = shorthandFor.get(longhand);
const parsedValue = longhandItem.parse(part);
if (typeof parsedValue === "string") {
font[longhand] = parsedValue;
}
}
break;
}
case fontVariant.property: {
if (font[longhand] === "normal") {
const parsedValue = fontVariant.parse(part);
if (typeof parsedValue === "string") {
if (parsedValue === "small-cap") {
font[longhand] = parsedValue;
} else if (parsedValue !== "normal") {
return;
}
}
}
break;
}
default:
}
}
}
} else {
const parsedFontSize = fontSize.parse(part);
if (typeof parsedFontSize === "string") {
fontSizeA = parsedFontSize;
} else {
const parsedFontFamily = fontFamily.parse(part);
if (typeof parsedFontFamily === "string") {
revFontFamily.push(parsedFontFamily);
} else {
return;
}
}
}
}
const family = fontFamily.parse(revFontFamily.toReversed().join(" "));
if (fontSizeA && family) {
font[fontSize.property] = fontSizeA;
fontFamilies.add(fontFamily.parse(family));
} else {
return;
}
}
for (const family of families) {
const parsedFontFamily = fontFamily.parse(family);
if (parsedFontFamily) {
fontFamilies.add(parsedFontFamily);
} else {
return;
}
}
font[fontFamily.property] = [...fontFamilies].join(", ");
return font;
}
module.exports = {
descriptor,
parse,
property,
shorthandFor
};
+106
View File
@@ -0,0 +1,106 @@
"use strict";
const parsers = require("../parsers");
// Constants
const { AST_TYPES } = parsers;
const property = "font-family";
const shorthand = "font";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the font-family property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v, {
delimiter: ","
});
const parsedValues = [];
for (const val of values) {
if (!val) {
return "";
}
const value = parsers.parsePropertyValue(property, val, {
caseSensitive: true
});
if (Array.isArray(value) && value.length) {
if (value.length === 1) {
const [{ name, type, value: itemValue }] = value;
switch (type) {
case AST_TYPES.FUNCTION: {
parsedValues.push(`${name}(${itemValue})`);
break;
}
case AST_TYPES.GLOBAL_KEYWORD:
case AST_TYPES.IDENTIFIER: {
if (name === "undefined") {
return;
}
parsedValues.push(name);
break;
}
case "String": {
const parsedValue = itemValue.replaceAll("\\", "").replaceAll('"', '\\"');
parsedValues.push(`"${parsedValue}"`);
break;
}
default: {
return;
}
}
} else {
const parts = [];
for (const item of value) {
const { name, type } = item;
if (type !== AST_TYPES.IDENTIFIER) {
return;
}
parts.push(name);
}
const parsedValue = parts.join(" ").replaceAll("\\", "").replaceAll('"', '\\"');
parsedValues.push(`"${parsedValue}"`);
}
} else if (typeof value === "string") {
parsedValues.push(value);
} else {
return;
}
}
if (parsedValues.length) {
return parsedValues.join(", ");
}
}
module.exports = {
descriptor,
parse,
property
};
+55
View File
@@ -0,0 +1,55 @@
"use strict";
const parsers = require("../parsers");
const property = "font-size";
const shorthand = "font";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the font-size property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+73
View File
@@ -0,0 +1,73 @@
"use strict";
const parsers = require("../parsers");
// Constants
const { AST_TYPES } = parsers;
const property = "font-style";
const shorthand = "font";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the font-style property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length) {
if (value.length === 1) {
const [{ name, type }] = value;
switch (type) {
case AST_TYPES.GLOBAL_KEYWORD:
case AST_TYPES.IDENTIFIER: {
return name;
}
default:
}
} else if (value.length === 2) {
const [part1, part2] = value;
const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name;
const val2 = parsers.resolveNumericValue([part2], {
type: "angle"
});
if (val1 && val1 === "oblique" && val2) {
return `${val1} ${val2}`;
}
}
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+68
View File
@@ -0,0 +1,68 @@
"use strict";
const parsers = require("../parsers");
const property = "font-variant";
const shorthand = "font";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the font-variant property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.splitValue(v);
const parsedValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveFunctionValue(value);
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
} else if (typeof value === "string") {
parsedValues.push(value);
}
}
if (parsedValues.length) {
if (parsedValues.length > 1) {
if (parsedValues.includes("normal") || parsedValues.includes("none")) {
return;
}
}
return parsedValues.join(" ");
}
}
module.exports = {
descriptor,
parse,
property
};
+59
View File
@@ -0,0 +1,59 @@
"use strict";
const parsers = require("../parsers");
const property = "font-weight";
const shorthand = "font";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the font-weight property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
const parsedValue = parsers.resolveNumericValue(value, {
min: 1,
max: 1000
});
if (!parsedValue) {
return;
}
return parsedValue;
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+52
View File
@@ -0,0 +1,52 @@
"use strict";
const parsers = require("../parsers");
const property = "height";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the height property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+51
View File
@@ -0,0 +1,51 @@
"use strict";
const parsers = require("../parsers");
const property = "left";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the left property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "lighting-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the lighting-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+54
View File
@@ -0,0 +1,54 @@
"use strict";
const parsers = require("../parsers");
const property = "line-height";
const shorthand = "font";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the line-height property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+83
View File
@@ -0,0 +1,83 @@
"use strict";
const parsers = require("../parsers");
const marginTop = require("./marginTop");
const marginRight = require("./marginRight");
const marginBottom = require("./marginBottom");
const marginLeft = require("./marginLeft");
const property = "margin";
const position = "edges";
const shorthandFor = new Map([
[marginTop.property, marginTop],
[marginRight.property, marginRight],
[marginBottom.property, marginBottom],
[marginLeft.property, marginLeft]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
for (const [longhand] of shorthandFor) {
this._setProperty(longhand, "");
}
this._setProperty(property, v);
} else {
const val = parse(v);
if (Array.isArray(val) || typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._positionShorthandSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the margin property value.
*
* @param {string} v - The value to parse.
* @returns {Array<string>|string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.parsePropertyValue(property, v);
const parsedValues = [];
if (Array.isArray(values) && values.length) {
if (values.length > 4) {
return;
}
for (const value of values) {
const parsedValue = parsers.resolveNumericValue([value], {
length: values.length,
type: "length"
});
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
}
} else if (typeof values === "string") {
parsedValues.push(values);
}
if (parsedValues.length) {
return parsedValues;
}
}
module.exports = {
descriptor,
parse,
position,
property,
shorthandFor
};
+57
View File
@@ -0,0 +1,57 @@
"use strict";
const parsers = require("../parsers");
const property = "margin-bottom";
const shorthand = "margin";
const position = "bottom";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._positionLonghandSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the margin-bottom property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
position,
property
};
+57
View File
@@ -0,0 +1,57 @@
"use strict";
const parsers = require("../parsers");
const property = "margin-left";
const shorthand = "margin";
const position = "left";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._positionLonghandSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the margin-left property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
position,
property
};
+57
View File
@@ -0,0 +1,57 @@
"use strict";
const parsers = require("../parsers");
const property = "margin-right";
const shorthand = "margin";
const position = "right";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._positionLonghandSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the margin-right property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
position,
property
};
+57
View File
@@ -0,0 +1,57 @@
"use strict";
const parsers = require("../parsers");
const property = "margin-top";
const shorthand = "margin";
const position = "top";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._positionLonghandSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the margin-top property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
position,
property
};
+51
View File
@@ -0,0 +1,51 @@
"use strict";
const parsers = require("../parsers");
const property = "opacity";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the opacity property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
clamp: true
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "outline-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the outline-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+84
View File
@@ -0,0 +1,84 @@
"use strict";
const parsers = require("../parsers");
const paddingTop = require("./paddingTop");
const paddingRight = require("./paddingRight");
const paddingBottom = require("./paddingBottom");
const paddingLeft = require("./paddingLeft");
const property = "padding";
const position = "edges";
const shorthandFor = new Map([
[paddingTop.property, paddingTop],
[paddingRight.property, paddingRight],
[paddingBottom.property, paddingBottom],
[paddingLeft.property, paddingLeft]
]);
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
for (const [longhand] of shorthandFor) {
this._setProperty(longhand, "");
}
this._setProperty(property, v);
} else {
const val = parse(v);
if (Array.isArray(val) || typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._positionShorthandSetter(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the padding property value.
*
* @param {string} v - The value to parse.
* @returns {Array<string>|string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const values = parsers.parsePropertyValue(property, v);
const parsedValues = [];
if (Array.isArray(values) && values.length) {
if (values.length > 4) {
return;
}
for (const value of values) {
const parsedValue = parsers.resolveNumericValue([value], {
length: values.length,
min: 0,
type: "length"
});
if (!parsedValue) {
return;
}
parsedValues.push(parsedValue);
}
} else if (typeof values === "string") {
parsedValues.push(values);
}
if (parsedValues.length) {
return parsedValues;
}
}
module.exports = {
descriptor,
parse,
position,
property,
shorthandFor
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "padding-bottom";
const shorthand = "padding";
const position = "bottom";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._positionLonghandSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the padding-bottom property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
position,
property
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "padding-left";
const shorthand = "padding";
const position = "left";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._positionLonghandSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the padding-left property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
position,
property
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "padding-right";
const shorthand = "padding";
const position = "right";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._positionLonghandSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the padding-right property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
position,
property
};
+58
View File
@@ -0,0 +1,58 @@
"use strict";
const parsers = require("../parsers");
const property = "padding-top";
const shorthand = "padding";
const position = "top";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._positionLonghandSetter(property, val, priority, shorthand);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the padding-top property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
position,
property
};
+51
View File
@@ -0,0 +1,51 @@
"use strict";
const parsers = require("../parsers");
const property = "right";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the right property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "stop-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the stop-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "text-emphasis-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the text-emphasis-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+51
View File
@@ -0,0 +1,51 @@
"use strict";
const parsers = require("../parsers");
const property = "top";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the top property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "-webkit-text-fill-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the -webkit-text-fill-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+49
View File
@@ -0,0 +1,49 @@
"use strict";
const parsers = require("../parsers");
const property = "-webkit-text-stroke-color";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the -webkit-text-stroke-color property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveColorValue(value);
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+52
View File
@@ -0,0 +1,52 @@
"use strict";
const parsers = require("../parsers");
const property = "width";
const descriptor = {
set(v) {
v = parsers.prepareValue(v);
if (parsers.hasVarFunc(v)) {
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority = this._priorities.get(property) ?? "";
this._setProperty(property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the width property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
if (v === "") {
return v;
}
const value = parsers.parsePropertyValue(property, v);
if (Array.isArray(value) && value.length === 1) {
return parsers.resolveNumericValue(value, {
min: 0,
type: "length"
});
} else if (typeof value === "string") {
return value;
}
}
module.exports = {
descriptor,
parse,
property
};
+142
View File
@@ -0,0 +1,142 @@
"use strict";
const parsers = require("../parsers");
// Constants
const { AST_TYPES } = parsers;
/**
* Creates a generic property descriptor for a given property. Such descriptors are used whenever we don't have a
* specific handler in `./properties/*.js`. They perform some basic logic that works as a fallback, and is correct for
* simple properties, but properties with more complex grammars will need their own handlers.
*
* @param {string} property - The canonical CSS property name (e.g. "backdrop-filter", not "backdropFilter").
* @param {object} opts - The options object.
* @param {boolean} opts.caseSensitive - True if value is case-sensitive, false otherwise.
* @param {object} [opts.dimensionTypes={}] - An object containing information about the dimension types used by this
* property, if any. Keys are a type of dimension, which determines which serializer to use, and values are the
* information used by the serializer to serialize a parsed value.
* @param {object} [opts.functionTypes={}] - An object containing information about the function types used by this
* property, if any. Keys are a type of function, which determines which function to use; values are ignored.
* @returns {object} The property descriptor object.
*/
function createGenericPropertyDescriptor(property, { caseSensitive, dimensionTypes = {}, functionTypes = {} }) {
return {
set(v) {
const value = parsers.prepareValue(v);
if (parsers.hasVarFunc(value)) {
this._setProperty(property, value);
} else {
const parsedValue = parsers.parsePropertyValue(property, v, {
caseSensitive
});
const priority = this._priorities.get(property) ?? "";
if (Array.isArray(parsedValue)) {
if (parsedValue.length === 1) {
const {
angle: angleType,
dimension: dimensionType,
length: lengthType,
number: numberType,
percentage: percentageType
} = dimensionTypes;
const { color: colorType, image: imageType, paint: paintType } = functionTypes;
const [{ name, type, value: itemValue }] = parsedValue;
switch (type) {
case AST_TYPES.CALC: {
this._setProperty(property, `${name}(${itemValue})`, priority);
break;
}
case AST_TYPES.DIMENSION: {
let val;
if (dimensionType && lengthType) {
val = parsers.serializeLength(parsedValue, lengthType);
if (!val) {
val = parsers.serializeDimension(parsedValue, dimensionType);
}
} else if (lengthType) {
val = parsers.serializeLength(parsedValue, lengthType);
} else {
val = parsers.serializeDimension(parsedValue, dimensionType);
}
this._setProperty(property, val, priority);
break;
}
case AST_TYPES.HASH: {
this._setProperty(property, parsers.serializeColor(parsedValue), priority);
break;
}
case AST_TYPES.NUMBER: {
let val;
if (numberType) {
val = parsers.serializeNumber(parsedValue, numberType);
} else if (angleType) {
val = parsers.serializeAngle(parsedValue, angleType);
} else if (lengthType) {
val = parsers.serializeLength(parsedValue, lengthType);
} else if (percentageType) {
val = parsers.serializePercentage(parsedValue, percentageType);
}
this._setProperty(property, val, priority);
break;
}
case AST_TYPES.GLOBAL_KEYWORD:
case AST_TYPES.IDENTIFIER: {
this._setProperty(property, name, priority);
break;
}
case AST_TYPES.PERCENTAGE: {
let numericType;
if (percentageType) {
numericType = percentageType;
} else if (dimensionType) {
numericType = dimensionType;
} else if (angleType) {
numericType = angleType;
} else if (lengthType) {
numericType = lengthType;
}
if (numericType) {
this._setProperty(property, parsers.resolveNumericValue(parsedValue, numericType), priority);
}
break;
}
case AST_TYPES.STRING: {
this._setProperty(property, parsers.serializeString(parsedValue), priority);
break;
}
case AST_TYPES.URL: {
this._setProperty(property, parsers.serializeURL(parsedValue), priority);
break;
}
case AST_TYPES.FUNCTION:
default: {
if (colorType || paintType) {
this._setProperty(property, parsers.serializeColor(parsedValue), priority);
} else if (imageType) {
this._setProperty(property, parsers.serializeGradient(parsedValue), priority);
} else {
this._setProperty(property, value, priority);
}
}
}
} else {
// Set the prepared value for lists containing multiple values.
this._setProperty(property, value, priority);
}
} else if (typeof parsedValue === "string") {
this._setProperty(property, parsedValue, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
}
module.exports = {
createGenericPropertyDescriptor
};
+28
View File
@@ -0,0 +1,28 @@
// Forked from https://github.com/jsdom/jsdom/blob/main/lib/jsdom/living/helpers/strings.js
"use strict";
/**
* Converts a string to ASCII lowercase.
*
* @see https://infra.spec.whatwg.org/#ascii-lowercase
* @param {string} s - The string to convert.
* @returns {string} The converted string.
*/
function asciiLowercase(s) {
if (!/[^\x00-\x7f]/.test(s)) {
return s.toLowerCase();
}
const len = s.length;
const out = new Array(len);
for (let i = 0; i < len; i++) {
const code = s.charCodeAt(i);
// If the character is between 'A' (65) and 'Z' (90), convert using bitwise OR with 32
out[i] = code >= 65 && code <= 90 ? String.fromCharCode(code | 32) : s[i];
}
return out.join("");
}
module.exports = {
asciiLowercase
};