From d8452e68384af92cbb676264354629c945f1a3ab Mon Sep 17 00:00:00 2001 From: David Ortner Date: Fri, 19 Aug 2022 13:14:07 +0200 Subject: [PATCH] #344@trivial: Continue on CSSStyleDeclaration. --- .../CSSStyleDeclarationPropertyManager.ts | 474 +++++++--- .../CSSStyleDeclarationPropertyValueParser.ts | 864 ++++++++++++------ ...eDeclarationPropertyWriterPropertyNames.ts | 57 -- .../CSSStyleDeclarationValueParser.ts | 10 + 4 files changed, 960 insertions(+), 445 deletions(-) delete mode 100644 packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyWriterPropertyNames.ts diff --git a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyManager.ts b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyManager.ts index 89b04a191..7c7656d4f 100644 --- a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyManager.ts +++ b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyManager.ts @@ -1,6 +1,4 @@ import ICSSStyleDeclarationPropertyValue from './ICSSStyleDeclarationPropertyValue'; -import CSSStyleDeclarationValueParser from './CSSStyleDeclarationPropertyValueParser'; -import CSSStyleDeclarationPropertyManagerPropertyNames from './CSSStyleDeclarationPropertyManagerPropertyNames'; import CSSStyleDeclarationPropertyValueParser from './CSSStyleDeclarationPropertyValueParser'; /** @@ -52,21 +50,39 @@ export default class CSSStyleDeclarationPropertyManager { if (!this.properties['margin-top']?.value) { return ''; } - return `${this.properties['margin-top']?.value} ${this.properties['margin-right']?.value} ${this.properties['margin-bottom']?.value} ${this.properties['margin-left']?.value}` + return `${this.properties['margin-top'].value} ${ + this.properties['margin-right']?.value || '' + } ${ + this.properties['margin-top'].value !== this.properties['margin-bottom']?.value + ? this.properties['margin-bottom']?.value || '' + : '' + } ${ + this.properties['margin-right'].value !== this.properties['margin-left']?.value + ? this.properties['margin-left']?.value || '' + : '' + }` .replace(/ /g, '') .trim(); case 'padding': if (!this.properties['padding-top']?.value) { return ''; } - return `${this.properties['padding-top']?.value} ${this.properties['padding-right']?.value} ${this.properties['padding-bottom']?.value} ${this.properties['padding-left']?.value}` + return `${this.properties['padding-top'].value} ${ + this.properties['padding-right']?.value || '' + } ${ + this.properties['padding-top'].value !== this.properties['padding-bottom']?.value + ? this.properties['padding-bottom']?.value || '' + : '' + } ${ + this.properties['padding-right'].value !== this.properties['padding-left']?.value + ? this.properties['padding-left']?.value || '' + : '' + }` .replace(/ /g, '') .trim(); case 'border': if ( !this.properties['border-top-width']?.value || - !this.properties['border-top-style']?.value || - !this.properties['border-top-color']?.value || this.properties['border-right-width']?.value !== this.properties['border-top-width']?.value || this.properties['border-right-style']?.value !== @@ -87,43 +103,102 @@ export default class CSSStyleDeclarationPropertyManager { ) { return ''; } - return `${this.properties['border-top-width'].value} ${this.properties['border-top-style'].value} ${this.properties['border-top-color'].value}`; + return `${this.properties['border-top-width'].value} ${ + this.properties['border-top-style']?.value || '' + } ${this.properties['border-top-color']?.value || ''}` + .replace(/ /g, '') + .trim(); case 'border-left': - if ( - !this.properties['border-left-width']?.value || - !this.properties['border-left-style']?.value || - !this.properties['border-left-color']?.value - ) { + if (!this.properties['border-left-width']?.value) { return ''; } - return `${this.properties['border-left-width'].value} ${this.properties['border-left-style'].value} ${this.properties['border-left-color'].value}`; + return `${this.properties['border-left-width'].value} ${ + this.properties['border-left-style']?.value || '' + } ${this.properties['border-left-color']?.value || ''}` + .replace(/ /g, '') + .trim(); case 'border-right': + if (!this.properties['border-right-width']?.value) { + return ''; + } + return `${this.properties['border-right-width'].value} ${ + this.properties['border-right-style']?.value || '' + } ${this.properties['border-right-color']?.value || ''}` + .replace(/ /g, '') + .trim(); + case 'border-top': + if (!this.properties['border-top-width']?.value) { + return ''; + } + return `${this.properties['border-top-width'].value} ${ + this.properties['border-top-style']?.value || '' + } ${this.properties['border-top-color']?.value || ''}` + .replace(/ /g, '') + .trim(); + case 'border-bottom': + if (!this.properties['border-bottom-width']?.value) { + return ''; + } + return `${this.properties['border-bottom-width'].value} ${ + this.properties['border-bottom-style']?.value || '' + } ${this.properties['border-bottom-color']?.value || ''}` + .replace(/ /g, '') + .trim(); + case 'border-color': if ( - !this.properties['border-right-width']?.value || - !this.properties['border-right-style']?.value || - !this.properties['border-right-color']?.value + !this.properties['border-top-color']?.value || + this.properties['border-top-color']?.value !== + this.properties['border-right-color']?.value || + this.properties['border-top-color']?.value !== + this.properties['border-bottom-color']?.value || + this.properties['border-top-color']?.value !== this.properties['border-left-color']?.value ) { return ''; } - return `${this.properties['border-right-width'].value} ${this.properties['border-right-style'].value} ${this.properties['border-right-color'].value}`; - case 'border-top': + return this.properties['border-top-color'].value; + case 'border-style': if ( - !this.properties['border-top-width']?.value || !this.properties['border-top-style']?.value || - !this.properties['border-top-color']?.value + this.properties['border-top-style']?.value !== + this.properties['border-right-style']?.value || + this.properties['border-top-style']?.value !== + this.properties['border-bottom-style']?.value || + this.properties['border-top-style']?.value !== this.properties['border-left-style']?.value ) { return ''; } - return `${this.properties['border-top-width'].value} ${this.properties['border-top-style'].value} ${this.properties['border-top-color'].value}`; - case 'border-bottom': + return this.properties['border-top-style'].value; + case 'border-width': if ( - !this.properties['border-bottom-width']?.value || - !this.properties['border-bottom-style']?.value || - !this.properties['border-bottom-color']?.value + !this.properties['border-top-width']?.value || + this.properties['border-top-width']?.value !== + this.properties['border-right-width']?.value || + this.properties['border-top-width']?.value !== + this.properties['border-bottom-width']?.value || + this.properties['border-top-width']?.value !== this.properties['border-left-width']?.value ) { return ''; } - return `${this.properties['border-bottom-width'].value} ${this.properties['border-bottom-style'].value} ${this.properties['border-bottom-color'].value}`; + return this.properties['border-top-width'].value; + case 'border-radius': + if (!this.properties['border-top-left-radius']?.value) { + return ''; + } + return `${this.properties['border-top-left-radius'].value} ${ + this.properties['border-top-right-radius'].value || '' + } ${ + this.properties['border-top-left-radius'].value !== + this.properties['border-bottom-right-radius'].value + ? this.properties['border-bottom-right-radius'].value || '' + : '' + } ${ + this.properties['border-top-right-radius'].value !== + this.properties['border-bottom-left-radius'].value + ? this.properties['border-bottom-left-radius'].value || '' + : '' + }` + .replace(/ /g, '') + .trim(); case 'background': if ( !this.properties['background-color']?.value && @@ -131,7 +206,18 @@ export default class CSSStyleDeclarationPropertyManager { ) { return ''; } - return `${this.properties['background-color']?.value} ${this.properties['background-image']?.value} ${this.properties['background-repeat']?.value} ${this.properties['background-attachment']?.value} ${this.properties['background-position']?.value}` + return `${this.properties['background-color']?.value || ''} ${ + this.properties['background-image']?.value || '' + } ${this.properties['background-repeat']?.value || ''} ${ + this.properties['background-repeat']?.value + ? this.properties['background-attachment']?.value || '' + : '' + } ${ + this.properties['background-repeat']?.value && + this.properties['background-attachment']?.value + ? this.properties['background-position']?.value || '' + : '' + }` .replace(/ /g, '') .trim(); case 'flex': @@ -143,6 +229,20 @@ export default class CSSStyleDeclarationPropertyManager { return ''; } return `${this.properties['flex-grow'].value} ${this.properties['flex-shrink'].value} ${this.properties['flex-basis'].value}`; + case 'font': + if (this.properties['font']?.value) { + return this.properties['font'].value; + } + if (!this.properties['font-family']?.value) { + return ''; + } + return `${this.properties['font-family'].value} ${ + this.properties['font-size'].value || '' + } ${this.properties['font-style'].value || ''} ${ + this.properties['font-weight'].value || '' + }` + .replace(/ /g, '') + .trim(); } return this.properties[name]?.value || ''; @@ -154,12 +254,92 @@ export default class CSSStyleDeclarationPropertyManager { * @param name Property name. */ public remove(name: string): void { - if (CSSStyleDeclarationPropertyManagerPropertyNames[name]) { - for (const propertyName of CSSStyleDeclarationPropertyManagerPropertyNames[name]) { - delete this.properties[propertyName]; - } - } else { - delete this.properties[name]; + switch (name) { + case 'border': + delete this.properties['border-top-width']; + delete this.properties['border-right-width']; + delete this.properties['border-bottom-width']; + delete this.properties['border-left-width']; + delete this.properties['border-top-style']; + delete this.properties['border-right-style']; + delete this.properties['border-bottom-style']; + delete this.properties['border-left-style']; + delete this.properties['border-top-color']; + delete this.properties['border-right-color']; + delete this.properties['border-bottom-color']; + delete this.properties['border-left-color']; + break; + case 'border-left': + delete this.properties['border-left-width']; + delete this.properties['border-left-style']; + delete this.properties['border-left-color']; + break; + case 'border-bottom': + delete this.properties['border-bottom-width']; + delete this.properties['border-bottom-style']; + delete this.properties['border-bottom-color']; + break; + case 'border-right': + delete this.properties['border-right-width']; + delete this.properties['border-right-style']; + delete this.properties['border-right-color']; + break; + case 'border-top': + delete this.properties['border-top-width']; + delete this.properties['border-top-style']; + delete this.properties['border-top-color']; + break; + case 'border-width': + delete this.properties['border-top-width']; + delete this.properties['border-right-width']; + delete this.properties['border-bottom-width']; + delete this.properties['border-left-width']; + break; + case 'border-style': + delete this.properties['border-top-style']; + delete this.properties['border-right-style']; + delete this.properties['border-bottom-style']; + delete this.properties['border-left-style']; + break; + case 'border-color': + delete this.properties['border-top-color']; + delete this.properties['border-right-color']; + delete this.properties['border-bottom-color']; + delete this.properties['border-left-color']; + break; + case 'border-radius': + delete this.properties['border-top-left-radius']; + delete this.properties['border-top-right-radius']; + delete this.properties['border-bottom-right-radius']; + delete this.properties['border-bottom-left-radius']; + break; + case 'background': + delete this.properties['background-color']; + delete this.properties['background-image']; + delete this.properties['background-repeat']; + delete this.properties['background-attachment']; + delete this.properties['background-position']; + break; + case 'flex': + delete this.properties['flex-grow']; + delete this.properties['flex-shrink']; + delete this.properties['flex-basis']; + break; + case 'padding': + delete this.properties['padding-top']; + delete this.properties['padding-right']; + delete this.properties['padding-bottom']; + delete this.properties['padding-left']; + break; + case 'margin': + delete this.properties['margin-top']; + delete this.properties['margin-right']; + delete this.properties['margin-bottom']; + delete this.properties['margin-left']; + break; + default: + delete this.properties[name]; + break; } } @@ -171,157 +351,207 @@ export default class CSSStyleDeclarationPropertyManager { * @param important Important. */ public set(name: string, value: string, important: boolean): void { - let propertyValues = null; + let properties = null; switch (name) { case 'border': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorder(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getBorder(value, important); break; case 'border-top': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderTop(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getBorderTop(value, important); break; - case 'border-left': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderLeft(value, important); + case 'border-right': + properties = CSSStyleDeclarationPropertyValueParser.getBorderRight(value, important); break; case 'border-bottom': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderBottom(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getBorderBottom(value, important); break; - case 'border-right': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderRight(value, important); + case 'border-left': + properties = CSSStyleDeclarationPropertyValueParser.getBorderLeft(value, important); break; case 'border-width': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderWidth(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getBorderWidth(value, important); break; case 'border-style': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderStyle(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getBorderStyle(value, important); break; case 'border-color': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderCollapse(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getBorderColor(value, important); + break; + case 'border-top-width': + properties = CSSStyleDeclarationPropertyValueParser.getBorderTopWidth(value, important); + break; + case 'border-right-width': + properties = CSSStyleDeclarationPropertyValueParser.getBorderRightWidth(value, important); + break; + case 'border-bottom-width': + properties = CSSStyleDeclarationPropertyValueParser.getBorderBottomWidth(value, important); + break; + case 'border-left-width': + properties = CSSStyleDeclarationPropertyValueParser.getBorderLeftWidth(value, important); + break; + case 'border-top-color': + properties = CSSStyleDeclarationPropertyValueParser.getBorderTopColor(value, important); + break; + case 'border-right-color': + properties = CSSStyleDeclarationPropertyValueParser.getBorderRightColor(value, important); + break; + case 'border-bottom-color': + properties = CSSStyleDeclarationPropertyValueParser.getBorderBottomColor(value, important); + break; + case 'border-left-color': + properties = CSSStyleDeclarationPropertyValueParser.getBorderLeftColor(value, important); + break; + case 'border-top-style': + properties = CSSStyleDeclarationPropertyValueParser.getBorderTopStyle(value, important); + break; + case 'border-right-style': + properties = CSSStyleDeclarationPropertyValueParser.getBorderRightStyle(value, important); + break; + case 'border-bottom-style': + properties = CSSStyleDeclarationPropertyValueParser.getBorderBottomStyle(value, important); + break; + case 'border-left-style': + properties = CSSStyleDeclarationPropertyValueParser.getBorderLeftStyle(value, important); break; case 'border-radius': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderRadius(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getBorderRadius(value, important); + break; + case 'border-top-left-radius': + properties = CSSStyleDeclarationPropertyValueParser.getBorderTopLeftRadius( + value, + important + ); + break; + case 'border-top-right-radius': + properties = CSSStyleDeclarationPropertyValueParser.getBorderTopRightRadius( + value, + important + ); + break; + case 'border-bottom-right-radius': + properties = CSSStyleDeclarationPropertyValueParser.getBorderBottomRightRadius( + value, + important + ); + break; + case 'border-bottom-right-radius': + properties = CSSStyleDeclarationPropertyValueParser.getBorderBottomLeftRadius( + value, + important + ); break; case 'border-collapse': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderCollapse(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getBorderCollapse(value, important); break; case 'clear': - propertyValues = CSSStyleDeclarationPropertyValueParser.getClear(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getClear(value, important); break; case 'clip': - propertyValues = CSSStyleDeclarationPropertyValueParser.getClip(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getClip(value, important); break; case 'css-float': + properties = CSSStyleDeclarationPropertyValueParser.getCSSFloat(value, important); + break; case 'float': - propertyValues = CSSStyleDeclarationPropertyValueParser.getFloat(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getFloat(value, important); break; case 'flex': - propertyValues = CSSStyleDeclarationPropertyValueParser.getFlex(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getFlex(value, important); break; case 'flex-shrink': - propertyValues = CSSStyleDeclarationPropertyValueParser.getFlexShrink(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getFlexShrink(value, important); break; case 'flex-grow': - propertyValues = CSSStyleDeclarationPropertyValueParser.getFlexGrow(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getFlexGrow(value, important); break; case 'flex-basis': - propertyValues = CSSStyleDeclarationPropertyValueParser.getFlexBasis(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getFlexBasis(value, important); break; case 'padding': - propertyValues = CSSStyleDeclarationPropertyValueParser.getPadding(value, important); - break; - case 'margin': - propertyValues = CSSStyleDeclarationPropertyValueParser.getMargin(value, important); - break; - case 'background': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBackground(value, important); - break; - case 'top': - propertyValues = CSSStyleDeclarationPropertyValueParser.getTop(value, important); - break; - case 'right': - propertyValues = CSSStyleDeclarationPropertyValueParser.getRight(value, important); - break; - case 'bottom': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBottom(value, important); - break; - case 'left': - propertyValues = CSSStyleDeclarationPropertyValueParser.getLeft(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getPadding(value, important); break; case 'padding-top': - propertyValues = CSSStyleDeclarationPropertyValueParser.getPaddingTop(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getPaddingTop(value, important); break; case 'padding-bottom': - propertyValues = CSSStyleDeclarationPropertyValueParser.getPaddingBottom(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getPaddingBottom(value, important); break; case 'padding-left': - propertyValues = CSSStyleDeclarationPropertyValueParser.getPaddingLeft(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getPaddingLeft(value, important); break; case 'padding-right': - propertyValues = CSSStyleDeclarationPropertyValueParser.getPaddingRight(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getPaddingRight(value, important); + break; + case 'margin': + properties = CSSStyleDeclarationPropertyValueParser.getMargin(value, important); break; case 'margin-top': - propertyValues = CSSStyleDeclarationPropertyValueParser.getMarginTop(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getMarginTop(value, important); break; case 'margin-bottom': - propertyValues = CSSStyleDeclarationPropertyValueParser.getMarginBottom(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getMarginBottom(value, important); break; case 'margin-left': - propertyValues = CSSStyleDeclarationPropertyValueParser.getMarginLeft(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getMarginLeft(value, important); break; case 'margin-right': - propertyValues = CSSStyleDeclarationPropertyValueParser.getMarginRight(value, important); + properties = CSSStyleDeclarationPropertyValueParser.getMarginRight(value, important); break; - case 'border-top-width': - propertyValues = CSSStyleDeclarationPropertyValueParser.getBorderTopWidth(value, important); + case 'background': + properties = CSSStyleDeclarationPropertyValueParser.getBackground(value, important); break; - case 'border-bottom-width': - case 'border-left-width': - case 'border-right-width': - value = CSSStyleDeclarationValueParser.getBorderWidth(value); - if (value) { - this.properties[name] = { name, important, value }; - } + case 'background-image': + properties = CSSStyleDeclarationPropertyValueParser.getBackgroundImage(value, important); + break; + case 'background-color': + properties = CSSStyleDeclarationPropertyValueParser.getBackgroundColor(value, important); + break; + case 'background-repeat': + properties = CSSStyleDeclarationPropertyValueParser.getBackgroundRepeat(value, important); + break; + case 'background-attachment': + properties = CSSStyleDeclarationPropertyValueParser.getBackgroundAttachment( + value, + important + ); + break; + case 'background-position': + properties = CSSStyleDeclarationPropertyValueParser.getBackgroundPosition(value, important); + break; + case 'top': + properties = CSSStyleDeclarationPropertyValueParser.getTop(value, important); + break; + case 'right': + properties = CSSStyleDeclarationPropertyValueParser.getRight(value, important); + break; + case 'bottom': + properties = CSSStyleDeclarationPropertyValueParser.getBottom(value, important); + break; + case 'left': + properties = CSSStyleDeclarationPropertyValueParser.getLeft(value, important); + break; + case 'font': + properties = CSSStyleDeclarationPropertyValueParser.getFont(value, important); break; case 'font-size': - value = CSSStyleDeclarationValueParser.getFontSize(value); - if (value) { - this.properties[name] = { name, important, value }; - } + properties = CSSStyleDeclarationPropertyValueParser.getFontSize(value, important); break; case 'color': - case 'flood-color': - case 'border-top-color': - case 'border-bottom-color': - case 'border-left-color': - case 'border-right-color': - value = CSSStyleDeclarationValueParser.getColor(value); - if (value) { - this.properties[name] = { name, important, value }; - } + properties = CSSStyleDeclarationPropertyValueParser.getColor(value, important); break; - case 'border-top-style': - case 'border-bottom-style': - case 'border-left-style': - case 'border-right-style': - value = CSSStyleDeclarationValueParser.getBorderStyle(value); - if (value) { - this.properties[name] = { name, important, value }; - } + case 'flood-color': + properties = CSSStyleDeclarationPropertyValueParser.getFloodColor(value, important); break; default: - if (value) { - this.properties[name] = { name, important, value }; - } + properties = value + ? { + [name]: { value, important } + } + : null; break; } - } - /** - * Reads a string. - * - * @param styleString Style string (e.g. "border: 2px solid red; font-size: 12px;"). - */ - private fromString(styleString: string): { - [k: string]: ICSSStyleDeclarationProperty; - } {} + Object.assign(this.properties, properties); + } } diff --git a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyValueParser.ts b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyValueParser.ts index 1959aa357..72aabd6a7 100644 --- a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyValueParser.ts +++ b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyValueParser.ts @@ -33,6 +33,16 @@ const FLEX_BASIS = [ ]; const CLEAR = ['none', 'left', 'right', 'both', 'inherit']; const FLOAT = ['none', 'left', 'right', 'inherit']; +const SYSTEM_FONT = [ + 'caption', + 'icon', + 'menu', + 'message-box', + 'small-caption', + 'status-bar', + 'inherit' +]; +const FONT_WEIGHT = ['normal', 'bold', 'bolder', 'lighter']; const FONT_SIZE = [ 'xx-small', 'x-small', @@ -54,26 +64,6 @@ const FONT_SIZE = [ * Computed style property parser. */ export default class CSSStyleDeclarationPropertyValueParser { - /** - * Returns border style. - * - * @param value Value. - * @param important Important. - * @returns Property values - */ - public static getBorderStyle( - value: string, - important: boolean - ): { - [key: string]: ICSSStyleDeclarationPropertyValue; - } { - const lowerValue = value.toLowerCase(); - if (BORDER_STYLE.includes(lowerValue)) { - return { 'border-style': { value: lowerValue, important } }; - } - return null; - } - /** * Returns border collapse. * @@ -192,57 +182,54 @@ export default class CSSStyleDeclarationPropertyValueParser { } /** - * Returns flex basis. + * Returns top. * * @param value Value. * @param important Important. * @returns Property values */ - public static getFlexBasis( + public static getTop( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const lowerValue = value.toLowerCase(); - if (FLEX_BASIS.includes(lowerValue)) { - return { 'flex-basis': { value: lowerValue, important } }; - } - return { 'flex-basis': { value: CSSStyleDeclarationValueParser.getLength(value), important } }; + const parsedValue = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); + return parsedValue ? { top: { value: parsedValue, important } } : null; } /** - * Returns flex shrink. + * Returns top. * * @param value Value. * @param important Important. * @returns Property values */ - public static getFlexShrink( + public static getRight( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const parsedValue = CSSStyleDeclarationValueParser.getInteger(value); - return parsedValue ? { 'flex-shrink': { value: parsedValue, important } } : null; + const parsedValue = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); + return parsedValue ? { right: { value: parsedValue, important } } : null; } /** - * Returns flex grow. + * Returns top. * * @param value Value. * @param important Important. * @returns Property values */ - public static getFlexGrow( + public static getBottom( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const parsedValue = CSSStyleDeclarationValueParser.getInteger(value); - return parsedValue ? { 'flex-grow': { value: parsedValue, important } } : null; + const parsedValue = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); + return parsedValue ? { bottom: { value: parsedValue, important } } : null; } /** @@ -252,65 +239,139 @@ export default class CSSStyleDeclarationPropertyValueParser { * @param important Important. * @returns Property values */ - public static getTop( + public static getLeft( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { const parsedValue = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); - return parsedValue ? { top: { value: parsedValue, important } } : null; + return parsedValue ? { left: { value: parsedValue, important } } : null; } /** - * Returns top. + * Returns clear. * * @param value Value. * @param important Important. * @returns Property values */ - public static getRight( + public static getClear( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const parsedValue = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); - return parsedValue ? { right: { value: parsedValue, important } } : null; + const lowerValue = value.toLowerCase(); + if (CLEAR.includes(lowerValue)) { + return { clear: { value: lowerValue, important } }; + } + return null; } /** - * Returns top. + * Returns clip + * + * Based on: + * https://github.com/jsdom/cssstyle/blob/master/lib/properties/clip.js * * @param value Value. * @param important Important. * @returns Property values */ - public static getBottom( + public static getClip( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const parsedValue = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); - return parsedValue ? { bottom: { value: parsedValue, important } } : null; + const lowerValue = value.toLowerCase(); + if (lowerValue === 'auto' || lowerValue === 'initial' || lowerValue === 'inherit') { + return { clip: { value: lowerValue, important } }; + } + const matches = lowerValue.match(RECT_REGEXP); + if (!matches) { + return null; + } + const parts = matches[1].split(/\s*,\s*/); + if (parts.length !== 4) { + return null; + } + for (const part of parts) { + if (!CSSStyleDeclarationValueParser.getMeasurement(part)) { + return null; + } + } + return { clip: { value, important } }; } /** - * Returns top. + * Returns float. * * @param value Value. * @param important Important. * @returns Property values */ - public static getLeft( + public static getFloat( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const parsedValue = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); - return parsedValue ? { left: { value: parsedValue, important } } : null; + const lowerValue = value.toLowerCase(); + if (FLOAT.includes(lowerValue)) { + return { float: { value: lowerValue, important } }; + } + return null; + } + + /** + * Returns float. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getCSSFloat( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const float = this.getFloat(value, important); + return float ? { 'css-float': float['float'] } : null; + } + + /** + * Returns border. + * + * @param value Value. + * @param important Important. + * @returns Property values. + */ + public static getBorder( + value: string, + important: boolean + ): { [key: string]: ICSSStyleDeclarationPropertyValue } { + const parts = value.split(/ +/); + const borderWidth = parts[0] ? this.getBorderWidth(parts[0], important) : ''; + const borderStyle = parts[1] ? this.getBorderStyle(parts[1], important) : ''; + const borderColor = parts[2] ? this.getBorderColor(parts[2], important) : ''; + const properties = {}; + + if (borderWidth) { + Object.assign(properties, borderWidth); + } + + if (borderStyle) { + Object.assign(properties, borderStyle); + } + + if (borderColor) { + Object.assign(properties, borderColor); + } + + return borderWidth && borderStyle !== null && borderColor !== null ? properties : null; } /** @@ -328,10 +389,70 @@ export default class CSSStyleDeclarationPropertyValueParser { } { const lowerValue = value.toLowerCase(); if (BORDER_WIDTH.includes(lowerValue)) { - return { 'border-width': { value: lowerValue, important } }; + return { + 'border-top-width': { value: lowerValue, important }, + 'border-right-width': { value: lowerValue, important }, + 'border-bottom-width': { value: lowerValue, important }, + 'border-left-width': { value: lowerValue, important } + }; } const length = CSSStyleDeclarationValueParser.getLength(value); - return length ? { 'border-width': { value: length, important } } : null; + return length + ? { + 'border-top-width': { value: length, important }, + 'border-right-width': { value: length, important }, + 'border-bottom-width': { value: length, important }, + 'border-left-width': { value: length, important } + } + : null; + } + /** + * Returns border style. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getBorderStyle( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const lowerValue = value.toLowerCase(); + if (BORDER_STYLE.includes(lowerValue)) { + return { + 'border-top-style': { value: lowerValue, important }, + 'border-right-style': { value: lowerValue, important }, + 'border-bottom-style': { value: lowerValue, important }, + 'border-left-style': { value: lowerValue, important } + }; + } + return null; + } + + /** + * Returns border color. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getBorderColor( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const color = CSSStyleDeclarationValueParser.getColor(value); + return color + ? { + 'border-top-color': { value: color, important }, + 'border-right-color': { value: color, important }, + 'border-bottom-color': { value: color, important }, + 'border-left-color': { value: color, important } + } + : null; } /** @@ -348,7 +469,7 @@ export default class CSSStyleDeclarationPropertyValueParser { [key: string]: ICSSStyleDeclarationPropertyValue; } { const borderWidth = this.getBorderWidth(value, important); - return borderWidth ? { 'border-top-width': borderWidth['border-width'] } : null; + return borderWidth ? { 'border-top-width': borderWidth['border-top-width'] } : null; } /** @@ -365,7 +486,7 @@ export default class CSSStyleDeclarationPropertyValueParser { [key: string]: ICSSStyleDeclarationPropertyValue; } { const borderWidth = this.getBorderWidth(value, important); - return borderWidth ? { 'border-right-width': borderWidth['border-width'] } : null; + return borderWidth ? { 'border-right-width': borderWidth['border-right-width'] } : null; } /** @@ -382,7 +503,7 @@ export default class CSSStyleDeclarationPropertyValueParser { [key: string]: ICSSStyleDeclarationPropertyValue; } { const borderWidth = this.getBorderWidth(value, important); - return borderWidth ? { 'border-bottom-width': borderWidth['border-width'] } : null; + return borderWidth ? { 'border-bottom-width': borderWidth['border-bottom-width'] } : null; } /** @@ -399,147 +520,143 @@ export default class CSSStyleDeclarationPropertyValueParser { [key: string]: ICSSStyleDeclarationPropertyValue; } { const borderWidth = this.getBorderWidth(value, important); - return borderWidth ? { 'border-left-width': borderWidth['border-width'] } : null; + return borderWidth ? { 'border-left-width': borderWidth['border-left-width'] } : null; } /** - * Returns clear. + * Returns border style. * * @param value Value. * @param important Important. * @returns Property values */ - public static getClear( + public static getBorderTopStyle( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const lowerValue = value.toLowerCase(); - if (CLEAR.includes(lowerValue)) { - return { clear: { value: lowerValue, important } }; - } - return null; + const borderStyle = this.getBorderStyle(value, important); + return borderStyle ? { 'border-top-style': borderStyle['border-top-style'] } : null; } /** - * Returns clip - * - * Based on: - * https://github.com/jsdom/cssstyle/blob/master/lib/properties/clip.js + * Returns border style. * * @param value Value. * @param important Important. * @returns Property values */ - public static getClip( + public static getBorderRightStyle( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const lowerValue = value.toLowerCase(); - if (lowerValue === 'auto' || lowerValue === 'initial' || lowerValue === 'inherit') { - return { clip: { value: lowerValue, important } }; - } - const matches = lowerValue.match(RECT_REGEXP); - if (!matches) { - return null; - } - const parts = matches[1].split(/\s*,\s*/); - if (parts.length !== 4) { - return null; - } - for (const part of parts) { - if (!CSSStyleDeclarationValueParser.getMeasurement(part)) { - return null; - } - } - return { clip: { value, important } }; + const borderStyle = this.getBorderStyle(value, important); + return borderStyle ? { 'border-right-style': borderStyle['border-right-style'] } : null; } /** - * Returns float. + * Returns border style. * * @param value Value. * @param important Important. * @returns Property values */ - public static getFloat( + public static getBorderBottomStyle( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const lowerValue = value.toLowerCase(); - if (FLOAT.includes(lowerValue)) { - return { float: { value: lowerValue, important } }; - } - return null; + const borderStyle = this.getBorderStyle(value, important); + return borderStyle ? { 'border-bottom-style': borderStyle['border-bottom-style'] } : null; } /** - * Returns font size. + * Returns border style. * * @param value Value. * @param important Important. * @returns Property values */ - public static getFontSize( + public static getBorderLeftStyle( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue; } { - const lowerValue = value.toLowerCase(); - if (FONT_SIZE.includes(lowerValue)) { - return { 'font-size': { value: lowerValue, important } }; - } - const measurement = - CSSStyleDeclarationValueParser.getLength(value) || - CSSStyleDeclarationValueParser.getPercentage(value); - return measurement ? { 'font-size': { value: measurement, important } } : null; + const borderStyle = this.getBorderStyle(value, important); + return borderStyle ? { 'border-left-style': borderStyle['border-left-style'] } : null; } /** - * Returns border. + * Returns border color. * * @param value Value. * @param important Important. - * @returns Property values. + * @returns Property values */ - public static getBorder( + public static getBorderTopColor( value: string, important: boolean - ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - const parts = value.split(/ +/); - const borderWidth = parts[0] ? this.getBorderWidth(parts[0], important) : null; - const borderStyle = parts[1] ? this.getBorderStyle(parts[1], important) : null; - const borderColor = parts[2] ? CSSStyleDeclarationValueParser.getColor(parts[2]) : null; - const propertyValues = {}; - - if (borderWidth) { - propertyValues['border-top-width'] = { ...borderWidth['border-width'] }; - propertyValues['border-right-width'] = { ...borderWidth['border-width'] }; - propertyValues['border-bottom-width'] = { ...borderWidth['border-width'] }; - propertyValues['border-left-width'] = { ...borderWidth['border-width'] }; - } + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const borderColor = this.getBorderColor(value, important); + return borderColor ? { 'border-top-color': borderColor['border-top-color'] } : null; + } - if (borderStyle) { - propertyValues['border-top-style'] = { ...borderStyle['border-style'] }; - propertyValues['border-right-style'] = { ...borderStyle['border-style'] }; - propertyValues['border-bottom-style'] = { ...borderStyle['border-style'] }; - propertyValues['border-left-style'] = { ...borderStyle['border-style'] }; - } + /** + * Returns border color. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getBorderRightColor( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const borderColor = this.getBorderColor(value, important); + return borderColor ? { 'border-right-color': borderColor['border-right-color'] } : null; + } - if (borderColor) { - propertyValues['border-top-style'] = { important, value: borderColor }; - propertyValues['border-right-style'] = { important, value: borderColor }; - propertyValues['border-bottom-style'] = { important, value: borderColor }; - propertyValues['border-left-style'] = { important, value: borderColor }; - } + /** + * Returns border color. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getBorderBottomColor( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const borderColor = this.getBorderColor(value, important); + return borderColor ? { 'border-bottom-color': borderColor['border-bottom-color'] } : null; + } - return borderWidth && borderStyle !== null && borderColor !== null ? propertyValues : null; + /** + * Returns border color. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getBorderLeftColor( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const borderColor = this.getBorderColor(value, important); + return borderColor ? { 'border-left-color': borderColor['border-left-color'] } : null; } /** @@ -554,30 +671,85 @@ export default class CSSStyleDeclarationPropertyValueParser { important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { const parts = value.split(/ +/); - const topLeft = parts[0] ? CSSStyleDeclarationValueParser.getMeasurement(parts[0]) : null; - const topRight = parts[1] ? CSSStyleDeclarationValueParser.getMeasurement(parts[1]) : null; - const bottomRight = parts[2] ? CSSStyleDeclarationValueParser.getMeasurement(parts[2]) : null; - const bottomLeft = parts[3] ? CSSStyleDeclarationValueParser.getMeasurement(parts[3]) : null; - const propertyValues = {}; - - if (topLeft) { - propertyValues['border-top-left-radius'] = { important, value: topLeft }; - } - if (topRight) { - propertyValues['border-top-right-radius'] = { important, value: topRight }; - } - if (bottomRight) { - propertyValues['border-bottom-right-radius'] = { important, value: bottomRight }; - } - if (bottomLeft) { - propertyValues['border-bottom-left-radius'] = { important, value: bottomLeft }; - } + const topLeft = parts[0] ? CSSStyleDeclarationValueParser.getMeasurement(parts[0]) : ''; + const topRight = parts[1] ? CSSStyleDeclarationValueParser.getMeasurement(parts[1]) : ''; + const bottomRight = parts[2] ? CSSStyleDeclarationValueParser.getMeasurement(parts[2]) : ''; + const bottomLeft = parts[3] ? CSSStyleDeclarationValueParser.getMeasurement(parts[3]) : ''; + const properties = {}; + + properties['border-top-left-radius'] = { important, value: topLeft }; + properties['border-top-right-radius'] = { important, value: topRight || topLeft }; + properties['border-bottom-right-radius'] = { important, value: bottomRight || topLeft }; + properties['border-bottom-left-radius'] = { + important, + value: bottomLeft || topRight || topLeft + }; return topLeft && topRight !== null && bottomRight !== null && bottomLeft !== null - ? propertyValues + ? properties : null; } + /** + * Returns border radius. + * + * @param value Value. + * @param important Important. + * @returns Property values. + */ + public static getBorderTopLeftRadius( + value: string, + important: boolean + ): { [key: string]: ICSSStyleDeclarationPropertyValue } { + const radius = this.getBorderRadius(value, important); + return radius ? { 'border-top-left-radius': radius['border-top-left-radius'] } : null; + } + + /** + * Returns border radius. + * + * @param value Value. + * @param important Important. + * @returns Property values. + */ + public static getBorderTopRightRadius( + value: string, + important: boolean + ): { [key: string]: ICSSStyleDeclarationPropertyValue } { + const radius = this.getBorderRadius(value, important); + return radius ? { 'border-top-right-radius': radius['border-top-right-radius'] } : null; + } + + /** + * Returns border radius. + * + * @param value Value. + * @param important Important. + * @returns Property values. + */ + public static getBorderBottomRightRadius( + value: string, + important: boolean + ): { [key: string]: ICSSStyleDeclarationPropertyValue } { + const radius = this.getBorderRadius(value, important); + return radius ? { 'border-bottom-right-radius': radius['border-bottom-right-radius'] } : null; + } + + /** + * Returns border radius. + * + * @param value Value. + * @param important Important. + * @returns Property values. + */ + public static getBorderBottomLeftRadius( + value: string, + important: boolean + ): { [key: string]: ICSSStyleDeclarationPropertyValue } { + const radius = this.getBorderRadius(value, important); + return radius ? { 'border-bottom-left-radius': radius['border-bottom-left-radius'] } : null; + } + /** * Returns border top, right, bottom or left. * @@ -589,7 +761,14 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getBorderByPosition('top', value, important); + const border = this.getBorder(value, important); + return border + ? { + 'border-top-width': border['border-top-width'], + 'border-top-style': border['border-top-style'], + 'border-top-color': border['border-top-color'] + } + : null; } /** @@ -603,7 +782,14 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getBorderByPosition('right', value, important); + const border = this.getBorder(value, important); + return border + ? { + 'border-right-width': border['border-right-width'], + 'border-right-style': border['border-right-style'], + 'border-right-color': border['border-right-color'] + } + : null; } /** @@ -617,7 +803,14 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getBorderByPosition('bottom', value, important); + const border = this.getBorder(value, important); + return border + ? { + 'border-bottom-width': border['border-bottom-width'], + 'border-bottom-style': border['border-bottom-style'], + 'border-bottom-color': border['border-bottom-color'] + } + : null; } /** @@ -631,7 +824,14 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getBorderByPosition('left', value, important); + const border = this.getBorder(value, important); + return border + ? { + 'border-left-width': border['border-left-width'], + 'border-left-style': border['border-left-style'], + 'border-left-color': border['border-left-color'] + } + : null; } /** @@ -645,26 +845,18 @@ export default class CSSStyleDeclarationPropertyValueParser { important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { const parts = value.split(/ +/); - const top = parts[0] ? this.getPaddingByPosition('top', parts[0], important) : null; - const right = parts[1] ? this.getPaddingByPosition('right', parts[0], important) : null; - const bottom = parts[2] ? this.getPaddingByPosition('bottom', parts[0], important) : null; - const left = parts[3] ? this.getPaddingByPosition('left', parts[0], important) : null; - const propertyValues = {}; - - if (top) { - Object.assign(propertyValues, top); - } - if (right) { - Object.assign(propertyValues, right); - } - if (bottom) { - Object.assign(propertyValues, bottom); - } - if (left) { - Object.assign(propertyValues, left); - } - - return top && right !== null && bottom !== null && left !== null ? propertyValues : null; + const top = parts[0] ? CSSStyleDeclarationValueParser.getMeasurement(parts[0]) : ''; + const right = parts[1] ? CSSStyleDeclarationValueParser.getMeasurement(parts[1]) : ''; + const bottom = parts[2] ? CSSStyleDeclarationValueParser.getMeasurement(parts[2]) : ''; + const left = parts[3] ? CSSStyleDeclarationValueParser.getMeasurement(parts[3]) : ''; + const properties = {}; + + properties['padding-top'] = { important, value: top }; + properties['padding-right'] = { important, value: right || top }; + properties['padding-bottom'] = { important, value: bottom || top }; + properties['padding-left'] = { important, value: left || right || top }; + + return top && right !== null && bottom !== null && left !== null ? properties : null; } /** @@ -678,7 +870,8 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getPaddingByPosition('top', value, important); + const padding = CSSStyleDeclarationValueParser.getMeasurement(value); + return padding ? { 'padding-top': { value: padding, important } } : null; } /** @@ -692,7 +885,8 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getPaddingByPosition('right', value, important); + const padding = CSSStyleDeclarationValueParser.getMeasurement(value); + return padding ? { 'padding-right': { value: padding, important } } : null; } /** @@ -706,7 +900,8 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getPaddingByPosition('bottom', value, important); + const padding = CSSStyleDeclarationValueParser.getMeasurement(value); + return padding ? { 'padding-bottom': { value: padding, important } } : null; } /** @@ -720,7 +915,8 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getPaddingByPosition('left', value, important); + const padding = CSSStyleDeclarationValueParser.getMeasurement(value); + return padding ? { 'padding-left': { value: padding, important } } : null; } /** @@ -735,26 +931,18 @@ export default class CSSStyleDeclarationPropertyValueParser { important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { const parts = value.split(/ +/); - const top = parts[0] ? this.getMarginByPosition('top', parts[0], important) : null; - const right = parts[1] ? this.getMarginByPosition('right', parts[0], important) : null; - const bottom = parts[2] ? this.getMarginByPosition('bottom', parts[0], important) : null; - const left = parts[3] ? this.getMarginByPosition('left', parts[0], important) : null; - const propertyValues = {}; - - if (top) { - Object.assign(propertyValues, top); - } - if (right) { - Object.assign(propertyValues, right); - } - if (bottom) { - Object.assign(propertyValues, bottom); - } - if (left) { - Object.assign(propertyValues, left); - } - - return top && right !== null && bottom !== null && left !== null ? propertyValues : null; + const top = parts[0] ? CSSStyleDeclarationValueParser.getMeasurementOrAuto(parts[0]) : ''; + const right = parts[1] ? CSSStyleDeclarationValueParser.getMeasurementOrAuto(parts[1]) : ''; + const bottom = parts[2] ? CSSStyleDeclarationValueParser.getMeasurementOrAuto(parts[2]) : ''; + const left = parts[3] ? CSSStyleDeclarationValueParser.getMeasurementOrAuto(parts[3]) : ''; + const properties = {}; + + properties['margin-top'] = { important, value: top }; + properties['margin-right'] = { important, value: right || top }; + properties['margin-bottom'] = { important, value: bottom || top }; + properties['margin-left'] = { important, value: left || right || top }; + + return top && right !== null && bottom !== null && left !== null ? properties : null; } /** @@ -768,7 +956,8 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getMarginByPosition('top', value, important); + const margin = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); + return margin ? { 'margin-top': { value: margin, important } } : null; } /** @@ -782,7 +971,8 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getMarginByPosition('right', value, important); + const margin = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); + return margin ? { 'margin-right': { value: margin, important } } : null; } /** @@ -796,7 +986,8 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getMarginByPosition('bottom', value, important); + const margin = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); + return margin ? { 'margin-bottom': { value: margin, important } } : null; } /** @@ -810,7 +1001,8 @@ export default class CSSStyleDeclarationPropertyValueParser { value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - return this.getMarginByPosition('left', value, important); + const margin = CSSStyleDeclarationValueParser.getMeasurementOrAuto(value); + return margin ? { 'margin-left': { value: margin, important } } : null; } /** @@ -854,25 +1046,79 @@ export default class CSSStyleDeclarationPropertyValueParser { } const parts = value.split(/ +/); - const flexGrow = parts[0] ? CSSStyleDeclarationValueParser.getInteger(parts[0]) : null; - const flexShrink = parts[1] ? CSSStyleDeclarationValueParser.getInteger(parts[1]) : null; - const flexBasis = parts[2] ? this.getFlexBasis(parts[2], important) : null; + const flexGrow = parts[0] ? this.getFlexGrow(parts[0], important) : ''; + const flexShrink = parts[1] ? this.getFlexShrink(parts[1], important) : ''; + const flexBasis = parts[2] ? this.getFlexBasis(parts[2], important) : ''; if (flexGrow && flexShrink && flexBasis) { return { ...flexBasis, - 'flex-grow': { important, value: flexGrow }, - 'flex-shrink': { important, value: flexShrink } + ...flexGrow, + ...flexBasis }; } return null; } + /** + * Returns flex basis. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getFlexBasis( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const lowerValue = value.toLowerCase(); + if (FLEX_BASIS.includes(lowerValue)) { + return { 'flex-basis': { value: lowerValue, important } }; + } + return { 'flex-basis': { value: CSSStyleDeclarationValueParser.getLength(value), important } }; + } + + /** + * Returns flex shrink. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getFlexShrink( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const parsedValue = CSSStyleDeclarationValueParser.getInteger(value); + return parsedValue ? { 'flex-shrink': { value: parsedValue, important } } : null; + } + + /** + * Returns flex grow. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getFlexGrow( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const parsedValue = CSSStyleDeclarationValueParser.getInteger(value); + return parsedValue ? { 'flex-grow': { value: parsedValue, important } } : null; + } + /** * Returns background. * - * @param name + * @param name Name. * @param value Value. * @param important Important. * @returns Property values. @@ -892,115 +1138,201 @@ export default class CSSStyleDeclarationPropertyValueParser { parts.unshift(''); } - const color = parts[0] ? CSSStyleDeclarationValueParser.getColor(parts[0]) : null; - const image = parts[1] ? CSSStyleDeclarationValueParser.getURL(parts[1]) : null; - const repeat = parts[2] ? this.getBackgroundRepeat(parts[2], important) : null; - const attachment = parts[3] ? this.getBackgroundAttachment(parts[3], important) : null; - const position = parts[4] ? this.getBackgroundPosition(parts[4], important) : null; - const propertyValues = {}; + const color = parts[0] ? this.getBackgroundColor(parts[0], important) : ''; + const image = parts[1] ? this.getBackgroundImage(parts[1], important) : ''; + const repeat = parts[2] ? this.getBackgroundRepeat(parts[2], important) : ''; + const attachment = parts[3] ? this.getBackgroundAttachment(parts[3], important) : ''; + const position = parts[4] ? this.getBackgroundPosition(parts[4], important) : ''; + const properties = {}; if (color) { - propertyValues['background-color'] = { important, value: color }; - } else if (image) { - propertyValues['background-image'] = { important, value: image }; + Object.assign(properties, color); + } + + if (image) { + Object.assign(properties, image); } if (repeat) { - Object.assign(propertyValues, repeat); + Object.assign(properties, repeat); } if (attachment) { - Object.assign(propertyValues, attachment); + Object.assign(properties, attachment); } if (position) { - Object.assign(propertyValues, position); + Object.assign(properties, position); } - return color || image ? propertyValues : null; + return (color || image) && repeat !== null && attachment !== null && position !== null + ? properties + : null; } /** - * Returns border top, right, bottom or left. + * Returns background color. * - * @param position Position. * @param value Value. * @param important Important. * @returns Property value. */ - private static getBorderByPosition( - position: 'top' | 'right' | 'bottom' | 'left', + public static getBackgroundColor( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - const parts = value.split(/ +/); - const borderWidth = parts[0] ? this.getBorderWidth(parts[0], important) : null; - const borderStyle = parts[1] ? this.getBorderStyle(parts[1], important) : null; - const borderColor = parts[2] ? CSSStyleDeclarationValueParser.getColor(parts[2]) : null; - const propertyValues = {}; + const color = CSSStyleDeclarationValueParser.getColor(value); - if (borderWidth !== null) { - propertyValues['border-' + position + '-width'] = borderWidth['border-width']; - } - if (borderStyle !== null) { - propertyValues['border-' + position + '-style'] = borderStyle['border-width']; - } - if (borderColor) { - propertyValues['border-' + position + '-color'] = { important, value: borderColor }; - } + return color + ? { + ['background-color']: { important, value: color } + } + : null; + } + + /** + * Returns background image. + * + * @param value Value. + * @param important Important. + * @returns Property value. + */ + public static getBackgroundImage( + value: string, + important: boolean + ): { [key: string]: ICSSStyleDeclarationPropertyValue } { + const image = CSSStyleDeclarationValueParser.getURL(value); - return borderWidth !== null ? propertyValues : null; + return image + ? { + ['background-image']: { important, value: image } + } + : null; } /** - * Returns margin. + * Returns color. * - * @param position Position. * @param value Value. * @param important Important. - * @returns Parsed value. + * @returns Property value. */ - private static getMarginByPosition( - position: 'top' | 'right' | 'bottom' | 'left', + public static getColor( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - const lowerValue = value.toLowerCase(); - if (lowerValue === 'auto') { - return { [`margin-${position}`]: { important, value: 'auto' } }; - } - const measurement = CSSStyleDeclarationValueParser.getMeasurement(value); - return measurement + const color = CSSStyleDeclarationValueParser.getColor(value); + + return color ? { - [`margin-${position}`]: { - important, - value: measurement - } + ['color']: { important, value: color } } : null; } /** - * Returns padding. + * Returns color. * - * @param position Position. * @param value Value. * @param important Important. - * @returns Parsed value. + * @returns Property value. */ - private static getPaddingByPosition( - position: 'top' | 'right' | 'bottom' | 'left', + public static getFloodColor( value: string, important: boolean ): { [key: string]: ICSSStyleDeclarationPropertyValue } { - const measurement = CSSStyleDeclarationValueParser.getMeasurement(value); - return measurement + const color = CSSStyleDeclarationValueParser.getColor(value); + + return color ? { - [`padding-${position}`]: { - important, - value: measurement - } + ['flood-color']: { important, value: color } } : null; } + + /** + * Returns font. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getFont( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const parts = value.split(/ +/); + let font; + let fontFamily; + let fontSize; + let fontStyle; + let fontVariant; + let fontWeight; + let lineHeight; + } + + /** + * Returns font variant. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getFontVariant( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const lowerValue = value.toLowerCase(); + return lowerValue === 'normal' || lowerValue === 'small-caps' + ? { 'font-size': { value: lowerValue, important } } + : null; + } + + /** + * Returns font weight. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getFontWeight( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const lowerValue = value.toLowerCase(); + if (FONT_WEIGHT.includes(lowerValue)) { + return { 'font-weight': { value: lowerValue, important } }; + } + const measurement = + CSSStyleDeclarationValueParser.getLength(value) || + CSSStyleDeclarationValueParser.getPercentage(value); + return measurement ? { 'font-size': { value: measurement, important } } : null; + } + + /** + * Returns font size. + * + * @param value Value. + * @param important Important. + * @returns Property values + */ + public static getFontSize( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const lowerValue = value.toLowerCase(); + if (FONT_SIZE.includes(lowerValue)) { + return { 'font-size': { value: lowerValue, important } }; + } + const measurement = CSSStyleDeclarationValueParser.getLengthOrPercentage(value); + return measurement ? { 'font-size': { value: measurement, important } } : null; + } } diff --git a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyWriterPropertyNames.ts b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyWriterPropertyNames.ts deleted file mode 100644 index 63ba2c3ed..000000000 --- a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyWriterPropertyNames.ts +++ /dev/null @@ -1,57 +0,0 @@ -export default { - border: [ - 'border-top-width', - 'border-right-width', - 'border-bottom-width', - 'border-left-width', - 'border-top-style', - 'border-right-style', - 'border-bottom-style', - 'border-left-style', - 'border-top-color', - 'border-right-color', - 'border-bottom-color', - 'border-left-color' - ], - ['border-left']: ['border-left-width', 'border-left-style', 'border-left-color'], - - ['border-bottom']: ['border-bottom-width', 'border-bottom-style', 'border-bottom-color'], - - ['border-right']: ['border-right-width', 'border-right-style', 'border-right-color'], - - ['border-top']: ['border-top-width', 'border-top-style', 'border-top-color'], - ['border-width']: [ - 'border-top-width', - 'border-right-width', - 'border-bottom-width', - 'border-left-width' - ], - ['border-style']: [ - 'border-top-style', - 'border-right-style', - 'border-bottom-style', - 'border-left-style' - ], - ['border-color']: [ - 'border-top-color', - 'border-right-color', - 'border-bottom-color', - 'border-left-color' - ], - ['border-radius']: [ - 'border-top-left-radius', - 'border-top-right-radius', - 'border-bottom-right-radius', - 'border-bottom-left-radius' - ], - ['background']: [ - 'background-color', - 'background-image', - 'background-repeat', - 'background-attachment', - 'background-position' - ], - ['flex']: ['flex-grow', 'flex-shrink', 'flex-basis'], - ['padding']: ['padding-top', 'padding-right', 'padding-bottom', 'padding-left'], - ['margin']: ['margin-top', 'margin-right', 'margin-bottom', 'margin-left'] -}; diff --git a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationValueParser.ts b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationValueParser.ts index b04c9379f..214045e76 100644 --- a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationValueParser.ts +++ b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationValueParser.ts @@ -40,6 +40,16 @@ export default class CSSStyleDeclarationValueParser { return null; } + /** + * Returns length or percentage. + * + * @param value Value. + * @returns Parsed value. + */ + public static getLengthOrPercentage(value: string): string { + return this.getLength(value) || this.getPercentage(value); + } + /** * Returns measurement. *