Skip to content

Commit

Permalink
#921@trivial: Continues on implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
capricorn86 committed May 17, 2023
1 parent 42a8ce1 commit 1871f3f
Show file tree
Hide file tree
Showing 13 changed files with 250 additions and 104 deletions.
Expand Up @@ -3,8 +3,8 @@ import Attr from '../../nodes/attr/Attr';
import CSSRule from '../CSSRule';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum';
import DOMException from '../../exception/DOMException';
import CSSStyleDeclarationElementStyle from './utilities/CSSStyleDeclarationElementStyle';
import CSSStyleDeclarationPropertyManager from './utilities/CSSStyleDeclarationPropertyManager';
import CSSStyleDeclarationElementStyle from './element-style/CSSStyleDeclarationElementStyle';
import CSSStyleDeclarationPropertyManager from './property-manager/CSSStyleDeclarationPropertyManager';

/**
* CSS Style Declaration.
Expand Down
Expand Up @@ -3,16 +3,17 @@ import IElement from '../../../nodes/element/IElement';
import IDocument from '../../../nodes/document/IDocument';
import IHTMLStyleElement from '../../../nodes/html-style-element/IHTMLStyleElement';
import INodeList from '../../../nodes/node/INodeList';
import CSSStyleDeclarationPropertyManager from './CSSStyleDeclarationPropertyManager';
import CSSStyleDeclarationPropertyManager from '../property-manager/CSSStyleDeclarationPropertyManager';
import NodeTypeEnum from '../../../nodes/node/NodeTypeEnum';
import CSSRuleTypeEnum from '../../CSSRuleTypeEnum';
import CSSMediaRule from '../../rules/CSSMediaRule';
import CSSRule from '../../CSSRule';
import CSSStyleRule from '../../rules/CSSStyleRule';
import CSSStyleDeclarationElementDefaultCSS from './CSSStyleDeclarationElementDefaultCSS';
import CSSStyleDeclarationElementInheritedProperties from './CSSStyleDeclarationElementInheritedProperties';
import CSSStyleDeclarationCSSParser from './CSSStyleDeclarationCSSParser';
import CSSStyleDeclarationElementDefaultCSS from './config/CSSStyleDeclarationElementDefaultCSS';
import CSSStyleDeclarationElementInheritedProperties from './config/CSSStyleDeclarationElementInheritedProperties';
import CSSStyleDeclarationCSSParser from '../css-parser/CSSStyleDeclarationCSSParser';
import QuerySelector from '../../../query-selector/QuerySelector';
import CSSMeasurementConverter from '../measurement-converter/CSSMeasurementConverter';

const CSS_VARIABLE_REGEXP = /var\( *(--[^) ]+)\)/g;

Expand Down Expand Up @@ -160,98 +161,63 @@ export default class CSSStyleDeclarationElementStyle {

// Concatenates all parent element CSS to one string.
const targetElement = parentElements[parentElements.length - 1];
let inheritedCSSText = '';
const propertyManager = new CSSStyleDeclarationPropertyManager();
const contextProperties: {
rootFontSize: string | null;
parentFontSize: string | null;
cssVariables: { [k: string]: string };
} = {
rootFontSize: null,
parentFontSize: null,
cssVariables: {}
};

for (const parentElement of parentElements) {
if (parentElement !== targetElement) {
parentElement.cssTexts.sort((a, b) => a.priorityWeight - b.priorityWeight);

if (CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName]) {
inheritedCSSText +=
CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName];
}

for (const cssText of parentElement.cssTexts) {
inheritedCSSText += cssText.cssText;
}
parentElement.cssTexts.sort((a, b) => a.priorityWeight - b.priorityWeight);

if (parentElement.element['_attributes']['style']?.value) {
inheritedCSSText += parentElement.element['_attributes']['style'].value;
}
let elementCSSText = '';
if (CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName]) {
elementCSSText +=
CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName];
}
}

const cssVariables: { [k: string]: string } = {};
const properties = {};
let targetCSSText =
CSSStyleDeclarationElementDefaultCSS[(<IElement>targetElement.element).tagName] || '';

targetElement.cssTexts.sort((a, b) => a.priorityWeight - b.priorityWeight);

for (const cssText of targetElement.cssTexts) {
targetCSSText += cssText.cssText;
}

if (targetElement.element['_attributes']['style']?.value) {
targetCSSText += targetElement.element['_attributes']['style'].value;
}

const combinedCSSText = inheritedCSSText + targetCSSText;

if (this.cache.propertyManager && this.cache.cssText === combinedCSSText) {
return this.cache.propertyManager;
}

// Parses the parent element CSS and stores CSS variables and inherited properties.
CSSStyleDeclarationCSSParser.parse(inheritedCSSText, (name, value, important) => {
if (name.startsWith('--')) {
const cssValue = this.getCSSValue(value, cssVariables);
if (cssValue) {
cssVariables[name] = cssValue;
}
return;
for (const cssText of parentElement.cssTexts) {
elementCSSText += cssText.cssText;
}

if (CSSStyleDeclarationElementInheritedProperties[name]) {
const cssValue = this.getCSSValue(value, cssVariables);
if (cssValue && (!properties[name]?.important || important)) {
properties[name] = {
value: cssValue,
important
};
}
if (parentElement.element['_attributes']['style']?.value) {
elementCSSText += parentElement.element['_attributes']['style'].value;
}
});

// Parses the target element CSS.
CSSStyleDeclarationCSSParser.parse(targetCSSText, (name, value, important) => {
if (name.startsWith('--')) {
const cssValue = this.getCSSValue(value, cssVariables);
if (cssValue && (!properties[name]?.important || important)) {
cssVariables[name] = cssValue;
properties[name] = {
value,
important
};
}
} else {
const cssValue = this.getCSSValue(value, cssVariables);
if (cssValue && (!properties[name]?.important || important)) {
properties[name] = {
value: cssValue,
important
};
CSSStyleDeclarationCSSParser.parse(elementCSSText, (name, value, important) => {
if (name.startsWith('--')) {
const cssValue = this.getCSSValue(value, contextProperties);
if (cssValue) {
contextProperties.cssVariables[name] = cssValue;
}
return;
}
}
});

const propertyManager = new CSSStyleDeclarationPropertyManager();

for (const name of Object.keys(properties)) {
propertyManager.set(name, properties[name].value, properties[name].important);
if (
CSSStyleDeclarationElementInheritedProperties[name] ||
parentElement === targetElement
) {
const cssValue = this.getCSSValue(value, contextProperties);
if (cssValue && (!propertyManager.get(name)?.important || important)) {
propertyManager.set(name, cssValue, important);
const fontSize = propertyManager.get('font-size');
if (fontSize !== null) {
if ((<IElement>parentElement.element).tagName === 'HTML') {
contextProperties.rootFontSize = fontSize.value;
} else if (parentElement !== targetElement) {
contextProperties.parentFontSize = fontSize.value;
}
}
}
}
});
}

this.cache.cssText = combinedCSSText;
this.cache.propertyManager = propertyManager;

return propertyManager;
Expand Down Expand Up @@ -320,20 +286,43 @@ export default class CSSStyleDeclarationElementStyle {
* Returns CSS value.
*
* @param value Value.
* @param cssVariables CSS variables.
* @param contextProperties Context properties.
* @param contextProperties.rootFontSize Root font size.
* @param contextProperties.parentFontSize Parent font size.
* @param contextProperties.cssVariables CSS variables.
* @returns CSS value.
*/
private getCSSValue(value: string, cssVariables: { [k: string]: string }): string {
private getCSSValue(
value: string,
contextProperties: {
rootFontSize: string | null;
parentFontSize: string | null;
cssVariables: { [k: string]: string };
}
): string {
const regexp = new RegExp(CSS_VARIABLE_REGEXP);
let newValue = value;
let match;

while ((match = regexp.exec(value)) !== null) {
const cssVariableValue = cssVariables[match[1]];
const cssVariableValue = contextProperties.cssVariables[match[1]];
if (!cssVariableValue) {
return null;
}
newValue = newValue.replace(match[0], cssVariableValue);
}

const valueInPixels = CSSMeasurementConverter.toPixels({
ownerWindow: this.element.ownerDocument.defaultView,
value: newValue,
rootFontSize: contextProperties.rootFontSize || 16,
parentFontSize: contextProperties.parentFontSize || 16
});

if (valueInPixels !== null) {
return valueInPixels + 'px';
}

return newValue;
}
}
Expand Up @@ -69,7 +69,7 @@ export default {
HEADER: 'display: block;',
HGROUP: 'display: block;',
HR: 'display: block;',
HTML: 'display: block;direction: ltr;',
HTML: 'display: block;direction: ltr;font: 16px "Times New Roman";',
I: '',
IFRAME: '',
INS: '',
Expand Down
@@ -0,0 +1,75 @@
import IWindow from '../../../window/IWindow';

/**
* CSS Measurement Converter.
*/
export default class CSSMeasurementConverter {
/**
* Returns measurement in pixels.
*
* @param options Options.
* @param options.ownerWindow Owner window.
* @param options.value Measurement (e.g. "10px", "10rem" or "10em").
* @param options.rootFontSize Root font size in pixels.
* @param options.parentFontSize Parent font size in pixels.
* @returns Measurement in pixels.
*/
public static toPixels(options: {
ownerWindow: IWindow;
value: string;
rootFontSize: string | number;
parentFontSize: string | number;
}): number | null {
const value = parseFloat(options.value);
const unit = options.value.replace(value.toString(), '');

if (isNaN(value)) {
return null;
}

switch (unit) {
case 'px':
return value;
case 'rem':
return this.round(value * parseFloat(<string>options.rootFontSize));
case 'em':
return this.round(value * parseFloat(<string>options.parentFontSize));
case 'vw':
return this.round((value * options.ownerWindow.innerWidth) / 100);
case 'vh':
return this.round((value * options.ownerWindow.innerHeight) / 100);
case 'vmin':
return this.round(
(value * Math.min(options.ownerWindow.innerWidth, options.ownerWindow.innerHeight)) / 100
);
case 'vmax':
return (
(value * Math.max(options.ownerWindow.innerWidth, options.ownerWindow.innerHeight)) / 100
);
case 'cm':
return this.round(value * 37.7812);
case 'mm':
return this.round(value * 3.7781);
case 'in':
return this.round(value * 96);
case 'pt':
return this.round(value * 1.3281);
case 'pc':
return this.round(value * 16);
case 'Q':
return this.round(value * 0.945);
default:
return null;
}
}

/**
* Rounds the number with 4 decimals.
*
* @param value Value.
* @returns Rounded value.
*/
public static round(value: number): number {
return Math.round(value * 10000) / 10000;
}
}
Expand Up @@ -2,7 +2,7 @@ import ICSSStyleDeclarationPropertyValue from './ICSSStyleDeclarationPropertyVal
import CSSStyleDeclarationPropertySetParser from './CSSStyleDeclarationPropertySetParser';
import CSSStyleDeclarationValueParser from './CSSStyleDeclarationValueParser';
import CSSStyleDeclarationPropertyGetParser from './CSSStyleDeclarationPropertyGetParser';
import CSSStyleDeclarationCSSParser from './CSSStyleDeclarationCSSParser';
import CSSStyleDeclarationCSSParser from '../css-parser/CSSStyleDeclarationCSSParser';

const TO_STRING_SHORTHAND_PROPERTIES = [
['margin'],
Expand Down Expand Up @@ -44,7 +44,7 @@ export default class CSSStyleDeclarationPropertyManager {
* @param name Property name.
* @returns Property value.
*/
public get(name: string): ICSSStyleDeclarationPropertyValue {
public get(name: string): ICSSStyleDeclarationPropertyValue | null {
if (this.properties[name]) {
return this.properties[name];
}
Expand Down
@@ -1,7 +1,8 @@
const COLOR_REGEXP =
/^#([0-9a-fA-F]{3,4}){1,2}$|^rgb\(([^)]*)\)$|^rgba\(([^)]*)\)$|^hsla?\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*(,\s*(-?\d+|-?\d*.\d+)\s*)?\)/;

const LENGTH_REGEXP = /^(0|[-+]?[0-9]*\.?[0-9]+(in|cm|em|mm|pt|pc|px|ex|rem|vh|vw|ch))$/;
const LENGTH_REGEXP =
/^(0|[-+]?[0-9]*\.?[0-9]+(in|cm|em|mm|pt|pc|px|ex|rem|vh|vw|ch|vw|vh|vmin|vmax|Q))$/;
const PERCENTAGE_REGEXP = /^[-+]?[0-9]*\.?[0-9]+%$/;
const DEGREE_REGEXP = /^[0-9]+deg$/;
const URL_REGEXP = /^url\(\s*([^)]*)\s*\)$/;
Expand Down

0 comments on commit 1871f3f

Please sign in to comment.