1575 lines
53 KiB
JavaScript
1575 lines
53 KiB
JavaScript
"use strict";
|
|
|
|
const propertyDefinitions = require("./generated/propertyDefinitions");
|
|
const { hasVarFunc, isGlobalKeyword, isValidPropertyValue, splitValue } = require("./parsers");
|
|
const background = require("./properties/background");
|
|
const backgroundColor = require("./properties/backgroundColor");
|
|
const backgroundSize = require("./properties/backgroundSize");
|
|
const border = require("./properties/border");
|
|
const borderWidth = require("./properties/borderWidth");
|
|
const borderStyle = require("./properties/borderStyle");
|
|
const borderColor = require("./properties/borderColor");
|
|
const borderTop = require("./properties/borderTop");
|
|
const borderRight = require("./properties/borderRight");
|
|
const borderBottom = require("./properties/borderBottom");
|
|
const borderLeft = require("./properties/borderLeft");
|
|
const flex = require("./properties/flex");
|
|
const font = require("./properties/font");
|
|
const margin = require("./properties/margin");
|
|
const padding = require("./properties/padding");
|
|
|
|
/* constants */
|
|
const BORDER_IMAGE = "border-image";
|
|
const TOP = "top";
|
|
const RIGHT = "right";
|
|
const BOTTOM = "bottom";
|
|
const LEFT = "left";
|
|
const WIDTH = "width";
|
|
const STYLE = "style";
|
|
const COLOR = "color";
|
|
const NONE = "none";
|
|
const TRBL_INDICES = {
|
|
[TOP]: 0,
|
|
[RIGHT]: 1,
|
|
[BOTTOM]: 2,
|
|
[LEFT]: 3
|
|
};
|
|
|
|
/* shorthands */
|
|
const shorthandProperties = new Map([
|
|
[background.property, background],
|
|
[
|
|
border.property,
|
|
{
|
|
definition: border.definition,
|
|
parse: border.parse,
|
|
shorthandFor: new Map([
|
|
...border.shorthandFor,
|
|
...border.positionShorthandFor,
|
|
[BORDER_IMAGE, null]
|
|
])
|
|
}
|
|
],
|
|
[borderWidth.property, borderWidth],
|
|
[borderStyle.property, borderStyle],
|
|
[borderColor.property, borderColor],
|
|
[borderTop.property, borderTop],
|
|
[borderRight.property, borderRight],
|
|
[borderBottom.property, borderBottom],
|
|
[borderLeft.property, borderLeft],
|
|
["flex", flex],
|
|
["font", font],
|
|
["margin", margin],
|
|
["padding", padding]
|
|
]);
|
|
|
|
/* borders */
|
|
const borderProperties = new Set([
|
|
border.property,
|
|
BORDER_IMAGE,
|
|
...border.shorthandFor.keys(),
|
|
...border.positionShorthandFor.keys(),
|
|
...borderTop.shorthandFor.keys(),
|
|
...borderRight.shorthandFor.keys(),
|
|
...borderBottom.shorthandFor.keys(),
|
|
...borderLeft.shorthandFor.keys()
|
|
]);
|
|
const borderPositions = new Set([TOP, RIGHT, BOTTOM, LEFT]);
|
|
const borderLines = new Set([WIDTH, STYLE, COLOR]);
|
|
|
|
/**
|
|
* Ensures consistent object shape.
|
|
*
|
|
* @param {string} property - The property name.
|
|
* @param {string} [value=""] - The property value.
|
|
* @param {string} [priority=""] - The priority.
|
|
* @returns {Object} The property item object.
|
|
*/
|
|
const createPropertyItem = (property, value = "", priority = "") => ({
|
|
property,
|
|
value,
|
|
priority
|
|
});
|
|
|
|
/**
|
|
* Retrieves a property item from the map or creates a default one if it doesn't exist.
|
|
*
|
|
* @param {string} property - The name of the property.
|
|
* @param {Map} properties - The map containing all properties.
|
|
* @returns {Object} The property item containing name, value, and priority.
|
|
*/
|
|
const getPropertyItem = (property, properties) => {
|
|
const propertyItem = properties.get(property) ?? createPropertyItem(property);
|
|
return propertyItem;
|
|
};
|
|
|
|
/**
|
|
* Calculates the value for a specific position (top, right, bottom, left)
|
|
* based on the array of values provided for a shorthand property.
|
|
*
|
|
* @param {string[]} positionValues - The values extracted from the shorthand property.
|
|
* @param {string} position - The specific position (top, right, bottom, left) to retrieve.
|
|
* @returns {string} The calculated value for the position.
|
|
*/
|
|
const getPositionValue = (positionValues, position) => {
|
|
const [val1, val2, val3, val4] = positionValues;
|
|
const index = TRBL_INDICES[position] ?? -1;
|
|
// If a specific position (top, right, bottom, left) is requested.
|
|
if (index !== -1) {
|
|
switch (positionValues.length) {
|
|
case 2: {
|
|
// Index 0 (Top) & 2 (Bottom) -> val1
|
|
// Index 1 (Right) & 3 (Left) -> val2
|
|
return index % 2 === 0 ? val1 : val2;
|
|
}
|
|
case 3: {
|
|
// Index 0 (Top) -> val1
|
|
// Index 1 (Right) & 3 (Left) -> val2
|
|
// Index 2 (Bottom) -> val3
|
|
if (index === 2) {
|
|
return val3;
|
|
}
|
|
return index % 2 === 0 ? val1 : val2;
|
|
}
|
|
case 4: {
|
|
return positionValues[index];
|
|
}
|
|
case 1:
|
|
default: {
|
|
return val1;
|
|
}
|
|
}
|
|
}
|
|
// Fallback logic for when no specific position is requested.
|
|
switch (positionValues.length) {
|
|
case 2: {
|
|
if (val1 === val2) {
|
|
return val1;
|
|
}
|
|
return `${val1} ${val2}`;
|
|
}
|
|
case 3: {
|
|
if (val1 === val3) {
|
|
if (val1 === val2) {
|
|
return val1;
|
|
}
|
|
return `${val1} ${val2}`;
|
|
}
|
|
return `${val1} ${val2} ${val3}`;
|
|
}
|
|
case 4: {
|
|
if (val2 === val4) {
|
|
if (val1 === val3) {
|
|
if (val1 === val2) {
|
|
return val1;
|
|
}
|
|
return `${val1} ${val2}`;
|
|
}
|
|
return `${val1} ${val2} ${val3}`;
|
|
}
|
|
return `${val1} ${val2} ${val3} ${val4}`;
|
|
}
|
|
case 1:
|
|
default: {
|
|
return val1;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Replaces the background shorthand property based on individual longhand values.
|
|
*
|
|
* @param {string} property - The specific background longhand property being updated.
|
|
* @param {Map} properties - The map of all properties.
|
|
* @param {Object} opt - Parsing options including global object and configurations.
|
|
* @returns {string} The constructed background shorthand string.
|
|
*/
|
|
const replaceBackgroundShorthand = (property, properties, opt) => {
|
|
const { value: propertyValue } = properties.get(property);
|
|
const parsedValue = background.shorthandFor.get(property).parse(propertyValue, opt);
|
|
const values = splitValue(parsedValue, {
|
|
delimiter: ","
|
|
});
|
|
const { value: shorthandValue } = properties.get(background.property);
|
|
const bgValues = background.parse(shorthandValue, opt);
|
|
const bgLength = bgValues.length;
|
|
if (property === backgroundColor.property) {
|
|
bgValues[bgLength - 1][property] = parsedValue[0];
|
|
} else {
|
|
for (let i = 0; i < bgLength; i++) {
|
|
bgValues[i][property] = values[i];
|
|
}
|
|
}
|
|
const backgrounds = [];
|
|
for (const bgValue of bgValues) {
|
|
const bg = [];
|
|
for (const [longhand, value] of Object.entries(bgValue)) {
|
|
if (!value || value === background.initialValues.get(longhand)) {
|
|
continue;
|
|
}
|
|
if (longhand === backgroundSize.property) {
|
|
bg.push(`/ ${value}`);
|
|
} else {
|
|
bg.push(value);
|
|
}
|
|
}
|
|
backgrounds.push(bg.join(" "));
|
|
}
|
|
return backgrounds.join(", ");
|
|
};
|
|
|
|
/**
|
|
* Checks if a property value matches the value within a border shorthand.
|
|
*
|
|
* @param {string} property - The property to check.
|
|
* @param {string} value - The value to compare.
|
|
* @param {string} shorthandValue - The shorthand string to parse and compare against.
|
|
* @param {Object} [opt={}] - Parsing options.
|
|
* @returns {boolean} True if the value matches the shorthand's value.
|
|
*/
|
|
const matchesBorderShorthandValue = (property, value, shorthandValue, opt = {}) => {
|
|
const { globalObject, options } = opt;
|
|
const obj = border.parse(shorthandValue, {
|
|
globalObject,
|
|
options
|
|
});
|
|
if (Object.hasOwn(obj, property)) {
|
|
return value === obj[property];
|
|
}
|
|
return value === border.initialValues.get(property);
|
|
};
|
|
|
|
/**
|
|
* Replaces or updates a value within a border shorthand string.
|
|
*
|
|
* @param {string} value - The new value to insert.
|
|
* @param {string} shorthandValue - The existing shorthand string.
|
|
* @param {Object} [opt={}] - Parsing options.
|
|
* @returns {string} The updated border shorthand string.
|
|
*/
|
|
const replaceBorderShorthandValue = (value, shorthandValue, opt = {}) => {
|
|
const { globalObject, options } = opt;
|
|
const borderFirstInitialKey = border.initialValues.keys().next().value;
|
|
const borderFirstInitialValue = border.initialValues.get(borderFirstInitialKey);
|
|
const parseOpt = {
|
|
globalObject,
|
|
options
|
|
};
|
|
const valueObj = border.parse(value, parseOpt);
|
|
const shorthandObj = shorthandValue
|
|
? border.parse(shorthandValue, parseOpt)
|
|
: {
|
|
[borderFirstInitialKey]: borderFirstInitialValue
|
|
};
|
|
const keys = border.shorthandFor.keys();
|
|
for (const key of keys) {
|
|
const initialValue = border.initialValues.get(key);
|
|
let parsedValue = initialValue;
|
|
if (Object.hasOwn(valueObj, key)) {
|
|
parsedValue = valueObj[key];
|
|
}
|
|
if (parsedValue === initialValue) {
|
|
if (key === borderFirstInitialKey) {
|
|
if (!Object.hasOwn(shorthandObj, key)) {
|
|
shorthandObj[key] = parsedValue;
|
|
}
|
|
} else {
|
|
delete shorthandObj[key];
|
|
}
|
|
} else {
|
|
shorthandObj[key] = parsedValue;
|
|
if (
|
|
shorthandObj[borderFirstInitialKey] &&
|
|
shorthandObj[borderFirstInitialKey] === borderFirstInitialValue
|
|
) {
|
|
delete shorthandObj[borderFirstInitialKey];
|
|
}
|
|
}
|
|
}
|
|
return Object.values(shorthandObj).join(" ");
|
|
};
|
|
|
|
/**
|
|
* Replaces a value at a specific position (top, right, bottom, left) within a position shorthand.
|
|
*
|
|
* @param {string} value - The new value to set.
|
|
* @param {string[]} positionValues - The array of existing position values.
|
|
* @param {string} position - The position to update.
|
|
* @returns {string} The updated shorthand string.
|
|
*/
|
|
const replacePositionValue = (value, positionValues, position) => {
|
|
const index = TRBL_INDICES[position] ?? -1;
|
|
let currentValues = positionValues;
|
|
if (index !== -1) {
|
|
// Loop for reducing array length (instead of recursion)
|
|
while (true) {
|
|
const [val1, val2, val3, val4] = currentValues;
|
|
switch (currentValues.length) {
|
|
case 2: {
|
|
if (val1 === val2) {
|
|
currentValues = [val1];
|
|
continue;
|
|
}
|
|
switch (index) {
|
|
// Top
|
|
case 0: {
|
|
if (val1 === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
return `${value} ${val2} ${val1}`;
|
|
}
|
|
// Right
|
|
case 1: {
|
|
if (val2 === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
return `${val1} ${value} ${val1} ${val2}`;
|
|
}
|
|
// Bottom
|
|
case 2: {
|
|
if (val1 === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
return `${val1} ${val2} ${value}`;
|
|
}
|
|
// Left
|
|
case 3:
|
|
default: {
|
|
if (val2 === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
return `${val1} ${val2} ${val1} ${value}`;
|
|
}
|
|
}
|
|
}
|
|
case 3: {
|
|
if (val1 === val3) {
|
|
currentValues = [val1, val2];
|
|
continue;
|
|
}
|
|
switch (index) {
|
|
// Top
|
|
case 0: {
|
|
if (val1 === value) {
|
|
return currentValues.join(" ");
|
|
} else if (val3 === value) {
|
|
return `${value} ${val2}`;
|
|
}
|
|
return `${value} ${val2} ${val3}`;
|
|
}
|
|
// Right
|
|
case 1: {
|
|
if (val2 === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
return `${val1} ${value} ${val3} ${val2}`;
|
|
}
|
|
// Bottom
|
|
case 2: {
|
|
if (val3 === value) {
|
|
return currentValues.join(" ");
|
|
} else if (val1 === value) {
|
|
return `${val1} ${val2}`;
|
|
}
|
|
return `${val1} ${val2} ${value}`;
|
|
}
|
|
// Left
|
|
case 3:
|
|
default: {
|
|
if (val2 === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
return `${val1} ${val2} ${val3} ${value}`;
|
|
}
|
|
}
|
|
}
|
|
case 4: {
|
|
if (val2 === val4) {
|
|
currentValues = [val1, val2, val3];
|
|
continue;
|
|
}
|
|
switch (index) {
|
|
// Top
|
|
case 0: {
|
|
if (val1 === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
return `${value} ${val2} ${val3} ${val4}`;
|
|
}
|
|
// Right
|
|
case 1: {
|
|
if (val2 === value) {
|
|
return currentValues.join(" ");
|
|
} else if (val4 === value) {
|
|
return `${val1} ${value} ${val3}`;
|
|
}
|
|
return `${val1} ${value} ${val3} ${val4}`;
|
|
}
|
|
// Bottom
|
|
case 2: {
|
|
if (val3 === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
return `${val1} ${val2} ${value} ${val4}`;
|
|
}
|
|
// Left
|
|
case 3:
|
|
default: {
|
|
if (val4 === value) {
|
|
return currentValues.join(" ");
|
|
} else if (val2 === value) {
|
|
return `${val1} ${val2} ${val3}`;
|
|
}
|
|
return `${val1} ${val2} ${val3} ${value}`;
|
|
}
|
|
}
|
|
}
|
|
case 1:
|
|
default: {
|
|
const [val] = currentValues;
|
|
if (val === value) {
|
|
return currentValues.join(" ");
|
|
}
|
|
switch (index) {
|
|
// Top
|
|
case 0: {
|
|
return `${value} ${val} ${val}`;
|
|
}
|
|
// Right
|
|
case 1: {
|
|
return `${val} ${value} ${val} ${val}`;
|
|
}
|
|
// Bottom
|
|
case 2: {
|
|
return `${val} ${val} ${value}`;
|
|
}
|
|
// Left
|
|
case 3:
|
|
default: {
|
|
return `${val} ${val} ${val} ${value}`;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Fallback logic for when no specific position is requested.
|
|
const [val1, val2, val3, val4] = currentValues;
|
|
switch (currentValues.length) {
|
|
case 2: {
|
|
if (val1 === val2) {
|
|
return val1;
|
|
}
|
|
return `${val1} ${val2}`;
|
|
}
|
|
case 3: {
|
|
if (val1 === val3) {
|
|
if (val1 === val2) {
|
|
return val1;
|
|
}
|
|
return `${val1} ${val2}`;
|
|
}
|
|
return `${val1} ${val2} ${val3}`;
|
|
}
|
|
case 4: {
|
|
if (val2 === val4) {
|
|
if (val1 === val3) {
|
|
if (val1 === val2) {
|
|
return val1;
|
|
}
|
|
return `${val1} ${val2}`;
|
|
}
|
|
return `${val1} ${val2} ${val3}`;
|
|
}
|
|
return `${val1} ${val2} ${val3} ${val4}`;
|
|
}
|
|
case 1:
|
|
default: {
|
|
return val1;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handles border property preparation when the value is a string.
|
|
*
|
|
* @param {Object} params - The parameters object.
|
|
* @param {string} params.property - The property name.
|
|
* @param {string} params.value - The property value.
|
|
* @param {string} params.priority - The property priority.
|
|
* @param {Map} params.properties - The map of properties.
|
|
* @param {Object} params.parts - The split property name parts.
|
|
* @param {Object} params.opt - Parsing options.
|
|
* @param {Map} params.borderItems - The map to store processed border items.
|
|
*/
|
|
const prepareBorderStringValue = ({
|
|
property,
|
|
value,
|
|
priority,
|
|
properties,
|
|
parts,
|
|
opt,
|
|
borderItems
|
|
}) => {
|
|
const { prop1, prop2, prop3 } = parts;
|
|
const { globalObject, options } = opt;
|
|
const parseOpt = {
|
|
globalObject,
|
|
options
|
|
};
|
|
const shorthandItem = getPropertyItem(border.property, properties);
|
|
const imageItem = getPropertyItem(BORDER_IMAGE, properties);
|
|
// Handle longhand properties.
|
|
if (prop3) {
|
|
const lineProperty = `${prop1}-${prop3}`;
|
|
const lineItem = getPropertyItem(lineProperty, properties);
|
|
const positionProperty = `${prop1}-${prop2}`;
|
|
const positionItem = getPropertyItem(positionProperty, properties);
|
|
const longhandProperty = `${prop1}-${prop2}-${prop3}`;
|
|
const longhandItem = getPropertyItem(longhandProperty, properties);
|
|
longhandItem.value = value;
|
|
longhandItem.priority = priority;
|
|
const propertyValue = hasVarFunc(value) ? "" : value;
|
|
if (propertyValue === "") {
|
|
shorthandItem.value = "";
|
|
lineItem.value = "";
|
|
positionItem.value = "";
|
|
} else if (isGlobalKeyword(propertyValue)) {
|
|
if (shorthandItem.value !== propertyValue) {
|
|
shorthandItem.value = "";
|
|
}
|
|
if (lineItem.value !== propertyValue) {
|
|
lineItem.value = "";
|
|
}
|
|
if (positionItem.value !== propertyValue) {
|
|
positionItem.value = "";
|
|
}
|
|
} else {
|
|
if (
|
|
shorthandItem.value &&
|
|
!matchesBorderShorthandValue(lineProperty, propertyValue, shorthandItem.value, parseOpt)
|
|
) {
|
|
shorthandItem.value = "";
|
|
}
|
|
if (lineItem.value) {
|
|
lineItem.value = replacePositionValue(propertyValue, splitValue(lineItem.value), prop2);
|
|
}
|
|
if (
|
|
positionItem.value &&
|
|
!matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, parseOpt)
|
|
) {
|
|
positionItem.value = "";
|
|
}
|
|
}
|
|
borderItems.set(border.property, shorthandItem);
|
|
borderItems.set(BORDER_IMAGE, imageItem);
|
|
borderItems.set(lineProperty, lineItem);
|
|
borderItems.set(positionProperty, positionItem);
|
|
borderItems.set(longhandProperty, longhandItem);
|
|
// Handle side-specific border shorthands (border-top, border-right, border-bottom, border-left).
|
|
} else if (prop2 && borderPositions.has(prop2)) {
|
|
const lineWidthProperty = `${prop1}-width`;
|
|
const lineWidthItem = getPropertyItem(lineWidthProperty, properties);
|
|
const lineStyleProperty = `${prop1}-style`;
|
|
const lineStyleItem = getPropertyItem(lineStyleProperty, properties);
|
|
const lineColorProperty = `${prop1}-color`;
|
|
const lineColorItem = getPropertyItem(lineColorProperty, properties);
|
|
const positionProperty = `${prop1}-${prop2}`;
|
|
const positionItem = getPropertyItem(positionProperty, properties);
|
|
positionItem.value = value;
|
|
positionItem.priority = priority;
|
|
const propertyValue = hasVarFunc(value) ? "" : value;
|
|
if (propertyValue === "") {
|
|
shorthandItem.value = "";
|
|
lineWidthItem.value = "";
|
|
lineStyleItem.value = "";
|
|
lineColorItem.value = "";
|
|
} else if (isGlobalKeyword(propertyValue)) {
|
|
if (shorthandItem.value !== propertyValue) {
|
|
shorthandItem.value = "";
|
|
}
|
|
if (lineWidthItem.value !== propertyValue) {
|
|
lineWidthItem.value = "";
|
|
}
|
|
if (lineStyleItem.value !== propertyValue) {
|
|
lineStyleItem.value = "";
|
|
}
|
|
if (lineColorItem.value !== propertyValue) {
|
|
lineColorItem.value = "";
|
|
}
|
|
} else {
|
|
if (
|
|
shorthandItem.value &&
|
|
!matchesBorderShorthandValue(property, propertyValue, shorthandItem.value, parseOpt)
|
|
) {
|
|
shorthandItem.value = "";
|
|
}
|
|
if (
|
|
lineWidthItem.value &&
|
|
isValidPropertyValue(lineWidthProperty, propertyValue, globalObject)
|
|
) {
|
|
lineWidthItem.value = propertyValue;
|
|
}
|
|
if (
|
|
lineStyleItem.value &&
|
|
isValidPropertyValue(lineStyleProperty, propertyValue, globalObject)
|
|
) {
|
|
lineStyleItem.value = propertyValue;
|
|
}
|
|
if (
|
|
lineColorItem.value &&
|
|
isValidPropertyValue(lineColorProperty, propertyValue, globalObject)
|
|
) {
|
|
lineColorItem.value = propertyValue;
|
|
}
|
|
}
|
|
for (const line of borderLines) {
|
|
const longhandProperty = `${prop1}-${prop2}-${line}`;
|
|
const longhandItem = getPropertyItem(longhandProperty, properties);
|
|
longhandItem.value = propertyValue;
|
|
longhandItem.priority = priority;
|
|
borderItems.set(longhandProperty, longhandItem);
|
|
}
|
|
borderItems.set(border.property, shorthandItem);
|
|
borderItems.set(BORDER_IMAGE, imageItem);
|
|
borderItems.set(lineWidthProperty, lineWidthItem);
|
|
borderItems.set(lineStyleProperty, lineStyleItem);
|
|
borderItems.set(lineColorProperty, lineColorItem);
|
|
borderItems.set(positionProperty, positionItem);
|
|
// Handle property-specific border shorthands (border-width, border-style, border-color).
|
|
} else if (prop2 && borderLines.has(prop2)) {
|
|
const lineProperty = `${prop1}-${prop2}`;
|
|
const lineItem = getPropertyItem(lineProperty, properties);
|
|
lineItem.value = value;
|
|
lineItem.priority = priority;
|
|
const propertyValue = hasVarFunc(value) ? "" : value;
|
|
if (propertyValue === "") {
|
|
shorthandItem.value = "";
|
|
} else if (isGlobalKeyword(propertyValue)) {
|
|
if (shorthandItem.value !== propertyValue) {
|
|
shorthandItem.value = "";
|
|
}
|
|
}
|
|
for (const position of borderPositions) {
|
|
const positionProperty = `${prop1}-${position}`;
|
|
const positionItem = getPropertyItem(positionProperty, properties);
|
|
const longhandProperty = `${prop1}-${position}-${prop2}`;
|
|
const longhandItem = getPropertyItem(longhandProperty, properties);
|
|
if (propertyValue) {
|
|
positionItem.value = replaceBorderShorthandValue(
|
|
propertyValue,
|
|
positionItem.value,
|
|
parseOpt
|
|
);
|
|
} else {
|
|
positionItem.value = "";
|
|
}
|
|
longhandItem.value = propertyValue;
|
|
longhandItem.priority = priority;
|
|
borderItems.set(positionProperty, positionItem);
|
|
borderItems.set(longhandProperty, longhandItem);
|
|
}
|
|
borderItems.set(border.property, shorthandItem);
|
|
borderItems.set(BORDER_IMAGE, imageItem);
|
|
borderItems.set(lineProperty, lineItem);
|
|
// Handle border shorthand.
|
|
} else {
|
|
const propertyValue = hasVarFunc(value) ? "" : value;
|
|
imageItem.value = propertyValue ? NONE : "";
|
|
for (const line of borderLines) {
|
|
const lineProperty = `${prop1}-${line}`;
|
|
const lineItem = getPropertyItem(lineProperty, properties);
|
|
lineItem.value = propertyValue;
|
|
lineItem.priority = priority;
|
|
borderItems.set(lineProperty, lineItem);
|
|
}
|
|
for (const position of borderPositions) {
|
|
const positionProperty = `${prop1}-${position}`;
|
|
const positionItem = getPropertyItem(positionProperty, properties);
|
|
positionItem.value = propertyValue;
|
|
positionItem.priority = priority;
|
|
borderItems.set(positionProperty, positionItem);
|
|
for (const line of borderLines) {
|
|
const longhandProperty = `${positionProperty}-${line}`;
|
|
const longhandItem = getPropertyItem(longhandProperty, properties);
|
|
longhandItem.value = propertyValue;
|
|
longhandItem.priority = priority;
|
|
borderItems.set(longhandProperty, longhandItem);
|
|
}
|
|
}
|
|
borderItems.set(property, shorthandItem);
|
|
borderItems.set(BORDER_IMAGE, imageItem);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handles border property preparation when the value is an array.
|
|
*
|
|
* @param {Object} params - The parameters object.
|
|
* @param {Array} params.value - The property value.
|
|
* @param {string} params.priority - The property priority.
|
|
* @param {Map} params.properties - The map of properties.
|
|
* @param {Object} params.parts - The split property name parts.
|
|
* @param {Object} params.opt - Parsing options.
|
|
* @param {Map} params.borderItems - The map to store processed border items.
|
|
*/
|
|
const prepareBorderArrayValue = ({ value, priority, properties, parts, opt, borderItems }) => {
|
|
const { prop1, prop2 } = parts;
|
|
const { globalObject, options } = opt;
|
|
const parseOpt = {
|
|
globalObject,
|
|
options
|
|
};
|
|
if (!value.length || !borderLines.has(prop2)) {
|
|
return;
|
|
}
|
|
const shorthandItem = getPropertyItem(border.property, properties);
|
|
const imageItem = getPropertyItem(BORDER_IMAGE, properties);
|
|
const lineProperty = `${prop1}-${prop2}`;
|
|
const lineItem = getPropertyItem(lineProperty, properties);
|
|
if (value.length === 1) {
|
|
const [propertyValue] = value;
|
|
if (shorthandItem.value) {
|
|
if (hasVarFunc(shorthandItem.value)) {
|
|
shorthandItem.value = "";
|
|
} else if (propertyValue) {
|
|
shorthandItem.value = replaceBorderShorthandValue(
|
|
propertyValue,
|
|
shorthandItem.value,
|
|
parseOpt
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
shorthandItem.value = "";
|
|
}
|
|
lineItem.value = value.join(" ");
|
|
lineItem.priority = priority;
|
|
const positionValues = {};
|
|
const [val1, val2, val3, val4] = value;
|
|
switch (value.length) {
|
|
case 2: {
|
|
positionValues.top = val1;
|
|
positionValues.right = val2;
|
|
positionValues.bottom = val1;
|
|
positionValues.left = val2;
|
|
break;
|
|
}
|
|
case 3: {
|
|
positionValues.top = val1;
|
|
positionValues.right = val2;
|
|
positionValues.bottom = val3;
|
|
positionValues.left = val2;
|
|
break;
|
|
}
|
|
case 4: {
|
|
positionValues.top = val1;
|
|
positionValues.right = val2;
|
|
positionValues.bottom = val3;
|
|
positionValues.left = val4;
|
|
break;
|
|
}
|
|
case 1:
|
|
default: {
|
|
positionValues.top = val1;
|
|
positionValues.right = val1;
|
|
positionValues.bottom = val1;
|
|
positionValues.left = val1;
|
|
}
|
|
}
|
|
for (const position of borderPositions) {
|
|
const positionProperty = `${prop1}-${position}`;
|
|
const positionItem = getPropertyItem(positionProperty, properties);
|
|
if (positionItem.value && positionValues[position]) {
|
|
positionItem.value = replaceBorderShorthandValue(
|
|
positionValues[position],
|
|
positionItem.value,
|
|
parseOpt
|
|
);
|
|
}
|
|
const longhandProperty = `${positionProperty}-${prop2}`;
|
|
const longhandItem = getPropertyItem(longhandProperty, properties);
|
|
longhandItem.value = positionValues[position];
|
|
longhandItem.priority = priority;
|
|
borderItems.set(positionProperty, positionItem);
|
|
borderItems.set(longhandProperty, longhandItem);
|
|
}
|
|
borderItems.set(border.property, shorthandItem);
|
|
borderItems.set(BORDER_IMAGE, imageItem);
|
|
borderItems.set(lineProperty, lineItem);
|
|
};
|
|
|
|
/**
|
|
* Handles border property preparation when the value is an object.
|
|
*
|
|
* @param {Object} params - The parameters object.
|
|
* @param {string} params.property - The property name.
|
|
* @param {Object} params.value - The property value.
|
|
* @param {string} params.priority - The property priority.
|
|
* @param {Map} params.properties - The map of properties.
|
|
* @param {Object} params.parts - The split property name parts.
|
|
* @param {Object} params.opt - Parsing options.
|
|
* @param {Map} params.borderItems - The map to store processed border items.
|
|
*/
|
|
const prepareBorderObjectValue = ({
|
|
property,
|
|
value,
|
|
priority,
|
|
properties,
|
|
parts,
|
|
opt,
|
|
borderItems
|
|
}) => {
|
|
const { prop1, prop2 } = parts;
|
|
const { globalObject, options } = opt;
|
|
const parseOpt = {
|
|
globalObject,
|
|
options
|
|
};
|
|
const imageItem = getPropertyItem(BORDER_IMAGE, properties);
|
|
// Handle position shorthands.
|
|
if (prop2) {
|
|
if (!borderPositions.has(prop2)) {
|
|
return;
|
|
}
|
|
const shorthandItem = getPropertyItem(border.property, properties);
|
|
const lineWidthProperty = `${prop1}-width`;
|
|
const lineWidthItem = getPropertyItem(lineWidthProperty, properties);
|
|
const lineStyleProperty = `${prop1}-style`;
|
|
const lineStyleItem = getPropertyItem(lineStyleProperty, properties);
|
|
const lineColorProperty = `${prop1}-color`;
|
|
const lineColorItem = getPropertyItem(lineColorProperty, properties);
|
|
const positionProperty = `${prop1}-${prop2}`;
|
|
const positionItem = getPropertyItem(positionProperty, properties);
|
|
if (shorthandItem.value) {
|
|
for (const positionValue of Object.values(value)) {
|
|
if (!matchesBorderShorthandValue(property, positionValue, shorthandItem.value, parseOpt)) {
|
|
shorthandItem.value = "";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
positionItem.value = Object.values(value).join(" ");
|
|
positionItem.priority = priority;
|
|
for (const line of borderLines) {
|
|
const longhandProperty = `${prop1}-${prop2}-${line}`;
|
|
const longhandItem = getPropertyItem(longhandProperty, properties);
|
|
const itemValue = Object.hasOwn(value, longhandProperty)
|
|
? value[longhandProperty]
|
|
: border.initialValues.get(`${prop1}-${line}`);
|
|
if (line === WIDTH && lineWidthItem.value) {
|
|
lineWidthItem.value = replacePositionValue(
|
|
itemValue,
|
|
splitValue(lineWidthItem.value),
|
|
prop2
|
|
);
|
|
} else if (line === STYLE && lineStyleItem.value) {
|
|
lineStyleItem.value = replacePositionValue(
|
|
itemValue,
|
|
splitValue(lineStyleItem.value),
|
|
prop2
|
|
);
|
|
} else if (line === COLOR && lineColorItem.value) {
|
|
lineColorItem.value = replacePositionValue(
|
|
itemValue,
|
|
splitValue(lineColorItem.value),
|
|
prop2
|
|
);
|
|
}
|
|
longhandItem.value = itemValue;
|
|
longhandItem.priority = priority;
|
|
borderItems.set(longhandProperty, longhandItem);
|
|
}
|
|
borderItems.set(border.property, shorthandItem);
|
|
borderItems.set(BORDER_IMAGE, imageItem);
|
|
borderItems.set(lineWidthProperty, lineWidthItem);
|
|
borderItems.set(lineStyleProperty, lineStyleItem);
|
|
borderItems.set(lineColorProperty, lineColorItem);
|
|
borderItems.set(positionProperty, positionItem);
|
|
// Handle border shorthand.
|
|
} else {
|
|
const shorthandItem = getPropertyItem(prop1, properties);
|
|
const lineWidthProperty = `${prop1}-width`;
|
|
const lineWidthItem = getPropertyItem(lineWidthProperty, properties);
|
|
const lineStyleProperty = `${prop1}-style`;
|
|
const lineStyleItem = getPropertyItem(lineStyleProperty, properties);
|
|
const lineColorProperty = `${prop1}-color`;
|
|
const lineColorItem = getPropertyItem(lineColorProperty, properties);
|
|
const propertyValue = Object.values(value).join(" ");
|
|
shorthandItem.value = propertyValue;
|
|
shorthandItem.priority = priority;
|
|
imageItem.value = propertyValue ? NONE : "";
|
|
if (Object.hasOwn(value, lineWidthProperty)) {
|
|
lineWidthItem.value = value[lineWidthProperty];
|
|
} else {
|
|
lineWidthItem.value = border.initialValues.get(lineWidthProperty);
|
|
}
|
|
lineWidthItem.priority = priority;
|
|
if (Object.hasOwn(value, lineStyleProperty)) {
|
|
lineStyleItem.value = value[lineStyleProperty];
|
|
} else {
|
|
lineStyleItem.value = border.initialValues.get(lineStyleProperty);
|
|
}
|
|
lineStyleItem.priority = priority;
|
|
if (Object.hasOwn(value, lineColorProperty)) {
|
|
lineColorItem.value = value[lineColorProperty];
|
|
} else {
|
|
lineColorItem.value = border.initialValues.get(lineColorProperty);
|
|
}
|
|
lineColorItem.priority = priority;
|
|
for (const position of borderPositions) {
|
|
const positionProperty = `${prop1}-${position}`;
|
|
const positionItem = getPropertyItem(positionProperty, properties);
|
|
positionItem.value = propertyValue;
|
|
positionItem.priority = priority;
|
|
for (const line of borderLines) {
|
|
const longhandProperty = `${positionProperty}-${line}`;
|
|
const longhandItem = getPropertyItem(longhandProperty, properties);
|
|
const lineProperty = `${prop1}-${line}`;
|
|
if (Object.hasOwn(value, lineProperty)) {
|
|
longhandItem.value = value[lineProperty];
|
|
} else {
|
|
longhandItem.value = border.initialValues.get(lineProperty);
|
|
}
|
|
longhandItem.priority = priority;
|
|
borderItems.set(longhandProperty, longhandItem);
|
|
}
|
|
borderItems.set(positionProperty, positionItem);
|
|
}
|
|
borderItems.set(property, shorthandItem);
|
|
borderItems.set(BORDER_IMAGE, imageItem);
|
|
borderItems.set(lineWidthProperty, lineWidthItem);
|
|
borderItems.set(lineStyleProperty, lineStyleItem);
|
|
borderItems.set(lineColorProperty, lineColorItem);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Prepares border properties by splitting shorthands and handling updates.
|
|
*
|
|
* @param {string} property - The border property name.
|
|
* @param {string|Array|Object} value - The value of the property.
|
|
* @param {string} priority - The priority of the property (e.g., "important").
|
|
* @param {Map} properties - The map of all properties.
|
|
* @param {Object} [opt={}] - Parsing options.
|
|
* @returns {Map|undefined} A map of expanded or updated border properties.
|
|
*/
|
|
const prepareBorderProperties = (property, value, priority, properties, opt = {}) => {
|
|
if (typeof property !== "string" || value === null) {
|
|
return;
|
|
}
|
|
if (!property.startsWith(border.property)) {
|
|
return;
|
|
}
|
|
let prop2, prop3;
|
|
if (property.length > border.property.length) {
|
|
// Check if next char is '-'
|
|
if (property.charCodeAt(border.property.length) !== 45) {
|
|
return;
|
|
}
|
|
// property is like "border-..."
|
|
const remainder = property.substring(border.property.length + 1);
|
|
const hyphenIndex = remainder.indexOf("-");
|
|
if (hyphenIndex !== -1) {
|
|
prop2 = remainder.substring(0, hyphenIndex);
|
|
prop3 = remainder.substring(hyphenIndex + 1);
|
|
} else {
|
|
prop2 = remainder;
|
|
}
|
|
}
|
|
if (
|
|
(borderPositions.has(prop2) && prop3 && !borderLines.has(prop3)) ||
|
|
(borderLines.has(prop2) && prop3)
|
|
) {
|
|
return;
|
|
}
|
|
const parts = {
|
|
prop1: border.property,
|
|
prop2,
|
|
prop3
|
|
};
|
|
const borderItems = new Map();
|
|
if (typeof value === "string") {
|
|
prepareBorderStringValue({
|
|
property,
|
|
value,
|
|
priority,
|
|
properties,
|
|
parts,
|
|
opt,
|
|
borderItems
|
|
});
|
|
} else if (Array.isArray(value)) {
|
|
prepareBorderArrayValue({
|
|
value,
|
|
priority,
|
|
properties,
|
|
parts,
|
|
opt,
|
|
borderItems
|
|
});
|
|
} else if (value && typeof value === "object") {
|
|
prepareBorderObjectValue({
|
|
property,
|
|
value,
|
|
priority,
|
|
properties,
|
|
parts,
|
|
opt,
|
|
borderItems
|
|
});
|
|
}
|
|
if (!borderItems.has(border.property)) {
|
|
return;
|
|
}
|
|
const borderProps = new Map([[border.property, borderItems.get(border.property)]]);
|
|
for (const line of borderLines) {
|
|
const lineProperty = `${border.property}-${line}`;
|
|
const lineItem = borderItems.get(lineProperty) ?? getPropertyItem(lineProperty, properties);
|
|
borderProps.set(lineProperty, lineItem);
|
|
}
|
|
for (const position of borderPositions) {
|
|
const positionProperty = `${border.property}-${position}`;
|
|
const positionItem =
|
|
borderItems.get(positionProperty) ?? getPropertyItem(positionProperty, properties);
|
|
borderProps.set(positionProperty, positionItem);
|
|
for (const line of borderLines) {
|
|
const longhandProperty = `${border.property}-${position}-${line}`;
|
|
const longhandItem =
|
|
borderItems.get(longhandProperty) ?? getPropertyItem(longhandProperty, properties);
|
|
borderProps.set(longhandProperty, longhandItem);
|
|
}
|
|
}
|
|
const borderImageItem = borderItems.get(BORDER_IMAGE) ?? createPropertyItem(BORDER_IMAGE);
|
|
borderProps.set(BORDER_IMAGE, borderImageItem);
|
|
return borderProps;
|
|
};
|
|
|
|
/**
|
|
* Generates a border line shorthand property if all line components are present.
|
|
*
|
|
* @param {Map} items - The map of collected property items.
|
|
* @param {string} property - The shorthand property name to generate.
|
|
* @param {string} [priority=""] - The priority of the property.
|
|
* @returns {Array} A key-value pair for the generated property.
|
|
*/
|
|
const generateBorderLineShorthand = (items, property, priority = "") => {
|
|
const values = [];
|
|
for (const [, item] of items) {
|
|
const { value: itemValue } = item;
|
|
values.push(itemValue);
|
|
}
|
|
const value = getPositionValue(values);
|
|
return [property, createPropertyItem(property, value, priority)];
|
|
};
|
|
|
|
/**
|
|
* Generates a border position shorthand property if all position components are present.
|
|
*
|
|
* @param {Map} items - The map of collected property items.
|
|
* @param {string} property - The shorthand property name to generate.
|
|
* @param {string} [priority=""] - The priority of the property.
|
|
* @returns {Array} A key-value pair for the generated property.
|
|
*/
|
|
const generateBorderPositionShorthand = (items, property, priority = "") => {
|
|
const values = [];
|
|
for (const [, item] of items) {
|
|
const { value: itemValue } = item;
|
|
values.push(itemValue);
|
|
}
|
|
const value = values.join(" ");
|
|
return [property, createPropertyItem(property, value, priority)];
|
|
};
|
|
|
|
/**
|
|
* Generates a border shorthand property if all components match.
|
|
*
|
|
* @param {Array} items - The collection of property values.
|
|
* @param {string} property - The shorthand property name to generate.
|
|
* @param {string} [priority=""] - The priority of the property.
|
|
* @returns {Array|undefined} A key-value pair for the generated property or undefined.
|
|
*/
|
|
const generateBorderShorthand = (items, property, priority = "") => {
|
|
const values = new Set(items);
|
|
if (values.size === 1) {
|
|
const value = values.keys().next().value;
|
|
return [property, createPropertyItem(property, value, priority)];
|
|
}
|
|
};
|
|
|
|
const borderCollectionConfig = {
|
|
[WIDTH]: {
|
|
shorthand: borderWidth.property,
|
|
generator: generateBorderLineShorthand
|
|
},
|
|
[STYLE]: {
|
|
shorthand: borderStyle.property,
|
|
generator: generateBorderLineShorthand
|
|
},
|
|
[COLOR]: {
|
|
shorthand: borderColor.property,
|
|
generator: generateBorderLineShorthand
|
|
},
|
|
[TOP]: {
|
|
shorthand: borderTop.property,
|
|
generator: generateBorderPositionShorthand
|
|
},
|
|
[RIGHT]: {
|
|
shorthand: borderRight.property,
|
|
generator: generateBorderPositionShorthand
|
|
},
|
|
[BOTTOM]: {
|
|
shorthand: borderBottom.property,
|
|
generator: generateBorderPositionShorthand
|
|
},
|
|
[LEFT]: {
|
|
shorthand: borderLeft.property,
|
|
generator: generateBorderPositionShorthand
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Processes and consolidates border-related longhands into shorthands where possible.
|
|
*
|
|
* @param {Map} properties - The map of current properties.
|
|
* @returns {Map} The updated map with consolidated border properties.
|
|
*/
|
|
const prepareBorderShorthands = (properties) => {
|
|
const borderCollections = {};
|
|
for (const key of Object.keys(borderCollectionConfig)) {
|
|
borderCollections[key] = {
|
|
...borderCollectionConfig[key],
|
|
items: new Map(),
|
|
priorityItems: new Map()
|
|
};
|
|
}
|
|
for (const [property, item] of properties) {
|
|
const { priority, value } = item;
|
|
let positionPart, linePart;
|
|
// We can assume property starts with "border-"
|
|
const firstHyphen = property.indexOf("-");
|
|
if (firstHyphen !== -1) {
|
|
const remainder = property.substring(firstHyphen + 1);
|
|
const secondHyphen = remainder.indexOf("-");
|
|
if (secondHyphen !== -1) {
|
|
positionPart = remainder.substring(0, secondHyphen);
|
|
linePart = remainder.substring(secondHyphen + 1);
|
|
} else {
|
|
positionPart = remainder;
|
|
linePart = undefined;
|
|
}
|
|
}
|
|
if (linePart && borderCollections[linePart]) {
|
|
const collection = borderCollections[linePart];
|
|
if (priority) {
|
|
collection.priorityItems.set(property, { property, value, priority });
|
|
} else {
|
|
collection.items.set(property, { property, value, priority });
|
|
}
|
|
}
|
|
if (positionPart && borderCollections[positionPart]) {
|
|
const collection = borderCollections[positionPart];
|
|
if (priority) {
|
|
collection.priorityItems.set(property, { property, value, priority });
|
|
} else {
|
|
collection.items.set(property, { property, value, priority });
|
|
}
|
|
}
|
|
}
|
|
const shorthandItems = [];
|
|
const shorthandPriorityItems = [];
|
|
for (const [key, collection] of Object.entries(borderCollections)) {
|
|
const { shorthand, generator, items, priorityItems } = collection;
|
|
const requiredSize = borderLines.has(key) ? 4 : 3;
|
|
if (items.size === requiredSize) {
|
|
const [property, item] = generator(items, shorthand) ?? [];
|
|
if (property && item) {
|
|
properties.set(property, item);
|
|
if (borderPositions.has(key) && properties.has(BORDER_IMAGE)) {
|
|
const { value: imageValue } = properties.get(BORDER_IMAGE);
|
|
if (imageValue === NONE) {
|
|
shorthandItems.push(item.value);
|
|
}
|
|
}
|
|
}
|
|
} else if (priorityItems.size === requiredSize) {
|
|
const [property, item] = generator(priorityItems, shorthand, "important") ?? [];
|
|
if (property && item) {
|
|
properties.set(property, item);
|
|
if (borderPositions.has(key) && properties.has(BORDER_IMAGE)) {
|
|
const { value: imageValue } = properties.get(BORDER_IMAGE);
|
|
if (imageValue === NONE) {
|
|
shorthandPriorityItems.push(item.value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const mixedPriorities = shorthandItems.length && shorthandPriorityItems.length;
|
|
const imageItem = createPropertyItem(BORDER_IMAGE, NONE);
|
|
if (shorthandItems.length === 4) {
|
|
const [property, item] = generateBorderShorthand(shorthandItems, border.property) ?? [];
|
|
if (property && item) {
|
|
properties.set(property, item);
|
|
properties.delete(BORDER_IMAGE);
|
|
properties.set(BORDER_IMAGE, imageItem);
|
|
}
|
|
} else if (shorthandPriorityItems.length === 4) {
|
|
const [property, item] =
|
|
generateBorderShorthand(shorthandPriorityItems, border.property, "important") ?? [];
|
|
if (property && item) {
|
|
properties.set(property, item);
|
|
properties.delete(BORDER_IMAGE);
|
|
properties.set(BORDER_IMAGE, imageItem);
|
|
}
|
|
} else if (properties.has(BORDER_IMAGE)) {
|
|
const { value: imageValue } = properties.get(BORDER_IMAGE);
|
|
if (imageValue === NONE) {
|
|
if (mixedPriorities) {
|
|
properties.delete(BORDER_IMAGE);
|
|
properties.set(BORDER_IMAGE, imageItem);
|
|
} else {
|
|
properties.delete(BORDER_IMAGE);
|
|
}
|
|
}
|
|
}
|
|
if (mixedPriorities) {
|
|
const items = [];
|
|
const priorityItems = [];
|
|
for (const item of properties) {
|
|
const [, { priority }] = item;
|
|
if (priority) {
|
|
priorityItems.push(item);
|
|
} else {
|
|
items.push(item);
|
|
}
|
|
}
|
|
const firstPropertyKey = properties.keys().next().value;
|
|
const { priority: firstPropertyPriority } = properties.get(firstPropertyKey);
|
|
if (firstPropertyPriority) {
|
|
return new Map([...priorityItems, ...items]);
|
|
}
|
|
return new Map([...items, ...priorityItems]);
|
|
}
|
|
if (properties.has(BORDER_IMAGE)) {
|
|
properties.delete(BORDER_IMAGE);
|
|
properties.set(BORDER_IMAGE, imageItem);
|
|
}
|
|
return properties;
|
|
};
|
|
|
|
/**
|
|
* Processes shorthand properties from the shorthands map.
|
|
*
|
|
* @param {Map} shorthands - The map containing shorthand property groups.
|
|
* @returns {Map} A map of processed shorthand properties.
|
|
*/
|
|
const processShorthandProperties = (shorthands) => {
|
|
const shorthandItems = new Map();
|
|
for (const [property, item] of shorthands) {
|
|
const shorthandItem = shorthandProperties.get(property);
|
|
if (item.size === shorthandItem.shorthandFor.size && shorthandItem.position) {
|
|
const positionValues = [];
|
|
let priority = "";
|
|
for (const { value: longhandValue, priority: longhandPriority } of item.values()) {
|
|
positionValues.push(longhandValue);
|
|
if (longhandPriority) {
|
|
priority = longhandPriority;
|
|
}
|
|
}
|
|
const value = getPositionValue(positionValues, shorthandItem.position);
|
|
shorthandItems.set(property, createPropertyItem(property, value, priority));
|
|
}
|
|
}
|
|
return shorthandItems;
|
|
};
|
|
|
|
/**
|
|
* Updates the longhand properties map with a new property item.
|
|
* If a property with normal priority already exists, it will be overwritten by the new item.
|
|
* If the existing property has "important" priority, it will not be overwritten.
|
|
*
|
|
* @param {string} property - The CSS property name.
|
|
* @param {Object} item - The property item object containing value and priority.
|
|
* @param {Map} longhandProperties - The map of longhand properties to update.
|
|
*/
|
|
const updateLonghandProperties = (property, item, longhandProperties) => {
|
|
if (longhandProperties.has(property)) {
|
|
const { priority: longhandPriority } = longhandProperties.get(property);
|
|
if (!longhandPriority) {
|
|
longhandProperties.delete(property);
|
|
longhandProperties.set(property, item);
|
|
}
|
|
} else {
|
|
longhandProperties.set(property, item);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Processes border properties from the borders map, expanding and normalizing them.
|
|
*
|
|
* @param {Map} borders - The map containing accumulated border properties.
|
|
* @param {Object} parseOpt - Options for parsing values.
|
|
* @returns {Map} A map of fully processed and normalized border properties.
|
|
*/
|
|
const processBorderProperties = (borders, parseOpt) => {
|
|
const longhandProperties = new Map();
|
|
for (const [property, item] of borders) {
|
|
if (shorthandProperties.has(property)) {
|
|
const { value, priority } = item;
|
|
if (property === border.property) {
|
|
const lineItems = border.parse(value, parseOpt);
|
|
for (const [key, initialValue] of border.initialValues) {
|
|
if (!Object.hasOwn(lineItems, key)) {
|
|
lineItems[key] = initialValue;
|
|
}
|
|
}
|
|
for (const lineProperty of Object.keys(lineItems)) {
|
|
let namePart, linePart;
|
|
const hyphenIndex = lineProperty.indexOf("-");
|
|
if (hyphenIndex !== -1) {
|
|
namePart = lineProperty.substring(0, hyphenIndex);
|
|
linePart = lineProperty.substring(hyphenIndex + 1);
|
|
} else {
|
|
// fallback for safety, though lineProperty from border.parse keys
|
|
// should have hyphen
|
|
namePart = lineProperty;
|
|
linePart = "";
|
|
}
|
|
const lineValue = lineItems[lineProperty];
|
|
for (const position of borderPositions) {
|
|
const longhandProperty = `${namePart}-${position}-${linePart}`;
|
|
const longhandItem = createPropertyItem(longhandProperty, lineValue, priority);
|
|
updateLonghandProperties(longhandProperty, longhandItem, longhandProperties);
|
|
}
|
|
}
|
|
if (value) {
|
|
longhandProperties.set(BORDER_IMAGE, createPropertyItem(BORDER_IMAGE, NONE, priority));
|
|
}
|
|
} else {
|
|
const shorthandItem = shorthandProperties.get(property);
|
|
const parsedItem = shorthandItem.parse(value, parseOpt);
|
|
if (Array.isArray(parsedItem)) {
|
|
let namePart, linePart;
|
|
const hyphenIndex = property.indexOf("-");
|
|
if (hyphenIndex !== -1) {
|
|
namePart = property.substring(0, hyphenIndex);
|
|
linePart = property.substring(hyphenIndex + 1);
|
|
} else {
|
|
namePart = property;
|
|
}
|
|
for (const position of borderPositions) {
|
|
const longhandProperty = `${namePart}-${position}-${linePart}`;
|
|
const longhandValue = getPositionValue(parsedItem, position);
|
|
const longhandItem = createPropertyItem(longhandProperty, longhandValue, priority);
|
|
updateLonghandProperties(longhandProperty, longhandItem, longhandProperties);
|
|
}
|
|
} else if (parsedItem) {
|
|
for (const [key, initialValue] of shorthandItem.initialValues) {
|
|
if (!Object.hasOwn(parsedItem, key)) {
|
|
parsedItem[key] = initialValue;
|
|
}
|
|
}
|
|
for (const longhandProperty of Object.keys(parsedItem)) {
|
|
const longhandValue = parsedItem[longhandProperty];
|
|
const longhandItem = createPropertyItem(longhandProperty, longhandValue, priority);
|
|
updateLonghandProperties(longhandProperty, longhandItem, longhandProperties);
|
|
}
|
|
}
|
|
}
|
|
} else if (longhandProperties.has(property)) {
|
|
const { priority } = longhandProperties.get(property);
|
|
if (!priority) {
|
|
longhandProperties.delete(property);
|
|
longhandProperties.set(property, item);
|
|
}
|
|
} else {
|
|
longhandProperties.set(property, item);
|
|
}
|
|
}
|
|
const borderItems = prepareBorderShorthands(longhandProperties);
|
|
return borderItems;
|
|
};
|
|
|
|
/**
|
|
* Normalize and prepare CSS properties, handling shorthands and longhands.
|
|
*
|
|
* @param {Map} properties - The initial map of properties.
|
|
* @param {Object} [opt={}] - Parsing options.
|
|
* @returns {Map} The normalized map of properties.
|
|
*/
|
|
const prepareProperties = (properties, opt = {}) => {
|
|
const { globalObject, options } = opt;
|
|
const parseOpt = {
|
|
globalObject,
|
|
options
|
|
};
|
|
const parsedProperties = new Map();
|
|
const shorthands = new Map();
|
|
const borders = new Map();
|
|
let hasPrecedingBackground = false;
|
|
for (const [property, item] of properties) {
|
|
const { value, priority } = item;
|
|
const { logicalPropertyGroup: shorthandProperty } = propertyDefinitions.get(property) ?? {};
|
|
if (borderProperties.has(property)) {
|
|
borders.set(property, { property, value, priority });
|
|
} else if (shorthandProperties.has(shorthandProperty)) {
|
|
if (!shorthands.has(shorthandProperty)) {
|
|
shorthands.set(shorthandProperty, new Map());
|
|
}
|
|
const longhandItems = shorthands.get(shorthandProperty);
|
|
if (longhandItems.size) {
|
|
const firstPropertyKey = longhandItems.keys().next().value;
|
|
const { priority: firstPropertyPriority } = longhandItems.get(firstPropertyKey);
|
|
if (priority === firstPropertyPriority) {
|
|
longhandItems.set(property, { property, value, priority });
|
|
shorthands.set(shorthandProperty, longhandItems);
|
|
} else {
|
|
parsedProperties.delete(shorthandProperty);
|
|
}
|
|
} else {
|
|
longhandItems.set(property, { property, value, priority });
|
|
shorthands.set(shorthandProperty, longhandItems);
|
|
}
|
|
parsedProperties.set(property, item);
|
|
} else if (shorthandProperties.has(property)) {
|
|
const shorthandItem = shorthandProperties.get(property);
|
|
const parsedValues = shorthandItem.parse(value, parseOpt);
|
|
let omitShorthandProperty = false;
|
|
if (Array.isArray(parsedValues)) {
|
|
const [parsedValue] = parsedValues;
|
|
if (typeof parsedValue === "string") {
|
|
for (const [longhandProperty, longhandItem] of shorthandItem.shorthandFor) {
|
|
if (!priority && properties.has(longhandProperty)) {
|
|
const { priority: longhandPriority } = properties.get(longhandProperty);
|
|
if (longhandPriority) {
|
|
omitShorthandProperty = true;
|
|
continue;
|
|
}
|
|
}
|
|
const { position } = longhandItem;
|
|
const longhandValue = getPositionValue([parsedValue], position);
|
|
parsedProperties.set(
|
|
longhandProperty,
|
|
createPropertyItem(longhandProperty, longhandValue, priority)
|
|
);
|
|
}
|
|
} else if (parsedValue) {
|
|
for (const longhandProperty of Object.keys(parsedValue)) {
|
|
const longhandValue = parsedValue[longhandProperty];
|
|
parsedProperties.set(
|
|
longhandProperty,
|
|
createPropertyItem(longhandProperty, longhandValue, priority)
|
|
);
|
|
}
|
|
}
|
|
} else if (parsedValues && typeof parsedValues !== "string") {
|
|
for (const longhandProperty of Object.keys(parsedValues)) {
|
|
const longhandValue = parsedValues[longhandProperty];
|
|
parsedProperties.set(
|
|
longhandProperty,
|
|
createPropertyItem(longhandProperty, longhandValue, priority)
|
|
);
|
|
}
|
|
}
|
|
if (!omitShorthandProperty) {
|
|
if (property === background.property) {
|
|
hasPrecedingBackground = true;
|
|
}
|
|
parsedProperties.set(property, createPropertyItem(property, value, priority));
|
|
}
|
|
} else {
|
|
parsedProperties.set(property, createPropertyItem(property, value, priority));
|
|
if (hasPrecedingBackground) {
|
|
const { value: shorthandValue, priority: shorthandPriority } = properties.get(
|
|
background.property
|
|
);
|
|
if ((!shorthandPriority || priority) && !hasVarFunc(shorthandValue)) {
|
|
const replacedShorthandValue = replaceBackgroundShorthand(
|
|
property,
|
|
parsedProperties,
|
|
parseOpt
|
|
);
|
|
properties.delete(background.property);
|
|
properties.set(
|
|
background.property,
|
|
createPropertyItem(background.property, replacedShorthandValue, shorthandPriority)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (shorthands.size) {
|
|
const shorthandItems = processShorthandProperties(shorthands);
|
|
for (const [property, item] of shorthandItems) {
|
|
parsedProperties.set(property, item);
|
|
}
|
|
}
|
|
if (borders.size) {
|
|
const borderItems = processBorderProperties(borders, parseOpt);
|
|
for (const [property, item] of borderItems) {
|
|
parsedProperties.set(property, item);
|
|
}
|
|
}
|
|
return parsedProperties;
|
|
};
|
|
|
|
/**
|
|
* Cleans up redundancy in border properties by removing longhands that are covered by shorthands.
|
|
*
|
|
* @param {Map} properties - The map of properties to normalize.
|
|
* @returns {Map} The normalized properties map.
|
|
*/
|
|
const normalizeProperties = (properties) => {
|
|
if (properties.has(border.property)) {
|
|
for (const line of borderLines) {
|
|
properties.delete(`${border.property}-${line}`);
|
|
}
|
|
for (const position of borderPositions) {
|
|
properties.delete(`${border.property}-${position}`);
|
|
for (const line of borderLines) {
|
|
properties.delete(`${border.property}-${position}-${line}`);
|
|
}
|
|
}
|
|
properties.delete(`${border.property}-image`);
|
|
}
|
|
for (const line of borderLines) {
|
|
const lineProperty = `${border.property}-${line}`;
|
|
if (properties.has(lineProperty)) {
|
|
for (const position of borderPositions) {
|
|
const positionProperty = `${border.property}-${position}`;
|
|
const longhandProperty = `${border.property}-${position}-${line}`;
|
|
properties.delete(positionProperty);
|
|
properties.delete(longhandProperty);
|
|
}
|
|
}
|
|
}
|
|
for (const position of borderPositions) {
|
|
const positionProperty = `${border.property}-${position}`;
|
|
if (properties.has(positionProperty)) {
|
|
const longhandProperties = [];
|
|
for (const line of borderLines) {
|
|
const longhandProperty = `${border.property}-${position}-${line}`;
|
|
longhandProperties.push(longhandProperty);
|
|
}
|
|
if (longhandProperties.length === 3) {
|
|
for (const longhandProperty of longhandProperties) {
|
|
properties.delete(longhandProperty);
|
|
}
|
|
} else {
|
|
properties.delete(positionProperty);
|
|
}
|
|
}
|
|
}
|
|
return properties;
|
|
};
|
|
|
|
module.exports = {
|
|
borderProperties,
|
|
getPositionValue,
|
|
normalizeProperties,
|
|
prepareBorderProperties,
|
|
prepareProperties,
|
|
shorthandProperties
|
|
};
|