happy-dom
Version:
Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG DOM and HTML.
795 lines (718 loc) • 25.3 kB
text/typescript
import CSSStyleDeclarationValueParser from './CSSStyleDeclarationValueParser.js';
import ICSSStyleDeclarationPropertyValue from './ICSSStyleDeclarationPropertyValue.js';
/**
* Computed style property parser.
*/
export default class CSSStyleDeclarationPropertyGetParser {
/**
* Returns margin.
*
* @param properties Properties.
* @returns Property value
*/
public static getMargin(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getPaddingLikeProperty(
['margin-top', 'margin-right', 'margin-bottom', 'margin-left'],
properties
);
}
/**
* Returns padding.
*
* @param properties Properties.
* @returns Property value
*/
public static getPadding(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getPaddingLikeProperty(
['padding-top', 'padding-right', 'padding-bottom', 'padding-left'],
properties
);
}
/**
* Returns outline.
*
* @param properties Properties.
* @returns Property value
*/
public static getOutline(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
if (
!properties['outline-color']?.value ||
!properties['outline-style']?.value ||
!properties['outline-width']?.value
) {
return null;
}
const important =
properties['outline-color'].important &&
properties['outline-style'].important &&
properties['outline-width'].important;
if (
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['outline-width'].value) &&
properties['outline-width'].value === properties['outline-style'].value &&
properties['outline-width'].value === properties['outline-color'].value
) {
return {
important,
value: properties['outline-width'].value
};
}
const values = [];
if (!CSSStyleDeclarationValueParser.getInitial(properties['outline-color']?.value)) {
values.push(properties['outline-color'].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['outline-style']?.value)) {
values.push(properties['outline-style'].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['outline-width'].value)) {
values.push(properties['outline-width'].value);
}
return {
important,
value: values.join(' ')
};
}
/**
* Returns border.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorder(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
if (
!properties['border-top-width']?.value ||
properties['border-top-width']?.value !== properties['border-right-width']?.value ||
properties['border-top-width']?.value !== properties['border-bottom-width']?.value ||
properties['border-top-width']?.value !== properties['border-left-width']?.value ||
!properties['border-top-style']?.value ||
properties['border-top-style']?.value !== properties['border-right-style']?.value ||
properties['border-top-style']?.value !== properties['border-bottom-style']?.value ||
properties['border-top-style']?.value !== properties['border-left-style']?.value ||
!properties['border-top-color']?.value ||
properties['border-top-color']?.value !== properties['border-right-color']?.value ||
properties['border-top-color']?.value !== properties['border-bottom-color']?.value ||
properties['border-top-color']?.value !== properties['border-left-color']?.value ||
!properties['border-image-source']?.value ||
!properties['border-image-slice']?.value ||
!properties['border-image-width']?.value ||
!properties['border-image-outset']?.value ||
!properties['border-image-repeat']?.value
) {
return null;
}
const important =
properties['border-top-width'].important &&
properties['border-right-width'].important &&
properties['border-bottom-width'].important &&
properties['border-left-width'].important &&
properties['border-top-style'].important &&
properties['border-right-style'].important &&
properties['border-bottom-style'].important &&
properties['border-left-style'].important &&
properties['border-top-color'].important &&
properties['border-right-color'].important &&
properties['border-bottom-color'].important &&
properties['border-left-color'].important &&
properties['border-image-source'].important &&
properties['border-image-slice'].important &&
properties['border-image-width'].important &&
properties['border-image-outset'].important &&
properties['border-image-repeat'].important;
if (
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['border-top-width'].value) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['border-top-style'].value) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['border-top-color'].value) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['border-image-source'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['border-image-slice'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['border-image-width'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['border-image-outset'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['border-image-repeat'].value)
) {
if (
properties['border-top-width'].value !== properties['border-top-style'].value ||
properties['border-top-width'].value !== properties['border-top-color'].value ||
properties['border-top-width'].value !== properties['border-image-source'].value ||
properties['border-top-width'].value !== properties['border-image-slice'].value ||
properties['border-top-width'].value !== properties['border-image-width'].value ||
properties['border-top-width'].value !== properties['border-image-outset'].value ||
properties['border-top-width'].value !== properties['border-image-repeat'].value
) {
return null;
}
return {
important,
value: properties['border-top-width'].value
};
}
const values = [];
if (!CSSStyleDeclarationValueParser.getInitial(properties['border-top-width'].value)) {
values.push(properties['border-top-width'].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['border-top-style'].value)) {
values.push(properties['border-top-style'].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['border-top-color'].value)) {
values.push(properties['border-top-color'].value);
}
return {
important,
value: values.join(' ')
};
}
/**
* Returns border.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderTop(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getBorderTopRightBottomLeft('top', properties);
}
/**
* Returns border.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderRight(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getBorderTopRightBottomLeft('right', properties);
}
/**
* Returns border.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderBottom(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getBorderTopRightBottomLeft('bottom', properties);
}
/**
* Returns border.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderLeft(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getBorderTopRightBottomLeft('left', properties);
}
/**
* Returns border.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderColor(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getPaddingLikeProperty(
['border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color'],
properties
);
}
/**
* Returns border.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderWidth(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getPaddingLikeProperty(
['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'],
properties
);
}
/**
* Returns border.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderStyle(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getPaddingLikeProperty(
['border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style'],
properties
);
}
/**
* Returns border radius.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderRadius(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
return this.getPaddingLikeProperty(
[
'border-top-left-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius'
],
properties
);
}
/**
* Returns border image.
*
* @param properties Properties.
* @returns Property value
*/
public static getBorderImage(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
if (
!properties['border-image-source']?.value ||
!properties['border-image-slice']?.value ||
!properties['border-image-width']?.value ||
!properties['border-image-outset']?.value ||
!properties['border-image-repeat']?.value
) {
return null;
}
const important =
properties['border-image-source'].important &&
properties['border-image-slice'].important &&
properties['border-image-width'].important &&
properties['border-image-outset'].important &&
properties['border-image-repeat'].important;
if (
CSSStyleDeclarationValueParser.getGlobal(properties['border-image-source'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['border-image-slice'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['border-image-width'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['border-image-outset'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['border-image-repeat'].value)
) {
if (
properties['border-image-source'].value !== properties['border-image-slice'].value ||
properties['border-image-source'].value !== properties['border-image-width'].value ||
properties['border-image-source'].value !== properties['border-image-outset'].value ||
properties['border-image-source'].value !== properties['border-image-repeat'].value
) {
return null;
}
return {
important,
value: properties['border-image-source'].value
};
}
return {
important,
value: `${properties['border-image-source'].value} ${properties['border-image-slice'].value} / ${properties['border-image-width'].value} / ${properties['border-image-outset'].value} ${properties['border-image-repeat'].value}`
};
}
/**
* Returns background.
*
* @param properties Properties.
* @returns Property value
*/
public static getBackground(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
if (
!properties['background-image']?.value ||
!properties['background-repeat']?.value ||
!properties['background-attachment']?.value ||
!properties['background-position-x']?.value ||
!properties['background-position-y']?.value ||
!properties['background-color']?.value ||
!properties['background-size']?.value ||
!properties['background-origin']?.value ||
!properties['background-clip']?.value
) {
return null;
}
const important =
properties['background-image'].important &&
properties['background-repeat'].important &&
properties['background-attachment'].important &&
properties['background-position-x'].important &&
properties['background-position-y'].important &&
properties['background-color'].important &&
properties['background-size'].important &&
properties['background-origin'].important &&
properties['background-clip'].important;
if (
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['background-image'].value) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['background-repeat'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['background-attachment'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['background-position-x'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['background-position-y'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['background-color'].value) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['background-size'].value) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties['background-origin'].value
) ||
CSSStyleDeclarationValueParser.getGlobalExceptInitial(properties['background-clip'].value)
) {
if (
properties['background-image'].value !== properties['background-repeat'].value ||
properties['background-image'].value !== properties['background-attachment'].value ||
properties['background-image'].value !== properties['background-position-x'].value ||
properties['background-image'].value !== properties['background-position-y'].value ||
properties['background-image'].value !== properties['background-color'].value ||
properties['background-image'].value !== properties['background-size'].value ||
properties['background-image'].value !== properties['background-origin'].value ||
properties['background-image'].value !== properties['background-clip'].value
) {
return null;
}
return {
important,
value: properties['background-image'].value
};
}
const values = [];
if (!CSSStyleDeclarationValueParser.getInitial(properties['background-image'].value)) {
values.push(properties['background-image'].value);
}
if (
!CSSStyleDeclarationValueParser.getInitial(properties['background-position-x'].value) &&
!CSSStyleDeclarationValueParser.getInitial(properties['background-position-y'].value) &&
!CSSStyleDeclarationValueParser.getInitial(properties['background-size'].value)
) {
values.push(
`${properties['background-position-x'].value} ${properties['background-position-y'].value} / ${properties['background-size'].value}`
);
} else if (
!CSSStyleDeclarationValueParser.getInitial(properties['background-position-x'].value) &&
!CSSStyleDeclarationValueParser.getInitial(properties['background-position-y'].value)
) {
values.push(
`${properties['background-position-x'].value} ${properties['background-position-y'].value}`
);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['background-repeat'].value)) {
values.push(properties['background-repeat'].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['background-attachment'].value)) {
values.push(properties['background-attachment'].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['background-origin'].value)) {
values.push(properties['background-origin'].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['background-clip'].value)) {
values.push(properties['background-clip'].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties['background-color'].value)) {
values.push(properties['background-color'].value);
}
return {
important,
value: values.join(' ')
};
}
/**
* Returns background position.
*
* @param properties Properties.
* @returns Property value
*/
public static getBackgroundPosition(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
if (
!properties['background-position-x']?.value ||
!properties['background-position-y']?.value
) {
return null;
}
const important =
properties['background-position-x'].important &&
properties['background-position-y'].important;
if (
CSSStyleDeclarationValueParser.getGlobal(properties['background-position-x'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['background-position-y'].value)
) {
if (properties['background-position-x'].value !== properties['background-position-y'].value) {
return null;
}
return {
important,
value: properties['background-position-x'].value
};
}
const positionX = properties['background-position-x'].value.replace(/ *, */g, ',').split(',');
const positionY = properties['background-position-y'].value.replace(/ *, */g, ',').split(',');
const parts = [];
for (let i = 0; i < positionX.length; i++) {
parts.push(`${positionX[i]} ${positionY[i]}`);
}
return {
important,
value: parts.join(', ')
};
}
/**
* Returns flex.
*
* @param properties Properties.
* @returns Property value
*/
public static getFlex(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
if (
!properties['flex-grow']?.value ||
!properties['flex-shrink']?.value ||
!properties['flex-basis']?.value
) {
return null;
}
const important =
properties['flex-grow'].important &&
properties['flex-shrink'].important &&
properties['flex-basis'].important;
if (
CSSStyleDeclarationValueParser.getGlobal(properties['flex-grow'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['flex-shrink'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['flex-basis'].value)
) {
if (
properties['flex-grow'].value !== properties['flex-shrink'].value ||
properties['flex-grow'].value !== properties['flex-basis'].value
) {
return null;
}
return {
important,
value: properties['flex-grow'].value
};
}
return {
important,
value: `${properties['flex-grow'].value} ${properties['flex-shrink'].value} ${properties['flex-basis'].value}`
};
}
/**
* Returns flex.
*
* @param properties Properties.
* @returns Property value
*/
public static getFont(properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}): ICSSStyleDeclarationPropertyValue {
if (
!properties['font-size']?.value ||
!properties['font-family']?.value ||
!properties['font-weight']?.value ||
!properties['font-style']?.value ||
!properties['font-variant']?.value ||
!properties['font-stretch']?.value ||
!properties['line-height']?.value
) {
return null;
}
const important =
properties['font-size'].important &&
properties['font-family'].important &&
properties['font-weight'].important &&
properties['font-style'].important &&
properties['font-variant'].important &&
properties['font-stretch'].important &&
properties['line-height'].important;
if (
CSSStyleDeclarationValueParser.getGlobal(properties['font-size'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['font-family'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['font-weight'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['font-style'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['font-variant'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['font-stretch'].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties['line-height'].value)
) {
if (
properties['font-size'].value !== properties['font-family'].value ||
properties['font-size'].value !== properties['font-weight'].value ||
properties['font-size'].value !== properties['font-style'].value ||
properties['font-size'].value !== properties['font-variant'].value ||
properties['font-size'].value !== properties['font-stretch'].value ||
properties['font-size'].value !== properties['line-height'].value
) {
return null;
}
return {
important,
value: properties['font-size'].value
};
}
const values = [];
if (properties['font-style'].value !== 'normal') {
values.push(properties['font-style'].value);
}
if (properties['font-variant'].value !== 'normal') {
values.push(properties['font-variant'].value);
}
if (properties['font-weight'].value !== 'normal') {
values.push(properties['font-weight'].value);
}
if (properties['font-stretch'].value !== 'normal') {
values.push(properties['font-stretch'].value);
}
if (properties['line-height'].value !== 'normal') {
values.push(`${properties['font-size'].value} / ${properties['line-height'].value}`);
} else {
values.push(properties['font-size'].value);
}
values.push(properties['font-family'].value);
return {
important,
value: values.join(' ')
};
}
/**
* Returns border.
*
* @param properties Properties.
* @param position
* @returns Property value
*/
private static getBorderTopRightBottomLeft(
position: 'top' | 'right' | 'bottom' | 'left',
properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}
): ICSSStyleDeclarationPropertyValue {
if (
!properties[`border-${position}-width`]?.value ||
!properties[`border-${position}-style`]?.value ||
!properties[`border-${position}-color`]?.value
) {
return null;
}
const important =
properties[`border-${position}-width`].important &&
properties[`border-${position}-style`].important &&
properties[`border-${position}-color`].important;
if (
CSSStyleDeclarationValueParser.getGlobalExceptInitial(
properties[`border-${position}-width`].value
) &&
properties[`border-${position}-width`].value ===
properties[`border-${position}-style`].value &&
properties[`border-${position}-width`].value === properties[`border-${position}-color`].value
) {
return {
important,
value: properties[`border-${position}-width`].value
};
}
const values = [];
if (!CSSStyleDeclarationValueParser.getInitial(properties[`border-${position}-width`].value)) {
values.push(properties[`border-${position}-width`].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties[`border-${position}-style`]?.value)) {
values.push(properties[`border-${position}-style`].value);
}
if (!CSSStyleDeclarationValueParser.getInitial(properties[`border-${position}-color`]?.value)) {
values.push(properties[`border-${position}-color`].value);
}
return {
important,
value: values.join(' ')
};
}
/**
* Returns a padding like property.
*
* @param properties Properties.
* @param position
* @param propertyNames
* @returns Property value
*/
private static getPaddingLikeProperty(
propertyNames: [string, string, string, string],
properties: {
[k: string]: ICSSStyleDeclarationPropertyValue;
}
): ICSSStyleDeclarationPropertyValue {
if (
!properties[propertyNames[0]]?.value ||
!properties[propertyNames[1]]?.value ||
!properties[propertyNames[2]]?.value ||
!properties[propertyNames[3]]?.value
) {
return null;
}
const important =
properties[propertyNames[0]].important &&
properties[propertyNames[1]].important &&
properties[propertyNames[2]].important &&
properties[propertyNames[3]].important;
if (
CSSStyleDeclarationValueParser.getGlobal(properties[propertyNames[0]].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties[propertyNames[1]].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties[propertyNames[2]].value) ||
CSSStyleDeclarationValueParser.getGlobal(properties[propertyNames[3]].value)
) {
if (
properties[propertyNames[0]].value !== properties[propertyNames[1]].value ||
properties[propertyNames[0]].value !== properties[propertyNames[2]].value ||
properties[propertyNames[0]].value !== properties[propertyNames[3]].value
) {
return null;
}
return {
important,
value: properties[propertyNames[0]].value
};
}
const values = [properties[propertyNames[0]].value];
if (
properties[propertyNames[1]].value !== properties[propertyNames[0]].value ||
properties[propertyNames[2]].value !== properties[propertyNames[0]].value ||
properties[propertyNames[3]].value !== properties[propertyNames[1]].value
) {
values.push(properties[propertyNames[1]].value);
}
if (
properties[propertyNames[2]].value !== properties[propertyNames[0]].value ||
properties[propertyNames[3]].value !== properties[propertyNames[1]].value
) {
values.push(properties[propertyNames[2]].value);
}
if (properties[propertyNames[3]].value !== properties[propertyNames[1]].value) {
values.push(properties[propertyNames[3]].value);
}
return {
important,
value: values.join(' ')
};
}
}