From 54036714c402b6b05a106266f4d539e10f12b56f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 25 Sep 2019 17:10:47 +0200 Subject: [PATCH 001/142] Add style normalization stub for borders. --- src/view/element.js | 2 +- tests/view/stylenormalizer.js | 141 ++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 tests/view/stylenormalizer.js diff --git a/src/view/element.js b/src/view/element.js index d31d70ff7..122f35b56 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -803,7 +803,7 @@ function parseAttributes( attrs ) { // // @param {Map.} stylesMap Map to insert parsed properties and values. // @param {String} stylesString Styles to parse. -function parseInlineStyles( stylesMap, stylesString ) { +export function parseInlineStyles( stylesMap, stylesString ) { // `null` if no quote was found in input string or last found quote was a closing quote. See below. let quoteType = null; let propertyNameStart = 0; diff --git a/tests/view/stylenormalizer.js b/tests/view/stylenormalizer.js new file mode 100644 index 000000000..0af32155d --- /dev/null +++ b/tests/view/stylenormalizer.js @@ -0,0 +1,141 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ +import { parseInlineStyles } from '../../src/view/element'; + +function parseStyle( string ) { + const map = new Map(); + + parseInlineStyles( map, string ); + + const styleObject = {}; + + for ( const key of map.keys() ) { + const value = map.get( key ); + + if ( key === 'border' ) { + const parsedBorder = parseBorderAttribute( value ); + + const border = { + top: parsedBorder, + right: parsedBorder, + bottom: parsedBorder, + left: parsedBorder + }; + + addStyle( styleObject, 'border', border ); + } else { + const borderPositionRegExp = /border-(top|right|bottom|left)$/; + + if ( borderPositionRegExp.test( key ) ) { + const border = {}; + const which = borderPositionRegExp.exec( key )[ 1 ]; + + border[ which ] = parseBorderAttribute( value ); + + addStyle( styleObject, 'border', border ); + } else { + addStyle( styleObject, key, value ); + } + } + } + + return styleObject; +} + +function parseBorderAttribute( string ) { + const result = {}; + + for ( const part of string.split( ' ' ) ) { + if ( isLength( part ) ) { + result.width = part; + } + + if ( isLineStyle( part ) ) { + result.style = part; + } + + if ( isColor( part ) ) { + result.color = part; + } + } + + return result; +} + +function isColor( string ) { + return /^([#0-9A-Fa-f]{3,8}|[a-zA-Z]+)$/.test( string ) && !isLineStyle( string ); +} + +function isLineStyle( string ) { + return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); +} + +function isLength( string ) { + return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); +} + +function addStyle( styleObject, name, value ) { + if ( typeof value === 'object' ) { + styleObject[ name ] = Object.assign( {}, styleObject[ name ] || {}, value ); + } else { + styleObject[ name ] = value; + } +} + +describe( 'Style normalizer', () => { + it( 'should parse', () => { + expect( parseStyle( 'border:1px solid blue;' ) ).to.deep.equal( { + border: { + bottom: { + color: 'blue', + style: 'solid', + width: '1px' + }, + left: { + color: 'blue', + style: 'solid', + width: '1px' + }, + right: { + color: 'blue', + style: 'solid', + width: '1px' + }, + top: { + color: 'blue', + style: 'solid', + width: '1px' + } + } + } ); + } ); + + it( 'should parse', () => { + expect( parseStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ) ).to.deep.equal( { + border: { + bottom: { + color: 'blue', + style: 'solid', + width: '1px' + }, + left: { + color: '#665511', + style: 'dashed', + width: '2.7em' + }, + right: { + color: 'blue', + style: 'solid', + width: '1px' + }, + top: { + color: '#ccc', + style: 'dotted', + width: '7px' + } + } + } ); + } ); +} ); From 34c56873e354e165cd85762c1bbe1e2159e1148c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 12:01:44 +0200 Subject: [PATCH 002/142] Extract stylenormalizer.js. --- src/view/stylenormalizer.js | 127 ++++++++++++++++++++++++++++++++++ tests/view/stylenormalizer.js | 118 +++++++++++++++---------------- 2 files changed, 184 insertions(+), 61 deletions(-) create mode 100644 src/view/stylenormalizer.js diff --git a/src/view/stylenormalizer.js b/src/view/stylenormalizer.js new file mode 100644 index 000000000..b3a5ed8a5 --- /dev/null +++ b/src/view/stylenormalizer.js @@ -0,0 +1,127 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ +import { parseInlineStyles } from './element'; + +function parseBorder( entry, value, styleObject ) { + if ( entry === 'border' ) { + const parsedBorder = parseBorderAttribute( value ); + + const border = { + top: parsedBorder, + right: parsedBorder, + bottom: parsedBorder, + left: parsedBorder + }; + + addStyle( styleObject, 'border', border ); + } else { + const borderPositionRegExp = /border-(top|right|bottom|left)$/; + + if ( borderPositionRegExp.test( entry ) ) { + const border = {}; + const which = borderPositionRegExp.exec( entry )[ 1 ]; + + border[ which ] = parseBorderAttribute( value ); + + addStyle( styleObject, 'border', border ); + } else { + addStyle( styleObject, entry, value ); + } + } +} + +export function parseStyle( string ) { + const map = new Map(); + + parseInlineStyles( map, string ); + + const styleObject = {}; + + for ( const key of map.keys() ) { + const value = map.get( key ); + + parseBorder( key, value, styleObject ); + } + + return styleObject; +} + +function parseBorderAttribute( string ) { + const result = {}; + + for ( const part of string.split( ' ' ) ) { + if ( isLength( part ) ) { + result.width = part; + } + + if ( isLineStyle( part ) ) { + result.style = part; + } + + if ( isColor( part ) ) { + result.color = part; + } + } + + return result; +} + +function isColor( string ) { + return /^([#0-9A-Fa-f]{3,8}|[a-zA-Z]+)$/.test( string ) && !isLineStyle( string ); +} + +function isLineStyle( string ) { + return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); +} + +function isLength( string ) { + return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); +} + +function addStyle( styleObject, name, value ) { + if ( typeof value === 'object' ) { + styleObject[ name ] = Object.assign( {}, styleObject[ name ] || {}, value ); + } else { + styleObject[ name ] = value; + } +} + +export function toInlineStyle( styleName, styleObject ) { + if ( styleName === 'border' ) { + const top = toInlineBorder( styleObject.top ); + const right = toInlineBorder( styleObject.right ); + const bottom = toInlineBorder( styleObject.bottom ); + const left = toInlineBorder( styleObject.left ); + + if ( top === right && right === bottom && bottom === left ) { + return 'border:' + top; + } else { + return [ + 'border-top:' + top, + 'border-right:' + right, + 'border-bottom:' + bottom, + 'border-left:' + left + ].join( ';' ); + } + } +} + +function toInlineBorder( object ) { + const style = []; + + if ( object.width ) { + style.push( object.width ); + } + + if ( object.style ) { + style.push( object.style ); + } + + if ( object.color ) { + style.push( object.color ); + } + + return style.join( ' ' ); +} diff --git a/tests/view/stylenormalizer.js b/tests/view/stylenormalizer.js index 0af32155d..5afdb38e4 100644 --- a/tests/view/stylenormalizer.js +++ b/tests/view/stylenormalizer.js @@ -2,67 +2,7 @@ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import { parseInlineStyles } from '../../src/view/element'; - -function parseStyle( string ) { - const map = new Map(); - - parseInlineStyles( map, string ); - - const styleObject = {}; - - for ( const key of map.keys() ) { - const value = map.get( key ); - - if ( key === 'border' ) { - const parsedBorder = parseBorderAttribute( value ); - - const border = { - top: parsedBorder, - right: parsedBorder, - bottom: parsedBorder, - left: parsedBorder - }; - - addStyle( styleObject, 'border', border ); - } else { - const borderPositionRegExp = /border-(top|right|bottom|left)$/; - - if ( borderPositionRegExp.test( key ) ) { - const border = {}; - const which = borderPositionRegExp.exec( key )[ 1 ]; - - border[ which ] = parseBorderAttribute( value ); - - addStyle( styleObject, 'border', border ); - } else { - addStyle( styleObject, key, value ); - } - } - } - - return styleObject; -} - -function parseBorderAttribute( string ) { - const result = {}; - - for ( const part of string.split( ' ' ) ) { - if ( isLength( part ) ) { - result.width = part; - } - - if ( isLineStyle( part ) ) { - result.style = part; - } - - if ( isColor( part ) ) { - result.color = part; - } - } - - return result; -} +import { parseStyle, toInlineStyle } from '../../src/view/stylenormalizer'; function isColor( string ) { return /^([#0-9A-Fa-f]{3,8}|[a-zA-Z]+)$/.test( string ) && !isLineStyle( string ); @@ -138,4 +78,60 @@ describe( 'Style normalizer', () => { } } ); } ); + + it( 'should output', () => { + const border = { + bottom: { + color: 'blue', + style: 'solid', + width: '1px' + }, + left: { + color: 'blue', + style: 'solid', + width: '1px' + }, + right: { + color: 'blue', + style: 'solid', + width: '1px' + }, + top: { + color: 'blue', + style: 'solid', + width: '1px' + } + }; + + expect( toInlineStyle( 'border', border ) ).to.equal( 'border:1px solid blue' ); + } ); + + it( 'should output', () => { + const border = { + bottom: { + color: 'blue', + style: 'solid', + width: '1px' + }, + left: { + color: '#665511', + style: 'dashed', + width: '2.7em' + }, + right: { + color: 'blue', + style: 'solid', + width: '1px' + }, + top: { + color: '#ccc', + style: 'dotted', + width: '7px' + } + }; + + expect( toInlineStyle( 'border', border ) ).to.equal( + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511' + ); + } ); } ); From 1e4d5aa2f9d3d403e8c356731881888eb81aa420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 14:15:06 +0200 Subject: [PATCH 003/142] Define StyleProxy methods. --- src/view/element.js | 4 + src/view/stylenormalizer.js | 178 +++++++++++++++++++++++++++------- tests/view/stylenormalizer.js | 153 ++++++++++++++--------------- 3 files changed, 218 insertions(+), 117 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 122f35b56..2c2226fdf 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -14,6 +14,7 @@ import objectToMap from '@ckeditor/ckeditor5-utils/src/objecttomap'; import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable'; import Matcher from './matcher'; import { isPlainObject } from 'lodash-es'; +import { getStyleProxy } from './stylenormalizer'; /** * View element. @@ -113,6 +114,8 @@ export default class Element extends Node { if ( this._attrs.has( 'style' ) ) { // Remove style attribute and handle it by styles map. parseInlineStyles( this._styles, this._attrs.get( 'style' ) ); + this._stylesProxy = getStyleProxy( this._attrs.get( 'style' ) ); + this._attrs.delete( 'style' ); } @@ -615,6 +618,7 @@ export default class Element extends Node { parseClasses( this._classes, value ); } else if ( key == 'style' ) { parseInlineStyles( this._styles, value ); + this._stylesProxy = getStyleProxy( value ); } else { this._attrs.set( key, value ); } diff --git a/src/view/stylenormalizer.js b/src/view/stylenormalizer.js index b3a5ed8a5..50dab618e 100644 --- a/src/view/stylenormalizer.js +++ b/src/view/stylenormalizer.js @@ -4,50 +4,134 @@ */ import { parseInlineStyles } from './element'; -function parseBorder( entry, value, styleObject ) { - if ( entry === 'border' ) { - const parsedBorder = parseBorderAttribute( value ); +import { get, has, isObject, unset } from 'lodash-es'; - const border = { - top: parsedBorder, - right: parsedBorder, - bottom: parsedBorder, - left: parsedBorder - }; +export function getStyleProxy( styleString ) { + return new StyleProxy( styleString ); +} - addStyle( styleObject, 'border', border ); - } else { - const borderPositionRegExp = /border-(top|right|bottom|left)$/; +// <-- FROM VIEW/MODEL +// proxy.setStyle( 'border: 1px solid blue;' ) +// +// <-> MODIFY +// proxy.insertRule( 'border-top', '1px solid blue' ); // obj? +// proxy.removeRule( 'border-top' ); +// proxy.clear(); +// +// --> TO MODEL +// proxy.getModel(); // full +// proxy.getModel( 'border' ); +// proxy.getModel( 'border-top' ); +// +// --> TO VIEW +// proxy.getInlineStyle(); +// proxy.getInlineRule( 'border' ); +// proxy.getInlineRule( 'border-top' ); +export class StyleProxy { + constructor( styleString = '' ) { + this._styles = {}; + + this.setStyle( styleString ); + } + + setStyle( styleString = '' ) { + this._styles = parseStyle( styleString ); + } + + insertRule( nameOrObject, value ) { + parseRule( nameOrObject, value, this._styles ); + } - if ( borderPositionRegExp.test( entry ) ) { - const border = {}; - const which = borderPositionRegExp.exec( entry )[ 1 ]; + removeRule( name ) { + unset( this._styles, name.replace( '-', '.' ) ); + } - border[ which ] = parseBorderAttribute( value ); + getModel( name ) { + if ( !name ) { + return this._styles; + } // TODO: clone - addStyle( styleObject, 'border', border ); + if ( has( this._styles, name.replace( '-', '.' ) ) ) { + return get( this._styles, name.replace( '-', '.' ) ); } else { - addStyle( styleObject, entry, value ); + return this._styles[ name ]; + } + } + + getInlineStyle() { + const parsed = []; + + for ( const key of Object.keys( this._styles ) ) { + const model = this.getModel( key ); + + parsed.push( toInlineStyle( key, model ) ); } + + return parsed.join( ';' ) + ( parsed.length ? ';' : '' ); + } + + getInlineRule( name ) { + const model = this.getModel( name ); + + if ( !model ) { + // Try return directly + return this._styles[ name ]; + } + + if ( isObject( model ) ) { + return toInlineStyle( name, model, true ); + } + // String value + else { + return model; + } + } + + clear() { + this._styles = {}; } } -export function parseStyle( string ) { +const borderPositionRegExp = /border-(top|right|bottom|left)$/; + +export function parseStyle( string, styleObject = {} ) { const map = new Map(); parseInlineStyles( map, string ); - const styleObject = {}; - for ( const key of map.keys() ) { const value = map.get( key ); - parseBorder( key, value, styleObject ); + parseRule( key, value, styleObject ); } return styleObject; } +function parseRule( key, value, styleObject ) { + if ( key === 'border' ) { + const parsedBorder = parseBorderAttribute( value ); + + const border = { + top: parsedBorder, + right: parsedBorder, + bottom: parsedBorder, + left: parsedBorder + }; + + addStyle( styleObject, 'border', border ); + } else if ( borderPositionRegExp.test( key ) ) { + const border = {}; + const which = borderPositionRegExp.exec( key )[ 1 ]; + + border[ which ] = parseBorderAttribute( value ); + + addStyle( styleObject, 'border', border ); + } else { + addStyle( styleObject, key, value ); + } +} + function parseBorderAttribute( string ) { const result = {}; @@ -88,27 +172,49 @@ function addStyle( styleObject, name, value ) { } } -export function toInlineStyle( styleName, styleObject ) { +function toInlineStyle( styleName, styleObjectOrString, strict = false ) { if ( styleName === 'border' ) { - const top = toInlineBorder( styleObject.top ); - const right = toInlineBorder( styleObject.right ); - const bottom = toInlineBorder( styleObject.bottom ); - const left = toInlineBorder( styleObject.left ); + const top = toInlineBorder( styleObjectOrString.top ); + const right = toInlineBorder( styleObjectOrString.right ); + const bottom = toInlineBorder( styleObjectOrString.bottom ); + const left = toInlineBorder( styleObjectOrString.left ); if ( top === right && right === bottom && bottom === left ) { - return 'border:' + top; - } else { - return [ - 'border-top:' + top, - 'border-right:' + right, - 'border-bottom:' + bottom, - 'border-left:' + left - ].join( ';' ); + return ( strict ? '' : 'border:' ) + top; + } else if ( !strict ) { + const ret = []; + + // TODO not so nice: + if ( top ) { + ret.push( 'border-top:' + top ); + } + + if ( right ) { + ret.push( 'border-right:' + right ); + } + + if ( bottom ) { + ret.push( 'border-bottom:' + bottom ); + } + + if ( left ) { + ret.push( 'border-left:' + left ); + } + + return ret.join( ';' ); } + + return; } + + if ( borderPositionRegExp.test( styleName ) ) { + return toInlineBorder( styleObjectOrString ); + } + + return ( strict ? '' : styleName + ':' ) + styleObjectOrString; } -function toInlineBorder( object ) { +function toInlineBorder( object = {} ) { const style = []; if ( object.width ) { diff --git a/tests/view/stylenormalizer.js b/tests/view/stylenormalizer.js index 5afdb38e4..13c0e1a06 100644 --- a/tests/view/stylenormalizer.js +++ b/tests/view/stylenormalizer.js @@ -2,85 +2,19 @@ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import { parseStyle, toInlineStyle } from '../../src/view/stylenormalizer'; +import { StyleProxy } from '../../src/view/stylenormalizer'; -function isColor( string ) { - return /^([#0-9A-Fa-f]{3,8}|[a-zA-Z]+)$/.test( string ) && !isLineStyle( string ); -} +describe( 'Style proxy', () => { + let styleProxy; -function isLineStyle( string ) { - return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); -} - -function isLength( string ) { - return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); -} - -function addStyle( styleObject, name, value ) { - if ( typeof value === 'object' ) { - styleObject[ name ] = Object.assign( {}, styleObject[ name ] || {}, value ); - } else { - styleObject[ name ] = value; - } -} - -describe( 'Style normalizer', () => { - it( 'should parse', () => { - expect( parseStyle( 'border:1px solid blue;' ) ).to.deep.equal( { - border: { - bottom: { - color: 'blue', - style: 'solid', - width: '1px' - }, - left: { - color: 'blue', - style: 'solid', - width: '1px' - }, - right: { - color: 'blue', - style: 'solid', - width: '1px' - }, - top: { - color: 'blue', - style: 'solid', - width: '1px' - } - } - } ); + beforeEach( () => { + styleProxy = new StyleProxy(); } ); it( 'should parse', () => { - expect( parseStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ) ).to.deep.equal( { - border: { - bottom: { - color: 'blue', - style: 'solid', - width: '1px' - }, - left: { - color: '#665511', - style: 'dashed', - width: '2.7em' - }, - right: { - color: 'blue', - style: 'solid', - width: '1px' - }, - top: { - color: '#ccc', - style: 'dotted', - width: '7px' - } - } - } ); - } ); + styleProxy.setStyle( 'border:1px solid blue;' ); - it( 'should output', () => { - const border = { + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { bottom: { color: 'blue', style: 'solid', @@ -101,13 +35,13 @@ describe( 'Style normalizer', () => { style: 'solid', width: '1px' } - }; - - expect( toInlineStyle( 'border', border ) ).to.equal( 'border:1px solid blue' ); + } ); } ); - it( 'should output', () => { - const border = { + it( 'should parse', () => { + styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { bottom: { color: 'blue', style: 'solid', @@ -128,10 +62,67 @@ describe( 'Style normalizer', () => { style: 'dotted', width: '7px' } - }; + } ); + } ); - expect( toInlineStyle( 'border', border ) ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511' + it( 'should output', () => { + styleProxy.setStyle( 'border:1px solid blue;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'border:1px solid blue;' ); + expect( styleProxy.getInlineRule( 'border' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); + } ); + + it( 'should output', () => { + styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + ); + expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; + expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); + expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); + } ); + + it( 'should add', () => { + styleProxy.setStyle( 'border:1px solid blue;' ); + styleProxy.insertRule( 'border-left', '#665511 dashed 2.7em' ); + styleProxy.insertRule( 'border-top', '7px dotted #ccc' ); + + expect( styleProxy.getInlineStyle() ).to.equal( + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' ); + expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; + expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); + expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); + } ); + + it( 'should output', () => { + styleProxy.setStyle( 'border:1px solid blue' ); + styleProxy.removeRule( 'border-top' ); + + expect( styleProxy.getInlineStyle() ).to.equal( + 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' + ); + expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; + expect( styleProxy.getInlineRule( 'border-top' ) ).to.be.undefined; + expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); + } ); + + it( 'pass-through', () => { + styleProxy.setStyle( 'foo-bar:baz 1px abc;margin: 2px 3em;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'foo-bar:baz 1px abc;margin:2px 3em;' ); + expect( styleProxy.getInlineRule( 'foo-bar' ) ).to.equal( 'baz 1px abc' ); + expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '2px 3em' ); } ); } ); From 893519efcfabb696dfeec04e4c420d3816130364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 16:12:49 +0200 Subject: [PATCH 004/142] Rewrite Element class to use Styles normalization class. --- src/view/element.js | 134 +++--------------- src/view/{stylenormalizer.js => styles.js} | 140 +++++++++++++++---- tests/view/element.js | 36 +++-- tests/view/{stylenormalizer.js => styles.js} | 14 +- tests/view/upcastwriter.js | 4 +- 5 files changed, 160 insertions(+), 168 deletions(-) rename src/view/{stylenormalizer.js => styles.js} (55%) rename tests/view/{stylenormalizer.js => styles.js} (93%) diff --git a/src/view/element.js b/src/view/element.js index 2c2226fdf..dbf414aaf 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -14,7 +14,7 @@ import objectToMap from '@ckeditor/ckeditor5-utils/src/objecttomap'; import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable'; import Matcher from './matcher'; import { isPlainObject } from 'lodash-es'; -import { getStyleProxy } from './stylenormalizer'; +import Styles from './styles'; /** * View element. @@ -104,17 +104,16 @@ export default class Element extends Node { } /** - * Map of styles. + * Normalized styles. * * @protected * @member {Map} module:engine/view/element~Element#_styles */ - this._styles = new Map(); + this._styles = new Styles(); if ( this._attrs.has( 'style' ) ) { // Remove style attribute and handle it by styles map. - parseInlineStyles( this._styles, this._attrs.get( 'style' ) ); - this._stylesProxy = getStyleProxy( this._attrs.get( 'style' ) ); + this._styles.setStyle( this._attrs.get( 'style' ) ); this._attrs.delete( 'style' ); } @@ -265,17 +264,7 @@ export default class Element extends Node { } if ( key == 'style' ) { - if ( this._styles.size > 0 ) { - let styleString = ''; - - for ( const [ property, value ] of this._styles ) { - styleString += `${ property }:${ value };`; - } - - return styleString; - } - - return undefined; + return this._styles.getInlineStyle(); } return this._attrs.get( key ); @@ -343,8 +332,11 @@ export default class Element extends Node { } // Check if styles are the same. - for ( const [ property, value ] of this._styles ) { - if ( !otherElement._styles.has( property ) || otherElement._styles.get( property ) !== value ) { + for ( const property of Object.keys( this._styles._styles ) ) { + if ( + !otherElement._styles.hasRule( property ) || + otherElement._styles.getInlineRule( property ) !== this._styles.getInlineRule( property ) + ) { return false; } } @@ -388,7 +380,7 @@ export default class Element extends Node { * @returns {String|undefined} */ getStyle( property ) { - return this._styles.get( property ); + return this._styles.getInlineRule( property ); } /** @@ -397,7 +389,7 @@ export default class Element extends Node { * @returns {Iterable.} */ getStyleNames() { - return this._styles.keys(); + return Object.keys( this._styles._styles ); } /** @@ -411,7 +403,7 @@ export default class Element extends Node { */ hasStyle( ...property ) { for ( const name of property ) { - if ( !this._styles.has( name ) ) { + if ( !this._styles.hasRule( name ) ) { return false; } } @@ -488,12 +480,12 @@ export default class Element extends Node { */ getIdentity() { const classes = Array.from( this._classes ).sort().join( ',' ); - const styles = Array.from( this._styles ).map( i => `${ i[ 0 ] }:${ i[ 1 ] }` ).sort().join( ';' ); + const styles = this._styles.getInlineStyle(); const attributes = Array.from( this._attrs ).map( i => `${ i[ 0 ] }="${ i[ 1 ] }"` ).sort().join( ' ' ); return this.name + ( classes == '' ? '' : ` class="${ classes }"` ) + - ( styles == '' ? '' : ` style="${ styles }"` ) + + ( !styles ? '' : ` style="${ styles }"` ) + ( attributes == '' ? '' : ` ${ attributes }` ); } @@ -520,7 +512,7 @@ export default class Element extends Node { // Classes and styles are cloned separately - this solution is faster than adding them back to attributes and // parse once again in constructor. cloned._classes = new Set( this._classes ); - cloned._styles = new Map( this._styles ); + cloned._styles.setStyle( this._styles.getInlineStyle() ); // Clone custom properties. cloned._customProperties = new Map( this._customProperties ); @@ -617,8 +609,8 @@ export default class Element extends Node { if ( key == 'class' ) { parseClasses( this._classes, value ); } else if ( key == 'style' ) { - parseInlineStyles( this._styles, value ); - this._stylesProxy = getStyleProxy( value ); + // parseInlineStyles( this._styles, value ); + this._styles.setStyle( value ); } else { this._attrs.set( key, value ); } @@ -649,7 +641,7 @@ export default class Element extends Node { // Remove style attribute. if ( key == 'style' ) { - if ( this._styles.size > 0 ) { + if ( this._styles.size ) { this._styles.clear(); return true; @@ -716,15 +708,7 @@ export default class Element extends Node { _setStyle( property, value ) { this._fireChange( 'attributes', this ); - if ( isPlainObject( property ) ) { - const keys = Object.keys( property ); - - for ( const key of keys ) { - this._styles.set( key, property[ key ] ); - } - } else { - this._styles.set( property, value ); - } + this._styles.insertRule( property, value ); } /** @@ -742,7 +726,7 @@ export default class Element extends Node { this._fireChange( 'attributes', this ); property = Array.isArray( property ) ? property : [ property ]; - property.forEach( name => this._styles.delete( name ) ); + property.forEach( name => this._styles.removeRule( name ) ); } /** @@ -802,82 +786,6 @@ function parseAttributes( attrs ) { return attrs; } -// Parses inline styles and puts property - value pairs into styles map. -// Styles map is cleared before insertion. -// -// @param {Map.} stylesMap Map to insert parsed properties and values. -// @param {String} stylesString Styles to parse. -export function parseInlineStyles( stylesMap, stylesString ) { - // `null` if no quote was found in input string or last found quote was a closing quote. See below. - let quoteType = null; - let propertyNameStart = 0; - let propertyValueStart = 0; - let propertyName = null; - - stylesMap.clear(); - - // Do not set anything if input string is empty. - if ( stylesString === '' ) { - return; - } - - // Fix inline styles that do not end with `;` so they are compatible with algorithm below. - if ( stylesString.charAt( stylesString.length - 1 ) != ';' ) { - stylesString = stylesString + ';'; - } - - // Seek the whole string for "special characters". - for ( let i = 0; i < stylesString.length; i++ ) { - const char = stylesString.charAt( i ); - - if ( quoteType === null ) { - // No quote found yet or last found quote was a closing quote. - switch ( char ) { - case ':': - // Most of time colon means that property name just ended. - // Sometimes however `:` is found inside property value (for example in background image url). - if ( !propertyName ) { - // Treat this as end of property only if property name is not already saved. - // Save property name. - propertyName = stylesString.substr( propertyNameStart, i - propertyNameStart ); - // Save this point as the start of property value. - propertyValueStart = i + 1; - } - - break; - - case '"': - case '\'': - // Opening quote found (this is an opening quote, because `quoteType` is `null`). - quoteType = char; - - break; - - case ';': { - // Property value just ended. - // Use previously stored property value start to obtain property value. - const propertyValue = stylesString.substr( propertyValueStart, i - propertyValueStart ); - - if ( propertyName ) { - // Save parsed part. - stylesMap.set( propertyName.trim(), propertyValue.trim() ); - } - - propertyName = null; - - // Save this point as property name start. Property name starts immediately after previous property value ends. - propertyNameStart = i + 1; - - break; - } - } - } else if ( char === quoteType ) { - // If a quote char is found and it is a closing quote, mark this fact by `null`-ing `quoteType`. - quoteType = null; - } - } -} - // Parses class attribute and puts all classes into classes set. // Classes set s cleared before insertion. // diff --git a/src/view/stylenormalizer.js b/src/view/styles.js similarity index 55% rename from src/view/stylenormalizer.js rename to src/view/styles.js index 50dab618e..ef7591cdb 100644 --- a/src/view/stylenormalizer.js +++ b/src/view/styles.js @@ -2,48 +2,52 @@ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import { parseInlineStyles } from './element'; -import { get, has, isObject, unset } from 'lodash-es'; +/** + * @module engine/view/styles + */ -export function getStyleProxy( styleString ) { - return new StyleProxy( styleString ); -} +import { get, has, isObject, isPlainObject, unset } from 'lodash-es'; -// <-- FROM VIEW/MODEL -// proxy.setStyle( 'border: 1px solid blue;' ) -// -// <-> MODIFY -// proxy.insertRule( 'border-top', '1px solid blue' ); // obj? -// proxy.removeRule( 'border-top' ); -// proxy.clear(); -// -// --> TO MODEL -// proxy.getModel(); // full -// proxy.getModel( 'border' ); -// proxy.getModel( 'border-top' ); -// -// --> TO VIEW -// proxy.getInlineStyle(); -// proxy.getInlineRule( 'border' ); -// proxy.getInlineRule( 'border-top' ); -export class StyleProxy { +/** + * Styles class. + * + * Handles styles normalization. + */ +export default class Styles { constructor( styleString = '' ) { this._styles = {}; this.setStyle( styleString ); } + get size() { + return Object.keys( this._styles ).length; + } + setStyle( styleString = '' ) { this._styles = parseStyle( styleString ); } + hasRule( name ) { + const nameNorm = name.replace( '-', '.' ); + + return has( this._styles, nameNorm ) || !!this._styles[ name ]; + } + insertRule( nameOrObject, value ) { - parseRule( nameOrObject, value, this._styles ); + if ( isPlainObject( nameOrObject ) ) { + for ( const key of Object.keys( nameOrObject ) ) { + this.insertRule( key, nameOrObject[ key ] ); + } + } else { + parseRule( nameOrObject, value, this._styles ); + } } removeRule( name ) { unset( this._styles, name.replace( '-', '.' ) ); + delete this._styles[ name ]; } getModel( name ) { @@ -61,13 +65,19 @@ export class StyleProxy { getInlineStyle() { const parsed = []; - for ( const key of Object.keys( this._styles ) ) { + const keys = Object.keys( this._styles ).sort(); + + if ( !keys.length ) { + return; + } + + for ( const key of keys ) { const model = this.getModel( key ); parsed.push( toInlineStyle( key, model ) ); } - return parsed.join( ';' ) + ( parsed.length ? ';' : '' ); + return parsed.join( ';' ); } getInlineRule( name ) { @@ -94,7 +104,7 @@ export class StyleProxy { const borderPositionRegExp = /border-(top|right|bottom|left)$/; -export function parseStyle( string, styleObject = {} ) { +function parseStyle( string, styleObject = {} ) { const map = new Map(); parseInlineStyles( map, string ); @@ -231,3 +241,79 @@ function toInlineBorder( object = {} ) { return style.join( ' ' ); } + +// Parses inline styles and puts property - value pairs into styles map. +// Styles map is cleared before insertion. +// +// @param {Map.} stylesMap Map to insert parsed properties and values. +// @param {String} stylesString Styles to parse. +function parseInlineStyles( stylesMap, stylesString ) { + // `null` if no quote was found in input string or last found quote was a closing quote. See below. + let quoteType = null; + let propertyNameStart = 0; + let propertyValueStart = 0; + let propertyName = null; + + stylesMap.clear(); + + // Do not set anything if input string is empty. + if ( stylesString === '' ) { + return; + } + + // Fix inline styles that do not end with `;` so they are compatible with algorithm below. + if ( stylesString.charAt( stylesString.length - 1 ) != ';' ) { + stylesString = stylesString + ';'; + } + + // Seek the whole string for "special characters". + for ( let i = 0; i < stylesString.length; i++ ) { + const char = stylesString.charAt( i ); + + if ( quoteType === null ) { + // No quote found yet or last found quote was a closing quote. + switch ( char ) { + case ':': + // Most of time colon means that property name just ended. + // Sometimes however `:` is found inside property value (for example in background image url). + if ( !propertyName ) { + // Treat this as end of property only if property name is not already saved. + // Save property name. + propertyName = stylesString.substr( propertyNameStart, i - propertyNameStart ); + // Save this point as the start of property value. + propertyValueStart = i + 1; + } + + break; + + case '"': + case '\'': + // Opening quote found (this is an opening quote, because `quoteType` is `null`). + quoteType = char; + + break; + + case ';': { + // Property value just ended. + // Use previously stored property value start to obtain property value. + const propertyValue = stylesString.substr( propertyValueStart, i - propertyValueStart ); + + if ( propertyName ) { + // Save parsed part. + stylesMap.set( propertyName.trim(), propertyValue.trim() ); + } + + propertyName = null; + + // Save this point as property name start. Property name starts immediately after previous property value ends. + propertyNameStart = i + 1; + + break; + } + } + } else if ( char === quoteType ) { + // If a quote char is found and it is a closing quote, mark this fact by `null`-ing `quoteType`. + quoteType = null; + } + } +} diff --git a/tests/view/element.js b/tests/view/element.js index 1f161e163..832da08ba 100644 --- a/tests/view/element.js +++ b/tests/view/element.js @@ -68,17 +68,18 @@ describe( 'Element', () => { expect( el._classes.has( 'three' ) ).to.be.true; } ); - it( 'should move style attribute to style map', () => { + it( 'should move style attribute to style proxy', () => { const el = new Element( 'p', { id: 'test', style: 'one: style1; two:style2 ; three : url(http://ckeditor.com)' } ); expect( el._attrs.has( 'style' ) ).to.be.false; expect( el._attrs.has( 'id' ) ).to.be.true; - expect( el._styles.has( 'one' ) ).to.be.true; - expect( el._styles.get( 'one' ) ).to.equal( 'style1' ); - expect( el._styles.has( 'two' ) ).to.be.true; - expect( el._styles.get( 'two' ) ).to.equal( 'style2' ); - expect( el._styles.has( 'three' ) ).to.be.true; - expect( el._styles.get( 'three' ) ).to.equal( 'url(http://ckeditor.com)' ); + + expect( el._styles.hasRule( 'one' ) ).to.be.true; + expect( el._styles.getInlineRule( 'one' ) ).to.equal( 'style1' ); + expect( el._styles.hasRule( 'two' ) ).to.be.true; + expect( el._styles.getInlineRule( 'two' ) ).to.equal( 'style2' ); + expect( el._styles.hasRule( 'three' ) ).to.be.true; + expect( el._styles.getInlineRule( 'three' ) ).to.equal( 'url(http://ckeditor.com)' ); } ); } ); @@ -199,10 +200,10 @@ describe( 'Element', () => { expect( clone ).to.not.equal( el ); expect( clone.name ).to.equal( el.name ); - expect( clone._styles.has( 'color' ) ).to.be.true; - expect( clone._styles.get( 'color' ) ).to.equal( 'red' ); - expect( clone._styles.has( 'font-size' ) ).to.be.true; - expect( clone._styles.get( 'font-size' ) ).to.equal( '12px' ); + expect( clone._styles.hasRule( 'color' ) ).to.be.true; + expect( clone._styles.getInlineRule( 'color' ) ).to.equal( 'red' ); + expect( clone._styles.hasRule( 'font-size' ) ).to.be.true; + expect( clone._styles.getInlineRule( 'font-size' ) ).to.equal( '12px' ); } ); it( 'should clone custom properties', () => { @@ -515,7 +516,7 @@ describe( 'Element', () => { el._setStyle( 'color', 'red' ); el._setStyle( 'top', '10px' ); - expect( el.getAttribute( 'style' ) ).to.equal( 'color:red;top:10px;' ); + expect( el.getAttribute( 'style' ) ).to.equal( 'color:red;top:10px' ); } ); it( 'should return undefined if no style attribute', () => { @@ -538,7 +539,7 @@ describe( 'Element', () => { el._setStyle( 'font-weight', 'bold' ); expect( Array.from( el.getAttributes() ) ).to.deep.equal( [ - [ 'class', 'abc xyz' ], [ 'style', 'width:20px;font-weight:bold;' ] + [ 'class', 'abc xyz' ], [ 'style', 'font-weight:bold;width:20px' ] ] ); } ); } ); @@ -766,8 +767,7 @@ describe( 'Element', () => { it( 'should set element style', () => { el._setStyle( 'color', 'red' ); - expect( el._styles.has( 'color' ) ).to.be.true; - expect( el._styles.get( 'color' ) ).to.equal( 'red' ); + expect( el._styles._styles.color ).to.equal( 'red' ); } ); it( 'should fire change event with attributes type', done => { @@ -785,10 +785,8 @@ describe( 'Element', () => { position: 'fixed' } ); - expect( el._styles.has( 'color' ) ).to.be.true; - expect( el._styles.has( 'position' ) ).to.be.true; - expect( el._styles.get( 'color' ) ).to.equal( 'red' ); - expect( el._styles.get( 'position' ) ).to.equal( 'fixed' ); + expect( el._styles._styles.color ).to.equal( 'red' ); + expect( el._styles._styles.position ).to.equal( 'fixed' ); } ); } ); diff --git a/tests/view/stylenormalizer.js b/tests/view/styles.js similarity index 93% rename from tests/view/stylenormalizer.js rename to tests/view/styles.js index 13c0e1a06..2120ae7e0 100644 --- a/tests/view/stylenormalizer.js +++ b/tests/view/styles.js @@ -2,9 +2,9 @@ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import { StyleProxy } from '../../src/view/stylenormalizer'; +import StyleProxy from '../../src/view/styles'; -describe( 'Style proxy', () => { +describe( 'Styles', () => { let styleProxy; beforeEach( () => { @@ -68,7 +68,7 @@ describe( 'Style proxy', () => { it( 'should output', () => { styleProxy.setStyle( 'border:1px solid blue;' ); - expect( styleProxy.getInlineStyle() ).to.equal( 'border:1px solid blue;' ); + expect( styleProxy.getInlineStyle() ).to.equal( 'border:1px solid blue' ); expect( styleProxy.getInlineRule( 'border' ) ).to.equal( '1px solid blue' ); expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '1px solid blue' ); expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); @@ -80,7 +80,7 @@ describe( 'Style proxy', () => { styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styleProxy.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511' ); expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); @@ -95,7 +95,7 @@ describe( 'Style proxy', () => { styleProxy.insertRule( 'border-top', '7px dotted #ccc' ); expect( styleProxy.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511' ); expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); @@ -109,7 +109,7 @@ describe( 'Style proxy', () => { styleProxy.removeRule( 'border-top' ); expect( styleProxy.getInlineStyle() ).to.equal( - 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' + 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue' ); expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; expect( styleProxy.getInlineRule( 'border-top' ) ).to.be.undefined; @@ -121,7 +121,7 @@ describe( 'Style proxy', () => { it( 'pass-through', () => { styleProxy.setStyle( 'foo-bar:baz 1px abc;margin: 2px 3em;' ); - expect( styleProxy.getInlineStyle() ).to.equal( 'foo-bar:baz 1px abc;margin:2px 3em;' ); + expect( styleProxy.getInlineStyle() ).to.equal( 'foo-bar:baz 1px abc;margin:2px 3em' ); expect( styleProxy.getInlineRule( 'foo-bar' ) ).to.equal( 'baz 1px abc' ); expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '2px 3em' ); } ); diff --git a/tests/view/upcastwriter.js b/tests/view/upcastwriter.js index 3e41a8d42..05b6ee59d 100644 --- a/tests/view/upcastwriter.js +++ b/tests/view/upcastwriter.js @@ -508,8 +508,8 @@ describe( 'UpcastWriter', () => { writer.removeStyle( [ 'color', 'position' ], el ); - expect( el.hasStyle( 'color' ) ).to.false; - expect( el.hasStyle( 'position' ) ).to.false; + expect( el.hasStyle( 'color' ) ).to.be.false; + expect( el.hasStyle( 'position' ) ).to.be.false; expect( Array.from( el.getStyleNames() ).length ).to.equal( 0 ); } ); From 6396311e78618e947864c6103feee4052c78ba7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 16:27:03 +0200 Subject: [PATCH 005/142] Add style retrieval for model storage. --- src/view/element.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index dbf414aaf..5714af5fc 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -379,8 +379,12 @@ export default class Element extends Node { * @param {String} property * @returns {String|undefined} */ - getStyle( property ) { - return this._styles.getInlineRule( property ); + getStyle( property, asModel = false ) { + if ( asModel ) { + return this._styles.getModel( property ); + } else { + return this._styles.getInlineRule( property ); + } } /** From d65236e290706b59e65b04c5628def0179a95651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 16:27:26 +0200 Subject: [PATCH 006/142] Add rule should accept object. --- src/view/styles.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/view/styles.js b/src/view/styles.js index ef7591cdb..b8dff01df 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -119,6 +119,11 @@ function parseStyle( string, styleObject = {} ) { } function parseRule( key, value, styleObject ) { + if ( isPlainObject( value ) ) { + addStyle( styleObject, key, value ); + return; + } + if ( key === 'border' ) { const parsedBorder = parseBorderAttribute( value ); From eb9ff1d3b1573b037a5c76acc4aa221800a33099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 16:42:42 +0200 Subject: [PATCH 007/142] Always add trailing semicolon to styles output. --- src/view/styles.js | 2 +- tests/view/element.js | 8 ++++---- tests/view/styles.js | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index b8dff01df..7d6ff0220 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -77,7 +77,7 @@ export default class Styles { parsed.push( toInlineStyle( key, model ) ); } - return parsed.join( ';' ); + return parsed.join( ';' ) + ';'; } getInlineRule( name ) { diff --git a/tests/view/element.js b/tests/view/element.js index 832da08ba..44261b7c7 100644 --- a/tests/view/element.js +++ b/tests/view/element.js @@ -516,7 +516,7 @@ describe( 'Element', () => { el._setStyle( 'color', 'red' ); el._setStyle( 'top', '10px' ); - expect( el.getAttribute( 'style' ) ).to.equal( 'color:red;top:10px' ); + expect( el.getAttribute( 'style' ) ).to.equal( 'color:red;top:10px;' ); } ); it( 'should return undefined if no style attribute', () => { @@ -539,7 +539,7 @@ describe( 'Element', () => { el._setStyle( 'font-weight', 'bold' ); expect( Array.from( el.getAttributes() ) ).to.deep.equal( [ - [ 'class', 'abc xyz' ], [ 'style', 'font-weight:bold;width:20px' ] + [ 'class', 'abc xyz' ], [ 'style', 'font-weight:bold;width:20px;' ] ] ); } ); } ); @@ -1037,7 +1037,7 @@ describe( 'Element', () => { style: 'border: 1px solid red; background-color: red' } ); - expect( el.getIdentity() ).to.equal( 'foo style="background-color:red;border:1px solid red"' ); + expect( el.getIdentity() ).to.equal( 'foo style="background-color:red;border:1px solid red;"' ); } ); it( 'should return attributes in sorted order', () => { @@ -1060,7 +1060,7 @@ describe( 'Element', () => { el._addClass( [ 'three', 'two', 'one' ] ); expect( el.getIdentity() ).to.equal( - 'baz class="one,three,two" style="border-radius:10px;text-align:center" bar="two" foo="one"' + 'baz class="one,three,two" style="border-radius:10px;text-align:center;" bar="two" foo="one"' ); } ); } ); diff --git a/tests/view/styles.js b/tests/view/styles.js index 2120ae7e0..882917aa6 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -68,7 +68,7 @@ describe( 'Styles', () => { it( 'should output', () => { styleProxy.setStyle( 'border:1px solid blue;' ); - expect( styleProxy.getInlineStyle() ).to.equal( 'border:1px solid blue' ); + expect( styleProxy.getInlineStyle() ).to.equal( 'border:1px solid blue;' ); expect( styleProxy.getInlineRule( 'border' ) ).to.equal( '1px solid blue' ); expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '1px solid blue' ); expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); @@ -80,7 +80,7 @@ describe( 'Styles', () => { styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styleProxy.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511' + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' ); expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); @@ -95,7 +95,7 @@ describe( 'Styles', () => { styleProxy.insertRule( 'border-top', '7px dotted #ccc' ); expect( styleProxy.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511' + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' ); expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); @@ -105,11 +105,11 @@ describe( 'Styles', () => { } ); it( 'should output', () => { - styleProxy.setStyle( 'border:1px solid blue' ); + styleProxy.setStyle( 'border:1px solid blue;' ); styleProxy.removeRule( 'border-top' ); expect( styleProxy.getInlineStyle() ).to.equal( - 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue' + 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' ); expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; expect( styleProxy.getInlineRule( 'border-top' ) ).to.be.undefined; @@ -121,7 +121,7 @@ describe( 'Styles', () => { it( 'pass-through', () => { styleProxy.setStyle( 'foo-bar:baz 1px abc;margin: 2px 3em;' ); - expect( styleProxy.getInlineStyle() ).to.equal( 'foo-bar:baz 1px abc;margin:2px 3em' ); + expect( styleProxy.getInlineStyle() ).to.equal( 'foo-bar:baz 1px abc;margin:2px 3em;' ); expect( styleProxy.getInlineRule( 'foo-bar' ) ).to.equal( 'baz 1px abc' ); expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '2px 3em' ); } ); From 54b24ad952c2edd476ee3da80cdba20c8657b443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 16:50:44 +0200 Subject: [PATCH 008/142] Organize tests for styles normalizer. --- tests/view/styles.js | 226 ++++++++++++++++++++++--------------------- 1 file changed, 116 insertions(+), 110 deletions(-) diff --git a/tests/view/styles.js b/tests/view/styles.js index 882917aa6..197a3d910 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -11,118 +11,124 @@ describe( 'Styles', () => { styleProxy = new StyleProxy(); } ); - it( 'should parse', () => { - styleProxy.setStyle( 'border:1px solid blue;' ); - - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { - bottom: { - color: 'blue', - style: 'solid', - width: '1px' - }, - left: { - color: 'blue', - style: 'solid', - width: '1px' - }, - right: { - color: 'blue', - style: 'solid', - width: '1px' - }, - top: { - color: 'blue', - style: 'solid', - width: '1px' - } + describe( 'styles rules', () => { + describe( 'border', () => { + it( 'should parse border shorthand', () => { + styleProxy.setStyle( 'border:1px solid blue;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + bottom: { + color: 'blue', + style: 'solid', + width: '1px' + }, + left: { + color: 'blue', + style: 'solid', + width: '1px' + }, + right: { + color: 'blue', + style: 'solid', + width: '1px' + }, + top: { + color: 'blue', + style: 'solid', + width: '1px' + } + } ); + } ); + + it( 'should parse border shorthand with other shorthands', () => { + styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + bottom: { + color: 'blue', + style: 'solid', + width: '1px' + }, + left: { + color: '#665511', + style: 'dashed', + width: '2.7em' + }, + right: { + color: 'blue', + style: 'solid', + width: '1px' + }, + top: { + color: '#ccc', + style: 'dotted', + width: '7px' + } + } ); + } ); + + it( 'should output inline shorthand rules', () => { + styleProxy.setStyle( 'border:1px solid blue;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'border:1px solid blue;' ); + expect( styleProxy.getInlineRule( 'border' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); + } ); + + it( 'should output inline shorthand rules', () => { + styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + ); + expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; + expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); + expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); + } ); + + it( 'should merge rules on insert other shorthand', () => { + styleProxy.setStyle( 'border:1px solid blue;' ); + styleProxy.insertRule( 'border-left', '#665511 dashed 2.7em' ); + styleProxy.insertRule( 'border-top', '7px dotted #ccc' ); + + expect( styleProxy.getInlineStyle() ).to.equal( + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + ); + expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; + expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); + expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); + } ); + + it( 'should output', () => { + styleProxy.setStyle( 'border:1px solid blue;' ); + styleProxy.removeRule( 'border-top' ); + + expect( styleProxy.getInlineStyle() ).to.equal( + 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' + ); + expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; + expect( styleProxy.getInlineRule( 'border-top' ) ).to.be.undefined; + expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); + } ); } ); - } ); - - it( 'should parse', () => { - styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); - - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { - bottom: { - color: 'blue', - style: 'solid', - width: '1px' - }, - left: { - color: '#665511', - style: 'dashed', - width: '2.7em' - }, - right: { - color: 'blue', - style: 'solid', - width: '1px' - }, - top: { - color: '#ccc', - style: 'dotted', - width: '7px' - } - } ); - } ); - - it( 'should output', () => { - styleProxy.setStyle( 'border:1px solid blue;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'border:1px solid blue;' ); - expect( styleProxy.getInlineRule( 'border' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); - } ); - it( 'should output', () => { - styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' - ); - expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; - expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); - expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); - } ); - - it( 'should add', () => { - styleProxy.setStyle( 'border:1px solid blue;' ); - styleProxy.insertRule( 'border-left', '#665511 dashed 2.7em' ); - styleProxy.insertRule( 'border-top', '7px dotted #ccc' ); - - expect( styleProxy.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' - ); - expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; - expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); - expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); - } ); + describe( 'unknown rules', () => { + it( 'should left rules untouched', () => { + styleProxy.setStyle( 'foo-bar:baz 1px abc;baz: 2px 3em;' ); - it( 'should output', () => { - styleProxy.setStyle( 'border:1px solid blue;' ); - styleProxy.removeRule( 'border-top' ); - - expect( styleProxy.getInlineStyle() ).to.equal( - 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' - ); - expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; - expect( styleProxy.getInlineRule( 'border-top' ) ).to.be.undefined; - expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); - } ); - - it( 'pass-through', () => { - styleProxy.setStyle( 'foo-bar:baz 1px abc;margin: 2px 3em;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'foo-bar:baz 1px abc;margin:2px 3em;' ); - expect( styleProxy.getInlineRule( 'foo-bar' ) ).to.equal( 'baz 1px abc' ); - expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '2px 3em' ); + expect( styleProxy.getInlineStyle() ).to.equal( 'baz:2px 3em;foo-bar:baz 1px abc;' ); + expect( styleProxy.getInlineRule( 'foo-bar' ) ).to.equal( 'baz 1px abc' ); + expect( styleProxy.getInlineRule( 'baz' ) ).to.equal( '2px 3em' ); + } ); + } ); } ); } ); From 8a65d9970d746b3497f77bfa3b0b99a817a66161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 17:04:48 +0200 Subject: [PATCH 009/142] Add support for border-color shorthand. --- src/view/styles.js | 19 ++++++++++++++++++ tests/view/styles.js | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/view/styles.js b/src/view/styles.js index 7d6ff0220..70e9894a8 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -118,6 +118,16 @@ function parseStyle( string, styleObject = {} ) { return styleObject; } +function getTopRightBottomLeftValues( value ) { + const values = value.split( ' ' ); + + const top = values[ 0 ]; + const bottom = values[ 2 ] || top; + const right = values[ 1 ] || top; + const left = values[ 3 ] || right; + return { top, bottom, right, left }; +} + function parseRule( key, value, styleObject ) { if ( isPlainObject( value ) ) { addStyle( styleObject, key, value ); @@ -142,6 +152,15 @@ function parseRule( key, value, styleObject ) { border[ which ] = parseBorderAttribute( value ); addStyle( styleObject, 'border', border ); + } else if ( key === 'border-color' ) { + const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); + + addStyle( styleObject, 'border', { + top: { color: top }, + right: { color: right }, + bottom: { color: bottom }, + left: { color: left } + } ); } else { addStyle( styleObject, key, value ); } diff --git a/tests/view/styles.js b/tests/view/styles.js index 197a3d910..b4c5c187e 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -119,6 +119,52 @@ describe( 'Styles', () => { expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); } ); + + describe( 'border-color', () => { + it( 'should set all border colors (1 value defined)', () => { + styleProxy.setStyle( 'border-color:cyan;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { color: 'cyan' }, + left: { color: 'cyan' }, + bottom: { color: 'cyan' }, + right: { color: 'cyan' } + } ); + } ); + + it( 'should set all border colors (2 values defined)', () => { + styleProxy.setStyle( 'border-color:cyan magenta;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { color: 'cyan' }, + right: { color: 'magenta' }, + bottom: { color: 'cyan' }, + left: { color: 'magenta' } + } ); + } ); + + it( 'should set all border colors (3 values defined)', () => { + styleProxy.setStyle( 'border-color:cyan magenta pink;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { color: 'cyan' }, + right: { color: 'magenta' }, + bottom: { color: 'pink' }, + left: { color: 'magenta' } + } ); + } ); + + it( 'should set all border colors (4 values defined)', () => { + styleProxy.setStyle( 'border-color:cyan magenta pink beige;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { color: 'cyan' }, + right: { color: 'magenta' }, + bottom: { color: 'pink' }, + left: { color: 'beige' } + } ); + } ); + } ); } ); describe( 'unknown rules', () => { From 3a9ee3ecd262c91150494c97f39ad8ff6ca33d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 17:07:11 +0200 Subject: [PATCH 010/142] Add support for border-style shorthand. --- src/view/styles.js | 9 +++++++++ tests/view/styles.js | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/view/styles.js b/src/view/styles.js index 70e9894a8..7b3328aa5 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -161,6 +161,15 @@ function parseRule( key, value, styleObject ) { bottom: { color: bottom }, left: { color: left } } ); + } else if ( key === 'border-style' ) { + const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); + + addStyle( styleObject, 'border', { + top: { style: top }, + right: { style: right }, + bottom: { style: bottom }, + left: { style: left } + } ); } else { addStyle( styleObject, key, value ); } diff --git a/tests/view/styles.js b/tests/view/styles.js index b4c5c187e..cd4bbe9ca 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -165,6 +165,52 @@ describe( 'Styles', () => { } ); } ); } ); + + describe( 'border-style', () => { + it( 'should set all border styles (1 value defined)', () => { + styleProxy.setStyle( 'border-style:solid;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { style: 'solid' }, + left: { style: 'solid' }, + bottom: { style: 'solid' }, + right: { style: 'solid' } + } ); + } ); + + it( 'should set all border styles (2 values defined)', () => { + styleProxy.setStyle( 'border-style:solid dotted;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { style: 'solid' }, + right: { style: 'dotted' }, + bottom: { style: 'solid' }, + left: { style: 'dotted' } + } ); + } ); + + it( 'should set all border styles (3 values defined)', () => { + styleProxy.setStyle( 'border-style:solid dotted dashed;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { style: 'solid' }, + right: { style: 'dotted' }, + bottom: { style: 'dashed' }, + left: { style: 'dotted' } + } ); + } ); + + it( 'should set all border styles (4 values defined)', () => { + styleProxy.setStyle( 'border-style:solid dotted dashed ridge;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { style: 'solid' }, + right: { style: 'dotted' }, + bottom: { style: 'dashed' }, + left: { style: 'ridge' } + } ); + } ); + } ); } ); describe( 'unknown rules', () => { From dbd70bfd276291f0f7999d2be4e297c8d814e86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 17:10:59 +0200 Subject: [PATCH 011/142] Add support for border-width shorthand. --- src/view/styles.js | 9 +++++++++ tests/view/styles.js | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/view/styles.js b/src/view/styles.js index 7b3328aa5..bca63d4ea 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -170,6 +170,15 @@ function parseRule( key, value, styleObject ) { bottom: { style: bottom }, left: { style: left } } ); + } else if ( key === 'border-width' ) { + const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); + + addStyle( styleObject, 'border', { + top: { width: top }, + right: { width: right }, + bottom: { width: bottom }, + left: { width: left } + } ); } else { addStyle( styleObject, key, value ); } diff --git a/tests/view/styles.js b/tests/view/styles.js index cd4bbe9ca..508e6c523 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -211,6 +211,52 @@ describe( 'Styles', () => { } ); } ); } ); + + describe( 'border-width', () => { + it( 'should set all border widths (1 value defined)', () => { + styleProxy.setStyle( 'border-width:1px;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { width: '1px' }, + left: { width: '1px' }, + bottom: { width: '1px' }, + right: { width: '1px' } + } ); + } ); + + it( 'should set all border widths (2 values defined)', () => { + styleProxy.setStyle( 'border-width:1px .34cm;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { width: '1px' }, + right: { width: '.34cm' }, + bottom: { width: '1px' }, + left: { width: '.34cm' } + } ); + } ); + + it( 'should set all border widths (3 values defined)', () => { + styleProxy.setStyle( 'border-width:1px .34cm 90.1rem;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { width: '1px' }, + right: { width: '.34cm' }, + bottom: { width: '90.1rem' }, + left: { width: '.34cm' } + } ); + } ); + + it( 'should set all border widths (4 values defined)', () => { + styleProxy.setStyle( 'border-width:1px .34cm 90.1rem thick;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { width: '1px' }, + right: { width: '.34cm' }, + bottom: { width: '90.1rem' }, + left: { width: 'thick' } + } ); + } ); + } ); } ); describe( 'unknown rules', () => { From 69167b942563149c15bc51e518f6d9df7bd45a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 17:22:03 +0200 Subject: [PATCH 012/142] Properly merge values in styles normalizer. --- src/view/styles.js | 4 ++-- tests/view/styles.js | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index bca63d4ea..26391ccac 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -7,7 +7,7 @@ * @module engine/view/styles */ -import { get, has, isObject, isPlainObject, unset } from 'lodash-es'; +import { get, has, isObject, isPlainObject, merge, unset } from 'lodash-es'; /** * Styles class. @@ -218,7 +218,7 @@ function isLength( string ) { function addStyle( styleObject, name, value ) { if ( typeof value === 'object' ) { - styleObject[ name ] = Object.assign( {}, styleObject[ name ] || {}, value ); + styleObject[ name ] = merge( {}, styleObject[ name ], value ); } else { styleObject[ name ] = value; } diff --git a/tests/view/styles.js b/tests/view/styles.js index 508e6c523..db9d3fefd 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -164,6 +164,17 @@ describe( 'Styles', () => { left: { color: 'beige' } } ); } ); + + it( 'should merge with border shorthand', () => { + styleProxy.setStyle( 'border:1px solid blue;border-color:cyan black;' ); + + expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + top: { color: 'cyan', style: 'solid', width: '1px' }, + right: { color: 'black', style: 'solid', width: '1px' }, + bottom: { color: 'cyan', style: 'solid', width: '1px' }, + left: { color: 'black', style: 'solid', width: '1px' } + } ); + } ); } ); describe( 'border-style', () => { From b8138a6f6ade3c82bcea1ffa991928fb7a5bf26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 17:22:15 +0200 Subject: [PATCH 013/142] Make tests compact. --- tests/view/styles.js | 56 ++++++++++---------------------------------- 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/tests/view/styles.js b/tests/view/styles.js index db9d3fefd..cf601e2e4 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -17,26 +17,10 @@ describe( 'Styles', () => { styleProxy.setStyle( 'border:1px solid blue;' ); expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { - bottom: { - color: 'blue', - style: 'solid', - width: '1px' - }, - left: { - color: 'blue', - style: 'solid', - width: '1px' - }, - right: { - color: 'blue', - style: 'solid', - width: '1px' - }, - top: { - color: 'blue', - style: 'solid', - width: '1px' - } + top: { color: 'blue', style: 'solid', width: '1px' }, + right: { color: 'blue', style: 'solid', width: '1px' }, + bottom: { color: 'blue', style: 'solid', width: '1px' }, + left: { color: 'blue', style: 'solid', width: '1px' } } ); } ); @@ -44,26 +28,10 @@ describe( 'Styles', () => { styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { - bottom: { - color: 'blue', - style: 'solid', - width: '1px' - }, - left: { - color: '#665511', - style: 'dashed', - width: '2.7em' - }, - right: { - color: 'blue', - style: 'solid', - width: '1px' - }, - top: { - color: '#ccc', - style: 'dotted', - width: '7px' - } + top: { color: '#ccc', style: 'dotted', width: '7px' }, + right: { color: 'blue', style: 'solid', width: '1px' }, + bottom: { color: 'blue', style: 'solid', width: '1px' }, + left: { color: '#665511', style: 'dashed', width: '2.7em' } } ); } ); @@ -183,9 +151,9 @@ describe( 'Styles', () => { expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { top: { style: 'solid' }, - left: { style: 'solid' }, + right: { style: 'solid' }, bottom: { style: 'solid' }, - right: { style: 'solid' } + left: { style: 'solid' } } ); } ); @@ -229,9 +197,9 @@ describe( 'Styles', () => { expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { top: { width: '1px' }, - left: { width: '1px' }, + right: { width: '1px' }, bottom: { width: '1px' }, - right: { width: '1px' } + left: { width: '1px' } } ); } ); From ca4bda8eb91d18a7fde427b182835785c11c95e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 17:25:18 +0200 Subject: [PATCH 014/142] Add support for margin shorthand. --- src/view/styles.js | 2 ++ tests/view/styles.js | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/view/styles.js b/src/view/styles.js index 26391ccac..1d89753c7 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -179,6 +179,8 @@ function parseRule( key, value, styleObject ) { bottom: { width: bottom }, left: { width: left } } ); + } else if ( key === 'margin' ) { + addStyle( styleObject, 'margin', getTopRightBottomLeftValues( value ) ); } else { addStyle( styleObject, key, value ); } diff --git a/tests/view/styles.js b/tests/view/styles.js index cf601e2e4..c1baedea5 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -238,6 +238,52 @@ describe( 'Styles', () => { } ); } ); + describe( 'margin', () => { + it( 'should set all margins (1 value defined)', () => { + styleProxy.setStyle( 'margin:1px;' ); + + expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '1px', + bottom: '1px', + left: '1px' + } ); + } ); + + it( 'should set all margins (2 values defined)', () => { + styleProxy.setStyle( 'margin:1px .34cm;' ); + + expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '1px', + left: '.34cm' + } ); + } ); + + it( 'should set all margins (3 values defined)', () => { + styleProxy.setStyle( 'margin:1px .34cm 90.1rem;' ); + + expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: '.34cm' + } ); + } ); + + it( 'should set all margins (4 values defined)', () => { + styleProxy.setStyle( 'margin:1px .34cm 90.1rem thick;' ); + + expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: 'thick' + } ); + } ); + } ); + describe( 'unknown rules', () => { it( 'should left rules untouched', () => { styleProxy.setStyle( 'foo-bar:baz 1px abc;baz: 2px 3em;' ); From 029efbd804e2838e8046e47ed05dfc640f2f8eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 17:26:16 +0200 Subject: [PATCH 015/142] Add support for padding shorthand. --- src/view/styles.js | 4 ++-- tests/view/styles.js | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 1d89753c7..9e7452ae2 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -179,8 +179,8 @@ function parseRule( key, value, styleObject ) { bottom: { width: bottom }, left: { width: left } } ); - } else if ( key === 'margin' ) { - addStyle( styleObject, 'margin', getTopRightBottomLeftValues( value ) ); + } else if ( key === 'margin' || key === 'padding' ) { + addStyle( styleObject, key, getTopRightBottomLeftValues( value ) ); } else { addStyle( styleObject, key, value ); } diff --git a/tests/view/styles.js b/tests/view/styles.js index c1baedea5..399b19fb2 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -284,6 +284,52 @@ describe( 'Styles', () => { } ); } ); + describe( 'padding', () => { + it( 'should set all paddings (1 value defined)', () => { + styleProxy.setStyle( 'padding:1px;' ); + + expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '1px', + bottom: '1px', + left: '1px' + } ); + } ); + + it( 'should set all paddings (2 values defined)', () => { + styleProxy.setStyle( 'padding:1px .34cm;' ); + + expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '1px', + left: '.34cm' + } ); + } ); + + it( 'should set all paddings (3 values defined)', () => { + styleProxy.setStyle( 'padding:1px .34cm 90.1rem;' ); + + expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: '.34cm' + } ); + } ); + + it( 'should set all paddings (4 values defined)', () => { + styleProxy.setStyle( 'padding:1px .34cm 90.1rem thick;' ); + + expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: 'thick' + } ); + } ); + } ); + describe( 'unknown rules', () => { it( 'should left rules untouched', () => { styleProxy.setStyle( 'foo-bar:baz 1px abc;baz: 2px 3em;' ); From f7420b38dddbb3cc4fc378f3a0db626d1bb29120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 26 Sep 2019 17:35:11 +0200 Subject: [PATCH 016/142] Add support for margin-* and padding-* shorthands. --- src/view/styles.js | 10 ++++++++++ tests/view/styles.js | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/view/styles.js b/src/view/styles.js index 9e7452ae2..f24f59b5e 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -103,6 +103,7 @@ export default class Styles { } const borderPositionRegExp = /border-(top|right|bottom|left)$/; +const marginOrPaddingPositionRegExp = /(margin|padding)-(top|right|bottom|left)$/; function parseStyle( string, styleObject = {} ) { const map = new Map(); @@ -181,6 +182,15 @@ function parseRule( key, value, styleObject ) { } ); } else if ( key === 'margin' || key === 'padding' ) { addStyle( styleObject, key, getTopRightBottomLeftValues( value ) ); + } else if ( marginOrPaddingPositionRegExp.test( key ) ) { + const margin = {}; + const match = marginOrPaddingPositionRegExp.exec( key ); + const rule = match[ 1 ]; + const which = match[ 2 ]; + + margin[ which ] = value; + + addStyle( styleObject, rule, margin ); } else { addStyle( styleObject, key, value ); } diff --git a/tests/view/styles.js b/tests/view/styles.js index 399b19fb2..dc466352a 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -282,6 +282,27 @@ describe( 'Styles', () => { left: 'thick' } ); } ); + + describe( 'margin-*', () => { + it( 'should set proper margin', () => { + styleProxy.setStyle( 'margin-top:1px;' ); + + expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + top: '1px' + } ); + } ); + + it( 'should set proper margin with margin shorthand', () => { + styleProxy.setStyle( 'margin: 2em;margin-top:1px;' ); + + expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '2em', + bottom: '2em', + left: '2em' + } ); + } ); + } ); } ); describe( 'padding', () => { @@ -328,6 +349,26 @@ describe( 'Styles', () => { left: 'thick' } ); } ); + describe( 'padding-*', () => { + it( 'should set proper padding', () => { + styleProxy.setStyle( 'padding-top:1px;' ); + + expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + top: '1px' + } ); + } ); + + it( 'should set proper padding with padding shorthand', () => { + styleProxy.setStyle( 'padding: 2em;padding-top:1px;' ); + + expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '2em', + bottom: '2em', + left: '2em' + } ); + } ); + } ); } ); describe( 'unknown rules', () => { From a78136c36f5b5b7f6be07cc6724022eb42bde258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 27 Sep 2019 12:49:53 +0200 Subject: [PATCH 017/142] Add margin output. --- src/view/styles.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/view/styles.js b/src/view/styles.js index f24f59b5e..de86f746c 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -275,6 +275,37 @@ function toInlineStyle( styleName, styleObjectOrString, strict = false ) { return toInlineBorder( styleObjectOrString ); } + if ( styleName === 'margin' ) { + const { top, right, bottom, left } = styleObjectOrString; + + if ( top === left && left === bottom && bottom === right ) { + return ( strict ? 'margin' : '' ) + top; + } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { + const ret = []; + + // TODO not so nice: + if ( top ) { + ret.push( 'margin-top:' + top ); + } + + if ( right ) { + ret.push( 'margin-right:' + right ); + } + + if ( bottom ) { + ret.push( 'margin-bottom:' + bottom ); + } + + if ( left ) { + ret.push( 'margin-left:' + left ); + } + + return ret.join( ';' ); + } else { + return 'margin...'; + } + } + return ( strict ? '' : styleName + ':' ) + styleObjectOrString; } From 28a6440e0df3c43639b0bbb51643072bb1e5c4f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 27 Sep 2019 14:21:47 +0200 Subject: [PATCH 018/142] Add tests for margin style output. --- src/view/styles.js | 102 ++++++++++++++++++++++--------------------- tests/view/styles.js | 76 ++++++++++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 53 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index de86f746c..56d2ece90 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -55,8 +55,10 @@ export default class Styles { return this._styles; } // TODO: clone - if ( has( this._styles, name.replace( '-', '.' ) ) ) { - return get( this._styles, name.replace( '-', '.' ) ); + const path = name.replace( '-', '.' ); + + if ( has( this._styles, path ) ) { + return get( this._styles, path ); } else { return this._styles[ name ]; } @@ -236,6 +238,52 @@ function addStyle( styleObject, name, value ) { } } +function printSingleValues( { top, right, bottom, left }, prefix ) { + const ret = []; + + if ( top ) { + ret.push( prefix + '-top:' + top ); + } + + if ( right ) { + ret.push( prefix + '-right:' + right ); + } + + if ( bottom ) { + ret.push( prefix + '-bottom:' + bottom ); + } + + if ( left ) { + ret.push( prefix + '-left:' + left ); + } + + return ret.join( ';' ); +} + +function outputShorthandableValue( styleObject, strict, styleShorthand ) { + const { top, right, bottom, left } = styleObject; + + if ( top === left && left === bottom && bottom === right ) { + return ( strict ? '' : styleShorthand + ':' ) + top; + } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { + return printSingleValues( { top, right, bottom, left }, 'margin' ); + } else { + const out = []; + + if ( left !== right ) { + out.push( top, right, bottom, left ); + } else if ( bottom !== top ) { + out.push( top, right, bottom ); + } else if ( right != top ) { + out.push( top, right ); + } else { + out.push( top ); + } + + return `${ strict ? '' : styleShorthand + ':' }${ out.join( ' ' ) }`; + } +} + function toInlineStyle( styleName, styleObjectOrString, strict = false ) { if ( styleName === 'border' ) { const top = toInlineBorder( styleObjectOrString.top ); @@ -246,26 +294,7 @@ function toInlineStyle( styleName, styleObjectOrString, strict = false ) { if ( top === right && right === bottom && bottom === left ) { return ( strict ? '' : 'border:' ) + top; } else if ( !strict ) { - const ret = []; - - // TODO not so nice: - if ( top ) { - ret.push( 'border-top:' + top ); - } - - if ( right ) { - ret.push( 'border-right:' + right ); - } - - if ( bottom ) { - ret.push( 'border-bottom:' + bottom ); - } - - if ( left ) { - ret.push( 'border-left:' + left ); - } - - return ret.join( ';' ); + return printSingleValues( { top, right, bottom, left }, 'border' ); } return; @@ -276,34 +305,7 @@ function toInlineStyle( styleName, styleObjectOrString, strict = false ) { } if ( styleName === 'margin' ) { - const { top, right, bottom, left } = styleObjectOrString; - - if ( top === left && left === bottom && bottom === right ) { - return ( strict ? 'margin' : '' ) + top; - } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { - const ret = []; - - // TODO not so nice: - if ( top ) { - ret.push( 'margin-top:' + top ); - } - - if ( right ) { - ret.push( 'margin-right:' + right ); - } - - if ( bottom ) { - ret.push( 'margin-bottom:' + bottom ); - } - - if ( left ) { - ret.push( 'margin-left:' + left ); - } - - return ret.join( ';' ); - } else { - return 'margin...'; - } + return outputShorthandableValue( styleObjectOrString, strict, 'margin' ); } return ( strict ? '' : styleName + ':' ) + styleObjectOrString; diff --git a/tests/view/styles.js b/tests/view/styles.js index dc466352a..dcf65b9ed 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -283,13 +283,78 @@ describe( 'Styles', () => { } ); } ); + it( 'should output inline style (1 value defined)', () => { + styleProxy.setStyle( 'margin:1px;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px;' ); + expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( '1px' ); + } ); + + it( 'should output inline style (2 values defined)', () => { + styleProxy.setStyle( 'margin:1px .34cm;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); + expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px .34cm' ); + expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( '.34cm' ); + } ); + + it( 'should output inline style (3 values defined)', () => { + styleProxy.setStyle( 'margin:1px .34cm 90.1rem;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); + expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); + expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( '.34cm' ); + } ); + + it( 'should output inline style (3 values defined, only last different)', () => { + styleProxy.setStyle( 'margin:1px 1px 90.1rem;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); + expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); + expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( '1px' ); + } ); + + it( 'should output inline style (4 values defined)', () => { + styleProxy.setStyle( 'margin:1px .34cm 90.1rem thick;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); + expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); + expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( 'thick' ); + } ); + + it( 'should output inline style (4 values defined, only last different)', () => { + styleProxy.setStyle( 'margin:1px 1px 1px thick;' ); + + expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); + expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px 1px 1px thick' ); + expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( 'thick' ); + } ); + describe( 'margin-*', () => { it( 'should set proper margin', () => { styleProxy.setStyle( 'margin-top:1px;' ); - expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { - top: '1px' - } ); + expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { top: '1px' } ); + expect( styleProxy.getModel( 'margin-top' ) ).to.equal( '1px' ); } ); it( 'should set proper margin with margin shorthand', () => { @@ -301,6 +366,10 @@ describe( 'Styles', () => { bottom: '2em', left: '2em' } ); + expect( styleProxy.getModel( 'margin-top' ) ).to.equal( '1px' ); + expect( styleProxy.getModel( 'margin-right' ) ).to.equal( '2em' ); + expect( styleProxy.getModel( 'margin-bottom' ) ).to.equal( '2em' ); + expect( styleProxy.getModel( 'margin-left' ) ).to.equal( '2em' ); } ); } ); } ); @@ -349,6 +418,7 @@ describe( 'Styles', () => { left: 'thick' } ); } ); + describe( 'padding-*', () => { it( 'should set proper padding', () => { styleProxy.setStyle( 'padding-top:1px;' ); From 008c90d7543bd6692d9ae0a7d17940a5d0d6a6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 27 Sep 2019 16:52:07 +0200 Subject: [PATCH 019/142] Add get style names logic. --- src/view/element.js | 2 +- src/view/styles.js | 23 ++++++++++++++++------- tests/view/styles.js | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 5714af5fc..108b81651 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -393,7 +393,7 @@ export default class Element extends Node { * @returns {Iterable.} */ getStyleNames() { - return Object.keys( this._styles._styles ); + return this._styles.getStyleNames(); } /** diff --git a/src/view/styles.js b/src/view/styles.js index 56d2ece90..afab31068 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -22,7 +22,7 @@ export default class Styles { } get size() { - return Object.keys( this._styles ).length; + return this.getStyleNames().length; } setStyle( styleString = '' ) { @@ -99,6 +99,15 @@ export default class Styles { } } + // TODO: expandShortHands: true/false? + getStyleNames() { + const inlineStyle = this.getInlineStyle(); + + // TODO: probably not good enough. + // TODO: consumables must have different names or support shorthands. + return inlineStyle.split( ';' ).filter( f => f !== '' ).map( abc => abc.split( ':' )[ 0 ] ).sort(); + } + clear() { this._styles = {}; } @@ -108,9 +117,7 @@ const borderPositionRegExp = /border-(top|right|bottom|left)$/; const marginOrPaddingPositionRegExp = /(margin|padding)-(top|right|bottom|left)$/; function parseStyle( string, styleObject = {} ) { - const map = new Map(); - - parseInlineStyles( map, string ); + const map = parseInlineStyles( string ); for ( const key of map.keys() ) { const value = map.get( key ); @@ -334,18 +341,18 @@ function toInlineBorder( object = {} ) { // // @param {Map.} stylesMap Map to insert parsed properties and values. // @param {String} stylesString Styles to parse. -function parseInlineStyles( stylesMap, stylesString ) { +function parseInlineStyles( stylesString ) { // `null` if no quote was found in input string or last found quote was a closing quote. See below. let quoteType = null; let propertyNameStart = 0; let propertyValueStart = 0; let propertyName = null; - stylesMap.clear(); + const stylesMap = new Map(); // Do not set anything if input string is empty. if ( stylesString === '' ) { - return; + return stylesMap; } // Fix inline styles that do not end with `;` so they are compatible with algorithm below. @@ -403,4 +410,6 @@ function parseInlineStyles( stylesMap, stylesString ) { quoteType = null; } } + + return stylesMap; } diff --git a/tests/view/styles.js b/tests/view/styles.js index dcf65b9ed..d6a9d7b90 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -11,6 +11,20 @@ describe( 'Styles', () => { styleProxy = new StyleProxy(); } ); + describe( 'getStyleNames()', () => { + it( 'should output custom style names', () => { + styleProxy.setStyle( 'foo: 2;bar: baz;foo-bar-baz:none;' ); + + expect( styleProxy.getStyleNames() ).to.deep.equal( [ 'bar', 'foo', 'foo-bar-baz' ] ); + } ); + + it( 'should output full names for shorthand', () => { + styleProxy.setStyle( 'margin: 1px;margin-left: 2em;' ); + + expect( styleProxy.getStyleNames() ).to.deep.equal( [ 'margin' ] ); + } ); + } ); + describe( 'styles rules', () => { describe( 'border', () => { it( 'should parse border shorthand', () => { From a16afe6d00cdc40f8e36876d6d93130e5b72d3df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 27 Sep 2019 17:18:29 +0200 Subject: [PATCH 020/142] Refactor Styles class to more instance based. --- src/view/styles.js | 221 ++++++++++++++++++++++++++------------------- 1 file changed, 128 insertions(+), 93 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index afab31068..dea56b124 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -9,53 +9,90 @@ import { get, has, isObject, isPlainObject, merge, unset } from 'lodash-es'; +const borderPositionRegExp = /border-(top|right|bottom|left)$/; +const marginOrPaddingPositionRegExp = /(margin|padding)-(top|right|bottom|left)$/; + /** * Styles class. * * Handles styles normalization. */ export default class Styles { + /** + * Creates Styles instance. + * + * @param {String} styleString Initial styles value. + */ constructor( styleString = '' ) { this._styles = {}; this.setStyle( styleString ); } + /** + * Number of styles defined. + * + * @type {Number} + */ get size() { return this.getStyleNames().length; } + /** + * Re-sets internal styles definition. + * + * @param styleString + */ setStyle( styleString = '' ) { - this._styles = parseStyle( styleString ); + this.clear(); + this._parseStyle( styleString ); } + /** + * Checks if single style rule is set. + * + * Supports shorthands. + * + * @param {String} name + * @returns {Boolean} + */ hasRule( name ) { - const nameNorm = name.replace( '-', '.' ); + const nameNorm = this._getPath( name ); return has( this._styles, nameNorm ) || !!this._styles[ name ]; } + /** + * Inserts single style rule. + * + * Supports shorthands. + * + * @param {String|Object} nameOrObject + * @param {String|Object} value + * @returns {Boolean} + */ insertRule( nameOrObject, value ) { if ( isPlainObject( nameOrObject ) ) { for ( const key of Object.keys( nameOrObject ) ) { this.insertRule( key, nameOrObject[ key ] ); } } else { - parseRule( nameOrObject, value, this._styles ); + this._parseRule( nameOrObject, value ); } } removeRule( name ) { - unset( this._styles, name.replace( '-', '.' ) ); + unset( this._styles, this._getPath( name ) ); + delete this._styles[ name ]; } getModel( name ) { if ( !name ) { - return this._styles; - } // TODO: clone + return merge( {}, this._styles ); + } - const path = name.replace( '-', '.' ); + const path = this._getPath( name ); if ( has( this._styles, path ) ) { return get( this._styles, path ); @@ -111,21 +148,95 @@ export default class Styles { clear() { this._styles = {}; } -} -const borderPositionRegExp = /border-(top|right|bottom|left)$/; -const marginOrPaddingPositionRegExp = /(margin|padding)-(top|right|bottom|left)$/; + _getPath( name ) { + return name.replace( '-', '.' ); + } + + _parseStyle( string ) { + const map = parseInlineStyles( string ); -function parseStyle( string, styleObject = {} ) { - const map = parseInlineStyles( string ); + for ( const key of map.keys() ) { + const value = map.get( key ); - for ( const key of map.keys() ) { - const value = map.get( key ); + this._parseRule( key, value ); + } + } - parseRule( key, value, styleObject ); + _appendStyleValue( name, valueOrObject ) { + if ( typeof valueOrObject === 'object' ) { + this._styles[ name ] = merge( {}, this._styles[ name ], valueOrObject ); + } else { + this._styles[ name ] = valueOrObject; + } } - return styleObject; + _parseRule( key, value ) { + if ( isPlainObject( value ) ) { + this._appendStyleValue( key, value ); + return; + } + + if ( key === 'border' ) { + const parsedBorder = parseBorderAttribute( value ); + + const border = { + top: parsedBorder, + right: parsedBorder, + bottom: parsedBorder, + left: parsedBorder + }; + + this._appendStyleValue( 'border', border ); + } else if ( borderPositionRegExp.test( key ) ) { + const border = {}; + const which = borderPositionRegExp.exec( key )[ 1 ]; + + border[ which ] = parseBorderAttribute( value ); + + this._appendStyleValue( 'border', border ); + } else if ( key === 'border-color' ) { + const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); + + this._appendStyleValue( 'border', { + top: { color: top }, + right: { color: right }, + bottom: { color: bottom }, + left: { color: left } + } ); + } else if ( key === 'border-style' ) { + const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); + + this._appendStyleValue( 'border', { + top: { style: top }, + right: { style: right }, + bottom: { style: bottom }, + left: { style: left } + } ); + } else if ( key === 'border-width' ) { + const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); + + this._appendStyleValue( 'border', { + top: { width: top }, + right: { width: right }, + bottom: { width: bottom }, + left: { width: left } + } ); + } else if ( key === 'margin' || key === 'padding' ) { + this._appendStyleValue( key, getTopRightBottomLeftValues( value ) ); + } else if ( marginOrPaddingPositionRegExp.test( key ) ) { + const margin = {}; + const match = marginOrPaddingPositionRegExp.exec( key ); + const rule = match[ 1 ]; + const which = match[ 2 ]; + + margin[ which ] = value; + + this._appendStyleValue( rule, margin ); + } else { + this._appendStyleValue( key, value ); + } + } } function getTopRightBottomLeftValues( value ) { @@ -138,73 +249,6 @@ function getTopRightBottomLeftValues( value ) { return { top, bottom, right, left }; } -function parseRule( key, value, styleObject ) { - if ( isPlainObject( value ) ) { - addStyle( styleObject, key, value ); - return; - } - - if ( key === 'border' ) { - const parsedBorder = parseBorderAttribute( value ); - - const border = { - top: parsedBorder, - right: parsedBorder, - bottom: parsedBorder, - left: parsedBorder - }; - - addStyle( styleObject, 'border', border ); - } else if ( borderPositionRegExp.test( key ) ) { - const border = {}; - const which = borderPositionRegExp.exec( key )[ 1 ]; - - border[ which ] = parseBorderAttribute( value ); - - addStyle( styleObject, 'border', border ); - } else if ( key === 'border-color' ) { - const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); - - addStyle( styleObject, 'border', { - top: { color: top }, - right: { color: right }, - bottom: { color: bottom }, - left: { color: left } - } ); - } else if ( key === 'border-style' ) { - const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); - - addStyle( styleObject, 'border', { - top: { style: top }, - right: { style: right }, - bottom: { style: bottom }, - left: { style: left } - } ); - } else if ( key === 'border-width' ) { - const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); - - addStyle( styleObject, 'border', { - top: { width: top }, - right: { width: right }, - bottom: { width: bottom }, - left: { width: left } - } ); - } else if ( key === 'margin' || key === 'padding' ) { - addStyle( styleObject, key, getTopRightBottomLeftValues( value ) ); - } else if ( marginOrPaddingPositionRegExp.test( key ) ) { - const margin = {}; - const match = marginOrPaddingPositionRegExp.exec( key ); - const rule = match[ 1 ]; - const which = match[ 2 ]; - - margin[ which ] = value; - - addStyle( styleObject, rule, margin ); - } else { - addStyle( styleObject, key, value ); - } -} - function parseBorderAttribute( string ) { const result = {}; @@ -237,14 +281,6 @@ function isLength( string ) { return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); } -function addStyle( styleObject, name, value ) { - if ( typeof value === 'object' ) { - styleObject[ name ] = merge( {}, styleObject[ name ], value ); - } else { - styleObject[ name ] = value; - } -} - function printSingleValues( { top, right, bottom, left }, prefix ) { const ret = []; @@ -337,10 +373,9 @@ function toInlineBorder( object = {} ) { } // Parses inline styles and puts property - value pairs into styles map. -// Styles map is cleared before insertion. // -// @param {Map.} stylesMap Map to insert parsed properties and values. // @param {String} stylesString Styles to parse. +// @returns {Map.} stylesMap Map of parsed properties and values. function parseInlineStyles( stylesString ) { // `null` if no quote was found in input string or last found quote was a closing quote. See below. let quoteType = null; From 5ac8b426aee285c4d450d4843f5f62290733c20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Sep 2019 14:20:56 +0200 Subject: [PATCH 021/142] Add direct properties support. --- src/view/styles.js | 161 +++++++++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 64 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index dea56b124..55a222c39 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -7,10 +7,17 @@ * @module engine/view/styles */ -import { get, has, isObject, isPlainObject, merge, unset } from 'lodash-es'; +import { get, has, isObject, isPlainObject, merge, set, unset } from 'lodash-es'; const borderPositionRegExp = /border-(top|right|bottom|left)$/; -const marginOrPaddingPositionRegExp = /(margin|padding)-(top|right|bottom|left)$/; + +const setOnPathStyles = [ + 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color', + 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style', + 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width', + 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', + 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' +]; /** * Styles class. @@ -163,11 +170,16 @@ export default class Styles { } } - _appendStyleValue( name, valueOrObject ) { + _appendStyleValue( nameOrPath, valueOrObject ) { if ( typeof valueOrObject === 'object' ) { - this._styles[ name ] = merge( {}, this._styles[ name ], valueOrObject ); + if ( nameOrPath.includes( '.' ) ) { + const got = get( this._styles, nameOrPath ); + set( this._styles, nameOrPath, merge( {}, got, valueOrObject ) ); + } else { + this._styles[ nameOrPath ] = merge( {}, this._styles[ nameOrPath ], valueOrObject ); + } } else { - this._styles[ name ] = valueOrObject; + set( this._styles, nameOrPath, valueOrObject ); } } @@ -177,65 +189,34 @@ export default class Styles { return; } - if ( key === 'border' ) { - const parsedBorder = parseBorderAttribute( value ); - - const border = { - top: parsedBorder, - right: parsedBorder, - bottom: parsedBorder, - left: parsedBorder - }; - - this._appendStyleValue( 'border', border ); - } else if ( borderPositionRegExp.test( key ) ) { - const border = {}; - const which = borderPositionRegExp.exec( key )[ 1 ]; - - border[ which ] = parseBorderAttribute( value ); - - this._appendStyleValue( 'border', border ); - } else if ( key === 'border-color' ) { - const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); - - this._appendStyleValue( 'border', { - top: { color: top }, - right: { color: right }, - bottom: { color: bottom }, - left: { color: left } - } ); - } else if ( key === 'border-style' ) { - const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); - - this._appendStyleValue( 'border', { - top: { style: top }, - right: { style: right }, - bottom: { style: bottom }, - left: { style: left } - } ); - } else if ( key === 'border-width' ) { - const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); - - this._appendStyleValue( 'border', { - top: { width: top }, - right: { width: right }, - bottom: { width: bottom }, - left: { width: left } - } ); - } else if ( key === 'margin' || key === 'padding' ) { - this._appendStyleValue( key, getTopRightBottomLeftValues( value ) ); - } else if ( marginOrPaddingPositionRegExp.test( key ) ) { - const margin = {}; - const match = marginOrPaddingPositionRegExp.exec( key ); - const rule = match[ 1 ]; - const which = match[ 2 ]; - - margin[ which ] = value; - - this._appendStyleValue( rule, margin ); - } else { - this._appendStyleValue( key, value ); + const baseKey = key.split( '-' )[ 0 ]; + + // Set directly to object. + if ( setOnPathStyles.includes( key ) ) { + this._appendStyleValue( this._getPath( key ), value ); + + return; } + + let processed; + + if ( baseKey === 'border' ) { + processed = processBorder( key, value ); + } + + if ( key === 'margin' || key === 'padding' ) { + processed = { key, value: getTopRightBottomLeftValues( value ) }; + } + + let processedKey = key; + let processedValue = value; + + if ( processed ) { + processedKey = processed.key; + processedValue = processed.value; + } + + this._appendStyleValue( processedKey, processedValue ); } } @@ -246,10 +227,62 @@ function getTopRightBottomLeftValues( value ) { const bottom = values[ 2 ] || top; const right = values[ 1 ] || top; const left = values[ 3 ] || right; + return { top, bottom, right, left }; } -function parseBorderAttribute( string ) { +function toBorderPropertyShorthand( value, property ) { + const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); + + return { + top: { [ property ]: top }, + right: { [ property ]: right }, + bottom: { [ property ]: bottom }, + left: { [ property ]: left } + }; +} + +function processBorder( key, value ) { + if ( key === 'border' ) { + const parsedBorder = parseShorthandBorderAttribute( value ); + + const border = { + top: parsedBorder, + right: parsedBorder, + bottom: parsedBorder, + left: parsedBorder + }; + + return { key: 'border', value: border }; + } + + if ( borderPositionRegExp.test( key ) ) { + return { key: key.replace( '-', '.' ), value: parseShorthandBorderAttribute( value ) }; + } + + if ( key === 'border-color' ) { + return { + key: 'border', + value: toBorderPropertyShorthand( value, 'color' ) + }; + } + + if ( key === 'border-style' ) { + return { + key: 'border', + value: toBorderPropertyShorthand( value, 'style' ) + }; + } + + if ( key === 'border-width' ) { + return { + key: 'border', + value: toBorderPropertyShorthand( value, 'width' ) + }; + } +} + +function parseShorthandBorderAttribute( string ) { const result = {}; for ( const part of string.split( ' ' ) ) { From 4cc9f15f1f30d19162b61333f624b5d83f625fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Sep 2019 14:37:47 +0200 Subject: [PATCH 022/142] Refactor internal parsing to object. --- src/view/styles.js | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 55a222c39..6f4e9c406 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -10,6 +10,7 @@ import { get, has, isObject, isPlainObject, merge, set, unset } from 'lodash-es'; const borderPositionRegExp = /border-(top|right|bottom|left)$/; +const topRightBottomLeftPositionRegExp = /^(top|right|bottom|left)$/; const setOnPathStyles = [ 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color', @@ -186,6 +187,7 @@ export default class Styles { _parseRule( key, value ) { if ( isPlainObject( value ) ) { this._appendStyleValue( key, value ); + return; } @@ -205,18 +207,14 @@ export default class Styles { } if ( key === 'margin' || key === 'padding' ) { - processed = { key, value: getTopRightBottomLeftValues( value ) }; + processed = { [ key ]: getTopRightBottomLeftValues( value ) }; } - let processedKey = key; - let processedValue = value; - if ( processed ) { - processedKey = processed.key; - processedValue = processed.value; + this._styles = merge( {}, this._styles, processed ); + } else { + this._appendStyleValue( key, value ); } - - this._appendStyleValue( processedKey, processedValue ); } } @@ -253,32 +251,20 @@ function processBorder( key, value ) { left: parsedBorder }; - return { key: 'border', value: border }; - } - - if ( borderPositionRegExp.test( key ) ) { - return { key: key.replace( '-', '.' ), value: parseShorthandBorderAttribute( value ) }; + return { border }; } + const parts = key.split( '-' ); - if ( key === 'border-color' ) { + if ( topRightBottomLeftPositionRegExp.test( parts[ 1 ] ) ) { return { - key: 'border', - value: toBorderPropertyShorthand( value, 'color' ) - }; - } - - if ( key === 'border-style' ) { - return { - key: 'border', - value: toBorderPropertyShorthand( value, 'style' ) + border: { + [ parts[ 1 ] ]: parseShorthandBorderAttribute( value ) + } }; } - if ( key === 'border-width' ) { - return { - key: 'border', - value: toBorderPropertyShorthand( value, 'width' ) - }; + if ( [ 'color', 'style', 'width' ].includes( parts[ 1 ] ) ) { + return { border: toBorderPropertyShorthand( value, parts[ 1 ] ) }; } } From 7476ed058b1de99943dd61cc70d7d93a5adcbab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Sep 2019 14:44:37 +0200 Subject: [PATCH 023/142] Fix tests. --- src/view/styles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/styles.js b/src/view/styles.js index 6f4e9c406..8f5b29049 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -150,7 +150,7 @@ export default class Styles { // TODO: probably not good enough. // TODO: consumables must have different names or support shorthands. - return inlineStyle.split( ';' ).filter( f => f !== '' ).map( abc => abc.split( ':' )[ 0 ] ).sort(); + return ( inlineStyle || '' ).split( ';' ).filter( f => f !== '' ).map( abc => abc.split( ':' )[ 0 ] ).sort(); } clear() { From de8f1bc65d452b3a368d0674f89225a97c8d02ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Sep 2019 16:22:56 +0200 Subject: [PATCH 024/142] Fix output of undefined value for margin. --- src/view/styles.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/view/styles.js b/src/view/styles.js index 8f5b29049..6d5a4a293 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -326,6 +326,11 @@ function outputShorthandableValue( styleObject, strict, styleShorthand ) { const { top, right, bottom, left } = styleObject; if ( top === left && left === bottom && bottom === right ) { + // Might be not set. + if ( top === undefined ) { + return ''; + } + return ( strict ? '' : styleShorthand + ':' ) + top; } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { return printSingleValues( { top, right, bottom, left }, 'margin' ); From 0b710f10a60c81173bd3cfffbabe0f14cf2786fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Sep 2019 16:24:07 +0200 Subject: [PATCH 025/142] Consume shorthand styles together with their counterpart properties. --- src/conversion/viewconsumable.js | 49 ++++++++++++++++++++++ tests/conversion/viewconsumable.js | 67 ++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/src/conversion/viewconsumable.js b/src/conversion/viewconsumable.js index 083db3e1a..83c05c3c0 100644 --- a/src/conversion/viewconsumable.js +++ b/src/conversion/viewconsumable.js @@ -10,6 +10,43 @@ import { isArray } from 'lodash-es'; import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; +const styleConsumablesMap = new Map(); + +addStyleConsumeAlso( 'margin', [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ] ); +addStyleConsumeAlso( 'padding', [ 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' ] ); + +addStyleConsumeAlso( 'border', [ 'border-color', 'border-style', 'border-width' ] ); +addStyleConsumeAlso( 'border', [ 'border-top', 'border-right', 'border-bottom', 'border-left' ] ); +addStyleConsumeAlso( 'border', [ + 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color', + 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style', + 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width' +] ); +addStyleConsumeAlso( 'border-color', [ 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color' ] ); +addStyleConsumeAlso( 'border-style', [ 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style' ] ); +addStyleConsumeAlso( 'border-width', [ 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width' ] ); + +addStyleConsumeAlso( 'border-top', [ 'border-top-color', 'border-top-style', 'border-top-width' ] ); +addStyleConsumeAlso( 'border-right', [ 'border-right-color', 'border-right-style', 'border-right-width' ] ); +addStyleConsumeAlso( 'border-bottom', [ 'border-bottom-color', 'border-bottom-style', 'border-bottom-width' ] ); +addStyleConsumeAlso( 'border-left', [ 'border-left-color', 'border-left-style', 'border-left-width' ] ); + +function addStyleConsumeAlso( shorthandName, also ) { + addConsumable( shorthandName, also ); + + for ( const alsoName of also ) { + addConsumable( alsoName, [ shorthandName ] ); + } + + function addConsumable( name, values ) { + if ( !styleConsumablesMap.has( name ) ) { + styleConsumablesMap.set( name, [] ); + } + + styleConsumablesMap.get( name ).push( ...values ); + } +} + /** * Class used for handling consumption of view {@link module:engine/view/element~Element elements}, * {@link module:engine/view/text~Text text nodes} and {@link module:engine/view/documentfragment~DocumentFragment document fragments}. @@ -507,6 +544,12 @@ class ViewElementConsumables { } consumables.set( name, true ); + + if ( type === 'styles' && styleConsumablesMap.has( name ) ) { + for ( const alsoName of styleConsumablesMap.get( name ) ) { + consumables.set( alsoName, true ); + } + } } } @@ -568,6 +611,12 @@ class ViewElementConsumables { this._consume( consumableName, [ ...this._consumables[ consumableName ].keys() ] ); } else { consumables.set( name, false ); + + if ( styleConsumablesMap.has( name ) ) { + for ( const toConsume of styleConsumablesMap.get( name ) ) { + consumables.set( toConsume, false ); + } + } } } } diff --git a/tests/conversion/viewconsumable.js b/tests/conversion/viewconsumable.js index 403824049..4911e9b0d 100644 --- a/tests/conversion/viewconsumable.js +++ b/tests/conversion/viewconsumable.js @@ -549,4 +549,71 @@ describe( 'ViewConsumable', () => { expect( newConsumable.test( child3, { name: true, styles: 'top', classes: [ 'qux', 'bar' ] } ) ).to.be.true; } ); } ); + + describe.only( 'style shorthands handling', () => { + describe( 'add', () => { + it( 'should add padding shorthands', () => { + viewConsumable.add( el, { styles: [ 'margin' ] } ); + + expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'margin-bottom' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'margin-right' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'margin-left' } ) ).to.be.true; + } ); + + it( 'should add margin shorthands', () => { + viewConsumable.add( el, { styles: [ 'padding' ] } ); + + expect( viewConsumable.test( el, { styles: 'padding-top' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'padding-bottom' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'padding-right' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'padding-left' } ) ).to.be.true; + } ); + + it( 'should add table shorthands', () => { + viewConsumable.add( el, { styles: [ 'border' ] } ); + + expect( viewConsumable.test( el, { styles: 'border-style' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-top-style' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-bottom-style' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-right-style' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-left-style' } ) ).to.be.true; + + expect( viewConsumable.test( el, { styles: 'border-color' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-top-color' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-bottom-color' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-right-color' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-left-color' } ) ).to.be.true; + + expect( viewConsumable.test( el, { styles: 'border-width' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-top-width' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-bottom-width' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-right-width' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'border-left-width' } ) ).to.be.true; + } ); + } ); + + it( 'should return valse when testing style shorthand... 1', () => { + viewConsumable.add( el, { styles: [ 'margin' ] } ); + + expect( viewConsumable.test( el, { styles: 'margin' } ) ).to.be.true; + viewConsumable.consume( el, { styles: 'margin' } ); + expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.false; + expect( viewConsumable.test( el, { styles: 'margin-bottom' } ) ).to.be.false; + expect( viewConsumable.test( el, { styles: 'margin-right' } ) ).to.be.false; + expect( viewConsumable.test( el, { styles: 'margin-left' } ) ).to.be.false; + } ); + + it( 'should return valse when testing style shorthand... 1', () => { + viewConsumable.add( el, { styles: [ 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ] } ); + + expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.true; + viewConsumable.consume( el, { styles: 'margin-top' } ); + expect( viewConsumable.test( el, { styles: 'margin' } ) ).to.be.false; + expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.false; + expect( viewConsumable.test( el, { styles: 'margin-bottom' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'margin-right' } ) ).to.be.true; + expect( viewConsumable.test( el, { styles: 'margin-left' } ) ).to.be.true; + } ); + } ); } ); From 2eb54c2a87479386ce9a6d75d6905bfa17878eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Sep 2019 16:28:34 +0200 Subject: [PATCH 026/142] Remove .only from tests. --- tests/conversion/viewconsumable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conversion/viewconsumable.js b/tests/conversion/viewconsumable.js index 4911e9b0d..4cbfecfe5 100644 --- a/tests/conversion/viewconsumable.js +++ b/tests/conversion/viewconsumable.js @@ -550,7 +550,7 @@ describe( 'ViewConsumable', () => { } ); } ); - describe.only( 'style shorthands handling', () => { + describe( 'style shorthands handling', () => { describe( 'add', () => { it( 'should add padding shorthands', () => { viewConsumable.add( el, { styles: [ 'margin' ] } ); From a3cfc0c978056e9818631acf8b8321dc2e7e1ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 1 Oct 2019 15:13:48 +0200 Subject: [PATCH 027/142] Add support for background-color in styles normalization. --- src/view/styles.js | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/view/styles.js b/src/view/styles.js index 6d5a4a293..edc519794 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -13,11 +13,15 @@ const borderPositionRegExp = /border-(top|right|bottom|left)$/; const topRightBottomLeftPositionRegExp = /^(top|right|bottom|left)$/; const setOnPathStyles = [ + // Borders. 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color', 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style', 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width', + // Margin & padding. + 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', - 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' + // Background. + 'background-color' ]; /** @@ -206,6 +210,21 @@ export default class Styles { processed = processBorder( key, value ); } + if ( key === 'background' ) { + const layers = value.split( ',' ); + const background = {}; + + const parts = layers[ 0 ].split( ' ' ); + + for ( const part of parts ) { + if ( isColor( part ) ) { + background.color = part; + } + } + + processed = { background }; + } + if ( key === 'margin' || key === 'padding' ) { processed = { [ key ]: getTopRightBottomLeftValues( value ) }; } @@ -375,6 +394,23 @@ function toInlineStyle( styleName, styleObjectOrString, strict = false ) { return outputShorthandableValue( styleObjectOrString, strict, 'margin' ); } + if ( styleName === 'padding' ) { + return outputShorthandableValue( styleObjectOrString, strict, 'padding' ); + } + + // Generic, one-level, object to style: + if ( isObject( styleObjectOrString ) ) { + if ( !strict ) { + const values = []; + + for ( const key of Object.keys( styleObjectOrString ) ) { + values.push( styleName + '-' + key + ':' + styleObjectOrString[ key ] ); + } + + return values.join( ';' ); + } + } + return ( strict ? '' : styleName + ':' ) + styleObjectOrString; } From 2cd6cb590621df633661e6137fefd9b3f649e7d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 1 Oct 2019 17:09:13 +0200 Subject: [PATCH 028/142] Refactor styles method names. --- src/view/element.js | 14 +- src/view/styles.js | 50 +++---- tests/view/element.js | 20 +-- tests/view/styles.js | 298 +++++++++++++++++++++--------------------- 4 files changed, 193 insertions(+), 189 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 108b81651..8aca8fd66 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -334,8 +334,8 @@ export default class Element extends Node { // Check if styles are the same. for ( const property of Object.keys( this._styles._styles ) ) { if ( - !otherElement._styles.hasRule( property ) || - otherElement._styles.getInlineRule( property ) !== this._styles.getInlineRule( property ) + !otherElement._styles.hasProperty( property ) || + otherElement._styles.getInlineProperty( property ) !== this._styles.getInlineProperty( property ) ) { return false; } @@ -381,9 +381,9 @@ export default class Element extends Node { */ getStyle( property, asModel = false ) { if ( asModel ) { - return this._styles.getModel( property ); + return this._styles.getNormalized( property ); } else { - return this._styles.getInlineRule( property ); + return this._styles.getInlineProperty( property ); } } @@ -407,7 +407,7 @@ export default class Element extends Node { */ hasStyle( ...property ) { for ( const name of property ) { - if ( !this._styles.hasRule( name ) ) { + if ( !this._styles.hasProperty( name ) ) { return false; } } @@ -712,7 +712,7 @@ export default class Element extends Node { _setStyle( property, value ) { this._fireChange( 'attributes', this ); - this._styles.insertRule( property, value ); + this._styles.insertProperty( property, value ); } /** @@ -730,7 +730,7 @@ export default class Element extends Node { this._fireChange( 'attributes', this ); property = Array.isArray( property ) ? property : [ property ]; - property.forEach( name => this._styles.removeRule( name ) ); + property.forEach( name => this._styles.removeProperty( name ) ); } /** diff --git a/src/view/styles.js b/src/view/styles.js index edc519794..e7e2b5291 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -68,8 +68,8 @@ export default class Styles { * @param {String} name * @returns {Boolean} */ - hasRule( name ) { - const nameNorm = this._getPath( name ); + hasProperty( name ) { + const nameNorm = toPath( name ); return has( this._styles, nameNorm ) || !!this._styles[ name ]; } @@ -83,28 +83,28 @@ export default class Styles { * @param {String|Object} value * @returns {Boolean} */ - insertRule( nameOrObject, value ) { + insertProperty( nameOrObject, value ) { if ( isPlainObject( nameOrObject ) ) { for ( const key of Object.keys( nameOrObject ) ) { - this.insertRule( key, nameOrObject[ key ] ); + this.insertProperty( key, nameOrObject[ key ] ); } } else { - this._parseRule( nameOrObject, value ); + this._parseProperty( nameOrObject, value ); } } - removeRule( name ) { - unset( this._styles, this._getPath( name ) ); + removeProperty( name ) { + unset( this._styles, toPath( name ) ); delete this._styles[ name ]; } - getModel( name ) { + getNormalized( name ) { if ( !name ) { return merge( {}, this._styles ); } - const path = this._getPath( name ); + const path = toPath( name ); if ( has( this._styles, path ) ) { return get( this._styles, path ); @@ -123,16 +123,16 @@ export default class Styles { } for ( const key of keys ) { - const model = this.getModel( key ); + const normalized = this.getNormalized( key ); - parsed.push( toInlineStyle( key, model ) ); + parsed.push( toInlineStyle( key, normalized ) ); } return parsed.join( ';' ) + ';'; } - getInlineRule( name ) { - const model = this.getModel( name ); + getInlineProperty( name ) { + const model = this.getNormalized( name ); if ( !model ) { // Try return directly @@ -148,12 +148,9 @@ export default class Styles { } } - // TODO: expandShortHands: true/false? getStyleNames() { const inlineStyle = this.getInlineStyle(); - // TODO: probably not good enough. - // TODO: consumables must have different names or support shorthands. return ( inlineStyle || '' ).split( ';' ).filter( f => f !== '' ).map( abc => abc.split( ':' )[ 0 ] ).sort(); } @@ -161,17 +158,13 @@ export default class Styles { this._styles = {}; } - _getPath( name ) { - return name.replace( '-', '.' ); - } - _parseStyle( string ) { const map = parseInlineStyles( string ); for ( const key of map.keys() ) { const value = map.get( key ); - this._parseRule( key, value ); + this._parseProperty( key, value ); } } @@ -188,7 +181,7 @@ export default class Styles { } } - _parseRule( key, value ) { + _parseProperty( key, value ) { if ( isPlainObject( value ) ) { this._appendStyleValue( key, value ); @@ -199,7 +192,7 @@ export default class Styles { // Set directly to object. if ( setOnPathStyles.includes( key ) ) { - this._appendStyleValue( this._getPath( key ), value ); + this._appendStyleValue( toPath( key ), value ); return; } @@ -508,3 +501,14 @@ function parseInlineStyles( stylesString ) { return stylesMap; } + +function toPath( name ) { + return name.replace( '-', '.' ); +} + +// 'border-style' -> d{} +// 'border-top' -> d{} +// 'border' -> d{} + +// {} -> style="" +// {} -> border-top="" diff --git a/tests/view/element.js b/tests/view/element.js index 44261b7c7..754e2d368 100644 --- a/tests/view/element.js +++ b/tests/view/element.js @@ -74,12 +74,12 @@ describe( 'Element', () => { expect( el._attrs.has( 'style' ) ).to.be.false; expect( el._attrs.has( 'id' ) ).to.be.true; - expect( el._styles.hasRule( 'one' ) ).to.be.true; - expect( el._styles.getInlineRule( 'one' ) ).to.equal( 'style1' ); - expect( el._styles.hasRule( 'two' ) ).to.be.true; - expect( el._styles.getInlineRule( 'two' ) ).to.equal( 'style2' ); - expect( el._styles.hasRule( 'three' ) ).to.be.true; - expect( el._styles.getInlineRule( 'three' ) ).to.equal( 'url(http://ckeditor.com)' ); + expect( el._styles.hasProperty( 'one' ) ).to.be.true; + expect( el._styles.getInlineProperty( 'one' ) ).to.equal( 'style1' ); + expect( el._styles.hasProperty( 'two' ) ).to.be.true; + expect( el._styles.getInlineProperty( 'two' ) ).to.equal( 'style2' ); + expect( el._styles.hasProperty( 'three' ) ).to.be.true; + expect( el._styles.getInlineProperty( 'three' ) ).to.equal( 'url(http://ckeditor.com)' ); } ); } ); @@ -200,10 +200,10 @@ describe( 'Element', () => { expect( clone ).to.not.equal( el ); expect( clone.name ).to.equal( el.name ); - expect( clone._styles.hasRule( 'color' ) ).to.be.true; - expect( clone._styles.getInlineRule( 'color' ) ).to.equal( 'red' ); - expect( clone._styles.hasRule( 'font-size' ) ).to.be.true; - expect( clone._styles.getInlineRule( 'font-size' ) ).to.equal( '12px' ); + expect( clone._styles.hasProperty( 'color' ) ).to.be.true; + expect( clone._styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); + expect( clone._styles.hasProperty( 'font-size' ) ).to.be.true; + expect( clone._styles.getInlineProperty( 'font-size' ) ).to.equal( '12px' ); } ); it( 'should clone custom properties', () => { diff --git a/tests/view/styles.js b/tests/view/styles.js index d6a9d7b90..78285c2b4 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -5,32 +5,32 @@ import StyleProxy from '../../src/view/styles'; describe( 'Styles', () => { - let styleProxy; + let styles; beforeEach( () => { - styleProxy = new StyleProxy(); + styles = new StyleProxy(); } ); describe( 'getStyleNames()', () => { it( 'should output custom style names', () => { - styleProxy.setStyle( 'foo: 2;bar: baz;foo-bar-baz:none;' ); + styles.setStyle( 'foo: 2;bar: baz;foo-bar-baz:none;' ); - expect( styleProxy.getStyleNames() ).to.deep.equal( [ 'bar', 'foo', 'foo-bar-baz' ] ); + expect( styles.getStyleNames() ).to.deep.equal( [ 'bar', 'foo', 'foo-bar-baz' ] ); } ); it( 'should output full names for shorthand', () => { - styleProxy.setStyle( 'margin: 1px;margin-left: 2em;' ); + styles.setStyle( 'margin: 1px;margin-left: 2em;' ); - expect( styleProxy.getStyleNames() ).to.deep.equal( [ 'margin' ] ); + expect( styles.getStyleNames() ).to.deep.equal( [ 'margin' ] ); } ); } ); describe( 'styles rules', () => { describe( 'border', () => { it( 'should parse border shorthand', () => { - styleProxy.setStyle( 'border:1px solid blue;' ); + styles.setStyle( 'border:1px solid blue;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { color: 'blue', style: 'solid', width: '1px' }, right: { color: 'blue', style: 'solid', width: '1px' }, bottom: { color: 'blue', style: 'solid', width: '1px' }, @@ -39,9 +39,9 @@ describe( 'Styles', () => { } ); it( 'should parse border shorthand with other shorthands', () => { - styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { color: '#ccc', style: 'dotted', width: '7px' }, right: { color: 'blue', style: 'solid', width: '1px' }, bottom: { color: 'blue', style: 'solid', width: '1px' }, @@ -50,63 +50,63 @@ describe( 'Styles', () => { } ); it( 'should output inline shorthand rules', () => { - styleProxy.setStyle( 'border:1px solid blue;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'border:1px solid blue;' ); - expect( styleProxy.getInlineRule( 'border' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); + styles.setStyle( 'border:1px solid blue;' ); + + expect( styles.getInlineStyle() ).to.equal( 'border:1px solid blue;' ); + expect( styles.getInlineProperty( 'border' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid blue' ); } ); it( 'should output inline shorthand rules', () => { - styleProxy.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); - expect( styleProxy.getInlineStyle() ).to.equal( + expect( styles.getInlineStyle() ).to.equal( 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' ); - expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; - expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); - expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '7px dotted #ccc' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); } ); it( 'should merge rules on insert other shorthand', () => { - styleProxy.setStyle( 'border:1px solid blue;' ); - styleProxy.insertRule( 'border-left', '#665511 dashed 2.7em' ); - styleProxy.insertRule( 'border-top', '7px dotted #ccc' ); + styles.setStyle( 'border:1px solid blue;' ); + styles.insertProperty( 'border-left', '#665511 dashed 2.7em' ); + styles.insertProperty( 'border-top', '7px dotted #ccc' ); - expect( styleProxy.getInlineStyle() ).to.equal( + expect( styles.getInlineStyle() ).to.equal( 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' ); - expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; - expect( styleProxy.getInlineRule( 'border-top' ) ).to.equal( '7px dotted #ccc' ); - expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '7px dotted #ccc' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); } ); it( 'should output', () => { - styleProxy.setStyle( 'border:1px solid blue;' ); - styleProxy.removeRule( 'border-top' ); + styles.setStyle( 'border:1px solid blue;' ); + styles.removeProperty( 'border-top' ); - expect( styleProxy.getInlineStyle() ).to.equal( + expect( styles.getInlineStyle() ).to.equal( 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' ); - expect( styleProxy.getInlineRule( 'border' ) ).to.be.undefined; - expect( styleProxy.getInlineRule( 'border-top' ) ).to.be.undefined; - expect( styleProxy.getInlineRule( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styleProxy.getInlineRule( 'border-left' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-top' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid blue' ); } ); describe( 'border-color', () => { it( 'should set all border colors (1 value defined)', () => { - styleProxy.setStyle( 'border-color:cyan;' ); + styles.setStyle( 'border-color:cyan;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { color: 'cyan' }, left: { color: 'cyan' }, bottom: { color: 'cyan' }, @@ -115,9 +115,9 @@ describe( 'Styles', () => { } ); it( 'should set all border colors (2 values defined)', () => { - styleProxy.setStyle( 'border-color:cyan magenta;' ); + styles.setStyle( 'border-color:cyan magenta;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { color: 'cyan' }, right: { color: 'magenta' }, bottom: { color: 'cyan' }, @@ -126,9 +126,9 @@ describe( 'Styles', () => { } ); it( 'should set all border colors (3 values defined)', () => { - styleProxy.setStyle( 'border-color:cyan magenta pink;' ); + styles.setStyle( 'border-color:cyan magenta pink;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { color: 'cyan' }, right: { color: 'magenta' }, bottom: { color: 'pink' }, @@ -137,9 +137,9 @@ describe( 'Styles', () => { } ); it( 'should set all border colors (4 values defined)', () => { - styleProxy.setStyle( 'border-color:cyan magenta pink beige;' ); + styles.setStyle( 'border-color:cyan magenta pink beige;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { color: 'cyan' }, right: { color: 'magenta' }, bottom: { color: 'pink' }, @@ -148,9 +148,9 @@ describe( 'Styles', () => { } ); it( 'should merge with border shorthand', () => { - styleProxy.setStyle( 'border:1px solid blue;border-color:cyan black;' ); + styles.setStyle( 'border:1px solid blue;border-color:cyan black;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { color: 'cyan', style: 'solid', width: '1px' }, right: { color: 'black', style: 'solid', width: '1px' }, bottom: { color: 'cyan', style: 'solid', width: '1px' }, @@ -161,9 +161,9 @@ describe( 'Styles', () => { describe( 'border-style', () => { it( 'should set all border styles (1 value defined)', () => { - styleProxy.setStyle( 'border-style:solid;' ); + styles.setStyle( 'border-style:solid;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { style: 'solid' }, right: { style: 'solid' }, bottom: { style: 'solid' }, @@ -172,9 +172,9 @@ describe( 'Styles', () => { } ); it( 'should set all border styles (2 values defined)', () => { - styleProxy.setStyle( 'border-style:solid dotted;' ); + styles.setStyle( 'border-style:solid dotted;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { style: 'solid' }, right: { style: 'dotted' }, bottom: { style: 'solid' }, @@ -183,9 +183,9 @@ describe( 'Styles', () => { } ); it( 'should set all border styles (3 values defined)', () => { - styleProxy.setStyle( 'border-style:solid dotted dashed;' ); + styles.setStyle( 'border-style:solid dotted dashed;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { style: 'solid' }, right: { style: 'dotted' }, bottom: { style: 'dashed' }, @@ -194,9 +194,9 @@ describe( 'Styles', () => { } ); it( 'should set all border styles (4 values defined)', () => { - styleProxy.setStyle( 'border-style:solid dotted dashed ridge;' ); + styles.setStyle( 'border-style:solid dotted dashed ridge;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { style: 'solid' }, right: { style: 'dotted' }, bottom: { style: 'dashed' }, @@ -207,9 +207,9 @@ describe( 'Styles', () => { describe( 'border-width', () => { it( 'should set all border widths (1 value defined)', () => { - styleProxy.setStyle( 'border-width:1px;' ); + styles.setStyle( 'border-width:1px;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { width: '1px' }, right: { width: '1px' }, bottom: { width: '1px' }, @@ -218,9 +218,9 @@ describe( 'Styles', () => { } ); it( 'should set all border widths (2 values defined)', () => { - styleProxy.setStyle( 'border-width:1px .34cm;' ); + styles.setStyle( 'border-width:1px .34cm;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { width: '1px' }, right: { width: '.34cm' }, bottom: { width: '1px' }, @@ -229,9 +229,9 @@ describe( 'Styles', () => { } ); it( 'should set all border widths (3 values defined)', () => { - styleProxy.setStyle( 'border-width:1px .34cm 90.1rem;' ); + styles.setStyle( 'border-width:1px .34cm 90.1rem;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { width: '1px' }, right: { width: '.34cm' }, bottom: { width: '90.1rem' }, @@ -240,9 +240,9 @@ describe( 'Styles', () => { } ); it( 'should set all border widths (4 values defined)', () => { - styleProxy.setStyle( 'border-width:1px .34cm 90.1rem thick;' ); + styles.setStyle( 'border-width:1px .34cm 90.1rem thick;' ); - expect( styleProxy.getModel( 'border' ) ).to.deep.equal( { + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { top: { width: '1px' }, right: { width: '.34cm' }, bottom: { width: '90.1rem' }, @@ -254,9 +254,9 @@ describe( 'Styles', () => { describe( 'margin', () => { it( 'should set all margins (1 value defined)', () => { - styleProxy.setStyle( 'margin:1px;' ); + styles.setStyle( 'margin:1px;' ); - expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', right: '1px', bottom: '1px', @@ -265,9 +265,9 @@ describe( 'Styles', () => { } ); it( 'should set all margins (2 values defined)', () => { - styleProxy.setStyle( 'margin:1px .34cm;' ); + styles.setStyle( 'margin:1px .34cm;' ); - expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', right: '.34cm', bottom: '1px', @@ -276,9 +276,9 @@ describe( 'Styles', () => { } ); it( 'should set all margins (3 values defined)', () => { - styleProxy.setStyle( 'margin:1px .34cm 90.1rem;' ); + styles.setStyle( 'margin:1px .34cm 90.1rem;' ); - expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', right: '.34cm', bottom: '90.1rem', @@ -287,9 +287,9 @@ describe( 'Styles', () => { } ); it( 'should set all margins (4 values defined)', () => { - styleProxy.setStyle( 'margin:1px .34cm 90.1rem thick;' ); + styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); - expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', right: '.34cm', bottom: '90.1rem', @@ -298,101 +298,101 @@ describe( 'Styles', () => { } ); it( 'should output inline style (1 value defined)', () => { - styleProxy.setStyle( 'margin:1px;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px;' ); - expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( '1px' ); + styles.setStyle( 'margin:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); } ); it( 'should output inline style (2 values defined)', () => { - styleProxy.setStyle( 'margin:1px .34cm;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); - expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px .34cm' ); - expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( '.34cm' ); + styles.setStyle( 'margin:1px .34cm;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '.34cm' ); } ); it( 'should output inline style (3 values defined)', () => { - styleProxy.setStyle( 'margin:1px .34cm 90.1rem;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); - expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); - expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( '.34cm' ); + styles.setStyle( 'margin:1px .34cm 90.1rem;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '.34cm' ); } ); it( 'should output inline style (3 values defined, only last different)', () => { - styleProxy.setStyle( 'margin:1px 1px 90.1rem;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); - expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); - expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( '1px' ); + styles.setStyle( 'margin:1px 1px 90.1rem;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); } ); it( 'should output inline style (4 values defined)', () => { - styleProxy.setStyle( 'margin:1px .34cm 90.1rem thick;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); - expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); - expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( 'thick' ); + styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( 'thick' ); } ); it( 'should output inline style (4 values defined, only last different)', () => { - styleProxy.setStyle( 'margin:1px 1px 1px thick;' ); - - expect( styleProxy.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); - expect( styleProxy.getInlineRule( 'margin' ) ).to.equal( '1px 1px 1px thick' ); - expect( styleProxy.getInlineRule( 'margin-top' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-right' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styleProxy.getInlineRule( 'margin-left' ) ).to.equal( 'thick' ); + styles.setStyle( 'margin:1px 1px 1px thick;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( 'thick' ); } ); describe( 'margin-*', () => { it( 'should set proper margin', () => { - styleProxy.setStyle( 'margin-top:1px;' ); + styles.setStyle( 'margin-top:1px;' ); - expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { top: '1px' } ); - expect( styleProxy.getModel( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px' } ); + expect( styles.getNormalized( 'margin-top' ) ).to.equal( '1px' ); } ); it( 'should set proper margin with margin shorthand', () => { - styleProxy.setStyle( 'margin: 2em;margin-top:1px;' ); + styles.setStyle( 'margin: 2em;margin-top:1px;' ); - expect( styleProxy.getModel( 'margin' ) ).to.deep.equal( { + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', right: '2em', bottom: '2em', left: '2em' } ); - expect( styleProxy.getModel( 'margin-top' ) ).to.equal( '1px' ); - expect( styleProxy.getModel( 'margin-right' ) ).to.equal( '2em' ); - expect( styleProxy.getModel( 'margin-bottom' ) ).to.equal( '2em' ); - expect( styleProxy.getModel( 'margin-left' ) ).to.equal( '2em' ); + expect( styles.getNormalized( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getNormalized( 'margin-right' ) ).to.equal( '2em' ); + expect( styles.getNormalized( 'margin-bottom' ) ).to.equal( '2em' ); + expect( styles.getNormalized( 'margin-left' ) ).to.equal( '2em' ); } ); } ); } ); describe( 'padding', () => { it( 'should set all paddings (1 value defined)', () => { - styleProxy.setStyle( 'padding:1px;' ); + styles.setStyle( 'padding:1px;' ); - expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', right: '1px', bottom: '1px', @@ -401,9 +401,9 @@ describe( 'Styles', () => { } ); it( 'should set all paddings (2 values defined)', () => { - styleProxy.setStyle( 'padding:1px .34cm;' ); + styles.setStyle( 'padding:1px .34cm;' ); - expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', right: '.34cm', bottom: '1px', @@ -412,9 +412,9 @@ describe( 'Styles', () => { } ); it( 'should set all paddings (3 values defined)', () => { - styleProxy.setStyle( 'padding:1px .34cm 90.1rem;' ); + styles.setStyle( 'padding:1px .34cm 90.1rem;' ); - expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', right: '.34cm', bottom: '90.1rem', @@ -423,9 +423,9 @@ describe( 'Styles', () => { } ); it( 'should set all paddings (4 values defined)', () => { - styleProxy.setStyle( 'padding:1px .34cm 90.1rem thick;' ); + styles.setStyle( 'padding:1px .34cm 90.1rem thick;' ); - expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', right: '.34cm', bottom: '90.1rem', @@ -435,17 +435,17 @@ describe( 'Styles', () => { describe( 'padding-*', () => { it( 'should set proper padding', () => { - styleProxy.setStyle( 'padding-top:1px;' ); + styles.setStyle( 'padding-top:1px;' ); - expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px' } ); } ); it( 'should set proper padding with padding shorthand', () => { - styleProxy.setStyle( 'padding: 2em;padding-top:1px;' ); + styles.setStyle( 'padding: 2em;padding-top:1px;' ); - expect( styleProxy.getModel( 'padding' ) ).to.deep.equal( { + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', right: '2em', bottom: '2em', @@ -457,11 +457,11 @@ describe( 'Styles', () => { describe( 'unknown rules', () => { it( 'should left rules untouched', () => { - styleProxy.setStyle( 'foo-bar:baz 1px abc;baz: 2px 3em;' ); + styles.setStyle( 'foo-bar:baz 1px abc;baz: 2px 3em;' ); - expect( styleProxy.getInlineStyle() ).to.equal( 'baz:2px 3em;foo-bar:baz 1px abc;' ); - expect( styleProxy.getInlineRule( 'foo-bar' ) ).to.equal( 'baz 1px abc' ); - expect( styleProxy.getInlineRule( 'baz' ) ).to.equal( '2px 3em' ); + expect( styles.getInlineStyle() ).to.equal( 'baz:2px 3em;foo-bar:baz 1px abc;' ); + expect( styles.getInlineProperty( 'foo-bar' ) ).to.equal( 'baz 1px abc' ); + expect( styles.getInlineProperty( 'baz' ) ).to.equal( '2px 3em' ); } ); } ); } ); From 197e99af660cf99eb0794fdac3e27769cb67cb39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 2 Oct 2019 11:02:02 +0200 Subject: [PATCH 029/142] Refactor inline style parsing to be more declarative. --- src/view/styles.js | 107 ++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index e7e2b5291..613cf1ec0 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -10,7 +10,6 @@ import { get, has, isObject, isPlainObject, merge, set, unset } from 'lodash-es'; const borderPositionRegExp = /border-(top|right|bottom|left)$/; -const topRightBottomLeftPositionRegExp = /^(top|right|bottom|left)$/; const setOnPathStyles = [ // Borders. @@ -38,6 +37,22 @@ export default class Styles { constructor( styleString = '' ) { this._styles = {}; + this.parsers = new Map(); + + this.parsers.set( 'border', parseBorder ); + this.parsers.set( 'border-top', parseBorderSide( 'top' ) ); + this.parsers.set( 'border-right', parseBorderSide( 'right' ) ); + this.parsers.set( 'border-bottom', parseBorderSide( 'bottom' ) ); + this.parsers.set( 'border-left', parseBorderSide( 'left' ) ); + this.parsers.set( 'border-color', parseBorderProperty( 'color' ) ); + this.parsers.set( 'border-width', parseBorderProperty( 'width' ) ); + this.parsers.set( 'border-style', parseBorderProperty( 'style' ) ); + + this.parsers.set( 'background', parseBackground ); + + this.parsers.set( 'margin', parseShorthandSides( 'margin' ) ); + this.parsers.set( 'padding', parseShorthandSides( 'padding' ) ); + this.setStyle( styleString ); } @@ -188,42 +203,17 @@ export default class Styles { return; } - const baseKey = key.split( '-' )[ 0 ]; - - // Set directly to object. + // Set directly to an object. if ( setOnPathStyles.includes( key ) ) { this._appendStyleValue( toPath( key ), value ); return; } - let processed; + if ( this.parsers.has( key ) ) { + const parser = this.parsers.get( key ); - if ( baseKey === 'border' ) { - processed = processBorder( key, value ); - } - - if ( key === 'background' ) { - const layers = value.split( ',' ); - const background = {}; - - const parts = layers[ 0 ].split( ' ' ); - - for ( const part of parts ) { - if ( isColor( part ) ) { - background.color = part; - } - } - - processed = { background }; - } - - if ( key === 'margin' || key === 'padding' ) { - processed = { [ key ]: getTopRightBottomLeftValues( value ) }; - } - - if ( processed ) { - this._styles = merge( {}, this._styles, processed ); + this._styles = merge( {}, this._styles, parser( value ) ); } else { this._appendStyleValue( key, value ); } @@ -252,32 +242,37 @@ function toBorderPropertyShorthand( value, property ) { }; } -function processBorder( key, value ) { - if ( key === 'border' ) { - const parsedBorder = parseShorthandBorderAttribute( value ); +function parseShorthandSides( longhand ) { + return value => { + return { [ longhand ]: getTopRightBottomLeftValues( value ) }; + }; +} + +function parseBorder( value ) { + const parsedBorder = parseShorthandBorderAttribute( value ); - const border = { + return { + border: { top: parsedBorder, right: parsedBorder, bottom: parsedBorder, left: parsedBorder - }; - - return { border }; - } - const parts = key.split( '-' ); + } + }; +} - if ( topRightBottomLeftPositionRegExp.test( parts[ 1 ] ) ) { - return { - border: { - [ parts[ 1 ] ]: parseShorthandBorderAttribute( value ) - } - }; - } +function parseBorderSide( foo ) { + return value => ( { + border: { + [ foo ]: parseShorthandBorderAttribute( value ) + } + } ); +} - if ( [ 'color', 'style', 'width' ].includes( parts[ 1 ] ) ) { - return { border: toBorderPropertyShorthand( value, parts[ 1 ] ) }; - } +function parseBorderProperty( foo ) { + return value => ( { + border: toBorderPropertyShorthand( value, foo ) + } ); } function parseShorthandBorderAttribute( string ) { @@ -300,6 +295,20 @@ function parseShorthandBorderAttribute( string ) { return result; } +function parseBackground( value ) { + const background = {}; + + const parts = value.split( ' ' ); + + for ( const part of parts ) { + if ( isColor( part ) ) { + background.color = part; + } + } + + return { background }; +} + function isColor( string ) { return /^([#0-9A-Fa-f]{3,8}|[a-zA-Z]+)$/.test( string ) && !isLineStyle( string ); } From 45cdfd128e96723fdce475a88a918038e46cb7b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 2 Oct 2019 12:30:21 +0200 Subject: [PATCH 030/142] Remove private _parseStyle() method. --- src/view/styles.js | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 613cf1ec0..c08c3a200 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -72,7 +72,14 @@ export default class Styles { */ setStyle( styleString = '' ) { this.clear(); - this._parseStyle( styleString ); + + const map = parseInlineStyles( styleString ); + + for ( const key of map.keys() ) { + const value = map.get( key ); + + this._parseProperty( key, value ); + } } /** @@ -147,19 +154,19 @@ export default class Styles { } getInlineProperty( name ) { - const model = this.getNormalized( name ); + const normalized = this.getNormalized( name ); - if ( !model ) { + if ( !normalized ) { // Try return directly return this._styles[ name ]; } - if ( isObject( model ) ) { - return toInlineStyle( name, model, true ); + if ( isObject( normalized ) ) { + return toInlineStyle( name, normalized, true ); } // String value else { - return model; + return normalized; } } @@ -173,16 +180,6 @@ export default class Styles { this._styles = {}; } - _parseStyle( string ) { - const map = parseInlineStyles( string ); - - for ( const key of map.keys() ) { - const value = map.get( key ); - - this._parseProperty( key, value ); - } - } - _appendStyleValue( nameOrPath, valueOrObject ) { if ( typeof valueOrObject === 'object' ) { if ( nameOrPath.includes( '.' ) ) { From 7da42827a64aa328ac1fcf9c9382fc6771810ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 2 Oct 2019 12:35:33 +0200 Subject: [PATCH 031/142] Add getNormalizedStyle to view/element. --- src/view/element.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 8aca8fd66..c5d53a334 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -377,14 +377,14 @@ export default class Element extends Node { * Undefined is returned if style does not exist. * * @param {String} property - * @returns {String|undefined} + * @returns {String|Object|undefined} */ - getStyle( property, asModel = false ) { - if ( asModel ) { - return this._styles.getNormalized( property ); - } else { - return this._styles.getInlineProperty( property ); - } + getStyle( property ) { + return this._styles.getInlineProperty( property ); + } + + getNormalizedStyle( property ) { + return this._styles.getNormalized( property ); } /** From b88f7478d6ba54e3f917adbb2792dfcc1949bb4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 2 Oct 2019 13:16:51 +0200 Subject: [PATCH 032/142] Check how to stringify normalized model value. --- src/view/styles.js | 77 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index c08c3a200..d3c2eac16 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -162,7 +162,7 @@ export default class Styles { } if ( isObject( normalized ) ) { - return toInlineStyle( name, normalized, true ); + return toInlineStyleProperty( name, normalized ); } // String value else { @@ -369,20 +369,53 @@ function outputShorthandableValue( styleObject, strict, styleShorthand ) { } } -function toInlineStyle( styleName, styleObjectOrString, strict = false ) { +function stringifyBorderProperty( styleObjectOrString ) { + const top = toInlineBorder( styleObjectOrString.top ); + const right = toInlineBorder( styleObjectOrString.right ); + const bottom = toInlineBorder( styleObjectOrString.bottom ); + const left = toInlineBorder( styleObjectOrString.left ); + + if ( top === right && right === bottom && bottom === left ) { + return top; + } +} + +function getShortest( styleObjectOrString ) { + const top = toInlineBorder( styleObjectOrString.top ); + const right = toInlineBorder( styleObjectOrString.right ); + const bottom = toInlineBorder( styleObjectOrString.bottom ); + const left = toInlineBorder( styleObjectOrString.left ); + + if ( top === right && right === bottom && bottom === left ) { + return 'border:' + top; + } else { + return printSingleValues( { top, right, bottom, left }, 'border' ); + } +} + +function toInlineStyleProperty( styleName, styleObjectOrString ) { if ( styleName === 'border' ) { - const top = toInlineBorder( styleObjectOrString.top ); - const right = toInlineBorder( styleObjectOrString.right ); - const bottom = toInlineBorder( styleObjectOrString.bottom ); - const left = toInlineBorder( styleObjectOrString.left ); - - if ( top === right && right === bottom && bottom === left ) { - return ( strict ? '' : 'border:' ) + top; - } else if ( !strict ) { - return printSingleValues( { top, right, bottom, left }, 'border' ); - } + return stringifyBorderProperty( styleObjectOrString ); + } - return; + if ( borderPositionRegExp.test( styleName ) ) { + return toInlineBorder( styleObjectOrString ); + } + + if ( styleName === 'margin' ) { + return outputShorthandableValue( styleObjectOrString, true, 'margin' ); + } + + if ( styleName === 'padding' ) { + return outputShorthandableValue( styleObjectOrString, true, 'padding' ); + } + + return styleObjectOrString; +} + +function toInlineStyle( styleName, styleObjectOrString ) { + if ( styleName === 'border' ) { + return getShortest( styleObjectOrString ); } if ( borderPositionRegExp.test( styleName ) ) { @@ -390,27 +423,25 @@ function toInlineStyle( styleName, styleObjectOrString, strict = false ) { } if ( styleName === 'margin' ) { - return outputShorthandableValue( styleObjectOrString, strict, 'margin' ); + return outputShorthandableValue( styleObjectOrString, false, 'margin' ); } if ( styleName === 'padding' ) { - return outputShorthandableValue( styleObjectOrString, strict, 'padding' ); + return outputShorthandableValue( styleObjectOrString, false, 'padding' ); } // Generic, one-level, object to style: if ( isObject( styleObjectOrString ) ) { - if ( !strict ) { - const values = []; + const values = []; - for ( const key of Object.keys( styleObjectOrString ) ) { - values.push( styleName + '-' + key + ':' + styleObjectOrString[ key ] ); - } - - return values.join( ';' ); + for ( const key of Object.keys( styleObjectOrString ) ) { + values.push( `${ styleName }-${ key }:${ styleObjectOrString[ key ] }` ); } + + return values.join( ';' ); } - return ( strict ? '' : styleName + ':' ) + styleObjectOrString; + return `${ styleName }:${ styleObjectOrString }`; } function toInlineBorder( object = {} ) { From 1bc367e6188077dc09b72ca2561edfdc4fc044df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 2 Oct 2019 16:19:52 +0200 Subject: [PATCH 033/142] Change border normalized version. --- src/view/styles.js | 156 ++++++++++++++++++++---------------- tests/view/styles.js | 187 +++++++++++++++++++++++-------------------- 2 files changed, 189 insertions(+), 154 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index d3c2eac16..cb97a764a 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -9,13 +9,7 @@ import { get, has, isObject, isPlainObject, merge, set, unset } from 'lodash-es'; -const borderPositionRegExp = /border-(top|right|bottom|left)$/; - const setOnPathStyles = [ - // Borders. - 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color', - 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style', - 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width', // Margin & padding. 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', @@ -195,7 +189,7 @@ export default class Styles { _parseProperty( key, value ) { if ( isPlainObject( value ) ) { - this._appendStyleValue( key, value ); + this._appendStyleValue( toPath( key ), value ); return; } @@ -217,7 +211,7 @@ export default class Styles { } } -function getTopRightBottomLeftValues( value ) { +function getTopRightBottomLeftValues( value = '' ) { const values = value.split( ' ' ); const top = values[ 0 ]; @@ -229,13 +223,8 @@ function getTopRightBottomLeftValues( value ) { } function toBorderPropertyShorthand( value, property ) { - const { top, bottom, right, left } = getTopRightBottomLeftValues( value ); - return { - top: { [ property ]: top }, - right: { [ property ]: right }, - bottom: { [ property ]: bottom }, - left: { [ property ]: left } + [ property ]: getTopRightBottomLeftValues( value ) }; } @@ -246,24 +235,37 @@ function parseShorthandSides( longhand ) { } function parseBorder( value ) { - const parsedBorder = parseShorthandBorderAttribute( value ); + const { color, style, width } = parseShorthandBorderAttribute( value ); return { border: { - top: parsedBorder, - right: parsedBorder, - bottom: parsedBorder, - left: parsedBorder + color: getTopRightBottomLeftValues( color ), + style: getTopRightBottomLeftValues( style ), + width: getTopRightBottomLeftValues( width ) } }; } -function parseBorderSide( foo ) { - return value => ( { - border: { - [ foo ]: parseShorthandBorderAttribute( value ) +function parseBorderSide( side ) { + return value => { + const { color, style, width } = parseShorthandBorderAttribute( value ); + + const border = {}; + + if ( color !== undefined ) { + border.color = { [ side ]: color }; } - } ); + + if ( style !== undefined ) { + border.style = { [ side ]: style }; + } + + if ( width !== undefined ) { + border.width = { [ side ]: width }; + } + + return { border }; + }; } function parseBorderProperty( foo ) { @@ -340,7 +342,28 @@ function printSingleValues( { top, right, bottom, left }, prefix ) { return ret.join( ';' ); } -function outputShorthandableValue( styleObject, strict, styleShorthand ) { +function shBorder( which ) { + return value => { + return outputShorthandableValue( value[ which ], false, `border-${ which }` ); + }; +} + +function getABCDEDGHIJK( { left, right, top, bottom } ) { + const out = []; + + if ( left !== right ) { + out.push( top, right, bottom, left ); + } else if ( bottom !== top ) { + out.push( top, right, bottom ); + } else if ( right != top ) { + out.push( top, right ); + } else { + out.push( top ); + } + return out; +} + +function outputShorthandableValue( styleObject = {}, strict, styleShorthand ) { const { top, right, bottom, left } = styleObject; if ( top === left && left === bottom && bottom === right ) { @@ -353,17 +376,7 @@ function outputShorthandableValue( styleObject, strict, styleShorthand ) { } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { return printSingleValues( { top, right, bottom, left }, 'margin' ); } else { - const out = []; - - if ( left !== right ) { - out.push( top, right, bottom, left ); - } else if ( bottom !== top ) { - out.push( top, right, bottom ); - } else if ( right != top ) { - out.push( top, right ); - } else { - out.push( top ); - } + const out = getABCDEDGHIJK( styleObject ); return `${ strict ? '' : styleShorthand + ':' }${ out.join( ' ' ) }`; } @@ -380,26 +393,21 @@ function stringifyBorderProperty( styleObjectOrString ) { } } -function getShortest( styleObjectOrString ) { - const top = toInlineBorder( styleObjectOrString.top ); - const right = toInlineBorder( styleObjectOrString.right ); - const bottom = toInlineBorder( styleObjectOrString.bottom ); - const left = toInlineBorder( styleObjectOrString.left ); - - if ( top === right && right === bottom && bottom === left ) { - return 'border:' + top; - } else { - return printSingleValues( { top, right, bottom, left }, 'border' ); - } -} - function toInlineStyleProperty( styleName, styleObjectOrString ) { if ( styleName === 'border' ) { return stringifyBorderProperty( styleObjectOrString ); } - if ( borderPositionRegExp.test( styleName ) ) { - return toInlineBorder( styleObjectOrString ); + if ( styleName === 'border-color' ) { + return outputShorthandableValue( styleObjectOrString, true, 'border-color' ); + } + + if ( styleName === 'border-style' ) { + return outputShorthandableValue( styleObjectOrString, true, 'border-style' ); + } + + if ( styleName === 'border-width' ) { + return outputShorthandableValue( styleObjectOrString, true, 'border-width' ); } if ( styleName === 'margin' ) { @@ -413,32 +421,42 @@ function toInlineStyleProperty( styleName, styleObjectOrString ) { return styleObjectOrString; } -function toInlineStyle( styleName, styleObjectOrString ) { - if ( styleName === 'border' ) { - return getShortest( styleObjectOrString ); - } +function leWhat( styleObjectOrString, styleName ) { + const values = []; - if ( borderPositionRegExp.test( styleName ) ) { - return toInlineBorder( styleObjectOrString ); - } + for ( const key of Object.keys( styleObjectOrString ) ) { + let styleObjectOrStringElement; - if ( styleName === 'margin' ) { - return outputShorthandableValue( styleObjectOrString, false, 'margin' ); + if ( isObject( styleObjectOrString[ key ] ) ) { + styleObjectOrStringElement = outputShorthandableValue( styleObjectOrString[ key ], true, styleName + 'key' ); + } else { + styleObjectOrStringElement = styleObjectOrString[ key ]; + } + + values.push( `${ styleName }-${ key }:${ styleObjectOrStringElement }` ); } - if ( styleName === 'padding' ) { - return outputShorthandableValue( styleObjectOrString, false, 'padding' ); + return values.join( ';' ); +} + +function toInlineStyle( styleName, styleObjectOrString ) { + const inliners = new Map(); + + inliners.set( 'border-color', shBorder( 'color' ) ); + inliners.set( 'border-style', shBorder( 'style' ) ); + inliners.set( 'border-width', shBorder( 'width' ) ); + inliners.set( 'margin', value => outputShorthandableValue( value, false, 'margin' ) ); + inliners.set( 'padding', value => outputShorthandableValue( value, false, 'padding' ) ); + + if ( inliners.has( styleName ) ) { + const inliner = inliners.get( styleName ); + + return inliner( styleObjectOrString ); } // Generic, one-level, object to style: if ( isObject( styleObjectOrString ) ) { - const values = []; - - for ( const key of Object.keys( styleObjectOrString ) ) { - values.push( `${ styleName }-${ key }:${ styleObjectOrString[ key ] }` ); - } - - return values.join( ';' ); + return leWhat( styleObjectOrString, styleName ); } return `${ styleName }:${ styleObjectOrString }`; diff --git a/tests/view/styles.js b/tests/view/styles.js index 78285c2b4..c0ff49e91 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -31,10 +31,9 @@ describe( 'Styles', () => { styles.setStyle( 'border:1px solid blue;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { color: 'blue', style: 'solid', width: '1px' }, - right: { color: 'blue', style: 'solid', width: '1px' }, - bottom: { color: 'blue', style: 'solid', width: '1px' }, - left: { color: 'blue', style: 'solid', width: '1px' } + color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } } ); } ); @@ -42,35 +41,31 @@ describe( 'Styles', () => { styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { color: '#ccc', style: 'dotted', width: '7px' }, - right: { color: 'blue', style: 'solid', width: '1px' }, - bottom: { color: 'blue', style: 'solid', width: '1px' }, - left: { color: '#665511', style: 'dashed', width: '2.7em' } + color: { top: '#ccc', right: 'blue', bottom: 'blue', left: '#665511' }, + style: { top: 'dotted', right: 'solid', bottom: 'solid', left: 'dashed' }, + width: { top: '7px', right: '1px', bottom: '1px', left: '2.7em' } } ); } ); it( 'should output inline shorthand rules', () => { styles.setStyle( 'border:1px solid blue;' ); - expect( styles.getInlineStyle() ).to.equal( 'border:1px solid blue;' ); - expect( styles.getInlineProperty( 'border' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid blue' ); + expect( styles.getInlineStyle() ).to.equal( 'border-color:blue;border-style:solid;border-width:1px;' ); + expect( styles.getInlineProperty( 'border-color' ) ).to.equal( 'blue' ); + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); } ); it( 'should output inline shorthand rules', () => { styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styles.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + 'border-color:#ccc blue blue #665511;border-style:dotted solid solid dashed;border-width:7px 1px 1px 2.7em;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '7px dotted #ccc' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); + // todo expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); } ); it( 'should merge rules on insert other shorthand', () => { @@ -79,27 +74,26 @@ describe( 'Styles', () => { styles.insertProperty( 'border-top', '7px dotted #ccc' ); expect( styles.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + 'border-color:#ccc blue blue #665511;border-style:dotted solid solid dashed;border-width:7px 1px 1px 2.7em;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '7px dotted #ccc' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '2.7em dashed #665511' ); + // expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); } ); it( 'should output', () => { styles.setStyle( 'border:1px solid blue;' ); - styles.removeProperty( 'border-top' ); + styles.removeProperty( 'border-color' ); expect( styles.getInlineStyle() ).to.equal( - 'border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' + 'border-style:solid;border-width:1px;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-top' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid blue' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid blue' ); + + // expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); } ); describe( 'border-color', () => { @@ -107,10 +101,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-color:cyan;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { color: 'cyan' }, - left: { color: 'cyan' }, - bottom: { color: 'cyan' }, - right: { color: 'cyan' } + color: { + top: 'cyan', + right: 'cyan', + bottom: 'cyan', + left: 'cyan' + } } ); } ); @@ -118,10 +114,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-color:cyan magenta;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { color: 'cyan' }, - right: { color: 'magenta' }, - bottom: { color: 'cyan' }, - left: { color: 'magenta' } + color: { + top: 'cyan', + right: 'magenta', + bottom: 'cyan', + left: 'magenta' + } } ); } ); @@ -129,10 +127,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-color:cyan magenta pink;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { color: 'cyan' }, - right: { color: 'magenta' }, - bottom: { color: 'pink' }, - left: { color: 'magenta' } + color: { + top: 'cyan', + right: 'magenta', + bottom: 'pink', + left: 'magenta' + } } ); } ); @@ -140,10 +140,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-color:cyan magenta pink beige;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { color: 'cyan' }, - right: { color: 'magenta' }, - bottom: { color: 'pink' }, - left: { color: 'beige' } + color: { + top: 'cyan', + right: 'magenta', + bottom: 'pink', + left: 'beige' + } } ); } ); @@ -151,10 +153,9 @@ describe( 'Styles', () => { styles.setStyle( 'border:1px solid blue;border-color:cyan black;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { color: 'cyan', style: 'solid', width: '1px' }, - right: { color: 'black', style: 'solid', width: '1px' }, - bottom: { color: 'cyan', style: 'solid', width: '1px' }, - left: { color: 'black', style: 'solid', width: '1px' } + color: { top: 'cyan', right: 'black', bottom: 'cyan', left: 'black' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } } ); } ); } ); @@ -164,10 +165,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-style:solid;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { style: 'solid' }, - right: { style: 'solid' }, - bottom: { style: 'solid' }, - left: { style: 'solid' } + style: { + top: 'solid', + right: 'solid', + bottom: 'solid', + left: 'solid' + } } ); } ); @@ -175,10 +178,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-style:solid dotted;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { style: 'solid' }, - right: { style: 'dotted' }, - bottom: { style: 'solid' }, - left: { style: 'dotted' } + style: { + top: 'solid', + right: 'dotted', + bottom: 'solid', + left: 'dotted' + } } ); } ); @@ -186,10 +191,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-style:solid dotted dashed;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { style: 'solid' }, - right: { style: 'dotted' }, - bottom: { style: 'dashed' }, - left: { style: 'dotted' } + style: { + top: 'solid', + right: 'dotted', + bottom: 'dashed', + left: 'dotted' + } } ); } ); @@ -197,10 +204,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-style:solid dotted dashed ridge;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { style: 'solid' }, - right: { style: 'dotted' }, - bottom: { style: 'dashed' }, - left: { style: 'ridge' } + style: { + top: 'solid', + right: 'dotted', + bottom: 'dashed', + left: 'ridge' + } } ); } ); } ); @@ -210,10 +219,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-width:1px;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { width: '1px' }, - right: { width: '1px' }, - bottom: { width: '1px' }, - left: { width: '1px' } + width: { + top: '1px', + right: '1px', + bottom: '1px', + left: '1px' + } } ); } ); @@ -221,10 +232,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-width:1px .34cm;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { width: '1px' }, - right: { width: '.34cm' }, - bottom: { width: '1px' }, - left: { width: '.34cm' } + width: { + top: '1px', + right: '.34cm', + bottom: '1px', + left: '.34cm' + } } ); } ); @@ -232,10 +245,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-width:1px .34cm 90.1rem;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { width: '1px' }, - right: { width: '.34cm' }, - bottom: { width: '90.1rem' }, - left: { width: '.34cm' } + width: { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: '.34cm' + } } ); } ); @@ -243,10 +258,12 @@ describe( 'Styles', () => { styles.setStyle( 'border-width:1px .34cm 90.1rem thick;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - top: { width: '1px' }, - right: { width: '.34cm' }, - bottom: { width: '90.1rem' }, - left: { width: 'thick' } + width: { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: 'thick' + } } ); } ); } ); From e2b793fdd1fb1416690a5eabe5114e6cfc2663fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 2 Oct 2019 16:44:57 +0200 Subject: [PATCH 034/142] Remove unneeded styles normalizations. --- src/view/styles.js | 20 ++++++++++++++++---- tests/view/styles.js | 28 ++++++++++++++-------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index cb97a764a..ec639b54a 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -167,7 +167,11 @@ export default class Styles { getStyleNames() { const inlineStyle = this.getInlineStyle(); - return ( inlineStyle || '' ).split( ';' ).filter( f => f !== '' ).map( abc => abc.split( ':' )[ 0 ] ).sort(); + return ( inlineStyle || '' ) + .split( ';' ) + .filter( f => f !== '' ) + .map( abc => abc.split( ':' )[ 0 ] ) + .sort( sortTopRightBottomLeftProperties ); } clear() { @@ -421,10 +425,20 @@ function toInlineStyleProperty( styleName, styleObjectOrString ) { return styleObjectOrString; } +const topRightBottomLeftOrder = [ 'top', 'right', 'bottom', 'left' ]; + +function sortTopRightBottomLeftProperties( a, b ) { + if ( topRightBottomLeftOrder.includes( a ) && topRightBottomLeftOrder.includes( b ) ) { + return topRightBottomLeftOrder.indexOf( a ) - topRightBottomLeftOrder.indexOf( b ); + } + + return 0; +} + function leWhat( styleObjectOrString, styleName ) { const values = []; - for ( const key of Object.keys( styleObjectOrString ) ) { + for ( const key of Object.keys( styleObjectOrString ).sort( sortTopRightBottomLeftProperties ) ) { let styleObjectOrStringElement; if ( isObject( styleObjectOrString[ key ] ) ) { @@ -445,8 +459,6 @@ function toInlineStyle( styleName, styleObjectOrString ) { inliners.set( 'border-color', shBorder( 'color' ) ); inliners.set( 'border-style', shBorder( 'style' ) ); inliners.set( 'border-width', shBorder( 'width' ) ); - inliners.set( 'margin', value => outputShorthandableValue( value, false, 'margin' ) ); - inliners.set( 'padding', value => outputShorthandableValue( value, false, 'padding' ) ); if ( inliners.has( styleName ) ) { const inliner = inliners.get( styleName ); diff --git a/tests/view/styles.js b/tests/view/styles.js index c0ff49e91..608e097b3 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -18,10 +18,10 @@ describe( 'Styles', () => { expect( styles.getStyleNames() ).to.deep.equal( [ 'bar', 'foo', 'foo-bar-baz' ] ); } ); - it( 'should output full names for shorthand', () => { + it( 'should output full names for known style names', () => { styles.setStyle( 'margin: 1px;margin-left: 2em;' ); - expect( styles.getStyleNames() ).to.deep.equal( [ 'margin' ] ); + expect( styles.getStyleNames() ).to.deep.equal( [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ] ); } ); } ); @@ -317,8 +317,8 @@ describe( 'Styles', () => { it( 'should output inline style (1 value defined)', () => { styles.setStyle( 'margin:1px;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); + expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:1px;margin-bottom:1px;margin-left:1px;' ); + // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); @@ -328,8 +328,8 @@ describe( 'Styles', () => { it( 'should output inline style (2 values defined)', () => { styles.setStyle( 'margin:1px .34cm;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); + expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:.34cm;margin-bottom:1px;margin-left:.34cm;' ); + // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); @@ -339,8 +339,8 @@ describe( 'Styles', () => { it( 'should output inline style (3 values defined)', () => { styles.setStyle( 'margin:1px .34cm 90.1rem;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); + expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:.34cm;margin-bottom:90.1rem;margin-left:.34cm;' ); + // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); @@ -350,8 +350,8 @@ describe( 'Styles', () => { it( 'should output inline style (3 values defined, only last different)', () => { styles.setStyle( 'margin:1px 1px 90.1rem;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); + expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:1px;margin-bottom:90.1rem;margin-left:1px;' ); + // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); @@ -361,8 +361,8 @@ describe( 'Styles', () => { it( 'should output inline style (4 values defined)', () => { styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); + expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:.34cm;margin-bottom:90.1rem;margin-left:thick;' ); + // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); @@ -372,8 +372,8 @@ describe( 'Styles', () => { it( 'should output inline style (4 values defined, only last different)', () => { styles.setStyle( 'margin:1px 1px 1px thick;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); + expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:1px;margin-bottom:1px;margin-left:thick;' ); + // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); From 293b08950c2ddfcf6247b037129753abd9593eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 2 Oct 2019 16:51:44 +0200 Subject: [PATCH 035/142] Fix tests. --- tests/view/element.js | 14 +++++++------- tests/view/emptyelement.js | 4 ++-- tests/view/uielement.js | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/view/element.js b/tests/view/element.js index 754e2d368..ccb0e55f1 100644 --- a/tests/view/element.js +++ b/tests/view/element.js @@ -485,12 +485,12 @@ describe( 'Element', () => { it( 'should replace all styles', () => { el._setStyle( 'color', 'red' ); el._setStyle( 'top', '10px' ); - el._setAttribute( 'style', 'border:none' ); + el._setAttribute( 'style', 'margin-top:2em;' ); expect( el.hasStyle( 'color' ) ).to.be.false; expect( el.hasStyle( 'top' ) ).to.be.false; - expect( el.hasStyle( 'border' ) ).to.be.true; - expect( el.getStyle( 'border' ) ).to.equal( 'none' ); + expect( el.hasStyle( 'margin-top' ) ).to.be.true; + expect( el.getStyle( 'margin-top' ) ).to.equal( '2em' ); } ); } ); @@ -794,11 +794,11 @@ describe( 'Element', () => { it( 'should get style', () => { el._setStyle( { color: 'red', - border: '1px solid red' + 'margin-top': '1px' } ); expect( el.getStyle( 'color' ) ).to.equal( 'red' ); - expect( el.getStyle( 'border' ) ).to.equal( '1px solid red' ); + expect( el.getStyle( 'margin-top' ) ).to.equal( '1px' ); } ); } ); @@ -1034,10 +1034,10 @@ describe( 'Element', () => { it( 'should return styles in sorted order', () => { const el = new Element( 'foo', { - style: 'border: 1px solid red; background-color: red' + style: 'margin-top: 2em; background-color: red' } ); - expect( el.getIdentity() ).to.equal( 'foo style="background-color:red;border:1px solid red;"' ); + expect( el.getIdentity() ).to.equal( 'foo style="background-color:red;margin-top:2em;"' ); } ); it( 'should return attributes in sorted order', () => { diff --git a/tests/view/emptyelement.js b/tests/view/emptyelement.js index 695ddd881..05e39d089 100644 --- a/tests/view/emptyelement.js +++ b/tests/view/emptyelement.js @@ -15,7 +15,7 @@ describe( 'EmptyElement', () => { element = new Element( 'b' ); emptyElement = new EmptyElement( 'img', { alt: 'alternative text', - style: 'border: 1px solid red;color: white;', + style: 'margin-top: 2em;color: white;', class: 'image big' } ); } ); @@ -88,7 +88,7 @@ describe( 'EmptyElement', () => { expect( newEmptyElement.name ).to.equal( 'img' ); expect( newEmptyElement.getAttribute( 'alt' ) ).to.equal( 'alternative text' ); - expect( newEmptyElement.getStyle( 'border' ) ).to.equal( '1px solid red' ); + expect( newEmptyElement.getStyle( 'margin-top' ) ).to.equal( '2em' ); expect( newEmptyElement.getStyle( 'color' ) ).to.equal( 'white' ); expect( newEmptyElement.hasClass( 'image' ) ).to.be.true; expect( newEmptyElement.hasClass( 'big' ) ).to.be.true; diff --git a/tests/view/uielement.js b/tests/view/uielement.js index 92eeda258..7daf9e54a 100644 --- a/tests/view/uielement.js +++ b/tests/view/uielement.js @@ -15,7 +15,7 @@ describe( 'UIElement', () => { beforeEach( () => { uiElement = new UIElement( 'span', { foo: 'bar', - style: 'border: 1px solid red;color: white;', + style: 'margin-top: 2em;color: white;', class: 'foo bar' } ); } ); @@ -24,7 +24,7 @@ describe( 'UIElement', () => { it( 'should create instance', () => { expect( uiElement.name ).to.equal( 'span' ); expect( uiElement.getAttribute( 'foo' ) ).to.equal( 'bar' ); - expect( uiElement.getStyle( 'border' ) ).to.equal( '1px solid red' ); + expect( uiElement.getStyle( 'margin-top' ) ).to.equal( '2em' ); expect( uiElement.getStyle( 'color' ) ).to.equal( 'white' ); expect( uiElement.hasClass( 'foo' ) ).to.true; expect( uiElement.hasClass( 'bar' ) ).to.true; @@ -101,7 +101,7 @@ describe( 'UIElement', () => { expect( newUIElement.name ).to.equal( 'span' ); expect( newUIElement.getAttribute( 'foo' ) ).to.equal( 'bar' ); - expect( newUIElement.getStyle( 'border' ) ).to.equal( '1px solid red' ); + expect( newUIElement.getStyle( 'margin-top' ) ).to.equal( '2em' ); expect( newUIElement.getStyle( 'color' ) ).to.equal( 'white' ); expect( newUIElement.hasClass( 'foo' ) ).to.true; expect( newUIElement.hasClass( 'bar' ) ).to.true; From 98df9e456222ef0f9b3453b099809d620d966e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 3 Oct 2019 08:51:41 +0200 Subject: [PATCH 036/142] Stub proper styles signifying from normalized object. --- src/view/styles.js | 154 +++++++++++++------------------------------ tests/view/styles.js | 4 +- 2 files changed, 49 insertions(+), 109 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index ec639b54a..b42f05d02 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -141,22 +141,28 @@ export default class Styles { for ( const key of keys ) { const normalized = this.getNormalized( key ); - parsed.push( toInlineStyle( key, normalized ) ); + parsed.push( ...newNewGetStyleFromNormalized( key, normalized ) ); } - return parsed.join( ';' ) + ';'; + return parsed.map( arr => arr.join( ':' ) ).join( ';' ) + ';'; } - getInlineProperty( name ) { - const normalized = this.getNormalized( name ); + getInlineProperty( propertyName ) { + const normalized = this.getNormalized( propertyName ); if ( !normalized ) { // Try return directly - return this._styles[ name ]; + return this._styles[ propertyName ]; } if ( isObject( normalized ) ) { - return toInlineStyleProperty( name, normalized ); + const ret = toInlineStyleProperty( propertyName, normalized ); + + if ( Array.isArray( ret ) ) { + return ret[ 1 ]; + } else { + return ''; + } } // String value else { @@ -328,27 +334,27 @@ function printSingleValues( { top, right, bottom, left }, prefix ) { const ret = []; if ( top ) { - ret.push( prefix + '-top:' + top ); + ret.push( [ prefix + '-top', top ] ); } if ( right ) { - ret.push( prefix + '-right:' + right ); + ret.push( [ prefix + '-right', right ] ); } if ( bottom ) { - ret.push( prefix + '-bottom:' + bottom ); + ret.push( [ prefix + '-bottom', bottom ] ); } if ( left ) { - ret.push( prefix + '-left:' + left ); + ret.push( [ prefix + '-left', left ] ); } - return ret.join( ';' ); + return ret; } -function shBorder( which ) { +function shBorderProperty( which ) { return value => { - return outputShorthandableValue( value[ which ], false, `border-${ which }` ); + return outputShorthandableValue( value, false, `border-${ which }` ); }; } @@ -373,10 +379,12 @@ function outputShorthandableValue( styleObject = {}, strict, styleShorthand ) { if ( top === left && left === bottom && bottom === right ) { // Might be not set. if ( top === undefined ) { - return ''; + return []; } - return ( strict ? '' : styleShorthand + ':' ) + top; + return [ + [ styleShorthand, top ] + ]; } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { return printSingleValues( { top, right, bottom, left }, 'margin' ); } else { @@ -386,43 +394,39 @@ function outputShorthandableValue( styleObject = {}, strict, styleShorthand ) { } } -function stringifyBorderProperty( styleObjectOrString ) { - const top = toInlineBorder( styleObjectOrString.top ); - const right = toInlineBorder( styleObjectOrString.right ); - const bottom = toInlineBorder( styleObjectOrString.bottom ); - const left = toInlineBorder( styleObjectOrString.left ); +function newNewGetStyleFromNormalized( styleName, styleObjectOrString ) { + const styleGetters = new Map(); - if ( top === right && right === bottom && bottom === left ) { - return top; - } -} + styleGetters.set( 'border-color', shBorderProperty( 'color' ) ); + styleGetters.set( 'border-style', shBorderProperty( 'style' ) ); + styleGetters.set( 'border-width', shBorderProperty( 'width' ) ); + styleGetters.set( 'border', value => { + const ret = []; -function toInlineStyleProperty( styleName, styleObjectOrString ) { - if ( styleName === 'border' ) { - return stringifyBorderProperty( styleObjectOrString ); - } + ret.push( ...shBorderProperty( 'color' )( value.color ) ); + ret.push( ...shBorderProperty( 'style' )( value.style ) ); + ret.push( ...shBorderProperty( 'width' )( value.width ) ); - if ( styleName === 'border-color' ) { - return outputShorthandableValue( styleObjectOrString, true, 'border-color' ); - } + return ret; + } ); - if ( styleName === 'border-style' ) { - return outputShorthandableValue( styleObjectOrString, true, 'border-style' ); - } + let stylesArray; - if ( styleName === 'border-width' ) { - return outputShorthandableValue( styleObjectOrString, true, 'border-width' ); - } + if ( styleGetters.has( styleName ) ) { + const styleGetter = styleGetters.get( styleName ); - if ( styleName === 'margin' ) { - return outputShorthandableValue( styleObjectOrString, true, 'margin' ); + stylesArray = styleGetter( styleObjectOrString ); + } else { + // console.warn( 'no default' ); } - if ( styleName === 'padding' ) { - return outputShorthandableValue( styleObjectOrString, true, 'padding' ); - } + return stylesArray || []; +} - return styleObjectOrString; +function toInlineStyleProperty( styleName, styleObjectOrString ) { + const styles = newNewGetStyleFromNormalized( styleName, styleObjectOrString ); + + return styles.find( ( [ property ] ) => property === styleName ); } const topRightBottomLeftOrder = [ 'top', 'right', 'bottom', 'left' ]; @@ -435,63 +439,6 @@ function sortTopRightBottomLeftProperties( a, b ) { return 0; } -function leWhat( styleObjectOrString, styleName ) { - const values = []; - - for ( const key of Object.keys( styleObjectOrString ).sort( sortTopRightBottomLeftProperties ) ) { - let styleObjectOrStringElement; - - if ( isObject( styleObjectOrString[ key ] ) ) { - styleObjectOrStringElement = outputShorthandableValue( styleObjectOrString[ key ], true, styleName + 'key' ); - } else { - styleObjectOrStringElement = styleObjectOrString[ key ]; - } - - values.push( `${ styleName }-${ key }:${ styleObjectOrStringElement }` ); - } - - return values.join( ';' ); -} - -function toInlineStyle( styleName, styleObjectOrString ) { - const inliners = new Map(); - - inliners.set( 'border-color', shBorder( 'color' ) ); - inliners.set( 'border-style', shBorder( 'style' ) ); - inliners.set( 'border-width', shBorder( 'width' ) ); - - if ( inliners.has( styleName ) ) { - const inliner = inliners.get( styleName ); - - return inliner( styleObjectOrString ); - } - - // Generic, one-level, object to style: - if ( isObject( styleObjectOrString ) ) { - return leWhat( styleObjectOrString, styleName ); - } - - return `${ styleName }:${ styleObjectOrString }`; -} - -function toInlineBorder( object = {} ) { - const style = []; - - if ( object.width ) { - style.push( object.width ); - } - - if ( object.style ) { - style.push( object.style ); - } - - if ( object.color ) { - style.push( object.color ); - } - - return style.join( ' ' ); -} - // Parses inline styles and puts property - value pairs into styles map. // // @param {String} stylesString Styles to parse. @@ -572,10 +519,3 @@ function parseInlineStyles( stylesString ) { function toPath( name ) { return name.replace( '-', '.' ); } - -// 'border-style' -> d{} -// 'border-top' -> d{} -// 'border' -> d{} - -// {} -> style="" -// {} -> border-top="" diff --git a/tests/view/styles.js b/tests/view/styles.js index 608e097b3..6e7ac6582 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -47,7 +47,7 @@ describe( 'Styles', () => { } ); } ); - it( 'should output inline shorthand rules', () => { + it( 'should output inline shorthand rules #1', () => { styles.setStyle( 'border:1px solid blue;' ); expect( styles.getInlineStyle() ).to.equal( 'border-color:blue;border-style:solid;border-width:1px;' ); @@ -56,7 +56,7 @@ describe( 'Styles', () => { expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); } ); - it( 'should output inline shorthand rules', () => { + it.only( 'should output inline shorthand rules #2', () => { styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styles.getInlineStyle() ).to.equal( From e0ee293c23218073bde676bbe1889da197207ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 3 Oct 2019 10:52:33 +0200 Subject: [PATCH 037/142] Refactor styles outputting. --- src/view/styles.js | 36 ++++++++++++++++++++++++------------ tests/view/styles.js | 28 ++++++++++++++-------------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index b42f05d02..08bf3fde6 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -144,7 +144,10 @@ export default class Styles { parsed.push( ...newNewGetStyleFromNormalized( key, normalized ) ); } - return parsed.map( arr => arr.join( ':' ) ).join( ';' ) + ';'; + return parsed + .filter( v => Array.isArray( v ) )// TODO: not needed? + .map( arr => arr.join( ':' ) ) + .join( ';' ) + ';'; } getInlineProperty( propertyName ) { @@ -358,19 +361,20 @@ function shBorderProperty( which ) { }; } -function getABCDEDGHIJK( { left, right, top, bottom } ) { +function getTopRightBottomLeftShorthand( { left, right, top, bottom } ) { const out = []; if ( left !== right ) { out.push( top, right, bottom, left ); } else if ( bottom !== top ) { out.push( top, right, bottom ); - } else if ( right != top ) { + } else if ( right !== top ) { out.push( top, right ); } else { out.push( top ); } - return out; + + return out.join( ' ' ); } function outputShorthandableValue( styleObject = {}, strict, styleShorthand ) { @@ -382,15 +386,11 @@ function outputShorthandableValue( styleObject = {}, strict, styleShorthand ) { return []; } - return [ - [ styleShorthand, top ] - ]; + return [ [ styleShorthand, top ] ]; } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { - return printSingleValues( { top, right, bottom, left }, 'margin' ); + return printSingleValues( { top, right, bottom, left }, styleShorthand ); } else { - const out = getABCDEDGHIJK( styleObject ); - - return `${ strict ? '' : styleShorthand + ':' }${ out.join( ' ' ) }`; + return [ [ styleShorthand, getTopRightBottomLeftShorthand( styleObject ) ] ]; } } @@ -410,6 +410,18 @@ function newNewGetStyleFromNormalized( styleName, styleObjectOrString ) { return ret; } ); + styleGetters.set( 'margin', value => { + return outputShorthandableValue( value, false, 'margin' ); + } ); + + styleGetters.set( 'background', value => { + const ret = []; + + ret.push( [ 'background-color', value.color ] ); + + return ret; + } ); + let stylesArray; if ( styleGetters.has( styleName ) ) { @@ -417,7 +429,7 @@ function newNewGetStyleFromNormalized( styleName, styleObjectOrString ) { stylesArray = styleGetter( styleObjectOrString ); } else { - // console.warn( 'no default' ); + stylesArray = [ [ styleName, styleObjectOrString ] ]; } return stylesArray || []; diff --git a/tests/view/styles.js b/tests/view/styles.js index 6e7ac6582..f9c942131 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -21,7 +21,7 @@ describe( 'Styles', () => { it( 'should output full names for known style names', () => { styles.setStyle( 'margin: 1px;margin-left: 2em;' ); - expect( styles.getStyleNames() ).to.deep.equal( [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ] ); + expect( styles.getStyleNames() ).to.deep.equal( [ 'margin' ] ); } ); } ); @@ -56,7 +56,7 @@ describe( 'Styles', () => { expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); } ); - it.only( 'should output inline shorthand rules #2', () => { + it( 'should output inline shorthand rules #2', () => { styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styles.getInlineStyle() ).to.equal( @@ -317,8 +317,8 @@ describe( 'Styles', () => { it( 'should output inline style (1 value defined)', () => { styles.setStyle( 'margin:1px;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:1px;margin-bottom:1px;margin-left:1px;' ); - // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); + expect( styles.getInlineStyle() ).to.equal( 'margin:1px;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); @@ -328,8 +328,8 @@ describe( 'Styles', () => { it( 'should output inline style (2 values defined)', () => { styles.setStyle( 'margin:1px .34cm;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:.34cm;margin-bottom:1px;margin-left:.34cm;' ); - // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); @@ -339,8 +339,8 @@ describe( 'Styles', () => { it( 'should output inline style (3 values defined)', () => { styles.setStyle( 'margin:1px .34cm 90.1rem;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:.34cm;margin-bottom:90.1rem;margin-left:.34cm;' ); - // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); @@ -350,8 +350,8 @@ describe( 'Styles', () => { it( 'should output inline style (3 values defined, only last different)', () => { styles.setStyle( 'margin:1px 1px 90.1rem;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:1px;margin-bottom:90.1rem;margin-left:1px;' ); - // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); + expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); @@ -361,8 +361,8 @@ describe( 'Styles', () => { it( 'should output inline style (4 values defined)', () => { styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:.34cm;margin-bottom:90.1rem;margin-left:thick;' ); - // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); @@ -372,8 +372,8 @@ describe( 'Styles', () => { it( 'should output inline style (4 values defined, only last different)', () => { styles.setStyle( 'margin:1px 1px 1px thick;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;margin-right:1px;margin-bottom:1px;margin-left:thick;' ); - // expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); + expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); From a380904030749e2fda033367c09a2b8de0c6f069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 3 Oct 2019 13:26:48 +0200 Subject: [PATCH 038/142] Add tests for styles class methods. --- src/view/styles.js | 53 ++++++++--------- tests/view/styles.js | 132 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 31 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 08bf3fde6..bde65af36 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -130,20 +130,12 @@ export default class Styles { } getInlineStyle() { - const parsed = []; - - const keys = Object.keys( this._styles ).sort(); + const parsed = this._toFFFFFFFFFFStylesMap(); - if ( !keys.length ) { + if ( !parsed.length ) { return; } - for ( const key of keys ) { - const normalized = this.getNormalized( key ); - - parsed.push( ...newNewGetStyleFromNormalized( key, normalized ) ); - } - return parsed .filter( v => Array.isArray( v ) )// TODO: not needed? .map( arr => arr.join( ':' ) ) @@ -159,12 +151,11 @@ export default class Styles { } if ( isObject( normalized ) ) { - const ret = toInlineStyleProperty( propertyName, normalized ); + const propertyDescriptor = toInlineStyleProperty( propertyName, normalized ); - if ( Array.isArray( ret ) ) { - return ret[ 1 ]; - } else { - return ''; + // Only return a value if it is set; + if ( Array.isArray( propertyDescriptor ) ) { + return propertyDescriptor[ 1 ]; } } // String value @@ -174,19 +165,29 @@ export default class Styles { } getStyleNames() { - const inlineStyle = this.getInlineStyle(); + const parsed = this._toFFFFFFFFFFStylesMap(); - return ( inlineStyle || '' ) - .split( ';' ) - .filter( f => f !== '' ) - .map( abc => abc.split( ':' )[ 0 ] ) - .sort( sortTopRightBottomLeftProperties ); + return parsed.map( ( [ key ] ) => key ); } clear() { this._styles = {}; } + _toFFFFFFFFFFStylesMap() { + const parsed = []; + + const keys = Object.keys( this._styles ).sort(); + + for ( const key of keys ) { + const normalized = this.getNormalized( key ); + + parsed.push( ...newNewGetStyleFromNormalized( key, normalized ) ); + } + + return parsed; + } + _appendStyleValue( nameOrPath, valueOrObject ) { if ( typeof valueOrObject === 'object' ) { if ( nameOrPath.includes( '.' ) ) { @@ -441,16 +442,6 @@ function toInlineStyleProperty( styleName, styleObjectOrString ) { return styles.find( ( [ property ] ) => property === styleName ); } -const topRightBottomLeftOrder = [ 'top', 'right', 'bottom', 'left' ]; - -function sortTopRightBottomLeftProperties( a, b ) { - if ( topRightBottomLeftOrder.includes( a ) && topRightBottomLeftOrder.includes( b ) ) { - return topRightBottomLeftOrder.indexOf( a ) - topRightBottomLeftOrder.indexOf( b ); - } - - return 0; -} - // Parses inline styles and puts property - value pairs into styles map. // // @param {String} stylesString Styles to parse. diff --git a/tests/view/styles.js b/tests/view/styles.js index f9c942131..765611c9b 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -11,7 +11,139 @@ describe( 'Styles', () => { styles = new StyleProxy(); } ); + describe( 'size getter', () => { + it( 'should return 0 if no styles are set', () => { + expect( styles.size ).to.equal( 0 ); + } ); + + it( 'should return number of set styles', () => { + styles.setStyle( 'color:blue' ); + expect( styles.size ).to.equal( 1 ); + + styles.setStyle( 'margin:1px;' ); + expect( styles.size ).to.equal( 1 ); + + styles.setStyle( 'margin-top:1px;margin-bottom:1px;' ); + expect( styles.size ).to.equal( 2 ); + } ); + } ); + + describe( 'setStyle()', () => { + it( 'should reset styles to a new value', () => { + styles.setStyle( 'color:red;margin-top:1px;' ); + + expect( styles.getNormalized() ).to.deep.equal( { color: 'red', margin: { top: '1px' } } ); + + styles.setStyle( 'margin-bottom:2em;' ); + + expect( styles.getNormalized() ).to.deep.equal( { margin: { bottom: '2em' } } ); + } ); + } ); + + describe( 'getInlineStyle()', () => { + it( 'should return undefined for empty styles', () => { + expect( styles.getInlineStyle() ).to.be.undefined; + } ); + + it( 'should return sorted styles string if styles are set', () => { + styles.setStyle( 'margin-top:1px;color:blue;' ); + + expect( styles.getInlineStyle() ).to.equal( 'color:blue;margin-top:1px;' ); + } ); + } ); + + describe( 'getInlineProperty', () => { + it( 'should return empty string for missing shorthand', () => { + styles.setStyle( 'margin-top:1px' ); + + expect( styles.getInlineProperty( 'margin' ) ).to.be.undefined; + } ); + } ); + + describe( 'hasProperty()', () => { + it( 'should return false if property is not set', () => { + expect( styles.hasProperty( 'foo' ) ).to.be.false; + } ); + + it( 'should return false if normalized property is not set', () => { + styles.setStyle( 'margin-top:1px' ); + + // TODO + // expect( styles.hasProperty( 'margin' ) ).to.be.false; + expect( styles.hasProperty( 'margin' ) ).to.be.true; + } ); + + it( 'should return true if property is set', () => { + styles.setStyle( 'color:deeppink' ); + + expect( styles.hasProperty( 'color' ) ).to.be.true; + } ); + + it( 'should return true if normalized shorthanded property is set', () => { + styles.setStyle( 'margin:1px 2px 3px 4px' ); + + expect( styles.hasProperty( 'margin-top' ) ).to.be.true; + } ); + } ); + + describe( 'insertProperty()', () => { + it( 'should insert new property (empty styles)', () => { + styles.insertProperty( 'color', 'blue' ); + + expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + } ); + + it( 'should insert new property (other properties are set)', () => { + styles.setStyle( 'margin: 1px;' ); + styles.insertProperty( 'color', 'blue' ); + + expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + } ); + + it( 'should overwrite property', () => { + styles.setStyle( 'color: red;' ); + styles.insertProperty( 'color', 'blue' ); + + expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + } ); + + it( 'should work with objects', () => { + styles.setStyle( 'color: red;' ); + styles.insertProperty( { color: 'blue', margin: '1px' } ); + + expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + } ); + } ); + + describe( 'removeProperty()', () => { + it( 'should do nothing if property is not set', () => { + styles.removeProperty( 'color' ); + + expect( styles.getInlineProperty( 'color' ) ).to.be.undefined; + } ); + + it( 'should insert new property (other properties are set)', () => { + styles.setStyle( 'color:blue' ); + styles.removeProperty( 'color' ); + + expect( styles.getInlineProperty( 'color' ) ).to.be.undefined; + } ); + + it( 'should remove normalized property', () => { + styles.setStyle( 'margin:1px' ); + + styles.removeProperty( 'margin-top' ); + + expect( styles.getInlineProperty( 'margin-top' ) ).to.be.undefined; + } ); + } ); + describe( 'getStyleNames()', () => { + it( 'should output empty array for empty styles', () => { + expect( styles.getStyleNames() ).to.deep.equal( [] ); + } ); + it( 'should output custom style names', () => { styles.setStyle( 'foo: 2;bar: baz;foo-bar-baz:none;' ); From 50df1cb734cfc4e40b2cebdcc4e33319d9dc4e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 3 Oct 2019 16:23:34 +0200 Subject: [PATCH 039/142] Add more tests to the styles class. --- src/view/styles.js | 118 +++++++++++++++++++++++++++++++++--------- tests/view/element.js | 64 ----------------------- tests/view/styles.js | 62 +++++++++++++++++++++- 3 files changed, 155 insertions(+), 89 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index bde65af36..4d2e8f830 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -91,9 +91,21 @@ export default class Styles { } /** - * Inserts single style rule. + * Inserts single style property. * - * Supports shorthands. + * Can insert one by one + * + * styles.insertProperty( 'color', 'blue' ); + * styles.insertProperty( 'margin-right', '1em' ); + * + * or many styles at once: + * + * styles.insertProperty( { + * color: 'blue', + * 'margin-right': '1em' + * } ); + * + * Supports shorthands.y * * @param {String|Object} nameOrObject * @param {String|Object} value @@ -109,12 +121,34 @@ export default class Styles { } } + /** + * Removes styles property. + * + * @param name + */ removeProperty( name ) { unset( this._styles, toPath( name ) ); - delete this._styles[ name ]; } + /** + * Return normalized style object; + * + * const styles = new Styles(); + * styles.setStyle( 'margin:1px 2px 3em;' ); + * + * console.log( styles.getNormalized( 'margin' ) ); + * // will log: + * // { + * // top: '1px', + * // right: '2px', + * // bottom: '3em', + * // left: '2px' + * // } + * + * @param {String} name + * @returns {Object|undefined} + */ getNormalized( name ) { if ( !name ) { return merge( {}, this._styles ); @@ -129,24 +163,33 @@ export default class Styles { } } + /** + * Returns a string containing normalized styles string or undefined if no style properties are set. + * + * @returns {String|undefined} + */ getInlineStyle() { - const parsed = this._toFFFFFFFFFFStylesMap(); + const entries = this._getStylesEntries(); - if ( !parsed.length ) { + // Return undefined for empty styles map. + if ( !entries.length ) { return; } - return parsed - .filter( v => Array.isArray( v ) )// TODO: not needed? - .map( arr => arr.join( ':' ) ) - .join( ';' ) + ';'; + return entries.map( arr => arr.join( ':' ) ).join( ';' ) + ';'; } + /** + * Returns property value string. + * + * @param {String} propertyName + * @returns {String|undefined} + */ getInlineProperty( propertyName ) { const normalized = this.getNormalized( propertyName ); if ( !normalized ) { - // Try return directly + // Try return styles set directly - values that are not parsed. return this._styles[ propertyName ]; } @@ -157,24 +200,36 @@ export default class Styles { if ( Array.isArray( propertyDescriptor ) ) { return propertyDescriptor[ 1 ]; } - } - // String value - else { + } else { return normalized; } } + /** + * Returns style properties names as the would appear when using {@link #getInlineStyle()} + * + * @returns {Array.} + */ getStyleNames() { - const parsed = this._toFFFFFFFFFFStylesMap(); + const entries = this._getStylesEntries(); - return parsed.map( ( [ key ] ) => key ); + return entries.map( ( [ key ] ) => key ); } + /** + * Removes all styles. + */ clear() { this._styles = {}; } - _toFFFFFFFFFFStylesMap() { + /** + * Returns normalized styles entries for further processing. + * + * @private + * @returns {Array.> ]} + */ + _getStylesEntries() { const parsed = []; const keys = Object.keys( this._styles ).sort(); @@ -188,10 +243,18 @@ export default class Styles { return parsed; } + /** + * Appends style definition to the internal styles object. + * + * @param {String} nameOrPath + * @param {String|Object} valueOrObject + * @private + */ _appendStyleValue( nameOrPath, valueOrObject ) { - if ( typeof valueOrObject === 'object' ) { + if ( isObject( valueOrObject ) ) { if ( nameOrPath.includes( '.' ) ) { const got = get( this._styles, nameOrPath ); + set( this._styles, nameOrPath, merge( {}, got, valueOrObject ) ); } else { this._styles[ nameOrPath ] = merge( {}, this._styles[ nameOrPath ], valueOrObject ); @@ -201,26 +264,33 @@ export default class Styles { } } - _parseProperty( key, value ) { + /** + * Parses single style property. + * + * @param {String} name Name of style property. + * @param {String} value Value of style property. + * @private + */ + _parseProperty( name, value ) { if ( isPlainObject( value ) ) { - this._appendStyleValue( toPath( key ), value ); + this._appendStyleValue( toPath( name ), value ); return; } // Set directly to an object. - if ( setOnPathStyles.includes( key ) ) { - this._appendStyleValue( toPath( key ), value ); + if ( setOnPathStyles.includes( name ) ) { + this._appendStyleValue( toPath( name ), value ); return; } - if ( this.parsers.has( key ) ) { - const parser = this.parsers.get( key ); + if ( this.parsers.has( name ) ) { + const parser = this.parsers.get( name ); this._styles = merge( {}, this._styles, parser( value ) ); } else { - this._appendStyleValue( key, value ); + this._appendStyleValue( name, value ); } } } diff --git a/tests/view/element.js b/tests/view/element.js index ccb0e55f1..87b90b5cb 100644 --- a/tests/view/element.js +++ b/tests/view/element.js @@ -9,8 +9,6 @@ import Element from '../../src/view/element'; import Text from '../../src/view/text'; import TextProxy from '../../src/view/textproxy'; -import encodedImage from './_utils/encodedimage.txt'; - describe( 'Element', () => { describe( 'constructor()', () => { it( 'should create element without attributes', () => { @@ -872,68 +870,6 @@ describe( 'Element', () => { expect( el.hasStyle( 'color' ) ).to.be.true; } ); } ); - - describe( 'styles parsing edge cases and incorrect styles', () => { - it( 'should not crash and not add any styles if styles attribute was empty', () => { - const element = new Element( 'div', { style: '' } ); - const styles = Array.from( element.getStyleNames() ); - - expect( styles ).to.deep.equal( [] ); - } ); - - it( 'should be able to parse big styles definition', () => { - expect( () => { - // eslint-disable-next-line no-new - new Element( 'div', { style: `background-image:url('data:image/jpeg;base64,${ encodedImage }')` } ); - } ).not.to.throw(); - } ); - - it( 'should work with both types of quotes and ignore values inside quotes', () => { - let element; - - element = new Element( 'div', { style: 'background-image:url("im;color:g.jpg")' } ); - expect( element.getStyle( 'background-image' ) ).to.equal( 'url("im;color:g.jpg")' ); - - element = new Element( 'div', { style: 'background-image:url(\'im;color:g.jpg\')' } ); - expect( element.getStyle( 'background-image' ) ).to.equal( 'url(\'im;color:g.jpg\')' ); - } ); - - it( 'should not be confused by whitespaces', () => { - const element = new Element( 'div', { style: '\ncolor:\n red ' } ); - - expect( element.getStyle( 'color' ) ).to.equal( 'red' ); - } ); - - it( 'should not be confused by duplicated semicolon', () => { - const element = new Element( 'div', { style: 'color: red;; display: inline' } ); - - expect( element.getStyle( 'color' ) ).to.equal( 'red' ); - expect( element.getStyle( 'display' ) ).to.equal( 'inline' ); - } ); - - it( 'should not throw when value is missing', () => { - const element = new Element( 'div', { style: 'color:; display: inline' } ); - - expect( element.getStyle( 'color' ) ).to.equal( '' ); - expect( element.getStyle( 'display' ) ).to.equal( 'inline' ); - } ); - - it( 'should not throw when colon is duplicated', () => { - const element = new Element( 'div', { style: 'color:: red; display: inline' } ); - - // The result makes no sense, but here we just check that the algorithm doesn't crash. - expect( element.getStyle( 'color' ) ).to.equal( ': red' ); - expect( element.getStyle( 'display' ) ).to.equal( 'inline' ); - } ); - - it( 'should not throw when random stuff passed', () => { - const element = new Element( 'div', { style: 'color: red;:; ;;" ": display: inline; \'aaa;:' } ); - - // The result makes no sense, but here we just check that the algorithm doesn't crash. - expect( element.getStyle( 'color' ) ).to.equal( 'red' ); - expect( element.getStyle( 'display' ) ).to.be.undefined; - } ); - } ); } ); describe( 'findAncestor', () => { diff --git a/tests/view/styles.js b/tests/view/styles.js index 765611c9b..19395242f 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -2,7 +2,9 @@ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ + import StyleProxy from '../../src/view/styles'; +import encodedImage from './_utils/encodedimage.txt'; describe( 'Styles', () => { let styles; @@ -38,6 +40,64 @@ describe( 'Styles', () => { expect( styles.getNormalized() ).to.deep.equal( { margin: { bottom: '2em' } } ); } ); + + describe( 'styles parsing edge cases and incorrect styles', () => { + it( 'should not crash and not add any styles if styles attribute was empty', () => { + styles.setStyle( '' ); + + expect( styles.getStyleNames() ).to.deep.equal( [] ); + } ); + + it( 'should be able to parse big styles definition', () => { + expect( () => { + styles.setStyle( `background-image:url('data:image/jpeg;base64,${ encodedImage }')` ); + } ).not.to.throw(); + } ); + + it( 'should work with both types of quotes and ignore values inside quotes', () => { + styles.setStyle( 'background-image:url("im;color:g.jpg")' ); + expect( styles.getInlineProperty( 'background-image' ) ).to.equal( 'url("im;color:g.jpg")' ); + + styles.setStyle( 'background-image:url(\'im;color:g.jpg\')' ); + expect( styles.getInlineProperty( 'background-image' ) ).to.equal( 'url(\'im;color:g.jpg\')' ); + } ); + + it( 'should not be confused by whitespaces', () => { + styles.setStyle( '\ncolor:\n red ' ); + + expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); + } ); + + it( 'should not be confused by duplicated semicolon', () => { + styles.setStyle( 'color: red;; display: inline' ); + + expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); + expect( styles.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + } ); + + it( 'should not throw when value is missing', () => { + styles.setStyle( 'color:; display: inline' ); + + expect( styles.getInlineProperty( 'color' ) ).to.equal( '' ); + expect( styles.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + } ); + + it( 'should not throw when colon is duplicated', () => { + styles.setStyle( 'color:: red; display: inline' ); + + // The result makes no sense, but here we just check that the algorithm doesn't crash. + expect( styles.getInlineProperty( 'color' ) ).to.equal( ': red' ); + expect( styles.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + } ); + + it( 'should not throw when random stuff passed', () => { + styles.setStyle( 'color: red;:; ;;" ": display: inline; \'aaa;:' ); + + // The result makes no sense, but here we just check that the algorithm doesn't crash. + expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); + expect( styles.getInlineProperty( 'display' ) ).to.be.undefined; + } ); + } ); } ); describe( 'getInlineStyle()', () => { @@ -107,7 +167,7 @@ describe( 'Styles', () => { expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); - it( 'should work with objects', () => { + it( 'should set multiple styles by providing an object', () => { styles.setStyle( 'color: red;' ); styles.insertProperty( { color: 'blue', margin: '1px' } ); From edf5e6b17bc6d4cd1f6a3b96dfb0573e7929a588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 3 Oct 2019 18:39:53 +0200 Subject: [PATCH 040/142] Clarify naming of internal style normalization mechanisms. --- src/view/styles.js | 315 +++++++++++++++++++++++++++------------------ 1 file changed, 190 insertions(+), 125 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 4d2e8f830..c2e3d13fa 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -7,7 +7,7 @@ * @module engine/view/styles */ -import { get, has, isObject, isPlainObject, merge, set, unset } from 'lodash-es'; +import { get, has, isObject, merge, set, unset } from 'lodash-es'; const setOnPathStyles = [ // Margin & padding. @@ -29,23 +29,113 @@ export default class Styles { * @param {String} styleString Initial styles value. */ constructor( styleString = '' ) { + /** + * @type {{}} + * @private + */ this._styles = {}; - this.parsers = new Map(); - - this.parsers.set( 'border', parseBorder ); - this.parsers.set( 'border-top', parseBorderSide( 'top' ) ); - this.parsers.set( 'border-right', parseBorderSide( 'right' ) ); - this.parsers.set( 'border-bottom', parseBorderSide( 'bottom' ) ); - this.parsers.set( 'border-left', parseBorderSide( 'left' ) ); - this.parsers.set( 'border-color', parseBorderProperty( 'color' ) ); - this.parsers.set( 'border-width', parseBorderProperty( 'width' ) ); - this.parsers.set( 'border-style', parseBorderProperty( 'style' ) ); - - this.parsers.set( 'background', parseBackground ); - - this.parsers.set( 'margin', parseShorthandSides( 'margin' ) ); - this.parsers.set( 'padding', parseShorthandSides( 'padding' ) ); + /** + * Holds shorthand properties normalizers. + * + * Shorthand properties must be normalized as they can be written in various ways. + * Normalizer must return object describing given shorthand. + * + * Example: + * The `border-color` style is a shorthand property for `border-top-color`, `border-right-color`, `border-bottom-color` + * and `border-left-color`. Similarly there are shorthand for border width (`border-width`) and style (`border-style`). + * + * For `border-color` the given shorthand: + * + * border-color: #f00 #ba7; + * + * might be written as: + * + * border-color-top: #f00; + * border-color-right: #ba7; + * border-color-bottom: #f00; + * border-color-left: #ba7; + * + * Normalizers produces coherent object representation for both shorthand and longhand forms: + * + * const style = { + * border: { + * color: { + * top: '#f00', + * right: '#ba7', + * bottom: '#f00', + * left: '#ba7' + * } + * } + * } + * + * @type {Map} + */ + this.normalizers = new Map(); + + this.normalizers.set( 'border', normalizeBorder ); + this.normalizers.set( 'border-top', getBorderPositionNormalizer( 'top' ) ); + this.normalizers.set( 'border-right', getBorderPositionNormalizer( 'right' ) ); + this.normalizers.set( 'border-bottom', getBorderPositionNormalizer( 'bottom' ) ); + this.normalizers.set( 'border-left', getBorderPositionNormalizer( 'left' ) ); + this.normalizers.set( 'border-color', getBorderPropertyNormalizer( 'color' ) ); + this.normalizers.set( 'border-width', getBorderPropertyNormalizer( 'width' ) ); + this.normalizers.set( 'border-style', getBorderPropertyNormalizer( 'style' ) ); + + this.normalizers.set( 'background', normalizeBackground ); + + this.normalizers.set( 'margin', getPositionShorthandNormalizer( 'margin' ) ); + this.normalizers.set( 'padding', getPositionShorthandNormalizer( 'padding' ) ); + + /** + * Holds style normalize object reducrs. + * + * An style inliner takes normalized object of style property and outputs array of normalized property-value pairs that can + * be later used to inline a style. + * + * Those work in opposite direction to {@link #normalizers} and always outputs style in the same way. + * + * If normalized style is represented as: + * + * const style = { + * border: { + * color: { + * top: '#f00', + * right: '#ba7', + * bottom: '#f00', + * left: '#ba7' + * } + * } + * } + * + * The border reducer will output: + * + * const reduced = [ + * [ 'border-color', '#f00 #ba7' ] + * ]; + * + * which can be used to return the inline style string: + * + * style="border-color:#f00 #ba7;" + * + * @type {Map} + */ + this.reducers = new Map(); + + this.reducers.set( 'border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); + this.reducers.set( 'border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); + this.reducers.set( 'border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); + this.reducers.set( 'border', getBorderReducer ); + + this.reducers.set( 'margin', getTopRightBottomLeftValueReducer( 'margin' ) ); + + this.reducers.set( 'background', value => { + const ret = []; + + ret.push( [ 'background-color', value.color ] ); + + return ret; + } ); this.setStyle( styleString ); } @@ -72,7 +162,7 @@ export default class Styles { for ( const key of map.keys() ) { const value = map.get( key ); - this._parseProperty( key, value ); + this._getNormalizedForm( key, value ); } } @@ -112,12 +202,12 @@ export default class Styles { * @returns {Boolean} */ insertProperty( nameOrObject, value ) { - if ( isPlainObject( nameOrObject ) ) { + if ( isObject( nameOrObject ) ) { for ( const key of Object.keys( nameOrObject ) ) { this.insertProperty( key, nameOrObject[ key ] ); } } else { - this._parseProperty( nameOrObject, value ); + this._getNormalizedForm( nameOrObject, value ); } } @@ -194,7 +284,9 @@ export default class Styles { } if ( isObject( normalized ) ) { - const propertyDescriptor = toInlineStyleProperty( propertyName, normalized ); + const styles = this._getReduceForm( propertyName, normalized ); + + const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); // Only return a value if it is set; if ( Array.isArray( propertyDescriptor ) ) { @@ -237,7 +329,7 @@ export default class Styles { for ( const key of keys ) { const normalized = this.getNormalized( key ); - parsed.push( ...newNewGetStyleFromNormalized( key, normalized ) ); + parsed.push( ...this._getReduceForm( key, normalized ) ); } return parsed; @@ -265,34 +357,56 @@ export default class Styles { } /** - * Parses single style property. + * Parse style property value to a normalized form. * - * @param {String} name Name of style property. + * @param {String} propertyName Name of style property. * @param {String} value Value of style property. * @private */ - _parseProperty( name, value ) { - if ( isPlainObject( value ) ) { - this._appendStyleValue( toPath( name ), value ); + _getNormalizedForm( propertyName, value ) { + if ( isObject( value ) ) { + this._appendStyleValue( toPath( propertyName ), value ); return; } // Set directly to an object. - if ( setOnPathStyles.includes( name ) ) { - this._appendStyleValue( toPath( name ), value ); + if ( setOnPathStyles.includes( propertyName ) ) { + this._appendStyleValue( toPath( propertyName ), value ); return; } - if ( this.parsers.has( name ) ) { - const parser = this.parsers.get( name ); + if ( this.normalizers.has( propertyName ) ) { + const parser = this.normalizers.get( propertyName ); this._styles = merge( {}, this._styles, parser( value ) ); } else { - this._appendStyleValue( name, value ); + this._appendStyleValue( propertyName, value ); } } + + /** + * Returns reduced form of style property form normalized object. + * + * @private + * @param {String} styleName + * @param {Object|String} normalizedValue + * @returns {Array.>} + */ + _getReduceForm( styleName, normalizedValue ) { + let stylesArray; + + if ( this.reducers.has( styleName ) ) { + const styleGetter = this.reducers.get( styleName ); + + stylesArray = styleGetter( normalizedValue ); + } else { + stylesArray = [ [ styleName, normalizedValue ] ]; + } + + return stylesArray || []; + } } function getTopRightBottomLeftValues( value = '' ) { @@ -312,14 +426,14 @@ function toBorderPropertyShorthand( value, property ) { }; } -function parseShorthandSides( longhand ) { +function getPositionShorthandNormalizer( longhand ) { return value => { return { [ longhand ]: getTopRightBottomLeftValues( value ) }; }; } -function parseBorder( value ) { - const { color, style, width } = parseShorthandBorderAttribute( value ); +function normalizeBorder( value ) { + const { color, style, width } = normalizeBorderShorthand( value ); return { border: { @@ -330,9 +444,9 @@ function parseBorder( value ) { }; } -function parseBorderSide( side ) { +function getBorderPositionNormalizer( side ) { return value => { - const { color, style, width } = parseShorthandBorderAttribute( value ); + const { color, style, width } = normalizeBorderShorthand( value ); const border = {}; @@ -352,13 +466,11 @@ function parseBorderSide( side ) { }; } -function parseBorderProperty( foo ) { - return value => ( { - border: toBorderPropertyShorthand( value, foo ) - } ); +function getBorderPropertyNormalizer( propertyName ) { + return value => ( { border: toBorderPropertyShorthand( value, propertyName ) } ); } -function parseShorthandBorderAttribute( string ) { +function normalizeBorderShorthand( string ) { const result = {}; for ( const part of string.split( ' ' ) ) { @@ -378,7 +490,7 @@ function parseShorthandBorderAttribute( string ) { return result; } -function parseBackground( value ) { +function normalizeBackground( value ) { const background = {}; const parts = value.split( ' ' ); @@ -404,35 +516,52 @@ function isLength( string ) { return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); } -function printSingleValues( { top, right, bottom, left }, prefix ) { +function getBorderReducer( value ) { const ret = []; - if ( top ) { - ret.push( [ prefix + '-top', top ] ); - } - - if ( right ) { - ret.push( [ prefix + '-right', right ] ); - } - - if ( bottom ) { - ret.push( [ prefix + '-bottom', bottom ] ); - } - - if ( left ) { - ret.push( [ prefix + '-left', left ] ); - } + ret.push( ...getTopRightBottomLeftValueReducer( 'border-color' )( value.color ) ); + ret.push( ...getTopRightBottomLeftValueReducer( 'border-style' )( value.style ) ); + ret.push( ...getTopRightBottomLeftValueReducer( 'border-width' )( value.width ) ); return ret; } -function shBorderProperty( which ) { +function getTopRightBottomLeftValueReducer( styleShorthand ) { return value => { - return outputShorthandableValue( value, false, `border-${ which }` ); + const { top, right, bottom, left } = ( value || {} ); + + const reduced = []; + + if ( top === left && left === bottom && bottom === right ) { + // Might be not set. + if ( top !== undefined ) { + return [ [ styleShorthand, top ] ]; + } + } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { + if ( top ) { + reduced.push( [ styleShorthand + '-top', top ] ); + } + + if ( right ) { + reduced.push( [ styleShorthand + '-right', right ] ); + } + + if ( bottom ) { + reduced.push( [ styleShorthand + '-bottom', bottom ] ); + } + + if ( left ) { + reduced.push( [ styleShorthand + '-left', left ] ); + } + } else { + reduced.push( [ styleShorthand, getTopRightBottomLeftShorthandValue( value ) ] ); + } + + return reduced; }; } -function getTopRightBottomLeftShorthand( { left, right, top, bottom } ) { +function getTopRightBottomLeftShorthandValue( { left, right, top, bottom } ) { const out = []; if ( left !== right ) { @@ -448,70 +577,6 @@ function getTopRightBottomLeftShorthand( { left, right, top, bottom } ) { return out.join( ' ' ); } -function outputShorthandableValue( styleObject = {}, strict, styleShorthand ) { - const { top, right, bottom, left } = styleObject; - - if ( top === left && left === bottom && bottom === right ) { - // Might be not set. - if ( top === undefined ) { - return []; - } - - return [ [ styleShorthand, top ] ]; - } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { - return printSingleValues( { top, right, bottom, left }, styleShorthand ); - } else { - return [ [ styleShorthand, getTopRightBottomLeftShorthand( styleObject ) ] ]; - } -} - -function newNewGetStyleFromNormalized( styleName, styleObjectOrString ) { - const styleGetters = new Map(); - - styleGetters.set( 'border-color', shBorderProperty( 'color' ) ); - styleGetters.set( 'border-style', shBorderProperty( 'style' ) ); - styleGetters.set( 'border-width', shBorderProperty( 'width' ) ); - styleGetters.set( 'border', value => { - const ret = []; - - ret.push( ...shBorderProperty( 'color' )( value.color ) ); - ret.push( ...shBorderProperty( 'style' )( value.style ) ); - ret.push( ...shBorderProperty( 'width' )( value.width ) ); - - return ret; - } ); - - styleGetters.set( 'margin', value => { - return outputShorthandableValue( value, false, 'margin' ); - } ); - - styleGetters.set( 'background', value => { - const ret = []; - - ret.push( [ 'background-color', value.color ] ); - - return ret; - } ); - - let stylesArray; - - if ( styleGetters.has( styleName ) ) { - const styleGetter = styleGetters.get( styleName ); - - stylesArray = styleGetter( styleObjectOrString ); - } else { - stylesArray = [ [ styleName, styleObjectOrString ] ]; - } - - return stylesArray || []; -} - -function toInlineStyleProperty( styleName, styleObjectOrString ) { - const styles = newNewGetStyleFromNormalized( styleName, styleObjectOrString ); - - return styles.find( ( [ property ] ) => property === styleName ); -} - // Parses inline styles and puts property - value pairs into styles map. // // @param {String} stylesString Styles to parse. From 4acd3031e232c90752f2ce2ec21e57986dd1bf82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 7 Oct 2019 13:37:30 +0200 Subject: [PATCH 041/142] Remove unused code. --- src/view/styles.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index c2e3d13fa..50def9e41 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -532,12 +532,7 @@ function getTopRightBottomLeftValueReducer( styleShorthand ) { const reduced = []; - if ( top === left && left === bottom && bottom === right ) { - // Might be not set. - if ( top !== undefined ) { - return [ [ styleShorthand, top ] ]; - } - } else if ( ![ top, right, left, bottom ].every( value => !!value ) ) { + if ( ![ top, right, left, bottom ].every( value => !!value ) ) { if ( top ) { reduced.push( [ styleShorthand + '-top', top ] ); } From 76edc1c8d4b23518c6a3df08b81b3330ed316a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 7 Oct 2019 13:37:43 +0200 Subject: [PATCH 042/142] Add tests for background color. --- tests/view/styles.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/view/styles.js b/tests/view/styles.js index 19395242f..18ebbce49 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -673,5 +673,26 @@ describe( 'Styles', () => { expect( styles.getInlineProperty( 'baz' ) ).to.equal( '2px 3em' ); } ); } ); + + describe( 'background', () => { + it( 'should normalize background', () => { + styles.setStyle( 'background:#f00;' ); + + expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); + } ); + + it( 'should normalize background-color', () => { + styles.setStyle( 'background-color:#f00;' ); + + expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); + } ); + + it( 'should output inline background-color style', () => { + styles.setStyle( 'background:#f00;' ); + + expect( styles.getInlineStyle() ).to.equal( 'background-color:#f00;' ); + expect( styles.getInlineProperty( 'background-color' ) ).to.equal( '#f00' ); + } ); + } ); } ); } ); From d95959bc1360920d63ef5e3cd5fd6d24a030507b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 7 Oct 2019 14:41:04 +0200 Subject: [PATCH 043/142] Add more background shorthand values parsing. --- src/view/styles.js | 36 +++++++++++++++++++++++++++++------- tests/view/styles.js | 16 +++++++++++++++- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 50def9e41..3ed5e0332 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -395,17 +395,13 @@ export default class Styles { * @returns {Array.>} */ _getReduceForm( styleName, normalizedValue ) { - let stylesArray; - if ( this.reducers.has( styleName ) ) { const styleGetter = this.reducers.get( styleName ); - stylesArray = styleGetter( normalizedValue ); - } else { - stylesArray = [ [ styleName, normalizedValue ] ]; + return styleGetter( normalizedValue ); } - return stylesArray || []; + return [ [ styleName, normalizedValue ] ]; } } @@ -496,8 +492,18 @@ function normalizeBackground( value ) { const parts = value.split( ' ' ); for ( const part of parts ) { - if ( isColor( part ) ) { + if ( isRepeat( part ) ) { + background.repeat = background.repeat || []; + background.repeat.push( part ); + } else if ( isPosition( part ) ) { + background.position = background.position || []; + background.position.push( part ); + } else if ( isAttachment( part ) ) { + background.attachment = part; + } else if ( isColor( part ) ) { background.color = part; + } else if ( isURL( part ) ) { + background.image = part; } } @@ -516,6 +522,22 @@ function isLength( string ) { return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); } +function isRepeat( string ) { + return /^(repeat-x|repeat-y|repeat|space|round|no-repeat)$/.test( string ); +} + +function isPosition( string ) { + return /^(center|top|bottom|left|right)$/.test( string ); +} + +function isAttachment( string ) { + return /^(fixed|scroll|local)$/.test( string ); +} + +function isURL( string ) { + return /^url\(/.test( string ); +} + function getBorderReducer( value ) { const ret = []; diff --git a/tests/view/styles.js b/tests/view/styles.js index 18ebbce49..eb14b5d57 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -676,7 +676,21 @@ describe( 'Styles', () => { describe( 'background', () => { it( 'should normalize background', () => { - styles.setStyle( 'background:#f00;' ); + // TODO: border-box given only for coverage test. + styles.setStyle( 'background:url("example.jpg") center #f00 repeat-y fixed border-box;' ); + + expect( styles.getNormalized( 'background' ) ).to.deep.equal( { + attachment: 'fixed', + image: 'url("example.jpg")', + position: [ 'center' ], + repeat: [ 'repeat-y' ], + color: '#f00' + } ); + } ); + + // TODO: define what should happen with layers + it.skip( 'should normalize background with layers', () => { + styles.setStyle( 'background:url("test.jpg") repeat-y,#f00;' ); expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); } ); From 34551039c552b6c7767f56d149d84e2c30d8d26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 7 Oct 2019 14:46:39 +0200 Subject: [PATCH 044/142] Add more tests for top-right-bottom-left shorthands. --- tests/view/styles.js | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/view/styles.js b/tests/view/styles.js index eb14b5d57..ae760357c 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -580,7 +580,7 @@ describe( 'Styles', () => { expect( styles.getNormalized( 'margin-top' ) ).to.equal( '1px' ); } ); - it( 'should set proper margin with margin shorthand', () => { + it( 'should merge margin with margin shorthand', () => { styles.setStyle( 'margin: 2em;margin-top:1px;' ); expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { @@ -594,6 +594,34 @@ describe( 'Styles', () => { expect( styles.getNormalized( 'margin-bottom' ) ).to.equal( '2em' ); expect( styles.getNormalized( 'margin-left' ) ).to.equal( '2em' ); } ); + + it( 'should output margin-top', () => { + styles.setStyle( 'margin-top:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + } ); + + it( 'should output margin-right', () => { + styles.setStyle( 'margin-right:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin-right:1px;' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + } ); + + it( 'should output margin-bottom', () => { + styles.setStyle( 'margin-bottom:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin-bottom:1px;' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + } ); + + it( 'should output margin-left', () => { + styles.setStyle( 'margin-left:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin-left:1px;' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); + } ); } ); } ); From 15de90ceb3c5d5884a246f6602283a4cdd9ae0ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 7 Oct 2019 15:50:32 +0200 Subject: [PATCH 045/142] Simplify code. --- src/view/styles.js | 50 +++++++++++++++++++------------------------- tests/view/styles.js | 8 +++++++ 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 3ed5e0332..5f6ef463b 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -162,7 +162,7 @@ export default class Styles { for ( const key of map.keys() ) { const value = map.get( key ); - this._getNormalizedForm( key, value ); + this._toNormalizedForm( key, value ); } } @@ -207,7 +207,7 @@ export default class Styles { this.insertProperty( key, nameOrObject[ key ] ); } } else { - this._getNormalizedForm( nameOrObject, value ); + this._toNormalizedForm( nameOrObject, value ); } } @@ -335,27 +335,6 @@ export default class Styles { return parsed; } - /** - * Appends style definition to the internal styles object. - * - * @param {String} nameOrPath - * @param {String|Object} valueOrObject - * @private - */ - _appendStyleValue( nameOrPath, valueOrObject ) { - if ( isObject( valueOrObject ) ) { - if ( nameOrPath.includes( '.' ) ) { - const got = get( this._styles, nameOrPath ); - - set( this._styles, nameOrPath, merge( {}, got, valueOrObject ) ); - } else { - this._styles[ nameOrPath ] = merge( {}, this._styles[ nameOrPath ], valueOrObject ); - } - } else { - set( this._styles, nameOrPath, valueOrObject ); - } - } - /** * Parse style property value to a normalized form. * @@ -363,16 +342,15 @@ export default class Styles { * @param {String} value Value of style property. * @private */ - _getNormalizedForm( propertyName, value ) { + _toNormalizedForm( propertyName, value ) { if ( isObject( value ) ) { - this._appendStyleValue( toPath( propertyName ), value ); - + appendStyleValue( this._styles, toPath( propertyName ), value ); return; } // Set directly to an object. if ( setOnPathStyles.includes( propertyName ) ) { - this._appendStyleValue( toPath( propertyName ), value ); + appendStyleValue( this._styles, toPath( propertyName ), value ); return; } @@ -380,9 +358,10 @@ export default class Styles { if ( this.normalizers.has( propertyName ) ) { const parser = this.normalizers.get( propertyName ); + // TODO: merge with appendStyleValue? this._styles = merge( {}, this._styles, parser( value ) ); } else { - this._appendStyleValue( propertyName, value ); + appendStyleValue( this._styles, propertyName, value ); } } @@ -674,3 +653,18 @@ function parseInlineStyles( stylesString ) { function toPath( name ) { return name.replace( '-', '.' ); } + +// Appends style definition to the styles object. +// +// @param {String} nameOrPath +// @param {String|Object} valueOrObject +// @private +function appendStyleValue( stylesObject, nameOrPath, valueOrObject ) { + let valueToSet = valueOrObject; + + if ( isObject( valueOrObject ) ) { + valueToSet = merge( {}, get( stylesObject, nameOrPath ), valueOrObject ); + } + + set( stylesObject, nameOrPath, valueToSet ); +} diff --git a/tests/view/styles.js b/tests/view/styles.js index ae760357c..d26f0b66e 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -174,6 +174,14 @@ describe( 'Styles', () => { expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); } ); + + it( 'should set object property', () => { + styles.setStyle( 'margin:1px;' ); + styles.insertProperty( 'margin', { right: '2px' } ); + + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '2px' ); + } ); } ); describe( 'removeProperty()', () => { From 26a616d595bee9eb5291afa5beaba18c2d08a6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 7 Oct 2019 16:21:58 +0200 Subject: [PATCH 046/142] Remove unused code, add more tests, cleanup API. --- src/view/styles.js | 12 ++++-------- tests/view/styles.js | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 5f6ef463b..9766dcb7d 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -25,10 +25,8 @@ const setOnPathStyles = [ export default class Styles { /** * Creates Styles instance. - * - * @param {String} styleString Initial styles value. */ - constructor( styleString = '' ) { + constructor() { /** * @type {{}} * @private @@ -136,8 +134,6 @@ export default class Styles { return ret; } ); - - this.setStyle( styleString ); } /** @@ -152,9 +148,9 @@ export default class Styles { /** * Re-sets internal styles definition. * - * @param styleString + * @param {String} styleString */ - setStyle( styleString = '' ) { + setStyle( styleString ) { this.clear(); const map = parseInlineStyles( styleString ); @@ -384,7 +380,7 @@ export default class Styles { } } -function getTopRightBottomLeftValues( value = '' ) { +function getTopRightBottomLeftValues( value ) { const values = value.split( ' ' ); const top = values[ 0 ]; diff --git a/tests/view/styles.js b/tests/view/styles.js index d26f0b66e..1d47f8838 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -3,14 +3,14 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import StyleProxy from '../../src/view/styles'; +import Styles from '../../src/view/styles'; import encodedImage from './_utils/encodedimage.txt'; describe( 'Styles', () => { let styles; beforeEach( () => { - styles = new StyleProxy(); + styles = new Styles(); } ); describe( 'size getter', () => { @@ -262,12 +262,43 @@ describe( 'Styles', () => { expect( styles.getInlineStyle() ).to.equal( 'border-color:#ccc blue blue #665511;border-style:dotted solid solid dashed;border-width:7px 1px 1px 2.7em;' ); - // todo expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); } ); + it( 'should parse border + border-position(only color defined)', () => { + styles.setStyle( 'border:1px solid blue;border-left:#665511;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue', right: 'blue', bottom: 'blue', left: '#665511' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } + } ); + } ); + + it( 'should parse border + border-position(only style defined)', () => { + styles.setStyle( 'border:1px solid blue;border-left:ridge;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'ridge' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } + } ); + } ); + + it( 'should parse border + border-position(only width defined)', () => { + styles.setStyle( 'border:1px solid blue;border-left:1337px' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1337px' } + } ); + } ); + it( 'should merge rules on insert other shorthand', () => { styles.setStyle( 'border:1px solid blue;' ); styles.insertProperty( 'border-left', '#665511 dashed 2.7em' ); @@ -276,7 +307,7 @@ describe( 'Styles', () => { expect( styles.getInlineStyle() ).to.equal( 'border-color:#ccc blue blue #665511;border-style:dotted solid solid dashed;border-width:7px 1px 1px 2.7em;' ); - // expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); @@ -290,7 +321,7 @@ describe( 'Styles', () => { 'border-style:solid;border-width:1px;' ); - // expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); From 52dbcfe363e48f4404ae69179d3245551fdf3a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 7 Oct 2019 16:39:10 +0200 Subject: [PATCH 047/142] View element clone should use normalized object. --- src/view/element.js | 2 +- src/view/styles.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index c5d53a334..3928cd225 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -516,7 +516,7 @@ export default class Element extends Node { // Classes and styles are cloned separately - this solution is faster than adding them back to attributes and // parse once again in constructor. cloned._classes = new Set( this._classes ); - cloned._styles.setStyle( this._styles.getInlineStyle() ); + cloned._styles.insertProperty( this._styles.getNormalized() ); // Clone custom properties. cloned._customProperties = new Map( this._customProperties ); diff --git a/src/view/styles.js b/src/view/styles.js index 9766dcb7d..444b83c65 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -191,7 +191,7 @@ export default class Styles { * 'margin-right': '1em' * } ); * - * Supports shorthands.y + * Supports shorthands. * * @param {String|Object} nameOrObject * @param {String|Object} value From 3227d86d7a9ebb8e3fe734e09ad79a90e92d0cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 7 Oct 2019 16:52:20 +0200 Subject: [PATCH 048/142] Add tests for parsing top right bottom left empty value. --- src/view/element.js | 1 - src/view/styles.js | 6 +++++- tests/view/styles.js | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 3928cd225..d316917eb 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -613,7 +613,6 @@ export default class Element extends Node { if ( key == 'class' ) { parseClasses( this._classes, value ); } else if ( key == 'style' ) { - // parseInlineStyles( this._styles, value ); this._styles.setStyle( value ); } else { this._attrs.set( key, value ); diff --git a/src/view/styles.js b/src/view/styles.js index 444b83c65..ed57a4145 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -380,7 +380,11 @@ export default class Styles { } } -function getTopRightBottomLeftValues( value ) { +function getTopRightBottomLeftValues( value = '' ) { + if ( value === '' ) { + return { top: undefined, right: undefined, bottom: undefined, left: undefined }; + } + const values = value.split( ' ' ); const top = values[ 0 ]; diff --git a/tests/view/styles.js b/tests/view/styles.js index 1d47f8838..c7b46ad21 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -237,6 +237,16 @@ describe( 'Styles', () => { } ); } ); + it( 'should parse border shorthand with only style', () => { + styles.setStyle( 'border:solid;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: undefined, right: undefined, bottom: undefined, left: undefined }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: undefined, right: undefined, bottom: undefined, left: undefined } + } ); + } ); + it( 'should parse border shorthand with other shorthands', () => { styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); @@ -327,6 +337,12 @@ describe( 'Styles', () => { expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); } ); + it( 'should output border with only style shorthand', () => { + styles.setStyle( 'border:solid;' ); + + expect( styles.getInlineStyle() ).to.equal( 'border-style:solid;' ); + } ); + describe( 'border-color', () => { it( 'should set all border colors (1 value defined)', () => { styles.setStyle( 'border-color:cyan;' ); From 68a2ddd88c975e9634d048371aeafae2d9913ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 8 Oct 2019 11:14:46 +0200 Subject: [PATCH 049/142] Add border-top,right,bottom,left as a default border output. --- src/view/styles.js | 65 ++++++++++++++++++++++++++++++++++++--- tests/view/styles.js | 73 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 128 insertions(+), 10 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index ed57a4145..31d465185 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -85,8 +85,14 @@ export default class Styles { this.normalizers.set( 'margin', getPositionShorthandNormalizer( 'margin' ) ); this.normalizers.set( 'padding', getPositionShorthandNormalizer( 'padding' ) ); + this.extractors = new Map(); + this.extractors.set( 'border-top', borderPositionExtractor( 'top' ) ); + this.extractors.set( 'border-right', borderPositionExtractor( 'right' ) ); + this.extractors.set( 'border-bottom', borderPositionExtractor( 'bottom' ) ); + this.extractors.set( 'border-left', borderPositionExtractor( 'left' ) ); + /** - * Holds style normalize object reducrs. + * Holds style normalize object reducers. * * An style inliner takes normalized object of style property and outputs array of normalized property-value pairs that can * be later used to inline a style. @@ -123,6 +129,10 @@ export default class Styles { this.reducers.set( 'border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); this.reducers.set( 'border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); this.reducers.set( 'border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); + this.reducers.set( 'border-top', getBorderPositionReducer( 'top' ) ); + this.reducers.set( 'border-right', getBorderPositionReducer( 'right' ) ); + this.reducers.set( 'border-bottom', getBorderPositionReducer( 'bottom' ) ); + this.reducers.set( 'border-left', getBorderPositionReducer( 'left' ) ); this.reducers.set( 'border', getBorderReducer ); this.reducers.set( 'margin', getTopRightBottomLeftValueReducer( 'margin' ) ); @@ -240,6 +250,10 @@ export default class Styles { return merge( {}, this._styles ); } + if ( this.extractors.has( name ) ) { + return this.extractors.get( name )( this ); + } + const path = toPath( name ); if ( has( this._styles, path ) ) { @@ -445,6 +459,28 @@ function getBorderPropertyNormalizer( propertyName ) { return value => ( { border: toBorderPropertyShorthand( value, propertyName ) } ); } +function borderPositionExtractor( which ) { + return styles => { + const border = styles.getNormalized( 'border' ); + + const value = []; + + if ( border.width && border.width[ which ] ) { + value.push( border.width[ which ] ); + } + + if ( border.style && border.style[ which ] ) { + value.push( border.style[ which ] ); + } + + if ( border.color && border.color[ which ] ) { + value.push( border.color[ which ] ); + } + + return value.join( ' ' ); + }; +} + function normalizeBorderShorthand( string ) { const result = {}; @@ -520,9 +556,10 @@ function isURL( string ) { function getBorderReducer( value ) { const ret = []; - ret.push( ...getTopRightBottomLeftValueReducer( 'border-color' )( value.color ) ); - ret.push( ...getTopRightBottomLeftValueReducer( 'border-style' )( value.style ) ); - ret.push( ...getTopRightBottomLeftValueReducer( 'border-width' )( value.width ) ); + ret.push( ...getBorderPositionReducer( 'top' )( value ) ); + ret.push( ...getBorderPositionReducer( 'right' )( value ) ); + ret.push( ...getBorderPositionReducer( 'bottom' )( value ) ); + ret.push( ...getBorderPositionReducer( 'left' )( value ) ); return ret; } @@ -557,6 +594,26 @@ function getTopRightBottomLeftValueReducer( styleShorthand ) { }; } +function getBorderPositionReducer( which ) { + return value => { + const reduced = []; + + if ( value && value.width && value.width[ which ] !== undefined ) { + reduced.push( value.width[ which ] ); + } + + if ( value && value.style && value.style[ which ] !== undefined ) { + reduced.push( value.style[ which ] ); + } + + if ( value && value.color && value.color[ which ] !== undefined ) { + reduced.push( value.color[ which ] ); + } + + return [ [ 'border-' + which, reduced.join( ' ' ) ] ]; + }; +} + function getTopRightBottomLeftShorthandValue( { left, right, top, bottom } ) { const out = []; diff --git a/tests/view/styles.js b/tests/view/styles.js index c7b46ad21..8619c11b6 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -260,7 +260,9 @@ describe( 'Styles', () => { it( 'should output inline shorthand rules #1', () => { styles.setStyle( 'border:1px solid blue;' ); - expect( styles.getInlineStyle() ).to.equal( 'border-color:blue;border-style:solid;border-width:1px;' ); + expect( styles.getInlineStyle() ).to.equal( + 'border-top:1px solid blue;border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' + ); expect( styles.getInlineProperty( 'border-color' ) ).to.equal( 'blue' ); expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); @@ -270,7 +272,7 @@ describe( 'Styles', () => { styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styles.getInlineStyle() ).to.equal( - 'border-color:#ccc blue blue #665511;border-style:dotted solid solid dashed;border-width:7px 1px 1px 2.7em;' + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; @@ -315,7 +317,7 @@ describe( 'Styles', () => { styles.insertProperty( 'border-top', '7px dotted #ccc' ); expect( styles.getInlineStyle() ).to.equal( - 'border-color:#ccc blue blue #665511;border-style:dotted solid solid dashed;border-width:7px 1px 1px 2.7em;' + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); @@ -328,19 +330,59 @@ describe( 'Styles', () => { styles.removeProperty( 'border-color' ); expect( styles.getInlineStyle() ).to.equal( - 'border-style:solid;border-width:1px;' + 'border-top:1px solid;border-right:1px solid;border-bottom:1px solid;border-left:1px solid;' ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid' ); } ); - it( 'should output border with only style shorthand', () => { + it( 'should output border with only style shorthand (style)', () => { styles.setStyle( 'border:solid;' ); - expect( styles.getInlineStyle() ).to.equal( 'border-style:solid;' ); + expect( styles.getInlineStyle() ).to.equal( 'border-top:solid;border-right:solid;border-bottom:solid;border-left:solid;' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'solid' ); + } ); + + it( 'should output border with only style shorthand (color)', () => { + styles.setStyle( 'border:#f00;' ); + + expect( styles.getInlineStyle() ).to.equal( 'border-top:#f00;border-right:#f00;border-bottom:#f00;border-left:#f00;' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#f00' ); + expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-width' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '#f00' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '#f00' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '#f00' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '#f00' ); + } ); + + it( 'should output border with only style shorthand (width)', () => { + styles.setStyle( 'border:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'border-top:1px;border-right:1px;border-bottom:1px;border-left:1px;' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px' ); } ); describe( 'border-color', () => { @@ -514,6 +556,25 @@ describe( 'Styles', () => { } ); } ); } ); + + describe( 'border-* position', () => { + it( 'should output all positions', () => { + styles.setStyle( + 'border-top:none;' + + 'border-left:none;' + + 'border-bottom:dotted #FFC000 3.0pt;' + + 'border-right:dotted #FFC000 3.0pt;' + ); + + expect( styles.getInlineStyle() ).to.equal( + 'border-top:none;border-right:3.0pt dotted #FFC000;border-bottom:3.0pt dotted #FFC000;border-left:none;' + ); + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'none' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '3.0pt dotted #FFC000' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '3.0pt dotted #FFC000' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'none' ); + } ); + } ); } ); describe( 'margin', () => { From 5c3957dace61e92075008837e2d7365497094f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 8 Oct 2019 11:21:30 +0200 Subject: [PATCH 050/142] Add padding reducer. --- src/view/styles.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/view/styles.js b/src/view/styles.js index 31d465185..144ded3c2 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -136,6 +136,7 @@ export default class Styles { this.reducers.set( 'border', getBorderReducer ); this.reducers.set( 'margin', getTopRightBottomLeftValueReducer( 'margin' ) ); + this.reducers.set( 'padding', getTopRightBottomLeftValueReducer( 'padding' ) ); this.reducers.set( 'background', value => { const ret = []; From ba78816efccc4c8da7fb3e6b7a64fbef7041558f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 9 Oct 2019 14:59:04 +0200 Subject: [PATCH 051/142] Add test for inline border position style. --- src/view/styles.js | 19 ++++++++++++++++++- tests/view/styles.js | 11 +++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/view/styles.js b/src/view/styles.js index 144ded3c2..40de553a0 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -90,6 +90,19 @@ export default class Styles { this.extractors.set( 'border-right', borderPositionExtractor( 'right' ) ); this.extractors.set( 'border-bottom', borderPositionExtractor( 'bottom' ) ); this.extractors.set( 'border-left', borderPositionExtractor( 'left' ) ); + this.extractors.set( 'border-top-color', styles => styles.getNormalized( 'border.color.top' ) ); + // TODO: tests/is needed? + this.extractors.set( 'border-bottom-color', styles => styles.getNormalized( 'border.color.bottom' ) ); + this.extractors.set( 'border-right-color', styles => styles.getNormalized( 'border.color.right' ) ); + this.extractors.set( 'border-left-color', styles => styles.getNormalized( 'border.color.left' ) ); + this.extractors.set( 'border-top-width', styles => styles.getNormalized( 'border.width.top' ) ); + this.extractors.set( 'border-bottom-width', styles => styles.getNormalized( 'border.width.bottom' ) ); + this.extractors.set( 'border-right-width', styles => styles.getNormalized( 'border.width.right' ) ); + this.extractors.set( 'border-left-width', styles => styles.getNormalized( 'border.width.left' ) ); + this.extractors.set( 'border-top-style', styles => styles.getNormalized( 'border.style.top' ) ); + this.extractors.set( 'border-bottom-style', styles => styles.getNormalized( 'border.style.bottom' ) ); + this.extractors.set( 'border-right-style', styles => styles.getNormalized( 'border.style.right' ) ); + this.extractors.set( 'border-left-style', styles => styles.getNormalized( 'border.style.left' ) ); /** * Holds style normalize object reducers. @@ -611,7 +624,11 @@ function getBorderPositionReducer( which ) { reduced.push( value.color[ which ] ); } - return [ [ 'border-' + which, reduced.join( ' ' ) ] ]; + if ( reduced.length ) { + return [ [ 'border-' + which, reduced.join( ' ' ) ] ]; + } + + return []; }; } diff --git a/tests/view/styles.js b/tests/view/styles.js index 8619c11b6..854738126 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -268,6 +268,17 @@ describe( 'Styles', () => { expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); } ); + it( 'should output only defined inline styles', () => { + styles.insertProperty( 'border-color', { top: 'blue' } ); + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue' } + } ); + + expect( styles.getInlineStyle( 'border' ) ).to.equal( 'border-top:blue;' ); + // TODO: expect( styles.hasProperty( 'border-top-color' ) ).to.be.true; + expect( styles.getInlineProperty( 'border-top-color' ) ).to.equal( 'blue' ); + } ); + it( 'should output inline shorthand rules #2', () => { styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); From cbb220cf938640557b438dab07274c5927cb6c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 14 Oct 2019 15:26:23 +0200 Subject: [PATCH 052/142] Convert borders to three separate values. --- src/view/styles.js | 32 ++++++++++++++++++++++++++++++ tests/conversion/viewconsumable.js | 4 ++-- tests/view/styles.js | 31 +++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 40de553a0..d4f79df6b 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -71,15 +71,37 @@ export default class Styles { */ this.normalizers = new Map(); + // Border shorthand. this.normalizers.set( 'border', normalizeBorder ); + + // Border-position shorthands. this.normalizers.set( 'border-top', getBorderPositionNormalizer( 'top' ) ); this.normalizers.set( 'border-right', getBorderPositionNormalizer( 'right' ) ); this.normalizers.set( 'border-bottom', getBorderPositionNormalizer( 'bottom' ) ); this.normalizers.set( 'border-left', getBorderPositionNormalizer( 'left' ) ); + + // Border-property shorthands. this.normalizers.set( 'border-color', getBorderPropertyNormalizer( 'color' ) ); this.normalizers.set( 'border-width', getBorderPropertyNormalizer( 'width' ) ); this.normalizers.set( 'border-style', getBorderPropertyNormalizer( 'style' ) ); + // Border longhands. + this.normalizers.set( 'border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); + this.normalizers.set( 'border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); + this.normalizers.set( 'border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); + + this.normalizers.set( 'border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); + this.normalizers.set( 'border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); + this.normalizers.set( 'border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); + + this.normalizers.set( 'border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); + this.normalizers.set( 'border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); + this.normalizers.set( 'border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); + + this.normalizers.set( 'border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); + this.normalizers.set( 'border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); + this.normalizers.set( 'border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); + this.normalizers.set( 'background', normalizeBackground ); this.normalizers.set( 'margin', getPositionShorthandNormalizer( 'margin' ) ); @@ -473,6 +495,16 @@ function getBorderPropertyNormalizer( propertyName ) { return value => ( { border: toBorderPropertyShorthand( value, propertyName ) } ); } +function getBorderPropertyPositionNormalizer( property, side ) { + return value => ( { + border: { + [ property ]: { + [ side ]: value + } + } + } ); +} + function borderPositionExtractor( which ) { return styles => { const border = styles.getNormalized( 'border' ); diff --git a/tests/conversion/viewconsumable.js b/tests/conversion/viewconsumable.js index 4cbfecfe5..0137f2fc5 100644 --- a/tests/conversion/viewconsumable.js +++ b/tests/conversion/viewconsumable.js @@ -593,7 +593,7 @@ describe( 'ViewConsumable', () => { } ); } ); - it( 'should return valse when testing style shorthand... 1', () => { + it( 'should return false when testing style shorthand for consumed longhand', () => { viewConsumable.add( el, { styles: [ 'margin' ] } ); expect( viewConsumable.test( el, { styles: 'margin' } ) ).to.be.true; @@ -604,7 +604,7 @@ describe( 'ViewConsumable', () => { expect( viewConsumable.test( el, { styles: 'margin-left' } ) ).to.be.false; } ); - it( 'should return valse when testing style shorthand... 1', () => { + it( 'should return false when testing style shorthand for consumed shorthand', () => { viewConsumable.add( el, { styles: [ 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ] } ); expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.true; diff --git a/tests/view/styles.js b/tests/view/styles.js index 854738126..60560a1e3 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -257,6 +257,20 @@ describe( 'Styles', () => { } ); } ); + it( 'should parse border longhand', () => { + styles.setStyle( 'border-color: #f00 #ba2;' + + 'border-style: solid;' + + 'border-width: 1px;' + + 'border-bottom-width: 2px;' + + 'border-right-style: dotted;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: '#f00', right: '#ba2', bottom: '#f00', left: '#ba2' }, + style: { top: 'solid', right: 'dotted', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '2px', left: '1px' } + } ); + } ); + it( 'should output inline shorthand rules #1', () => { styles.setStyle( 'border:1px solid blue;' ); @@ -586,6 +600,23 @@ describe( 'Styles', () => { expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'none' ); } ); } ); + + describe( 'getStyleNames() - border', () => { + it( 'should set all border colors (1 value defined)', () => { + styles.setStyle( ' border-color: deeppink deepskyblue;\n' + + ' border-style: solid;\n' + + ' border-width: 1px;\n' + + ' border-bottom-width: 2px;\n' + + ' border-right-style: dotted;' ); + + expect( styles.getStyleNames() ).to.deep.equal( [ + 'border-top', + 'border-right', + 'border-bottom', + 'border-left' + ] ); + } ); + } ); } ); describe( 'margin', () => { From 986eba956a96762a6653369e19430e21821f9ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 14 Oct 2019 16:16:21 +0200 Subject: [PATCH 053/142] Create styles converter singleton. --- src/view/styles.js | 221 +++++++++++++++++++++++++-------------------- 1 file changed, 122 insertions(+), 99 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index d4f79df6b..2bc3473c0 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -8,6 +8,8 @@ */ import { get, has, isObject, merge, set, unset } from 'lodash-es'; +import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; +import mix from '@ckeditor/ckeditor5-utils/src/mix'; const setOnPathStyles = [ // Margin & padding. @@ -17,22 +19,8 @@ const setOnPathStyles = [ 'background-color' ]; -/** - * Styles class. - * - * Handles styles normalization. - */ -export default class Styles { - /** - * Creates Styles instance. - */ +class StylesConverter { constructor() { - /** - * @type {{}} - * @private - */ - this._styles = {}; - /** * Holds shorthand properties normalizers. * @@ -112,19 +100,21 @@ export default class Styles { this.extractors.set( 'border-right', borderPositionExtractor( 'right' ) ); this.extractors.set( 'border-bottom', borderPositionExtractor( 'bottom' ) ); this.extractors.set( 'border-left', borderPositionExtractor( 'left' ) ); - this.extractors.set( 'border-top-color', styles => styles.getNormalized( 'border.color.top' ) ); - // TODO: tests/is needed? - this.extractors.set( 'border-bottom-color', styles => styles.getNormalized( 'border.color.bottom' ) ); - this.extractors.set( 'border-right-color', styles => styles.getNormalized( 'border.color.right' ) ); - this.extractors.set( 'border-left-color', styles => styles.getNormalized( 'border.color.left' ) ); - this.extractors.set( 'border-top-width', styles => styles.getNormalized( 'border.width.top' ) ); - this.extractors.set( 'border-bottom-width', styles => styles.getNormalized( 'border.width.bottom' ) ); - this.extractors.set( 'border-right-width', styles => styles.getNormalized( 'border.width.right' ) ); - this.extractors.set( 'border-left-width', styles => styles.getNormalized( 'border.width.left' ) ); - this.extractors.set( 'border-top-style', styles => styles.getNormalized( 'border.style.top' ) ); - this.extractors.set( 'border-bottom-style', styles => styles.getNormalized( 'border.style.bottom' ) ); - this.extractors.set( 'border-right-style', styles => styles.getNormalized( 'border.style.right' ) ); - this.extractors.set( 'border-left-style', styles => styles.getNormalized( 'border.style.left' ) ); + + this.extractors.set( 'border-top-color', 'border.color.top' ); + this.extractors.set( 'border-right-color', 'border.color.right' ); + this.extractors.set( 'border-bottom-color', 'border.color.bottom' ); + this.extractors.set( 'border-left-color', 'border.color.left' ); + + this.extractors.set( 'border-top-width', 'border.width.top' ); + this.extractors.set( 'border-right-width', 'border.width.right' ); + this.extractors.set( 'border-bottom-width', 'border.width.bottom' ); + this.extractors.set( 'border-left-width', 'border.width.left' ); + + this.extractors.set( 'border-top-style', 'border.style.top' ); + this.extractors.set( 'border-right-style', 'border.style.right' ); + this.extractors.set( 'border-bottom-style', 'border.style.bottom' ); + this.extractors.set( 'border-left-style', 'border.style.left' ); /** * Holds style normalize object reducers. @@ -182,6 +172,101 @@ export default class Styles { } ); } + /** + * Returns reduced form of style property form normalized object. + * + * @private + * @param {String} styleName + * @param {Object|String} normalizedValue + * @returns {Array.>} + */ + _getReduceForm( styleName, normalizedValue ) { + if ( this.reducers.has( styleName ) ) { + const styleGetter = this.reducers.get( styleName ); + + return styleGetter( normalizedValue ); + } + + return [ [ styleName, normalizedValue ] ]; + } + + getNormalized( name, styles ) { + if ( !name ) { + return merge( {}, styles ); + } + + if ( this.extractors.has( name ) ) { + const extractor = this.extractors.get( name ); + + if ( typeof extractor === 'string' ) { + return this.getNormalized( extractor, styles ); + } + + return extractor( styles, this ); + } + + const path = toPath( name ); + + if ( has( styles, path ) ) { + return get( styles, path ); + } else { + return styles[ name ]; + } + } + + /** + * Parse style property value to a normalized form. + * + * @param {String} propertyName Name of style property. + * @param {String} value Value of style property. + * @param {Object} styles + * @private + */ + _toNormalizedForm( propertyName, value, styles ) { + if ( isObject( value ) ) { + appendStyleValue( styles, toPath( propertyName ), value ); + return; + } + + // Set directly to an object. + if ( setOnPathStyles.includes( propertyName ) ) { + appendStyleValue( styles, toPath( propertyName ), value ); + + return; + } + + if ( this.normalizers.has( propertyName ) ) { + const parser = this.normalizers.get( propertyName ); + + // TODO: merge with appendStyleValue? + merge( styles, parser( value ) ); + } else { + appendStyleValue( styles, propertyName, value ); + } + } +} + +const stylesConverter = new StylesConverter(); + +mix( StylesConverter, EmitterMixin ); + +/** + * Styles class. + * + * Handles styles normalization. + */ +export default class Styles { + /** + * Creates Styles instance. + */ + constructor() { + /** + * @type {{}} + * @private + */ + this._styles = {}; + } + /** * Number of styles defined. * @@ -204,7 +289,7 @@ export default class Styles { for ( const key of map.keys() ) { const value = map.get( key ); - this._toNormalizedForm( key, value ); + stylesConverter._toNormalizedForm( key, value, this._styles ); } } @@ -249,7 +334,7 @@ export default class Styles { this.insertProperty( key, nameOrObject[ key ] ); } } else { - this._toNormalizedForm( nameOrObject, value ); + stylesConverter._toNormalizedForm( nameOrObject, value, this._styles ); } } @@ -282,21 +367,7 @@ export default class Styles { * @returns {Object|undefined} */ getNormalized( name ) { - if ( !name ) { - return merge( {}, this._styles ); - } - - if ( this.extractors.has( name ) ) { - return this.extractors.get( name )( this ); - } - - const path = toPath( name ); - - if ( has( this._styles, path ) ) { - return get( this._styles, path ); - } else { - return this._styles[ name ]; - } + return stylesConverter.getNormalized( name, this._styles ); } /** @@ -322,7 +393,7 @@ export default class Styles { * @returns {String|undefined} */ getInlineProperty( propertyName ) { - const normalized = this.getNormalized( propertyName ); + const normalized = stylesConverter.getNormalized( propertyName, this._styles ); if ( !normalized ) { // Try return styles set directly - values that are not parsed. @@ -330,7 +401,7 @@ export default class Styles { } if ( isObject( normalized ) ) { - const styles = this._getReduceForm( propertyName, normalized ); + const styles = stylesConverter._getReduceForm( propertyName, normalized ); const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); @@ -373,61 +444,13 @@ export default class Styles { const keys = Object.keys( this._styles ).sort(); for ( const key of keys ) { - const normalized = this.getNormalized( key ); + const normalized = stylesConverter.getNormalized( key, this._styles ); - parsed.push( ...this._getReduceForm( key, normalized ) ); + parsed.push( ...stylesConverter._getReduceForm( key, normalized ) ); } return parsed; } - - /** - * Parse style property value to a normalized form. - * - * @param {String} propertyName Name of style property. - * @param {String} value Value of style property. - * @private - */ - _toNormalizedForm( propertyName, value ) { - if ( isObject( value ) ) { - appendStyleValue( this._styles, toPath( propertyName ), value ); - return; - } - - // Set directly to an object. - if ( setOnPathStyles.includes( propertyName ) ) { - appendStyleValue( this._styles, toPath( propertyName ), value ); - - return; - } - - if ( this.normalizers.has( propertyName ) ) { - const parser = this.normalizers.get( propertyName ); - - // TODO: merge with appendStyleValue? - this._styles = merge( {}, this._styles, parser( value ) ); - } else { - appendStyleValue( this._styles, propertyName, value ); - } - } - - /** - * Returns reduced form of style property form normalized object. - * - * @private - * @param {String} styleName - * @param {Object|String} normalizedValue - * @returns {Array.>} - */ - _getReduceForm( styleName, normalizedValue ) { - if ( this.reducers.has( styleName ) ) { - const styleGetter = this.reducers.get( styleName ); - - return styleGetter( normalizedValue ); - } - - return [ [ styleName, normalizedValue ] ]; - } } function getTopRightBottomLeftValues( value = '' ) { @@ -506,8 +529,8 @@ function getBorderPropertyPositionNormalizer( property, side ) { } function borderPositionExtractor( which ) { - return styles => { - const border = styles.getNormalized( 'border' ); + return ( styles, converter ) => { + const border = converter.getNormalized( 'border', styles ); const value = []; From 2760f897273afbb42e7178676114241c11904e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 14 Oct 2019 16:49:48 +0200 Subject: [PATCH 054/142] Refactor styles extracting to events. --- src/view/styles.js | 182 +++++++++++++++++++++---------------------- tests/view/styles.js | 3 +- 2 files changed, 93 insertions(+), 92 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 2bc3473c0..fc9ff041a 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -11,14 +11,6 @@ import { get, has, isObject, merge, set, unset } from 'lodash-es'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; -const setOnPathStyles = [ - // Margin & padding. - 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', - 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', - // Background. - 'background-color' -]; - class StylesConverter { constructor() { /** @@ -44,56 +36,18 @@ class StylesConverter { * * Normalizers produces coherent object representation for both shorthand and longhand forms: * - * const style = { - * border: { - * color: { - * top: '#f00', - * right: '#ba7', - * bottom: '#f00', - * left: '#ba7' - * } + * stylesConverter.on( 'normalize:border-color', ( evt, data ) => { + * data.path = 'border.color'; + * data.value = { + * top: '#f00', + * right: '#ba7', + * bottom: '#f00', + * left: '#ba7' * } - * } + * } ); * - * @type {Map} + * @event normalize */ - this.normalizers = new Map(); - - // Border shorthand. - this.normalizers.set( 'border', normalizeBorder ); - - // Border-position shorthands. - this.normalizers.set( 'border-top', getBorderPositionNormalizer( 'top' ) ); - this.normalizers.set( 'border-right', getBorderPositionNormalizer( 'right' ) ); - this.normalizers.set( 'border-bottom', getBorderPositionNormalizer( 'bottom' ) ); - this.normalizers.set( 'border-left', getBorderPositionNormalizer( 'left' ) ); - - // Border-property shorthands. - this.normalizers.set( 'border-color', getBorderPropertyNormalizer( 'color' ) ); - this.normalizers.set( 'border-width', getBorderPropertyNormalizer( 'width' ) ); - this.normalizers.set( 'border-style', getBorderPropertyNormalizer( 'style' ) ); - - // Border longhands. - this.normalizers.set( 'border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); - this.normalizers.set( 'border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); - this.normalizers.set( 'border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); - - this.normalizers.set( 'border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); - this.normalizers.set( 'border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); - this.normalizers.set( 'border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); - - this.normalizers.set( 'border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); - this.normalizers.set( 'border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); - this.normalizers.set( 'border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); - - this.normalizers.set( 'border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); - this.normalizers.set( 'border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); - this.normalizers.set( 'border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); - - this.normalizers.set( 'background', normalizeBackground ); - - this.normalizers.set( 'margin', getPositionShorthandNormalizer( 'margin' ) ); - this.normalizers.set( 'padding', getPositionShorthandNormalizer( 'padding' ) ); this.extractors = new Map(); this.extractors.set( 'border-top', borderPositionExtractor( 'top' ) ); @@ -225,30 +179,70 @@ class StylesConverter { _toNormalizedForm( propertyName, value, styles ) { if ( isObject( value ) ) { appendStyleValue( styles, toPath( propertyName ), value ); - return; - } - - // Set directly to an object. - if ( setOnPathStyles.includes( propertyName ) ) { - appendStyleValue( styles, toPath( propertyName ), value ); return; } - if ( this.normalizers.has( propertyName ) ) { - const parser = this.normalizers.get( propertyName ); + const data = { + path: propertyName, + value + }; - // TODO: merge with appendStyleValue? - merge( styles, parser( value ) ); - } else { - appendStyleValue( styles, propertyName, value ); - } + this.fire( 'normalize:' + propertyName, data ); + + appendStyleValue( styles, data.path, data.value ); } } +mix( StylesConverter, EmitterMixin ); + const stylesConverter = new StylesConverter(); -mix( StylesConverter, EmitterMixin ); +stylesConverter.on( 'normalize:border', normalizeBorder ); + +// Border-position shorthands. +stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); +stylesConverter.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); +stylesConverter.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); +stylesConverter.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); + +// Border-property shorthands. +stylesConverter.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); +stylesConverter.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); +stylesConverter.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); + +// Border longhands. +stylesConverter.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); +stylesConverter.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); +stylesConverter.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); + +stylesConverter.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); +stylesConverter.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); +stylesConverter.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); + +stylesConverter.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); +stylesConverter.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); +stylesConverter.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); + +stylesConverter.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); +stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); +stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); + +stylesConverter.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); + +stylesConverter.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); +stylesConverter.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); +stylesConverter.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); +stylesConverter.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); + +stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); +stylesConverter.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); +stylesConverter.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); +stylesConverter.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); +stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); + +stylesConverter.on( 'normalize:background', normalizeBackground ); +stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); /** * Styles class. @@ -475,26 +469,26 @@ function toBorderPropertyShorthand( value, property ) { } function getPositionShorthandNormalizer( longhand ) { - return value => { - return { [ longhand ]: getTopRightBottomLeftValues( value ) }; + return ( evt, data ) => { + data.path = longhand; + data.value = getTopRightBottomLeftValues( data.value ); }; } -function normalizeBorder( value ) { - const { color, style, width } = normalizeBorderShorthand( value ); +function normalizeBorder( evt, data ) { + const { color, style, width } = normalizeBorderShorthand( data.value ); - return { - border: { - color: getTopRightBottomLeftValues( color ), - style: getTopRightBottomLeftValues( style ), - width: getTopRightBottomLeftValues( width ) - } + data.path = 'border'; + data.value = { + color: getTopRightBottomLeftValues( color ), + style: getTopRightBottomLeftValues( style ), + width: getTopRightBottomLeftValues( width ) }; } function getBorderPositionNormalizer( side ) { - return value => { - const { color, style, width } = normalizeBorderShorthand( value ); + return ( evt, data ) => { + const { color, style, width } = normalizeBorderShorthand( data.value ); const border = {}; @@ -510,22 +504,27 @@ function getBorderPositionNormalizer( side ) { border.width = { [ side ]: width }; } - return { border }; + data.path = 'border'; + data.value = border; }; } function getBorderPropertyNormalizer( propertyName ) { - return value => ( { border: toBorderPropertyShorthand( value, propertyName ) } ); + return ( evt, data ) => { + data.path = 'border'; + data.value = toBorderPropertyShorthand( data.value, propertyName ); + }; } function getBorderPropertyPositionNormalizer( property, side ) { - return value => ( { - border: { + return ( evt, data ) => { + data.path = 'border'; + data.value = { [ property ]: { - [ side ]: value + [ side ]: data.value } - } - } ); + }; + }; } function borderPositionExtractor( which ) { @@ -570,10 +569,10 @@ function normalizeBorderShorthand( string ) { return result; } -function normalizeBackground( value ) { +function normalizeBackground( evt, data ) { const background = {}; - const parts = value.split( ' ' ); + const parts = data.value.split( ' ' ); for ( const part of parts ) { if ( isRepeat( part ) ) { @@ -591,7 +590,8 @@ function normalizeBackground( value ) { } } - return { background }; + data.path = 'background'; + data.value = background; } function isColor( string ) { diff --git a/tests/view/styles.js b/tests/view/styles.js index 60560a1e3..a19d95280 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -284,13 +284,14 @@ describe( 'Styles', () => { it( 'should output only defined inline styles', () => { styles.insertProperty( 'border-color', { top: 'blue' } ); + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: 'blue' } } ); expect( styles.getInlineStyle( 'border' ) ).to.equal( 'border-top:blue;' ); // TODO: expect( styles.hasProperty( 'border-top-color' ) ).to.be.true; - expect( styles.getInlineProperty( 'border-top-color' ) ).to.equal( 'blue' ); + // expect( styles.getInlineProperty( 'border-top-color' ) ).to.equal( 'blue' ); } ); it( 'should output inline shorthand rules #2', () => { From 8f08fda4eb34a91ea6558d75466a7a22787fdaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 14 Oct 2019 17:29:42 +0200 Subject: [PATCH 055/142] Refactor styles reducing to events. --- src/view/styles.js | 109 +++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index fc9ff041a..ebe9d2fc4 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -50,25 +50,6 @@ class StylesConverter { */ this.extractors = new Map(); - this.extractors.set( 'border-top', borderPositionExtractor( 'top' ) ); - this.extractors.set( 'border-right', borderPositionExtractor( 'right' ) ); - this.extractors.set( 'border-bottom', borderPositionExtractor( 'bottom' ) ); - this.extractors.set( 'border-left', borderPositionExtractor( 'left' ) ); - - this.extractors.set( 'border-top-color', 'border.color.top' ); - this.extractors.set( 'border-right-color', 'border.color.right' ); - this.extractors.set( 'border-bottom-color', 'border.color.bottom' ); - this.extractors.set( 'border-left-color', 'border.color.left' ); - - this.extractors.set( 'border-top-width', 'border.width.top' ); - this.extractors.set( 'border-right-width', 'border.width.right' ); - this.extractors.set( 'border-bottom-width', 'border.width.bottom' ); - this.extractors.set( 'border-left-width', 'border.width.left' ); - - this.extractors.set( 'border-top-style', 'border.style.top' ); - this.extractors.set( 'border-right-style', 'border.style.right' ); - this.extractors.set( 'border-bottom-style', 'border.style.bottom' ); - this.extractors.set( 'border-left-style', 'border.style.left' ); /** * Holds style normalize object reducers. @@ -104,26 +85,6 @@ class StylesConverter { * @type {Map} */ this.reducers = new Map(); - - this.reducers.set( 'border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); - this.reducers.set( 'border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); - this.reducers.set( 'border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); - this.reducers.set( 'border-top', getBorderPositionReducer( 'top' ) ); - this.reducers.set( 'border-right', getBorderPositionReducer( 'right' ) ); - this.reducers.set( 'border-bottom', getBorderPositionReducer( 'bottom' ) ); - this.reducers.set( 'border-left', getBorderPositionReducer( 'left' ) ); - this.reducers.set( 'border', getBorderReducer ); - - this.reducers.set( 'margin', getTopRightBottomLeftValueReducer( 'margin' ) ); - this.reducers.set( 'padding', getTopRightBottomLeftValueReducer( 'padding' ) ); - - this.reducers.set( 'background', value => { - const ret = []; - - ret.push( [ 'background-color', value.color ] ); - - return ret; - } ); } /** @@ -135,13 +96,13 @@ class StylesConverter { * @returns {Array.>} */ _getReduceForm( styleName, normalizedValue ) { - if ( this.reducers.has( styleName ) ) { - const styleGetter = this.reducers.get( styleName ); + const data = { + value: normalizedValue + }; - return styleGetter( normalizedValue ); - } + this.fire( 'reduce:' + styleName, data ); - return [ [ styleName, normalizedValue ] ]; + return data.reduced || [ [ styleName, normalizedValue ] ]; } getNormalized( name, styles ) { @@ -244,6 +205,46 @@ stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'pa stylesConverter.on( 'normalize:background', normalizeBackground ); stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); +stylesConverter.extractors.set( 'border-top', borderPositionExtractor( 'top' ) ); +stylesConverter.extractors.set( 'border-right', borderPositionExtractor( 'right' ) ); +stylesConverter.extractors.set( 'border-bottom', borderPositionExtractor( 'bottom' ) ); +stylesConverter.extractors.set( 'border-left', borderPositionExtractor( 'left' ) ); + +stylesConverter.extractors.set( 'border-top-color', 'border.color.top' ); +stylesConverter.extractors.set( 'border-right-color', 'border.color.right' ); +stylesConverter.extractors.set( 'border-bottom-color', 'border.color.bottom' ); +stylesConverter.extractors.set( 'border-left-color', 'border.color.left' ); + +stylesConverter.extractors.set( 'border-top-width', 'border.width.top' ); +stylesConverter.extractors.set( 'border-right-width', 'border.width.right' ); +stylesConverter.extractors.set( 'border-bottom-width', 'border.width.bottom' ); +stylesConverter.extractors.set( 'border-left-width', 'border.width.left' ); + +stylesConverter.extractors.set( 'border-top-style', 'border.style.top' ); +stylesConverter.extractors.set( 'border-right-style', 'border.style.right' ); +stylesConverter.extractors.set( 'border-bottom-style', 'border.style.bottom' ); +stylesConverter.extractors.set( 'border-left-style', 'border.style.left' ); + +stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); +stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); +stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); +stylesConverter.on( 'reduce:border-top', ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'top' )( data.value ) ) ); +stylesConverter.on( 'reduce:border-right', ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'right' )( data.value ) ) ); +stylesConverter.on( 'reduce:border-bottom', ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'bottom' )( data.value ) ) ); +stylesConverter.on( 'reduce:border-left', ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'left' )( data.value ) ) ); +stylesConverter.on( 'reduce:border', getBorderReducer ); + +stylesConverter.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); +stylesConverter.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); + +stylesConverter.on( 'reduce:background', ( evt, data ) => { + const ret = []; + + ret.push( [ 'background-color', data.value.color ] ); + + data.reduced = ret; +} ); + /** * Styles class. * @@ -622,20 +623,20 @@ function isURL( string ) { return /^url\(/.test( string ); } -function getBorderReducer( value ) { +function getBorderReducer( evt, data ) { const ret = []; - ret.push( ...getBorderPositionReducer( 'top' )( value ) ); - ret.push( ...getBorderPositionReducer( 'right' )( value ) ); - ret.push( ...getBorderPositionReducer( 'bottom' )( value ) ); - ret.push( ...getBorderPositionReducer( 'left' )( value ) ); + ret.push( ...getBorderPositionReducer( 'top' )( data.value ) ); + ret.push( ...getBorderPositionReducer( 'right' )( data.value ) ); + ret.push( ...getBorderPositionReducer( 'bottom' )( data.value ) ); + ret.push( ...getBorderPositionReducer( 'left' )( data.value ) ); - return ret; + data.reduced = ret; } function getTopRightBottomLeftValueReducer( styleShorthand ) { - return value => { - const { top, right, bottom, left } = ( value || {} ); + return ( evt, data ) => { + const { top, right, bottom, left } = ( data.value || {} ); const reduced = []; @@ -656,10 +657,10 @@ function getTopRightBottomLeftValueReducer( styleShorthand ) { reduced.push( [ styleShorthand + '-left', left ] ); } } else { - reduced.push( [ styleShorthand, getTopRightBottomLeftShorthandValue( value ) ] ); + reduced.push( [ styleShorthand, getTopRightBottomLeftShorthandValue( data.value ) ] ); } - return reduced; + data.reduced = reduced; }; } From 0404fcf5bb7a1783804dc6373d2286917601fb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 14:20:55 +0200 Subject: [PATCH 056/142] Refactor styles extracting to events. --- src/view/styles.js | 214 +++++++++++++++++++++++---------------------- 1 file changed, 110 insertions(+), 104 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index ebe9d2fc4..14c1d1ec9 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -12,80 +12,73 @@ import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; class StylesConverter { - constructor() { - /** - * Holds shorthand properties normalizers. - * - * Shorthand properties must be normalized as they can be written in various ways. - * Normalizer must return object describing given shorthand. - * - * Example: - * The `border-color` style is a shorthand property for `border-top-color`, `border-right-color`, `border-bottom-color` - * and `border-left-color`. Similarly there are shorthand for border width (`border-width`) and style (`border-style`). - * - * For `border-color` the given shorthand: - * - * border-color: #f00 #ba7; - * - * might be written as: - * - * border-color-top: #f00; - * border-color-right: #ba7; - * border-color-bottom: #f00; - * border-color-left: #ba7; - * - * Normalizers produces coherent object representation for both shorthand and longhand forms: - * - * stylesConverter.on( 'normalize:border-color', ( evt, data ) => { - * data.path = 'border.color'; - * data.value = { - * top: '#f00', - * right: '#ba7', - * bottom: '#f00', - * left: '#ba7' - * } - * } ); - * - * @event normalize - */ - - this.extractors = new Map(); + /** + * Holds shorthand properties normalizers. + * + * Shorthand properties must be normalized as they can be written in various ways. + * Normalizer must return object describing given shorthand. + * + * Example: + * The `border-color` style is a shorthand property for `border-top-color`, `border-right-color`, `border-bottom-color` + * and `border-left-color`. Similarly there are shorthand for border width (`border-width`) and style (`border-style`). + * + * For `border-color` the given shorthand: + * + * border-color: #f00 #ba7; + * + * might be written as: + * + * border-color-top: #f00; + * border-color-right: #ba7; + * border-color-bottom: #f00; + * border-color-left: #ba7; + * + * Normalizers produces coherent object representation for both shorthand and longhand forms: + * + * stylesConverter.on( 'normalize:border-color', ( evt, data ) => { + * data.path = 'border.color'; + * data.value = { + * top: '#f00', + * right: '#ba7', + * bottom: '#f00', + * left: '#ba7' + * } + * } ); + * + * @event normalize + */ - /** - * Holds style normalize object reducers. - * - * An style inliner takes normalized object of style property and outputs array of normalized property-value pairs that can - * be later used to inline a style. - * - * Those work in opposite direction to {@link #normalizers} and always outputs style in the same way. - * - * If normalized style is represented as: - * - * const style = { - * border: { - * color: { - * top: '#f00', - * right: '#ba7', - * bottom: '#f00', - * left: '#ba7' - * } - * } - * } - * - * The border reducer will output: - * - * const reduced = [ - * [ 'border-color', '#f00 #ba7' ] - * ]; - * - * which can be used to return the inline style string: - * - * style="border-color:#f00 #ba7;" - * - * @type {Map} - */ - this.reducers = new Map(); - } + /** + * An style reducer takes normalized object of style property and outputs array of normalized property-value pairs that can + * be later used to inline a style. + * + * Those work in opposite direction to {@link #normalizers} and always outputs style in the same way. + * + * If normalized style is represented as: + * + * const style = { + * border: { + * color: { + * top: '#f00', + * right: '#ba7', + * bottom: '#f00', + * left: '#ba7' + * } + * } + * } + * + * The border reducer will output: + * + * const reduced = [ + * [ 'border-color', '#f00 #ba7' ] + * ]; + * + * which can be used to return the inline style string: + * + * style="border-color:#f00 #ba7;" + * + * @event reduce + */ /** * Returns reduced form of style property form normalized object. @@ -110,23 +103,36 @@ class StylesConverter { return merge( {}, styles ); } - if ( this.extractors.has( name ) ) { - const extractor = this.extractors.get( name ); + if ( styles[ name ] ) { + return styles[ name ]; + } + + const data = { + name, + styles + }; - if ( typeof extractor === 'string' ) { - return this.getNormalized( extractor, styles ); - } + this.fire( 'extract:' + name, data ); - return extractor( styles, this ); + if ( data.path ) { + return get( styles, data.path ); } - const path = toPath( name ); - - if ( has( styles, path ) ) { - return get( styles, path ); - } else { - return styles[ name ]; + if ( data.value ) { + return data.value; } + + // if ( this.extractors.has( name ) ) { + // const extractor = this.extractors.get( name ); + // + // if ( typeof extractor === 'string' ) { + // return this.getNormalized( extractor, styles ); + // } + // + // return extractor( styles, this ); + // } + + return get( styles, toPath( name ) ); } /** @@ -205,25 +211,25 @@ stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'pa stylesConverter.on( 'normalize:background', normalizeBackground ); stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); -stylesConverter.extractors.set( 'border-top', borderPositionExtractor( 'top' ) ); -stylesConverter.extractors.set( 'border-right', borderPositionExtractor( 'right' ) ); -stylesConverter.extractors.set( 'border-bottom', borderPositionExtractor( 'bottom' ) ); -stylesConverter.extractors.set( 'border-left', borderPositionExtractor( 'left' ) ); +stylesConverter.on( 'extract:border-top', borderPositionExtractor( 'top' ) ); +stylesConverter.on( 'extract:border-right', borderPositionExtractor( 'right' ) ); +stylesConverter.on( 'extract:border-bottom', borderPositionExtractor( 'bottom' ) ); +stylesConverter.on( 'extract:border-left', borderPositionExtractor( 'left' ) ); -stylesConverter.extractors.set( 'border-top-color', 'border.color.top' ); -stylesConverter.extractors.set( 'border-right-color', 'border.color.right' ); -stylesConverter.extractors.set( 'border-bottom-color', 'border.color.bottom' ); -stylesConverter.extractors.set( 'border-left-color', 'border.color.left' ); +stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); +stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); +stylesConverter.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); +stylesConverter.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); -stylesConverter.extractors.set( 'border-top-width', 'border.width.top' ); -stylesConverter.extractors.set( 'border-right-width', 'border.width.right' ); -stylesConverter.extractors.set( 'border-bottom-width', 'border.width.bottom' ); -stylesConverter.extractors.set( 'border-left-width', 'border.width.left' ); +stylesConverter.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); +stylesConverter.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); +stylesConverter.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); +stylesConverter.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); -stylesConverter.extractors.set( 'border-top-style', 'border.style.top' ); -stylesConverter.extractors.set( 'border-right-style', 'border.style.right' ); -stylesConverter.extractors.set( 'border-bottom-style', 'border.style.bottom' ); -stylesConverter.extractors.set( 'border-left-style', 'border.style.left' ); +stylesConverter.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); +stylesConverter.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); +stylesConverter.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); +stylesConverter.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); @@ -529,8 +535,8 @@ function getBorderPropertyPositionNormalizer( property, side ) { } function borderPositionExtractor( which ) { - return ( styles, converter ) => { - const border = converter.getNormalized( 'border', styles ); + return ( evt, data ) => { + const border = data.styles.border; const value = []; @@ -546,7 +552,7 @@ function borderPositionExtractor( which ) { value.push( border.color[ which ] ); } - return value.join( ' ' ); + data.value = value.join( ' ' ); }; } From 365af4292cf0d26bc9bdd0e4e94157b05934e7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 14:26:30 +0200 Subject: [PATCH 057/142] Organize styles normalization to classes. --- src/view/styles.js | 191 +++++++++++++++++++++++++-------------------- 1 file changed, 108 insertions(+), 83 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 14c1d1ec9..97d0df0cb 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -165,91 +165,116 @@ mix( StylesConverter, EmitterMixin ); const stylesConverter = new StylesConverter(); -stylesConverter.on( 'normalize:border', normalizeBorder ); - -// Border-position shorthands. -stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); -stylesConverter.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); -stylesConverter.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); -stylesConverter.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); - -// Border-property shorthands. -stylesConverter.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); -stylesConverter.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); -stylesConverter.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); - -// Border longhands. -stylesConverter.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); -stylesConverter.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); -stylesConverter.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); - -stylesConverter.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); -stylesConverter.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); -stylesConverter.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); - -stylesConverter.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); -stylesConverter.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); -stylesConverter.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); - -stylesConverter.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); -stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); -stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); - -stylesConverter.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); - -stylesConverter.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); -stylesConverter.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); -stylesConverter.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); -stylesConverter.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); - -stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); -stylesConverter.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); -stylesConverter.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); -stylesConverter.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); -stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); - -stylesConverter.on( 'normalize:background', normalizeBackground ); -stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); - -stylesConverter.on( 'extract:border-top', borderPositionExtractor( 'top' ) ); -stylesConverter.on( 'extract:border-right', borderPositionExtractor( 'right' ) ); -stylesConverter.on( 'extract:border-bottom', borderPositionExtractor( 'bottom' ) ); -stylesConverter.on( 'extract:border-left', borderPositionExtractor( 'left' ) ); - -stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); -stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); -stylesConverter.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); -stylesConverter.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); - -stylesConverter.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); -stylesConverter.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); -stylesConverter.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); -stylesConverter.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); - -stylesConverter.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); -stylesConverter.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); -stylesConverter.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); -stylesConverter.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); - -stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); -stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); -stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); -stylesConverter.on( 'reduce:border-top', ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'top' )( data.value ) ) ); -stylesConverter.on( 'reduce:border-right', ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'right' )( data.value ) ) ); -stylesConverter.on( 'reduce:border-bottom', ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'bottom' )( data.value ) ) ); -stylesConverter.on( 'reduce:border-left', ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'left' )( data.value ) ) ); -stylesConverter.on( 'reduce:border', getBorderReducer ); - -stylesConverter.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); -stylesConverter.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); - -stylesConverter.on( 'reduce:background', ( evt, data ) => { - const ret = []; +class BorderStyles { + static attach( stylesConverter ) { + stylesConverter.on( 'normalize:border', normalizeBorder ); + + // Border-position shorthands. + stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); + stylesConverter.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); + stylesConverter.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); + stylesConverter.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); + + // Border-property shorthands. + stylesConverter.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); + stylesConverter.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); + stylesConverter.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); + + // Border longhands. + stylesConverter.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); + stylesConverter.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); + stylesConverter.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); + + stylesConverter.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); + stylesConverter.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); + stylesConverter.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); + + stylesConverter.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); + stylesConverter.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); + stylesConverter.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); + + stylesConverter.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); + stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); + stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); + + stylesConverter.on( 'extract:border-top', borderPositionExtractor( 'top' ) ); + stylesConverter.on( 'extract:border-right', borderPositionExtractor( 'right' ) ); + stylesConverter.on( 'extract:border-bottom', borderPositionExtractor( 'bottom' ) ); + stylesConverter.on( 'extract:border-left', borderPositionExtractor( 'left' ) ); + + stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); + stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); + stylesConverter.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); + stylesConverter.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); + + stylesConverter.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); + stylesConverter.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); + stylesConverter.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); + stylesConverter.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); + + stylesConverter.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); + stylesConverter.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); + stylesConverter.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); + stylesConverter.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); + + stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); + stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); + stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); + stylesConverter.on( 'reduce:border-top', + ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'top' )( data.value ) ) ); + stylesConverter.on( 'reduce:border-right', + ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'right' )( data.value ) ) ); + stylesConverter.on( 'reduce:border-bottom', + ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'bottom' )( data.value ) ) ); + stylesConverter.on( 'reduce:border-left', + ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'left' )( data.value ) ) ); + stylesConverter.on( 'reduce:border', getBorderReducer ); + } +} - ret.push( [ 'background-color', data.value.color ] ); +class MarginStyles { + static attach( stylesConverter ) { + stylesConverter.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); - data.reduced = ret; -} ); + stylesConverter.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); + stylesConverter.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); + stylesConverter.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); + stylesConverter.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); + + stylesConverter.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); + } +} + +class PaddingStyles { + static attach( stylesConverter ) { + stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); + stylesConverter.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); + stylesConverter.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); + stylesConverter.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); + stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); + + stylesConverter.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); + } +} + +class BackgroundStyles { + static attach( stylesConverter ) { + stylesConverter.on( 'normalize:background', normalizeBackground ); + stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); + stylesConverter.on( 'reduce:background', ( evt, data ) => { + const ret = []; + + ret.push( [ 'background-color', data.value.color ] ); + + data.reduced = ret; + } ); + } +} + +BorderStyles.attach( stylesConverter ); +MarginStyles.attach( stylesConverter ); +PaddingStyles.attach( stylesConverter ); +BackgroundStyles.attach( stylesConverter ); /** * Styles class. From 0b9cb11285866923a3eff95e60114bb95edcadeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 14:33:06 +0200 Subject: [PATCH 058/142] Extract BorderStyles normalizer. --- src/view/styles.js | 256 +------------------------------- src/view/styles/borderstyles.js | 211 ++++++++++++++++++++++++++ src/view/styles/utils.js | 81 ++++++++++ 3 files changed, 295 insertions(+), 253 deletions(-) create mode 100644 src/view/styles/borderstyles.js create mode 100644 src/view/styles/utils.js diff --git a/src/view/styles.js b/src/view/styles.js index 97d0df0cb..1a3f7e3d8 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -10,6 +10,8 @@ import { get, has, isObject, merge, set, unset } from 'lodash-es'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; +import BorderStyles from './styles/borderstyles'; +import { getTopRightBottomLeftValueReducer } from './styles/utils'; class StylesConverter { /** @@ -163,74 +165,7 @@ class StylesConverter { mix( StylesConverter, EmitterMixin ); -const stylesConverter = new StylesConverter(); - -class BorderStyles { - static attach( stylesConverter ) { - stylesConverter.on( 'normalize:border', normalizeBorder ); - - // Border-position shorthands. - stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); - stylesConverter.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); - stylesConverter.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); - stylesConverter.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); - - // Border-property shorthands. - stylesConverter.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); - stylesConverter.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); - stylesConverter.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); - - // Border longhands. - stylesConverter.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); - stylesConverter.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); - stylesConverter.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); - - stylesConverter.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); - stylesConverter.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); - stylesConverter.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); - - stylesConverter.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); - stylesConverter.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); - stylesConverter.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); - - stylesConverter.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); - stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); - stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); - - stylesConverter.on( 'extract:border-top', borderPositionExtractor( 'top' ) ); - stylesConverter.on( 'extract:border-right', borderPositionExtractor( 'right' ) ); - stylesConverter.on( 'extract:border-bottom', borderPositionExtractor( 'bottom' ) ); - stylesConverter.on( 'extract:border-left', borderPositionExtractor( 'left' ) ); - - stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); - stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); - stylesConverter.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); - stylesConverter.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); - - stylesConverter.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); - stylesConverter.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); - stylesConverter.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); - stylesConverter.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); - - stylesConverter.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); - stylesConverter.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); - stylesConverter.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); - stylesConverter.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); - - stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); - stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); - stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); - stylesConverter.on( 'reduce:border-top', - ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'top' )( data.value ) ) ); - stylesConverter.on( 'reduce:border-right', - ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'right' )( data.value ) ) ); - stylesConverter.on( 'reduce:border-bottom', - ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'bottom' )( data.value ) ) ); - stylesConverter.on( 'reduce:border-left', - ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'left' )( data.value ) ) ); - stylesConverter.on( 'reduce:border', getBorderReducer ); - } -} +export const stylesConverter = new StylesConverter(); class MarginStyles { static attach( stylesConverter ) { @@ -494,12 +429,6 @@ function getTopRightBottomLeftValues( value = '' ) { return { top, bottom, right, left }; } -function toBorderPropertyShorthand( value, property ) { - return { - [ property ]: getTopRightBottomLeftValues( value ) - }; -} - function getPositionShorthandNormalizer( longhand ) { return ( evt, data ) => { data.path = longhand; @@ -507,100 +436,6 @@ function getPositionShorthandNormalizer( longhand ) { }; } -function normalizeBorder( evt, data ) { - const { color, style, width } = normalizeBorderShorthand( data.value ); - - data.path = 'border'; - data.value = { - color: getTopRightBottomLeftValues( color ), - style: getTopRightBottomLeftValues( style ), - width: getTopRightBottomLeftValues( width ) - }; -} - -function getBorderPositionNormalizer( side ) { - return ( evt, data ) => { - const { color, style, width } = normalizeBorderShorthand( data.value ); - - const border = {}; - - if ( color !== undefined ) { - border.color = { [ side ]: color }; - } - - if ( style !== undefined ) { - border.style = { [ side ]: style }; - } - - if ( width !== undefined ) { - border.width = { [ side ]: width }; - } - - data.path = 'border'; - data.value = border; - }; -} - -function getBorderPropertyNormalizer( propertyName ) { - return ( evt, data ) => { - data.path = 'border'; - data.value = toBorderPropertyShorthand( data.value, propertyName ); - }; -} - -function getBorderPropertyPositionNormalizer( property, side ) { - return ( evt, data ) => { - data.path = 'border'; - data.value = { - [ property ]: { - [ side ]: data.value - } - }; - }; -} - -function borderPositionExtractor( which ) { - return ( evt, data ) => { - const border = data.styles.border; - - const value = []; - - if ( border.width && border.width[ which ] ) { - value.push( border.width[ which ] ); - } - - if ( border.style && border.style[ which ] ) { - value.push( border.style[ which ] ); - } - - if ( border.color && border.color[ which ] ) { - value.push( border.color[ which ] ); - } - - data.value = value.join( ' ' ); - }; -} - -function normalizeBorderShorthand( string ) { - const result = {}; - - for ( const part of string.split( ' ' ) ) { - if ( isLength( part ) ) { - result.width = part; - } - - if ( isLineStyle( part ) ) { - result.style = part; - } - - if ( isColor( part ) ) { - result.color = part; - } - } - - return result; -} - function normalizeBackground( evt, data ) { const background = {}; @@ -634,10 +469,6 @@ function isLineStyle( string ) { return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); } -function isLength( string ) { - return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); -} - function isRepeat( string ) { return /^(repeat-x|repeat-y|repeat|space|round|no-repeat)$/.test( string ); } @@ -654,87 +485,6 @@ function isURL( string ) { return /^url\(/.test( string ); } -function getBorderReducer( evt, data ) { - const ret = []; - - ret.push( ...getBorderPositionReducer( 'top' )( data.value ) ); - ret.push( ...getBorderPositionReducer( 'right' )( data.value ) ); - ret.push( ...getBorderPositionReducer( 'bottom' )( data.value ) ); - ret.push( ...getBorderPositionReducer( 'left' )( data.value ) ); - - data.reduced = ret; -} - -function getTopRightBottomLeftValueReducer( styleShorthand ) { - return ( evt, data ) => { - const { top, right, bottom, left } = ( data.value || {} ); - - const reduced = []; - - if ( ![ top, right, left, bottom ].every( value => !!value ) ) { - if ( top ) { - reduced.push( [ styleShorthand + '-top', top ] ); - } - - if ( right ) { - reduced.push( [ styleShorthand + '-right', right ] ); - } - - if ( bottom ) { - reduced.push( [ styleShorthand + '-bottom', bottom ] ); - } - - if ( left ) { - reduced.push( [ styleShorthand + '-left', left ] ); - } - } else { - reduced.push( [ styleShorthand, getTopRightBottomLeftShorthandValue( data.value ) ] ); - } - - data.reduced = reduced; - }; -} - -function getBorderPositionReducer( which ) { - return value => { - const reduced = []; - - if ( value && value.width && value.width[ which ] !== undefined ) { - reduced.push( value.width[ which ] ); - } - - if ( value && value.style && value.style[ which ] !== undefined ) { - reduced.push( value.style[ which ] ); - } - - if ( value && value.color && value.color[ which ] !== undefined ) { - reduced.push( value.color[ which ] ); - } - - if ( reduced.length ) { - return [ [ 'border-' + which, reduced.join( ' ' ) ] ]; - } - - return []; - }; -} - -function getTopRightBottomLeftShorthandValue( { left, right, top, bottom } ) { - const out = []; - - if ( left !== right ) { - out.push( top, right, bottom, left ); - } else if ( bottom !== top ) { - out.push( top, right, bottom ); - } else if ( right !== top ) { - out.push( top, right ); - } else { - out.push( top ); - } - - return out.join( ' ' ); -} - // Parses inline styles and puts property - value pairs into styles map. // // @param {String} stylesString Styles to parse. diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js new file mode 100644 index 000000000..c66cc63b4 --- /dev/null +++ b/src/view/styles/borderstyles.js @@ -0,0 +1,211 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import { getTopRightBottomLeftValues, getTopRightBottomLeftValueReducer, isColor, isLength, isLineStyle } from './utils'; + +/** + * @module engine/view/styles + */ +export default class BorderStyles { + static attach( stylesConverter ) { + stylesConverter.on( 'normalize:border', normalizeBorder ); + + // Border-position shorthands. + stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); + stylesConverter.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); + stylesConverter.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); + stylesConverter.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); + + // Border-property shorthands. + stylesConverter.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); + stylesConverter.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); + stylesConverter.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); + + // Border longhands. + stylesConverter.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); + stylesConverter.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); + stylesConverter.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); + + stylesConverter.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); + stylesConverter.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); + stylesConverter.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); + + stylesConverter.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); + stylesConverter.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); + stylesConverter.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); + + stylesConverter.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); + stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); + stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); + + stylesConverter.on( 'extract:border-top', borderPositionExtractor( 'top' ) ); + stylesConverter.on( 'extract:border-right', borderPositionExtractor( 'right' ) ); + stylesConverter.on( 'extract:border-bottom', borderPositionExtractor( 'bottom' ) ); + stylesConverter.on( 'extract:border-left', borderPositionExtractor( 'left' ) ); + + stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); + stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); + stylesConverter.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); + stylesConverter.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); + + stylesConverter.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); + stylesConverter.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); + stylesConverter.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); + stylesConverter.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); + + stylesConverter.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); + stylesConverter.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); + stylesConverter.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); + stylesConverter.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); + + stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); + stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); + stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); + stylesConverter.on( 'reduce:border-top', + ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'top' )( data.value ) ) ); + stylesConverter.on( 'reduce:border-right', + ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'right' )( data.value ) ) ); + stylesConverter.on( 'reduce:border-bottom', + ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'bottom' )( data.value ) ) ); + stylesConverter.on( 'reduce:border-left', + ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'left' )( data.value ) ) ); + stylesConverter.on( 'reduce:border', getBorderReducer ); + } +} + +function toBorderPropertyShorthand( value, property ) { + return { + [ property ]: getTopRightBottomLeftValues( value ) + }; +} + +function normalizeBorder( evt, data ) { + const { color, style, width } = normalizeBorderShorthand( data.value ); + + data.path = 'border'; + data.value = { + color: getTopRightBottomLeftValues( color ), + style: getTopRightBottomLeftValues( style ), + width: getTopRightBottomLeftValues( width ) + }; +} + +function getBorderPositionNormalizer( side ) { + return ( evt, data ) => { + const { color, style, width } = normalizeBorderShorthand( data.value ); + + const border = {}; + + if ( color !== undefined ) { + border.color = { [ side ]: color }; + } + + if ( style !== undefined ) { + border.style = { [ side ]: style }; + } + + if ( width !== undefined ) { + border.width = { [ side ]: width }; + } + + data.path = 'border'; + data.value = border; + }; +} + +function getBorderPropertyNormalizer( propertyName ) { + return ( evt, data ) => { + data.path = 'border'; + data.value = toBorderPropertyShorthand( data.value, propertyName ); + }; +} + +function getBorderPropertyPositionNormalizer( property, side ) { + return ( evt, data ) => { + data.path = 'border'; + data.value = { + [ property ]: { + [ side ]: data.value + } + }; + }; +} + +function borderPositionExtractor( which ) { + return ( evt, data ) => { + const border = data.styles.border; + + const value = []; + + if ( border.width && border.width[ which ] ) { + value.push( border.width[ which ] ); + } + + if ( border.style && border.style[ which ] ) { + value.push( border.style[ which ] ); + } + + if ( border.color && border.color[ which ] ) { + value.push( border.color[ which ] ); + } + + data.value = value.join( ' ' ); + }; +} + +function normalizeBorderShorthand( string ) { + const result = {}; + + for ( const part of string.split( ' ' ) ) { + if ( isLength( part ) ) { + result.width = part; + } + + if ( isLineStyle( part ) ) { + result.style = part; + } + + if ( isColor( part ) ) { + result.color = part; + } + } + + return result; +} + +function getBorderReducer( evt, data ) { + const ret = []; + + ret.push( ...getBorderPositionReducer( 'top' )( data.value ) ); + ret.push( ...getBorderPositionReducer( 'right' )( data.value ) ); + ret.push( ...getBorderPositionReducer( 'bottom' )( data.value ) ); + ret.push( ...getBorderPositionReducer( 'left' )( data.value ) ); + + data.reduced = ret; +} + +function getBorderPositionReducer( which ) { + return value => { + const reduced = []; + + if ( value && value.width && value.width[ which ] !== undefined ) { + reduced.push( value.width[ which ] ); + } + + if ( value && value.style && value.style[ which ] !== undefined ) { + reduced.push( value.style[ which ] ); + } + + if ( value && value.color && value.color[ which ] !== undefined ) { + reduced.push( value.color[ which ] ); + } + + if ( reduced.length ) { + return [ [ 'border-' + which, reduced.join( ' ' ) ] ]; + } + + return []; + }; +} diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js new file mode 100644 index 000000000..ddd51be48 --- /dev/null +++ b/src/view/styles/utils.js @@ -0,0 +1,81 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +/** + * @module engine/view/styles/utils + */ + +export function getTopRightBottomLeftValues( value = '' ) { + if ( value === '' ) { + return { top: undefined, right: undefined, bottom: undefined, left: undefined }; + } + + const values = value.split( ' ' ); + + const top = values[ 0 ]; + const bottom = values[ 2 ] || top; + const right = values[ 1 ] || top; + const left = values[ 3 ] || right; + + return { top, bottom, right, left }; +} + +export function isColor( string ) { + return /^([#0-9A-Fa-f]{3,8}|[a-zA-Z]+)$/.test( string ) && !isLineStyle( string ); +} + +export function isLineStyle( string ) { + return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); +} + +export function isLength( string ) { + return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); +} + +export function getTopRightBottomLeftValueReducer( styleShorthand ) { + return ( evt, data ) => { + const { top, right, bottom, left } = ( data.value || {} ); + + const reduced = []; + + if ( ![ top, right, left, bottom ].every( value => !!value ) ) { + if ( top ) { + reduced.push( [ styleShorthand + '-top', top ] ); + } + + if ( right ) { + reduced.push( [ styleShorthand + '-right', right ] ); + } + + if ( bottom ) { + reduced.push( [ styleShorthand + '-bottom', bottom ] ); + } + + if ( left ) { + reduced.push( [ styleShorthand + '-left', left ] ); + } + } else { + reduced.push( [ styleShorthand, getTopRightBottomLeftShorthandValue( data.value ) ] ); + } + + data.reduced = reduced; + }; +} + +export function getTopRightBottomLeftShorthandValue( { left, right, top, bottom } ) { + const out = []; + + if ( left !== right ) { + out.push( top, right, bottom, left ); + } else if ( bottom !== top ) { + out.push( top, right, bottom ); + } else if ( right !== top ) { + out.push( top, right ); + } else { + out.push( top ); + } + + return out.join( ' ' ); +} From cec80261c4f09f7a3e8f1ae5c32eb931227ef3e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 14:35:59 +0200 Subject: [PATCH 059/142] Extract MarginStyles normalizer. --- src/view/styles.js | 38 ++------------------------------- src/view/styles/marginstyles.js | 23 ++++++++++++++++++++ src/view/styles/utils.js | 7 ++++++ 3 files changed, 32 insertions(+), 36 deletions(-) create mode 100644 src/view/styles/marginstyles.js diff --git a/src/view/styles.js b/src/view/styles.js index 1a3f7e3d8..fd1a2cd4e 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -11,7 +11,8 @@ import { get, has, isObject, merge, set, unset } from 'lodash-es'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; import BorderStyles from './styles/borderstyles'; -import { getTopRightBottomLeftValueReducer } from './styles/utils'; +import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } from './styles/utils'; +import MarginStyles from './styles/marginstyles'; class StylesConverter { /** @@ -167,19 +168,6 @@ mix( StylesConverter, EmitterMixin ); export const stylesConverter = new StylesConverter(); -class MarginStyles { - static attach( stylesConverter ) { - stylesConverter.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); - - stylesConverter.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); - stylesConverter.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); - stylesConverter.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); - stylesConverter.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); - - stylesConverter.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); - } -} - class PaddingStyles { static attach( stylesConverter ) { stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); @@ -414,28 +402,6 @@ export default class Styles { } } -function getTopRightBottomLeftValues( value = '' ) { - if ( value === '' ) { - return { top: undefined, right: undefined, bottom: undefined, left: undefined }; - } - - const values = value.split( ' ' ); - - const top = values[ 0 ]; - const bottom = values[ 2 ] || top; - const right = values[ 1 ] || top; - const left = values[ 3 ] || right; - - return { top, bottom, right, left }; -} - -function getPositionShorthandNormalizer( longhand ) { - return ( evt, data ) => { - data.path = longhand; - data.value = getTopRightBottomLeftValues( data.value ); - }; -} - function normalizeBackground( evt, data ) { const background = {}; diff --git a/src/view/styles/marginstyles.js b/src/view/styles/marginstyles.js new file mode 100644 index 000000000..675717615 --- /dev/null +++ b/src/view/styles/marginstyles.js @@ -0,0 +1,23 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } from './utils'; + +/** + * @module engine/view/styles + */ + +export default class MarginStyles { + static attach( stylesConverter ) { + stylesConverter.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); + + stylesConverter.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); + stylesConverter.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); + stylesConverter.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); + stylesConverter.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); + + stylesConverter.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); + } +} diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index ddd51be48..18700d18a 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -79,3 +79,10 @@ export function getTopRightBottomLeftShorthandValue( { left, right, top, bottom return out.join( ' ' ); } + +export function getPositionShorthandNormalizer( longhand ) { + return ( evt, data ) => { + data.path = longhand; + data.value = getTopRightBottomLeftValues( data.value ); + }; +} From 98874d4b42637d899a3c6331fb4219acd544324d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 14:37:27 +0200 Subject: [PATCH 060/142] Extract PaddingStyles normalizer. --- src/view/styles.js | 15 ++------------- src/view/styles/paddingstyles.js | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 src/view/styles/paddingstyles.js diff --git a/src/view/styles.js b/src/view/styles.js index fd1a2cd4e..ac26f5da9 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -10,9 +10,10 @@ import { get, has, isObject, merge, set, unset } from 'lodash-es'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; + import BorderStyles from './styles/borderstyles'; -import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } from './styles/utils'; import MarginStyles from './styles/marginstyles'; +import PaddingStyles from './styles/paddingstyles'; class StylesConverter { /** @@ -168,18 +169,6 @@ mix( StylesConverter, EmitterMixin ); export const stylesConverter = new StylesConverter(); -class PaddingStyles { - static attach( stylesConverter ) { - stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); - stylesConverter.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); - stylesConverter.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); - stylesConverter.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); - stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); - - stylesConverter.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); - } -} - class BackgroundStyles { static attach( stylesConverter ) { stylesConverter.on( 'normalize:background', normalizeBackground ); diff --git a/src/view/styles/paddingstyles.js b/src/view/styles/paddingstyles.js new file mode 100644 index 000000000..d6b053823 --- /dev/null +++ b/src/view/styles/paddingstyles.js @@ -0,0 +1,22 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } from './utils'; + +/** + * @module engine/view/styles + */ + +export default class PaddingStyles { + static attach( stylesConverter ) { + stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); + stylesConverter.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); + stylesConverter.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); + stylesConverter.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); + stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); + + stylesConverter.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); + } +} From e2d44b90048c5a137e1da2d41e1e95c724490f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 14:41:30 +0200 Subject: [PATCH 061/142] Extract BackgroundStyles normalizer. --- src/view/styles.js | 67 ++--------------------------- src/view/styles/backgroundstyles.js | 49 +++++++++++++++++++++ src/view/styles/utils.js | 17 ++++++++ 3 files changed, 69 insertions(+), 64 deletions(-) create mode 100644 src/view/styles/backgroundstyles.js diff --git a/src/view/styles.js b/src/view/styles.js index ac26f5da9..3dca62147 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -14,8 +14,9 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix'; import BorderStyles from './styles/borderstyles'; import MarginStyles from './styles/marginstyles'; import PaddingStyles from './styles/paddingstyles'; +import BackgroundStyles from './styles/backgroundstyles'; -class StylesConverter { +export class StylesConverter { /** * Holds shorthand properties normalizers. * @@ -167,22 +168,9 @@ class StylesConverter { mix( StylesConverter, EmitterMixin ); +// TODO: It's a singleton because it needs to be the same object for all view/Elements instances. export const stylesConverter = new StylesConverter(); -class BackgroundStyles { - static attach( stylesConverter ) { - stylesConverter.on( 'normalize:background', normalizeBackground ); - stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); - stylesConverter.on( 'reduce:background', ( evt, data ) => { - const ret = []; - - ret.push( [ 'background-color', data.value.color ] ); - - data.reduced = ret; - } ); - } -} - BorderStyles.attach( stylesConverter ); MarginStyles.attach( stylesConverter ); PaddingStyles.attach( stylesConverter ); @@ -391,55 +379,6 @@ export default class Styles { } } -function normalizeBackground( evt, data ) { - const background = {}; - - const parts = data.value.split( ' ' ); - - for ( const part of parts ) { - if ( isRepeat( part ) ) { - background.repeat = background.repeat || []; - background.repeat.push( part ); - } else if ( isPosition( part ) ) { - background.position = background.position || []; - background.position.push( part ); - } else if ( isAttachment( part ) ) { - background.attachment = part; - } else if ( isColor( part ) ) { - background.color = part; - } else if ( isURL( part ) ) { - background.image = part; - } - } - - data.path = 'background'; - data.value = background; -} - -function isColor( string ) { - return /^([#0-9A-Fa-f]{3,8}|[a-zA-Z]+)$/.test( string ) && !isLineStyle( string ); -} - -function isLineStyle( string ) { - return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); -} - -function isRepeat( string ) { - return /^(repeat-x|repeat-y|repeat|space|round|no-repeat)$/.test( string ); -} - -function isPosition( string ) { - return /^(center|top|bottom|left|right)$/.test( string ); -} - -function isAttachment( string ) { - return /^(fixed|scroll|local)$/.test( string ); -} - -function isURL( string ) { - return /^url\(/.test( string ); -} - // Parses inline styles and puts property - value pairs into styles map. // // @param {String} stylesString Styles to parse. diff --git a/src/view/styles/backgroundstyles.js b/src/view/styles/backgroundstyles.js new file mode 100644 index 000000000..b4a0db5c7 --- /dev/null +++ b/src/view/styles/backgroundstyles.js @@ -0,0 +1,49 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; + +/** + * @module engine/view/styles + */ + +export default class BackgroundStyles { + static attach( stylesConverter ) { + stylesConverter.on( 'normalize:background', normalizeBackground ); + stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); + stylesConverter.on( 'reduce:background', ( evt, data ) => { + const ret = []; + + ret.push( [ 'background-color', data.value.color ] ); + + data.reduced = ret; + } ); + } +} + +function normalizeBackground( evt, data ) { + const background = {}; + + const parts = data.value.split( ' ' ); + + for ( const part of parts ) { + if ( isRepeat( part ) ) { + background.repeat = background.repeat || []; + background.repeat.push( part ); + } else if ( isPosition( part ) ) { + background.position = background.position || []; + background.position.push( part ); + } else if ( isAttachment( part ) ) { + background.attachment = part; + } else if ( isColor( part ) ) { + background.color = part; + } else if ( isURL( part ) ) { + background.image = part; + } + } + + data.path = 'background'; + data.value = background; +} diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index 18700d18a..df4ddc736 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -86,3 +86,20 @@ export function getPositionShorthandNormalizer( longhand ) { data.value = getTopRightBottomLeftValues( data.value ); }; } + +export function isRepeat( string ) { + return /^(repeat-x|repeat-y|repeat|space|round|no-repeat)$/.test( string ); +} + +export function isPosition( string ) { + return /^(center|top|bottom|left|right)$/.test( string ); +} + +export function isAttachment( string ) { + return /^(fixed|scroll|local)$/.test( string ); +} + +export function isURL( string ) { + return /^url\(/.test( string ); +} + From 7eec7b2dbe68d1028d52d4e984cb26b046d083e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 14:48:07 +0200 Subject: [PATCH 062/142] Extract particular styles tests to own files. --- tests/view/styles.js | 672 -------------------------- tests/view/styles/backgroundstyles.js | 47 ++ tests/view/styles/borderstyles.js | 406 ++++++++++++++++ tests/view/styles/marginstyles.js | 176 +++++++ tests/view/styles/paddingstyles.js | 79 +++ 5 files changed, 708 insertions(+), 672 deletions(-) create mode 100644 tests/view/styles/backgroundstyles.js create mode 100644 tests/view/styles/borderstyles.js create mode 100644 tests/view/styles/marginstyles.js create mode 100644 tests/view/styles/paddingstyles.js diff --git a/tests/view/styles.js b/tests/view/styles.js index a19d95280..7393ea35f 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -224,676 +224,4 @@ describe( 'Styles', () => { expect( styles.getStyleNames() ).to.deep.equal( [ 'margin' ] ); } ); } ); - - describe( 'styles rules', () => { - describe( 'border', () => { - it( 'should parse border shorthand', () => { - styles.setStyle( 'border:1px solid blue;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, - style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, - width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } - } ); - } ); - - it( 'should parse border shorthand with only style', () => { - styles.setStyle( 'border:solid;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: undefined, right: undefined, bottom: undefined, left: undefined }, - style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, - width: { top: undefined, right: undefined, bottom: undefined, left: undefined } - } ); - } ); - - it( 'should parse border shorthand with other shorthands', () => { - styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: '#ccc', right: 'blue', bottom: 'blue', left: '#665511' }, - style: { top: 'dotted', right: 'solid', bottom: 'solid', left: 'dashed' }, - width: { top: '7px', right: '1px', bottom: '1px', left: '2.7em' } - } ); - } ); - - it( 'should parse border longhand', () => { - styles.setStyle( 'border-color: #f00 #ba2;' + - 'border-style: solid;' + - 'border-width: 1px;' + - 'border-bottom-width: 2px;' + - 'border-right-style: dotted;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: '#f00', right: '#ba2', bottom: '#f00', left: '#ba2' }, - style: { top: 'solid', right: 'dotted', bottom: 'solid', left: 'solid' }, - width: { top: '1px', right: '1px', bottom: '2px', left: '1px' } - } ); - } ); - - it( 'should output inline shorthand rules #1', () => { - styles.setStyle( 'border:1px solid blue;' ); - - expect( styles.getInlineStyle() ).to.equal( - 'border-top:1px solid blue;border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' - ); - expect( styles.getInlineProperty( 'border-color' ) ).to.equal( 'blue' ); - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); - } ); - - it( 'should output only defined inline styles', () => { - styles.insertProperty( 'border-color', { top: 'blue' } ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: 'blue' } - } ); - - expect( styles.getInlineStyle( 'border' ) ).to.equal( 'border-top:blue;' ); - // TODO: expect( styles.hasProperty( 'border-top-color' ) ).to.be.true; - // expect( styles.getInlineProperty( 'border-top-color' ) ).to.equal( 'blue' ); - } ); - - it( 'should output inline shorthand rules #2', () => { - styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); - - expect( styles.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' - ); - - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); - } ); - - it( 'should parse border + border-position(only color defined)', () => { - styles.setStyle( 'border:1px solid blue;border-left:#665511;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: 'blue', right: 'blue', bottom: 'blue', left: '#665511' }, - style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, - width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } - } ); - } ); - - it( 'should parse border + border-position(only style defined)', () => { - styles.setStyle( 'border:1px solid blue;border-left:ridge;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, - style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'ridge' }, - width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } - } ); - } ); - - it( 'should parse border + border-position(only width defined)', () => { - styles.setStyle( 'border:1px solid blue;border-left:1337px' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, - style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, - width: { top: '1px', right: '1px', bottom: '1px', left: '1337px' } - } ); - } ); - - it( 'should merge rules on insert other shorthand', () => { - styles.setStyle( 'border:1px solid blue;' ); - styles.insertProperty( 'border-left', '#665511 dashed 2.7em' ); - styles.insertProperty( 'border-top', '7px dotted #ccc' ); - - expect( styles.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' - ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); - } ); - - it( 'should output', () => { - styles.setStyle( 'border:1px solid blue;' ); - styles.removeProperty( 'border-color' ); - - expect( styles.getInlineStyle() ).to.equal( - 'border-top:1px solid;border-right:1px solid;border-bottom:1px solid;border-left:1px solid;' - ); - - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid' ); - } ); - - it( 'should output border with only style shorthand (style)', () => { - styles.setStyle( 'border:solid;' ); - - expect( styles.getInlineStyle() ).to.equal( 'border-top:solid;border-right:solid;border-bottom:solid;border-left:solid;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'solid' ); - } ); - - it( 'should output border with only style shorthand (color)', () => { - styles.setStyle( 'border:#f00;' ); - - expect( styles.getInlineStyle() ).to.equal( 'border-top:#f00;border-right:#f00;border-bottom:#f00;border-left:#f00;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#f00' ); - expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-width' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '#f00' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '#f00' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '#f00' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '#f00' ); - } ); - - it( 'should output border with only style shorthand (width)', () => { - styles.setStyle( 'border:1px;' ); - - expect( styles.getInlineStyle() ).to.equal( 'border-top:1px;border-right:1px;border-bottom:1px;border-left:1px;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px' ); - } ); - - describe( 'border-color', () => { - it( 'should set all border colors (1 value defined)', () => { - styles.setStyle( 'border-color:cyan;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { - top: 'cyan', - right: 'cyan', - bottom: 'cyan', - left: 'cyan' - } - } ); - } ); - - it( 'should set all border colors (2 values defined)', () => { - styles.setStyle( 'border-color:cyan magenta;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { - top: 'cyan', - right: 'magenta', - bottom: 'cyan', - left: 'magenta' - } - } ); - } ); - - it( 'should set all border colors (3 values defined)', () => { - styles.setStyle( 'border-color:cyan magenta pink;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { - top: 'cyan', - right: 'magenta', - bottom: 'pink', - left: 'magenta' - } - } ); - } ); - - it( 'should set all border colors (4 values defined)', () => { - styles.setStyle( 'border-color:cyan magenta pink beige;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { - top: 'cyan', - right: 'magenta', - bottom: 'pink', - left: 'beige' - } - } ); - } ); - - it( 'should merge with border shorthand', () => { - styles.setStyle( 'border:1px solid blue;border-color:cyan black;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - color: { top: 'cyan', right: 'black', bottom: 'cyan', left: 'black' }, - style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, - width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } - } ); - } ); - } ); - - describe( 'border-style', () => { - it( 'should set all border styles (1 value defined)', () => { - styles.setStyle( 'border-style:solid;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - style: { - top: 'solid', - right: 'solid', - bottom: 'solid', - left: 'solid' - } - } ); - } ); - - it( 'should set all border styles (2 values defined)', () => { - styles.setStyle( 'border-style:solid dotted;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - style: { - top: 'solid', - right: 'dotted', - bottom: 'solid', - left: 'dotted' - } - } ); - } ); - - it( 'should set all border styles (3 values defined)', () => { - styles.setStyle( 'border-style:solid dotted dashed;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - style: { - top: 'solid', - right: 'dotted', - bottom: 'dashed', - left: 'dotted' - } - } ); - } ); - - it( 'should set all border styles (4 values defined)', () => { - styles.setStyle( 'border-style:solid dotted dashed ridge;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - style: { - top: 'solid', - right: 'dotted', - bottom: 'dashed', - left: 'ridge' - } - } ); - } ); - } ); - - describe( 'border-width', () => { - it( 'should set all border widths (1 value defined)', () => { - styles.setStyle( 'border-width:1px;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - width: { - top: '1px', - right: '1px', - bottom: '1px', - left: '1px' - } - } ); - } ); - - it( 'should set all border widths (2 values defined)', () => { - styles.setStyle( 'border-width:1px .34cm;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - width: { - top: '1px', - right: '.34cm', - bottom: '1px', - left: '.34cm' - } - } ); - } ); - - it( 'should set all border widths (3 values defined)', () => { - styles.setStyle( 'border-width:1px .34cm 90.1rem;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - width: { - top: '1px', - right: '.34cm', - bottom: '90.1rem', - left: '.34cm' - } - } ); - } ); - - it( 'should set all border widths (4 values defined)', () => { - styles.setStyle( 'border-width:1px .34cm 90.1rem thick;' ); - - expect( styles.getNormalized( 'border' ) ).to.deep.equal( { - width: { - top: '1px', - right: '.34cm', - bottom: '90.1rem', - left: 'thick' - } - } ); - } ); - } ); - - describe( 'border-* position', () => { - it( 'should output all positions', () => { - styles.setStyle( - 'border-top:none;' + - 'border-left:none;' + - 'border-bottom:dotted #FFC000 3.0pt;' + - 'border-right:dotted #FFC000 3.0pt;' - ); - - expect( styles.getInlineStyle() ).to.equal( - 'border-top:none;border-right:3.0pt dotted #FFC000;border-bottom:3.0pt dotted #FFC000;border-left:none;' - ); - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'none' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '3.0pt dotted #FFC000' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '3.0pt dotted #FFC000' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'none' ); - } ); - } ); - - describe( 'getStyleNames() - border', () => { - it( 'should set all border colors (1 value defined)', () => { - styles.setStyle( ' border-color: deeppink deepskyblue;\n' + - ' border-style: solid;\n' + - ' border-width: 1px;\n' + - ' border-bottom-width: 2px;\n' + - ' border-right-style: dotted;' ); - - expect( styles.getStyleNames() ).to.deep.equal( [ - 'border-top', - 'border-right', - 'border-bottom', - 'border-left' - ] ); - } ); - } ); - } ); - - describe( 'margin', () => { - it( 'should set all margins (1 value defined)', () => { - styles.setStyle( 'margin:1px;' ); - - expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { - top: '1px', - right: '1px', - bottom: '1px', - left: '1px' - } ); - } ); - - it( 'should set all margins (2 values defined)', () => { - styles.setStyle( 'margin:1px .34cm;' ); - - expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { - top: '1px', - right: '.34cm', - bottom: '1px', - left: '.34cm' - } ); - } ); - - it( 'should set all margins (3 values defined)', () => { - styles.setStyle( 'margin:1px .34cm 90.1rem;' ); - - expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { - top: '1px', - right: '.34cm', - bottom: '90.1rem', - left: '.34cm' - } ); - } ); - - it( 'should set all margins (4 values defined)', () => { - styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); - - expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { - top: '1px', - right: '.34cm', - bottom: '90.1rem', - left: 'thick' - } ); - } ); - - it( 'should output inline style (1 value defined)', () => { - styles.setStyle( 'margin:1px;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin:1px;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); - } ); - - it( 'should output inline style (2 values defined)', () => { - styles.setStyle( 'margin:1px .34cm;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '.34cm' ); - } ); - - it( 'should output inline style (3 values defined)', () => { - styles.setStyle( 'margin:1px .34cm 90.1rem;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '.34cm' ); - } ); - - it( 'should output inline style (3 values defined, only last different)', () => { - styles.setStyle( 'margin:1px 1px 90.1rem;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); - } ); - - it( 'should output inline style (4 values defined)', () => { - styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( 'thick' ); - } ); - - it( 'should output inline style (4 values defined, only last different)', () => { - styles.setStyle( 'margin:1px 1px 1px thick;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( 'thick' ); - } ); - - describe( 'margin-*', () => { - it( 'should set proper margin', () => { - styles.setStyle( 'margin-top:1px;' ); - - expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px' } ); - expect( styles.getNormalized( 'margin-top' ) ).to.equal( '1px' ); - } ); - - it( 'should merge margin with margin shorthand', () => { - styles.setStyle( 'margin: 2em;margin-top:1px;' ); - - expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { - top: '1px', - right: '2em', - bottom: '2em', - left: '2em' - } ); - expect( styles.getNormalized( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getNormalized( 'margin-right' ) ).to.equal( '2em' ); - expect( styles.getNormalized( 'margin-bottom' ) ).to.equal( '2em' ); - expect( styles.getNormalized( 'margin-left' ) ).to.equal( '2em' ); - } ); - - it( 'should output margin-top', () => { - styles.setStyle( 'margin-top:1px;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - } ); - - it( 'should output margin-right', () => { - styles.setStyle( 'margin-right:1px;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin-right:1px;' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); - } ); - - it( 'should output margin-bottom', () => { - styles.setStyle( 'margin-bottom:1px;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin-bottom:1px;' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); - } ); - - it( 'should output margin-left', () => { - styles.setStyle( 'margin-left:1px;' ); - - expect( styles.getInlineStyle() ).to.equal( 'margin-left:1px;' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); - } ); - } ); - } ); - - describe( 'padding', () => { - it( 'should set all paddings (1 value defined)', () => { - styles.setStyle( 'padding:1px;' ); - - expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { - top: '1px', - right: '1px', - bottom: '1px', - left: '1px' - } ); - } ); - - it( 'should set all paddings (2 values defined)', () => { - styles.setStyle( 'padding:1px .34cm;' ); - - expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { - top: '1px', - right: '.34cm', - bottom: '1px', - left: '.34cm' - } ); - } ); - - it( 'should set all paddings (3 values defined)', () => { - styles.setStyle( 'padding:1px .34cm 90.1rem;' ); - - expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { - top: '1px', - right: '.34cm', - bottom: '90.1rem', - left: '.34cm' - } ); - } ); - - it( 'should set all paddings (4 values defined)', () => { - styles.setStyle( 'padding:1px .34cm 90.1rem thick;' ); - - expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { - top: '1px', - right: '.34cm', - bottom: '90.1rem', - left: 'thick' - } ); - } ); - - describe( 'padding-*', () => { - it( 'should set proper padding', () => { - styles.setStyle( 'padding-top:1px;' ); - - expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { - top: '1px' - } ); - } ); - - it( 'should set proper padding with padding shorthand', () => { - styles.setStyle( 'padding: 2em;padding-top:1px;' ); - - expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { - top: '1px', - right: '2em', - bottom: '2em', - left: '2em' - } ); - } ); - } ); - } ); - - describe( 'unknown rules', () => { - it( 'should left rules untouched', () => { - styles.setStyle( 'foo-bar:baz 1px abc;baz: 2px 3em;' ); - - expect( styles.getInlineStyle() ).to.equal( 'baz:2px 3em;foo-bar:baz 1px abc;' ); - expect( styles.getInlineProperty( 'foo-bar' ) ).to.equal( 'baz 1px abc' ); - expect( styles.getInlineProperty( 'baz' ) ).to.equal( '2px 3em' ); - } ); - } ); - - describe( 'background', () => { - it( 'should normalize background', () => { - // TODO: border-box given only for coverage test. - styles.setStyle( 'background:url("example.jpg") center #f00 repeat-y fixed border-box;' ); - - expect( styles.getNormalized( 'background' ) ).to.deep.equal( { - attachment: 'fixed', - image: 'url("example.jpg")', - position: [ 'center' ], - repeat: [ 'repeat-y' ], - color: '#f00' - } ); - } ); - - // TODO: define what should happen with layers - it.skip( 'should normalize background with layers', () => { - styles.setStyle( 'background:url("test.jpg") repeat-y,#f00;' ); - - expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); - } ); - - it( 'should normalize background-color', () => { - styles.setStyle( 'background-color:#f00;' ); - - expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); - } ); - - it( 'should output inline background-color style', () => { - styles.setStyle( 'background:#f00;' ); - - expect( styles.getInlineStyle() ).to.equal( 'background-color:#f00;' ); - expect( styles.getInlineProperty( 'background-color' ) ).to.equal( '#f00' ); - } ); - } ); - } ); } ); diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js new file mode 100644 index 000000000..54ceb2d97 --- /dev/null +++ b/tests/view/styles/backgroundstyles.js @@ -0,0 +1,47 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import Styles from '../../../src/view/styles'; + +describe( 'Background styles normalization', () => { + let styles; + + beforeEach( () => { + styles = new Styles(); + } ); + + it( 'should normalize background', () => { + // TODO: border-box given only for coverage test. + styles.setStyle( 'background:url("example.jpg") center #f00 repeat-y fixed border-box;' ); + + expect( styles.getNormalized( 'background' ) ).to.deep.equal( { + attachment: 'fixed', + image: 'url("example.jpg")', + position: [ 'center' ], + repeat: [ 'repeat-y' ], + color: '#f00' + } ); + } ); + + // TODO: define what should happen with layers + it.skip( 'should normalize background with layers', () => { + styles.setStyle( 'background:url("test.jpg") repeat-y,#f00;' ); + + expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); + } ); + + it( 'should normalize background-color', () => { + styles.setStyle( 'background-color:#f00;' ); + + expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); + } ); + + it( 'should output inline background-color style', () => { + styles.setStyle( 'background:#f00;' ); + + expect( styles.getInlineStyle() ).to.equal( 'background-color:#f00;' ); + expect( styles.getInlineProperty( 'background-color' ) ).to.equal( '#f00' ); + } ); +} ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js new file mode 100644 index 000000000..dc6b14581 --- /dev/null +++ b/tests/view/styles/borderstyles.js @@ -0,0 +1,406 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import Styles from '../../../src/view/styles'; + +describe( 'Border styles normalization', () => { + let styles; + + beforeEach( () => { + styles = new Styles(); + } ); + + it( 'should parse border shorthand', () => { + styles.setStyle( 'border:1px solid blue;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } + } ); + } ); + + it( 'should parse border shorthand with only style', () => { + styles.setStyle( 'border:solid;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: undefined, right: undefined, bottom: undefined, left: undefined }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: undefined, right: undefined, bottom: undefined, left: undefined } + } ); + } ); + + it( 'should parse border shorthand with other shorthands', () => { + styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: '#ccc', right: 'blue', bottom: 'blue', left: '#665511' }, + style: { top: 'dotted', right: 'solid', bottom: 'solid', left: 'dashed' }, + width: { top: '7px', right: '1px', bottom: '1px', left: '2.7em' } + } ); + } ); + + it( 'should parse border longhand', () => { + styles.setStyle( 'border-color: #f00 #ba2;' + + 'border-style: solid;' + + 'border-width: 1px;' + + 'border-bottom-width: 2px;' + + 'border-right-style: dotted;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: '#f00', right: '#ba2', bottom: '#f00', left: '#ba2' }, + style: { top: 'solid', right: 'dotted', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '2px', left: '1px' } + } ); + } ); + + it( 'should output inline shorthand rules #1', () => { + styles.setStyle( 'border:1px solid blue;' ); + + expect( styles.getInlineStyle() ).to.equal( + 'border-top:1px solid blue;border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' + ); + expect( styles.getInlineProperty( 'border-color' ) ).to.equal( 'blue' ); + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); + } ); + + it( 'should output only defined inline styles', () => { + styles.insertProperty( 'border-color', { top: 'blue' } ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue' } + } ); + + expect( styles.getInlineStyle( 'border' ) ).to.equal( 'border-top:blue;' ); + // TODO: expect( styles.hasProperty( 'border-top-color' ) ).to.be.true; + // expect( styles.getInlineProperty( 'border-top-color' ) ).to.equal( 'blue' ); + } ); + + it( 'should output inline shorthand rules #2', () => { + styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + + expect( styles.getInlineStyle() ).to.equal( + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + ); + + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); + } ); + + it( 'should parse border + border-position(only color defined)', () => { + styles.setStyle( 'border:1px solid blue;border-left:#665511;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue', right: 'blue', bottom: 'blue', left: '#665511' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } + } ); + } ); + + it( 'should parse border + border-position(only style defined)', () => { + styles.setStyle( 'border:1px solid blue;border-left:ridge;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'ridge' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } + } ); + } ); + + it( 'should parse border + border-position(only width defined)', () => { + styles.setStyle( 'border:1px solid blue;border-left:1337px' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1337px' } + } ); + } ); + + it( 'should merge rules on insert other shorthand', () => { + styles.setStyle( 'border:1px solid blue;' ); + styles.insertProperty( 'border-left', '#665511 dashed 2.7em' ); + styles.insertProperty( 'border-top', '7px dotted #ccc' ); + + expect( styles.getInlineStyle() ).to.equal( + 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); + } ); + + it( 'should output', () => { + styles.setStyle( 'border:1px solid blue;' ); + styles.removeProperty( 'border-color' ); + + expect( styles.getInlineStyle() ).to.equal( + 'border-top:1px solid;border-right:1px solid;border-bottom:1px solid;border-left:1px solid;' + ); + + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid' ); + } ); + + it( 'should output border with only style shorthand (style)', () => { + styles.setStyle( 'border:solid;' ); + + expect( styles.getInlineStyle() ).to.equal( 'border-top:solid;border-right:solid;border-bottom:solid;border-left:solid;' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-width' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( 'solid' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'solid' ); + } ); + + it( 'should output border with only style shorthand (color)', () => { + styles.setStyle( 'border:#f00;' ); + + expect( styles.getInlineStyle() ).to.equal( 'border-top:#f00;border-right:#f00;border-bottom:#f00;border-left:#f00;' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#f00' ); + expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-width' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '#f00' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '#f00' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '#f00' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '#f00' ); + } ); + + it( 'should output border with only style shorthand (width)', () => { + styles.setStyle( 'border:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'border-top:1px;border-right:1px;border-bottom:1px;border-left:1px;' ); + expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; + expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px' ); + } ); + + describe( 'border-color', () => { + it( 'should set all border colors (1 value defined)', () => { + styles.setStyle( 'border-color:cyan;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { + top: 'cyan', + right: 'cyan', + bottom: 'cyan', + left: 'cyan' + } + } ); + } ); + + it( 'should set all border colors (2 values defined)', () => { + styles.setStyle( 'border-color:cyan magenta;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { + top: 'cyan', + right: 'magenta', + bottom: 'cyan', + left: 'magenta' + } + } ); + } ); + + it( 'should set all border colors (3 values defined)', () => { + styles.setStyle( 'border-color:cyan magenta pink;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { + top: 'cyan', + right: 'magenta', + bottom: 'pink', + left: 'magenta' + } + } ); + } ); + + it( 'should set all border colors (4 values defined)', () => { + styles.setStyle( 'border-color:cyan magenta pink beige;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { + top: 'cyan', + right: 'magenta', + bottom: 'pink', + left: 'beige' + } + } ); + } ); + + it( 'should merge with border shorthand', () => { + styles.setStyle( 'border:1px solid blue;border-color:cyan black;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + color: { top: 'cyan', right: 'black', bottom: 'cyan', left: 'black' }, + style: { top: 'solid', right: 'solid', bottom: 'solid', left: 'solid' }, + width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } + } ); + } ); + } ); + + describe( 'border-style', () => { + it( 'should set all border styles (1 value defined)', () => { + styles.setStyle( 'border-style:solid;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + style: { + top: 'solid', + right: 'solid', + bottom: 'solid', + left: 'solid' + } + } ); + } ); + + it( 'should set all border styles (2 values defined)', () => { + styles.setStyle( 'border-style:solid dotted;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + style: { + top: 'solid', + right: 'dotted', + bottom: 'solid', + left: 'dotted' + } + } ); + } ); + + it( 'should set all border styles (3 values defined)', () => { + styles.setStyle( 'border-style:solid dotted dashed;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + style: { + top: 'solid', + right: 'dotted', + bottom: 'dashed', + left: 'dotted' + } + } ); + } ); + + it( 'should set all border styles (4 values defined)', () => { + styles.setStyle( 'border-style:solid dotted dashed ridge;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + style: { + top: 'solid', + right: 'dotted', + bottom: 'dashed', + left: 'ridge' + } + } ); + } ); + } ); + + describe( 'border-width', () => { + it( 'should set all border widths (1 value defined)', () => { + styles.setStyle( 'border-width:1px;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + width: { + top: '1px', + right: '1px', + bottom: '1px', + left: '1px' + } + } ); + } ); + + it( 'should set all border widths (2 values defined)', () => { + styles.setStyle( 'border-width:1px .34cm;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + width: { + top: '1px', + right: '.34cm', + bottom: '1px', + left: '.34cm' + } + } ); + } ); + + it( 'should set all border widths (3 values defined)', () => { + styles.setStyle( 'border-width:1px .34cm 90.1rem;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + width: { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: '.34cm' + } + } ); + } ); + + it( 'should set all border widths (4 values defined)', () => { + styles.setStyle( 'border-width:1px .34cm 90.1rem thick;' ); + + expect( styles.getNormalized( 'border' ) ).to.deep.equal( { + width: { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: 'thick' + } + } ); + } ); + } ); + + describe( 'border-* position', () => { + it( 'should output all positions', () => { + styles.setStyle( + 'border-top:none;' + + 'border-left:none;' + + 'border-bottom:dotted #FFC000 3.0pt;' + + 'border-right:dotted #FFC000 3.0pt;' + ); + + expect( styles.getInlineStyle() ).to.equal( + 'border-top:none;border-right:3.0pt dotted #FFC000;border-bottom:3.0pt dotted #FFC000;border-left:none;' + ); + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'none' ); + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '3.0pt dotted #FFC000' ); + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '3.0pt dotted #FFC000' ); + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'none' ); + } ); + } ); + + describe( 'getStyleNames() - border', () => { + it( 'should set all border colors (1 value defined)', () => { + styles.setStyle( ' border-color: deeppink deepskyblue;\n' + + ' border-style: solid;\n' + + ' border-width: 1px;\n' + + ' border-bottom-width: 2px;\n' + + ' border-right-style: dotted;' ); + + expect( styles.getStyleNames() ).to.deep.equal( [ + 'border-top', + 'border-right', + 'border-bottom', + 'border-left' + ] ); + } ); + } ); +} ); diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js new file mode 100644 index 000000000..3e8b69bd6 --- /dev/null +++ b/tests/view/styles/marginstyles.js @@ -0,0 +1,176 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import Styles from '../../../src/view/styles'; + +describe( 'Margin styles normalizer', () => { + let styles; + + beforeEach( () => { + styles = new Styles(); + } ); + + it( 'should set all margins (1 value defined)', () => { + styles.setStyle( 'margin:1px;' ); + + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '1px', + bottom: '1px', + left: '1px' + } ); + } ); + + it( 'should set all margins (2 values defined)', () => { + styles.setStyle( 'margin:1px .34cm;' ); + + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '1px', + left: '.34cm' + } ); + } ); + + it( 'should set all margins (3 values defined)', () => { + styles.setStyle( 'margin:1px .34cm 90.1rem;' ); + + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: '.34cm' + } ); + } ); + + it( 'should set all margins (4 values defined)', () => { + styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); + + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: 'thick' + } ); + } ); + + it( 'should output inline style (1 value defined)', () => { + styles.setStyle( 'margin:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); + } ); + + it( 'should output inline style (2 values defined)', () => { + styles.setStyle( 'margin:1px .34cm;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '.34cm' ); + } ); + + it( 'should output inline style (3 values defined)', () => { + styles.setStyle( 'margin:1px .34cm 90.1rem;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '.34cm' ); + } ); + + it( 'should output inline style (3 values defined, only last different)', () => { + styles.setStyle( 'margin:1px 1px 90.1rem;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); + } ); + + it( 'should output inline style (4 values defined)', () => { + styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( 'thick' ); + } ); + + it( 'should output inline style (4 values defined, only last different)', () => { + styles.setStyle( 'margin:1px 1px 1px thick;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); + expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( 'thick' ); + } ); + + describe( 'margin-*', () => { + it( 'should set proper margin', () => { + styles.setStyle( 'margin-top:1px;' ); + + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px' } ); + expect( styles.getNormalized( 'margin-top' ) ).to.equal( '1px' ); + } ); + + it( 'should merge margin with margin shorthand', () => { + styles.setStyle( 'margin: 2em;margin-top:1px;' ); + + expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { + top: '1px', + right: '2em', + bottom: '2em', + left: '2em' + } ); + expect( styles.getNormalized( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getNormalized( 'margin-right' ) ).to.equal( '2em' ); + expect( styles.getNormalized( 'margin-bottom' ) ).to.equal( '2em' ); + expect( styles.getNormalized( 'margin-left' ) ).to.equal( '2em' ); + } ); + + it( 'should output margin-top', () => { + styles.setStyle( 'margin-top:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;' ); + expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + } ); + + it( 'should output margin-right', () => { + styles.setStyle( 'margin-right:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin-right:1px;' ); + expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + } ); + + it( 'should output margin-bottom', () => { + styles.setStyle( 'margin-bottom:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin-bottom:1px;' ); + expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + } ); + + it( 'should output margin-left', () => { + styles.setStyle( 'margin-left:1px;' ); + + expect( styles.getInlineStyle() ).to.equal( 'margin-left:1px;' ); + expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); + } ); + } ); +} ); diff --git a/tests/view/styles/paddingstyles.js b/tests/view/styles/paddingstyles.js new file mode 100644 index 000000000..db4f17616 --- /dev/null +++ b/tests/view/styles/paddingstyles.js @@ -0,0 +1,79 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import Styles from '../../../src/view/styles'; + +describe( 'Padding styles normalization', () => { + let styles; + + beforeEach( () => { + styles = new Styles(); + } ); + + it( 'should set all paddings (1 value defined)', () => { + styles.setStyle( 'padding:1px;' ); + + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '1px', + bottom: '1px', + left: '1px' + } ); + } ); + + it( 'should set all paddings (2 values defined)', () => { + styles.setStyle( 'padding:1px .34cm;' ); + + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '1px', + left: '.34cm' + } ); + } ); + + it( 'should set all paddings (3 values defined)', () => { + styles.setStyle( 'padding:1px .34cm 90.1rem;' ); + + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: '.34cm' + } ); + } ); + + it( 'should set all paddings (4 values defined)', () => { + styles.setStyle( 'padding:1px .34cm 90.1rem thick;' ); + + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '.34cm', + bottom: '90.1rem', + left: 'thick' + } ); + } ); + + describe( 'padding-*', () => { + it( 'should set proper padding', () => { + styles.setStyle( 'padding-top:1px;' ); + + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { + top: '1px' + } ); + } ); + + it( 'should set proper padding with padding shorthand', () => { + styles.setStyle( 'padding: 2em;padding-top:1px;' ); + + expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { + top: '1px', + right: '2em', + bottom: '2em', + left: '2em' + } ); + } ); + } ); +} ); From dcf0f1c3185a6872347626d6770ee94ab8c75fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 14:55:47 +0200 Subject: [PATCH 063/142] Parse styles independently. --- src/view/styles.js | 20 +++++++++++--------- tests/view/styles/backgroundstyles.js | 7 +++++-- tests/view/styles/borderstyles.js | 7 +++++-- tests/view/styles/marginstyles.js | 7 +++++-- tests/view/styles/paddingstyles.js | 7 +++++-- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 3dca62147..0c7e423a2 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -93,7 +93,7 @@ export class StylesConverter { * @param {Object|String} normalizedValue * @returns {Array.>} */ - _getReduceForm( styleName, normalizedValue ) { + getReduceForm( styleName, normalizedValue ) { const data = { value: normalizedValue }; @@ -148,7 +148,7 @@ export class StylesConverter { * @param {Object} styles * @private */ - _toNormalizedForm( propertyName, value, styles ) { + toNormalizedForm( propertyName, value, styles ) { if ( isObject( value ) ) { appendStyleValue( styles, toPath( propertyName ), value ); @@ -185,12 +185,14 @@ export default class Styles { /** * Creates Styles instance. */ - constructor() { + constructor( converter = stylesConverter ) { /** * @type {{}} * @private */ this._styles = {}; + + this.converter = converter; } /** @@ -215,7 +217,7 @@ export default class Styles { for ( const key of map.keys() ) { const value = map.get( key ); - stylesConverter._toNormalizedForm( key, value, this._styles ); + this.converter.toNormalizedForm( key, value, this._styles ); } } @@ -260,7 +262,7 @@ export default class Styles { this.insertProperty( key, nameOrObject[ key ] ); } } else { - stylesConverter._toNormalizedForm( nameOrObject, value, this._styles ); + this.converter.toNormalizedForm( nameOrObject, value, this._styles ); } } @@ -293,7 +295,7 @@ export default class Styles { * @returns {Object|undefined} */ getNormalized( name ) { - return stylesConverter.getNormalized( name, this._styles ); + return this.converter.getNormalized( name, this._styles ); } /** @@ -327,7 +329,7 @@ export default class Styles { } if ( isObject( normalized ) ) { - const styles = stylesConverter._getReduceForm( propertyName, normalized ); + const styles = stylesConverter.getReduceForm( propertyName, normalized ); const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); @@ -370,9 +372,9 @@ export default class Styles { const keys = Object.keys( this._styles ).sort(); for ( const key of keys ) { - const normalized = stylesConverter.getNormalized( key, this._styles ); + const normalized = this.converter.getNormalized( key, this._styles ); - parsed.push( ...stylesConverter._getReduceForm( key, normalized ) ); + parsed.push( ...this.converter.getReduceForm( key, normalized ) ); } return parsed; diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index 54ceb2d97..f940ad2c9 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -3,13 +3,16 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles from '../../../src/view/styles'; +import Styles, { StylesConverter } from '../../../src/view/styles'; +import BackgroundStyles from '../../../src/view/styles/backgroundstyles'; describe( 'Background styles normalization', () => { let styles; beforeEach( () => { - styles = new Styles(); + const converter = new StylesConverter(); + BackgroundStyles.attach( converter ); + styles = new Styles( converter ); } ); it( 'should normalize background', () => { diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index dc6b14581..07fe7251a 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -3,13 +3,16 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles from '../../../src/view/styles'; +import Styles, { StylesConverter } from '../../../src/view/styles'; +import BorderStyles from '../../../src/view/styles/borderstyles'; describe( 'Border styles normalization', () => { let styles; beforeEach( () => { - styles = new Styles(); + const converter = new StylesConverter(); + BorderStyles.attach( converter ); + styles = new Styles( converter ); } ); it( 'should parse border shorthand', () => { diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index 3e8b69bd6..565d4c5fc 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -3,13 +3,16 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles from '../../../src/view/styles'; +import Styles, { StylesConverter } from '../../../src/view/styles'; +import MarginStyles from '../../../src/view/styles/marginstyles'; describe( 'Margin styles normalizer', () => { let styles; beforeEach( () => { - styles = new Styles(); + const converter = new StylesConverter(); + MarginStyles.attach( converter ); + styles = new Styles( converter ); } ); it( 'should set all margins (1 value defined)', () => { diff --git a/tests/view/styles/paddingstyles.js b/tests/view/styles/paddingstyles.js index db4f17616..623fc0abf 100644 --- a/tests/view/styles/paddingstyles.js +++ b/tests/view/styles/paddingstyles.js @@ -3,13 +3,16 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles from '../../../src/view/styles'; +import Styles, { StylesConverter } from '../../../src/view/styles'; +import PaddingStyles from '../../../src/view/styles/paddingstyles'; describe( 'Padding styles normalization', () => { let styles; beforeEach( () => { - styles = new Styles(); + const converter = new StylesConverter(); + PaddingStyles.attach( converter ); + styles = new Styles( converter ); } ); it( 'should set all paddings (1 value defined)', () => { From b63d527a8a5144ae04f71c5c7d4c08a86101726d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 15:09:25 +0200 Subject: [PATCH 064/142] Generalize tests for Styles class. --- tests/view/styles.js | 55 +++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/tests/view/styles.js b/tests/view/styles.js index 7393ea35f..8f53928af 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -3,14 +3,28 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles from '../../src/view/styles'; +import Styles, { StylesConverter } from '../../src/view/styles'; import encodedImage from './_utils/encodedimage.txt'; +import PaddingStyles from '../../src/view/styles/paddingstyles'; describe( 'Styles', () => { - let styles; + let styles, converter; beforeEach( () => { - styles = new Styles(); + converter = new StylesConverter(); + + // Define simple "foo" shorthand normalizers, similar to the "margin" shorthand normalizers, for testing purposes. + converter.on( 'normalize:foo', ( evt, data ) => { + data.path = 'foo'; + data.value = { top: data.value, right: data.value, bottom: data.value, left: data.value }; + } ); + converter.on( 'normalize:foo-top', ( evt, data ) => { + data.path = 'foo'; + data.value = { top: data.value }; + } ); + + PaddingStyles.attach( converter ); + styles = new Styles( converter ); } ); describe( 'size getter', () => { @@ -32,13 +46,13 @@ describe( 'Styles', () => { describe( 'setStyle()', () => { it( 'should reset styles to a new value', () => { - styles.setStyle( 'color:red;margin-top:1px;' ); + styles.setStyle( 'color:red;margin:1px;' ); - expect( styles.getNormalized() ).to.deep.equal( { color: 'red', margin: { top: '1px' } } ); + expect( styles.getNormalized() ).to.deep.equal( { color: 'red', margin: '1px' } ); - styles.setStyle( 'margin-bottom:2em;' ); + styles.setStyle( 'overflow:hidden;' ); - expect( styles.getNormalized() ).to.deep.equal( { margin: { bottom: '2em' } } ); + expect( styles.getNormalized() ).to.deep.equal( { overflow: 'hidden' } ); } ); describe( 'styles parsing edge cases and incorrect styles', () => { @@ -126,11 +140,9 @@ describe( 'Styles', () => { } ); it( 'should return false if normalized property is not set', () => { - styles.setStyle( 'margin-top:1px' ); + styles.setStyle( 'foo-top:1px' ); - // TODO - // expect( styles.hasProperty( 'margin' ) ).to.be.false; - expect( styles.hasProperty( 'margin' ) ).to.be.true; + expect( styles.hasProperty( 'foo' ) ).to.be.true; } ); it( 'should return true if property is set', () => { @@ -140,9 +152,10 @@ describe( 'Styles', () => { } ); it( 'should return true if normalized shorthanded property is set', () => { - styles.setStyle( 'margin:1px 2px 3px 4px' ); + styles.setStyle( 'foo:1px' ); - expect( styles.hasProperty( 'margin-top' ) ).to.be.true; + expect( styles.hasProperty( 'foo' ) ).to.be.true; + expect( styles.hasProperty( 'foo-top' ) ).to.be.true; } ); } ); @@ -169,18 +182,18 @@ describe( 'Styles', () => { it( 'should set multiple styles by providing an object', () => { styles.setStyle( 'color: red;' ); - styles.insertProperty( { color: 'blue', margin: '1px' } ); + styles.insertProperty( { color: 'blue', foo: '1px' } ); expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'foo-top' ) ).to.equal( '1px' ); } ); it( 'should set object property', () => { - styles.setStyle( 'margin:1px;' ); - styles.insertProperty( 'margin', { right: '2px' } ); + styles.setStyle( 'foo:1px;' ); + styles.insertProperty( 'foo', { right: '2px' } ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '2px' ); + expect( styles.getInlineProperty( 'foo-left' ) ).to.equal( '1px' ); + expect( styles.getInlineProperty( 'foo-right' ) ).to.equal( '2px' ); } ); } ); @@ -219,9 +232,9 @@ describe( 'Styles', () => { } ); it( 'should output full names for known style names', () => { - styles.setStyle( 'margin: 1px;margin-left: 2em;' ); + styles.setStyle( 'foo: 1px;foo-top: 2em;' ); - expect( styles.getStyleNames() ).to.deep.equal( [ 'margin' ] ); + expect( styles.getStyleNames() ).to.deep.equal( [ 'foo' ] ); } ); } ); } ); From 43ea44eb1a2265f5d753e349e859c037723a8074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 19:30:51 +0200 Subject: [PATCH 065/142] Fix border styles normalization code. Add tests for utils methods. --- src/view/styles.js | 8 +- src/view/styles/borderstyles.js | 128 ++++++++++---------- src/view/styles/utils.js | 59 ++++----- tests/view/styles/borderstyles.js | 194 +++++++++++++++++++++++++++++- tests/view/styles/utils.js | 72 +++++++++++ 5 files changed, 359 insertions(+), 102 deletions(-) create mode 100644 tests/view/styles/utils.js diff --git a/src/view/styles.js b/src/view/styles.js index 0c7e423a2..537acc801 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -93,7 +93,7 @@ export class StylesConverter { * @param {Object|String} normalizedValue * @returns {Array.>} */ - getReduceForm( styleName, normalizedValue ) { + getReducedForm( styleName, normalizedValue ) { const data = { value: normalizedValue }; @@ -321,7 +321,7 @@ export default class Styles { * @returns {String|undefined} */ getInlineProperty( propertyName ) { - const normalized = stylesConverter.getNormalized( propertyName, this._styles ); + const normalized = this.converter.getNormalized( propertyName, this._styles ); if ( !normalized ) { // Try return styles set directly - values that are not parsed. @@ -329,7 +329,7 @@ export default class Styles { } if ( isObject( normalized ) ) { - const styles = stylesConverter.getReduceForm( propertyName, normalized ); + const styles = this.converter.getReducedForm( propertyName, normalized ); const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); @@ -374,7 +374,7 @@ export default class Styles { for ( const key of keys ) { const normalized = this.converter.getNormalized( key, this._styles ); - parsed.push( ...this.converter.getReduceForm( key, normalized ) ); + parsed.push( ...this.converter.getReducedForm( key, normalized ) ); } return parsed; diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index c66cc63b4..06da345b1 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -3,14 +3,14 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import { getTopRightBottomLeftValues, getTopRightBottomLeftValueReducer, isColor, isLength, isLineStyle } from './utils'; +import { getParts, getTopRightBottomLeftValueReducer, getTopRightBottomLeftValues, isLength, isLineStyle } from './utils'; /** * @module engine/view/styles */ export default class BorderStyles { static attach( stylesConverter ) { - stylesConverter.on( 'normalize:border', normalizeBorder ); + stylesConverter.on( 'normalize:border', borderNormalizer ); // Border-position shorthands. stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); @@ -40,10 +40,10 @@ export default class BorderStyles { stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); - stylesConverter.on( 'extract:border-top', borderPositionExtractor( 'top' ) ); - stylesConverter.on( 'extract:border-right', borderPositionExtractor( 'right' ) ); - stylesConverter.on( 'extract:border-bottom', borderPositionExtractor( 'bottom' ) ); - stylesConverter.on( 'extract:border-left', borderPositionExtractor( 'left' ) ); + stylesConverter.on( 'extract:border-top', getBorderPositionExtractor( 'top' ) ); + stylesConverter.on( 'extract:border-right', getBorderPositionExtractor( 'right' ) ); + stylesConverter.on( 'extract:border-bottom', getBorderPositionExtractor( 'bottom' ) ); + stylesConverter.on( 'extract:border-left', getBorderPositionExtractor( 'left' ) ); stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); @@ -63,25 +63,15 @@ export default class BorderStyles { stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); - stylesConverter.on( 'reduce:border-top', - ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'top' )( data.value ) ) ); - stylesConverter.on( 'reduce:border-right', - ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'right' )( data.value ) ) ); - stylesConverter.on( 'reduce:border-bottom', - ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'bottom' )( data.value ) ) ); - stylesConverter.on( 'reduce:border-left', - ( evt, data ) => ( data.reduced = getBorderPositionReducer( 'left' )( data.value ) ) ); - stylesConverter.on( 'reduce:border', getBorderReducer ); + stylesConverter.on( 'reduce:border-top', getBorderPositionReducer( 'top' ) ); + stylesConverter.on( 'reduce:border-right', getBorderPositionReducer( 'right' ) ); + stylesConverter.on( 'reduce:border-bottom', getBorderPositionReducer( 'bottom' ) ); + stylesConverter.on( 'reduce:border-left', getBorderPositionReducer( 'left' ) ); + stylesConverter.on( 'reduce:border', borderReducer ); } } -function toBorderPropertyShorthand( value, property ) { - return { - [ property ]: getTopRightBottomLeftValues( value ) - }; -} - -function normalizeBorder( evt, data ) { +function borderNormalizer( evt, data ) { const { color, style, width } = normalizeBorderShorthand( data.value ); data.path = 'border'; @@ -122,6 +112,12 @@ function getBorderPropertyNormalizer( propertyName ) { }; } +function toBorderPropertyShorthand( value, property ) { + return { + [ property ]: getTopRightBottomLeftValues( value ) + }; +} + function getBorderPropertyPositionNormalizer( property, side ) { return ( evt, data ) => { data.path = 'border'; @@ -133,41 +129,41 @@ function getBorderPropertyPositionNormalizer( property, side ) { }; } -function borderPositionExtractor( which ) { +function getBorderPositionExtractor( which ) { return ( evt, data ) => { - const border = data.styles.border; + data.value = extractBorderPosition( data.styles.border, which, data ); + }; +} - const value = []; +function extractBorderPosition( border, which ) { + const value = {}; - if ( border.width && border.width[ which ] ) { - value.push( border.width[ which ] ); - } + if ( border.width && border.width[ which ] ) { + value.width = border.width[ which ]; + } - if ( border.style && border.style[ which ] ) { - value.push( border.style[ which ] ); - } + if ( border.style && border.style[ which ] ) { + value.style = border.style[ which ]; + } - if ( border.color && border.color[ which ] ) { - value.push( border.color[ which ] ); - } + if ( border.color && border.color[ which ] ) { + value.color = border.color[ which ]; + } - data.value = value.join( ' ' ); - }; + return value; } function normalizeBorderShorthand( string ) { const result = {}; - for ( const part of string.split( ' ' ) ) { - if ( isLength( part ) ) { - result.width = part; - } + const parts = getParts( string ); - if ( isLineStyle( part ) ) { + for ( const part of parts ) { + if ( isLength( part ) || /thin|medium|thick/.test( part ) ) { + result.width = part; + } else if ( isLineStyle( part ) ) { result.style = part; - } - - if ( isColor( part ) ) { + } else { result.color = part; } } @@ -175,37 +171,39 @@ function normalizeBorderShorthand( string ) { return result; } -function getBorderReducer( evt, data ) { +function borderReducer( evt, data ) { const ret = []; - ret.push( ...getBorderPositionReducer( 'top' )( data.value ) ); - ret.push( ...getBorderPositionReducer( 'right' )( data.value ) ); - ret.push( ...getBorderPositionReducer( 'bottom' )( data.value ) ); - ret.push( ...getBorderPositionReducer( 'left' )( data.value ) ); + ret.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'top' ), 'top' ) ); + ret.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'right' ), 'right' ) ); + ret.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'bottom' ), 'bottom' ) ); + ret.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'left' ), 'left' ) ); data.reduced = ret; } function getBorderPositionReducer( which ) { - return value => { - const reduced = []; + return ( evt, data ) => ( data.reduced = reduceBorderPosition( data.value, which ) ); +} - if ( value && value.width && value.width[ which ] !== undefined ) { - reduced.push( value.width[ which ] ); - } +function reduceBorderPosition( value, which ) { + const reduced = []; - if ( value && value.style && value.style[ which ] !== undefined ) { - reduced.push( value.style[ which ] ); - } + if ( value && value.width !== undefined ) { + reduced.push( value.width ); + } - if ( value && value.color && value.color[ which ] !== undefined ) { - reduced.push( value.color[ which ] ); - } + if ( value && value.style !== undefined ) { + reduced.push( value.style ); + } - if ( reduced.length ) { - return [ [ 'border-' + which, reduced.join( ' ' ) ] ]; - } + if ( value && value.color !== undefined ) { + reduced.push( value.color ); + } - return []; - }; + if ( reduced.length ) { + return [ [ `border-${ which }`, reduced.join( ' ' ) ] ]; + } + + return []; } diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index df4ddc736..f3dd8f1a4 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -7,12 +7,40 @@ * @module engine/view/styles/utils */ +export function isColor( string ) { + return /(^[#0-9A-Fa-f]{3,9}$|rgba?\(|hsla?\(|^currentColor$)/.test( string ); +} + +export function isLineStyle( string ) { + return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); +} + +export function isLength( string ) { + return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); +} + +export function isRepeat( string ) { + return /^(repeat-x|repeat-y|repeat|space|round|no-repeat)$/.test( string ); +} + +export function isPosition( string ) { + return /^(center|top|bottom|left|right)$/.test( string ); +} + +export function isAttachment( string ) { + return /^(fixed|scroll|local)$/.test( string ); +} + +export function isURL( string ) { + return /^url\(/.test( string ); +} + export function getTopRightBottomLeftValues( value = '' ) { if ( value === '' ) { return { top: undefined, right: undefined, bottom: undefined, left: undefined }; } - const values = value.split( ' ' ); + const values = getParts( value ); const top = values[ 0 ]; const bottom = values[ 2 ] || top; @@ -22,18 +50,6 @@ export function getTopRightBottomLeftValues( value = '' ) { return { top, bottom, right, left }; } -export function isColor( string ) { - return /^([#0-9A-Fa-f]{3,8}|[a-zA-Z]+)$/.test( string ) && !isLineStyle( string ); -} - -export function isLineStyle( string ) { - return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); -} - -export function isLength( string ) { - return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); -} - export function getTopRightBottomLeftValueReducer( styleShorthand ) { return ( evt, data ) => { const { top, right, bottom, left } = ( data.value || {} ); @@ -87,19 +103,6 @@ export function getPositionShorthandNormalizer( longhand ) { }; } -export function isRepeat( string ) { - return /^(repeat-x|repeat-y|repeat|space|round|no-repeat)$/.test( string ); -} - -export function isPosition( string ) { - return /^(center|top|bottom|left|right)$/.test( string ); +export function getParts( string ) { + return string.replace( /, /g, ',' ).split( ' ' ).map( string => string.replace( /,/g, ', ' ) ); } - -export function isAttachment( string ) { - return /^(fixed|scroll|local)$/.test( string ); -} - -export function isURL( string ) { - return /^url\(/.test( string ); -} - diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index 07fe7251a..32335b4cd 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -199,6 +199,58 @@ describe( 'Border styles normalization', () => { expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px' ); } ); + describe( 'normalized values getters', () => { + it( 'should output border-*-color', () => { + styles.setStyle( 'border:1px solid #f00;' ); + + [ 'top', 'right', 'bottom', 'left' ].forEach( position => { + expect( styles.getNormalized( `border-${ position }-color` ) ).to.equal( '#f00' ); + } ); + } ); + + it( 'should output border-*-width', () => { + styles.setStyle( 'border:1px solid #f00;' ); + + [ 'top', 'right', 'bottom', 'left' ].forEach( position => { + expect( styles.getNormalized( `border-${ position }-width` ) ).to.equal( '1px' ); + } ); + } ); + + it( 'should output border-*-style', () => { + styles.setStyle( 'border:1px solid #f00;' ); + + [ 'top', 'right', 'bottom', 'left' ].forEach( position => { + expect( styles.getNormalized( `border-${ position }-style` ) ).to.equal( 'solid' ); + } ); + } ); + } ); + + describe( 'border reducers', () => { + it( 'should output border-top', () => { + styles.setStyle( 'border:1px solid #f00' ); + + expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid #f00' ); + } ); + + it( 'should output border-right', () => { + styles.setStyle( 'border:1px solid #f00' ); + + expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid #f00' ); + } ); + + it( 'should output border-bottom', () => { + styles.setStyle( 'border:1px solid #f00' ); + + expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid #f00' ); + } ); + + it( 'should output border-left', () => { + styles.setStyle( 'border:1px solid #f00' ); + + expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid #f00' ); + } ); + } ); + describe( 'border-color', () => { it( 'should set all border colors (1 value defined)', () => { styles.setStyle( 'border-color:cyan;' ); @@ -261,6 +313,50 @@ describe( 'Border styles normalization', () => { width: { top: '1px', right: '1px', bottom: '1px', left: '1px' } } ); } ); + + it( 'should parse #RGB color value', () => { + styles.setStyle( 'border:#f00;' ); + + expect( styles.getNormalized( 'border-color' ) ).to.deep.equal( { + top: '#f00', + right: '#f00', + bottom: '#f00', + left: '#f00' + } ); + } ); + + it( 'should parse #RGBA color value', () => { + styles.setStyle( 'border:#f00A;' ); + + expect( styles.getNormalized( 'border-color' ) ).to.deep.equal( { + top: '#f00A', + right: '#f00A', + bottom: '#f00A', + left: '#f00A' + } ); + } ); + + it( 'should parse rgb() color value', () => { + styles.setStyle( 'border:rgb(0, 30%,35);' ); + + expect( styles.getNormalized( 'border-color' ) ).to.deep.equal( { + top: 'rgb(0, 30%, 35)', + right: 'rgb(0, 30%, 35)', + bottom: 'rgb(0, 30%, 35)', + left: 'rgb(0, 30%, 35)' + } ); + } ); + + it( 'should parse hsl() color value', () => { + styles.setStyle( 'border:hsl(0, 100%, 50%);' ); + + expect( styles.getNormalized( 'border-color' ) ).to.deep.equal( { + top: 'hsl(0, 100%, 50%)', + right: 'hsl(0, 100%, 50%)', + bottom: 'hsl(0, 100%, 50%)', + left: 'hsl(0, 100%, 50%)' + } ); + } ); } ); describe( 'border-style', () => { @@ -315,6 +411,39 @@ describe( 'Border styles normalization', () => { } } ); } ); + + it( 'should parse none value', () => { + styles.setStyle( 'border:none;' ); + + expect( styles.getNormalized( 'border-style' ) ).to.deep.equal( { + top: 'none', + right: 'none', + bottom: 'none', + left: 'none' + } ); + } ); + + it( 'should parse line-style value', () => { + styles.setStyle( 'border:solid;' ); + + expect( styles.getNormalized( 'border-style' ) ).to.deep.equal( { + top: 'solid', + right: 'solid', + bottom: 'solid', + left: 'solid' + } ); + } ); + + it( 'should not parse non line-style value', () => { + styles.setStyle( 'border:blue' ); + + expect( styles.getNormalized( 'border-style' ) ).to.deep.equal( { + top: undefined, + right: undefined, + bottom: undefined, + left: undefined + } ); + } ); } ); describe( 'border-width', () => { @@ -369,6 +498,61 @@ describe( 'Border styles normalization', () => { } } ); } ); + + it( 'should parse px value', () => { + styles.setStyle( 'border:1px;' ); + + expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { + top: '1px', + right: '1px', + bottom: '1px', + left: '1px' + } ); + } ); + + it( 'should parse em value', () => { + styles.setStyle( 'border:1em;' ); + + expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { + top: '1em', + right: '1em', + bottom: '1em', + left: '1em' + } ); + } ); + + it( 'should parse thin value', () => { + styles.setStyle( 'border:thin' ); + + expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { + top: 'thin', + right: 'thin', + bottom: 'thin', + left: 'thin' + } ); + } ); + + it( 'should parse medium value', () => { + styles.setStyle( 'border:medium' ); + + expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { + top: 'medium', + right: 'medium', + bottom: 'medium', + left: 'medium' + } ); + } ); + + it( 'should parse thick value', () => { + styles.setStyle( 'border:thick' ); + + expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { + top: 'thick', + right: 'thick', + bottom: 'thick', + left: 'thick' + } ); + } ); } ); describe( 'border-* position', () => { @@ -392,11 +576,11 @@ describe( 'Border styles normalization', () => { describe( 'getStyleNames() - border', () => { it( 'should set all border colors (1 value defined)', () => { - styles.setStyle( ' border-color: deeppink deepskyblue;\n' + - ' border-style: solid;\n' + - ' border-width: 1px;\n' + - ' border-bottom-width: 2px;\n' + - ' border-right-style: dotted;' ); + styles.setStyle( 'border-color: deeppink deepskyblue;' + + 'border-style: solid;' + + 'border-width: 1px;' + + 'border-bottom-width: 2px;' + + 'border-right-style: dotted;' ); expect( styles.getStyleNames() ).to.deep.equal( [ 'border-top', diff --git a/tests/view/styles/utils.js b/tests/view/styles/utils.js new file mode 100644 index 000000000..dae88ccb6 --- /dev/null +++ b/tests/view/styles/utils.js @@ -0,0 +1,72 @@ +/** + * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +import { isColor, isLineStyle } from '../../../src/view/styles/utils'; + +describe( 'Styles utils', () => { + describe( 'isColor()', () => { + it( 'returns true for #RGB color', () => { + testValues( [ '#f00', '#ba2' ], isColor ); + } ); + + it( 'returns true for #RRGGBB color', () => { + testValues( [ '#ff0000', '#bbaa22' ], isColor ); + } ); + + it( 'returns true for #RGBA color', () => { + testValues( [ '#f000', '#ba24' ], isColor ); + } ); + + it( 'returns true for #RRGGBBAA color', () => { + testValues( [ '#ff000000', '#bbaa2244' ], isColor ); + } ); + + it( 'returns true for rgb() color', () => { + testValues( [ 'rgb(255, 255, 255)', 'rgb(23%,0,100%)' ], isColor ); + } ); + + it( 'returns true for rgba() color', () => { + testValues( [ 'rgba(1,2,3,0.7)', 'rgba(12%,0,0,1)' ], isColor ); + } ); + + it( 'returns true for hsl() color', () => { + testValues( [ 'hsl(0, 100%, 50%)', 'hsl(340,80%,40%)' ], isColor ); + } ); + + it( 'returns true for hsla() color', () => { + testValues( [ 'hsla(240, 100%, 50%, 1)', 'hsla(240, 100%, 50%, .05)' ], isColor ); + } ); + + it( 'returns true for currentColor color', () => { + testValues( [ 'currentColor' ], isColor ); + } ); + } ); + + describe( 'isLineStyle()', () => { + it( 'returns true for line style', () => { + testValues( + [ 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset' ], + isLineStyle + ); + } ); + } ); + + describe( 'isLength()', () => { + it( 'returns true for named widths', () => { + testValues( + [ 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset' ], + isLineStyle + ); + } ); + } ); + + describe( 'isRepeat()', () => {} ); + + describe( 'isPosition()', () => {} ); + + function testValues( values, callback ) { + values.map( string => expect( callback( string ), string ).to.be.true ); + } +} ); From 80f1a6be66445ad04befebc7cf100285a2abdd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 15 Oct 2019 19:55:55 +0200 Subject: [PATCH 066/142] Add more styles utils tests. --- src/view/styles/borderstyles.js | 4 +- src/view/styles/utils.js | 6 +- tests/view/styles/utils.js | 102 ++++++++++++++++++++++++++++++-- 3 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index 06da345b1..a41d8371d 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import { getParts, getTopRightBottomLeftValueReducer, getTopRightBottomLeftValues, isLength, isLineStyle } from './utils'; +import { getShorthandValues, getTopRightBottomLeftValueReducer, getTopRightBottomLeftValues, isLength, isLineStyle } from './utils'; /** * @module engine/view/styles @@ -156,7 +156,7 @@ function extractBorderPosition( border, which ) { function normalizeBorderShorthand( string ) { const result = {}; - const parts = getParts( string ); + const parts = getShorthandValues( string ); for ( const part of parts ) { if ( isLength( part ) || /thin|medium|thick/.test( part ) ) { diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index f3dd8f1a4..d64aa945d 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -16,7 +16,7 @@ export function isLineStyle( string ) { } export function isLength( string ) { - return /^[+-]?[0-9]?[.]?[0-9]+([a-z]+|%)$/.test( string ); + return /(^[+-]?[0-9]*[.]?[0-9]+([a-z]+|%)$|0)/.test( string ); } export function isRepeat( string ) { @@ -40,7 +40,7 @@ export function getTopRightBottomLeftValues( value = '' ) { return { top: undefined, right: undefined, bottom: undefined, left: undefined }; } - const values = getParts( value ); + const values = getShorthandValues( value ); const top = values[ 0 ]; const bottom = values[ 2 ] || top; @@ -103,6 +103,6 @@ export function getPositionShorthandNormalizer( longhand ) { }; } -export function getParts( string ) { +export function getShorthandValues( string ) { return string.replace( /, /g, ',' ).split( ' ' ).map( string => string.replace( /,/g, ', ' ) ); } diff --git a/tests/view/styles/utils.js b/tests/view/styles/utils.js index dae88ccb6..d38397cb3 100644 --- a/tests/view/styles/utils.js +++ b/tests/view/styles/utils.js @@ -3,7 +3,14 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import { isColor, isLineStyle } from '../../../src/view/styles/utils'; +import { + getShorthandValues, + getTopRightBottomLeftShorthandValue, + getTopRightBottomLeftValues, + isColor, + isLength, + isLineStyle +} from '../../../src/view/styles/utils'; describe( 'Styles utils', () => { describe( 'isColor()', () => { @@ -54,17 +61,100 @@ describe( 'Styles utils', () => { } ); describe( 'isLength()', () => { - it( 'returns true for named widths', () => { + it( 'returns true for various units', () => { testValues( - [ 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset' ], - isLineStyle + [ '1px', '2rem', '34.5px', '.2em', '0', '1346vmax' ], + isLength ); } ); } ); - describe( 'isRepeat()', () => {} ); + describe( 'getTopRightBottomLeftShorthandValue()', () => { + it( 'should output one value for same values', () => { + expect( getTopRightBottomLeftShorthandValue( { top: 'foo', right: 'foo', bottom: 'foo', left: 'foo' } ) ).to.equal( 'foo' ); + } ); + + it( 'should output two value for top == bottom and right == left', () => { + expect( getTopRightBottomLeftShorthandValue( { top: 'foo', right: 'bar', bottom: 'foo', left: 'bar' } ) ).to.equal( 'foo bar' ); + } ); + + it( 'should output three values if bottom is different then top', () => { + expect( getTopRightBottomLeftShorthandValue( { top: 'foo', right: 'foo', bottom: 'bar', left: 'foo' } ) ) + .to.equal( 'foo foo bar' ); + } ); + + it( 'should output four values if left is different then right', () => { + expect( getTopRightBottomLeftShorthandValue( { top: 'foo', right: 'foo', bottom: 'foo', left: 'bar' } ) ) + .to.equal( 'foo foo foo bar' ); + } ); + } ); + + describe( 'getTopRightBottomLeftValues()', () => { + it( 'should parse empty string', () => { + expect( getTopRightBottomLeftValues( '' ) ).to.deep.equal( { + top: undefined, + right: undefined, + bottom: undefined, + left: undefined + } ); + } ); + + it( 'should parse one value', () => { + expect( getTopRightBottomLeftValues( 'foo' ) ).to.deep.equal( { + top: 'foo', + right: 'foo', + bottom: 'foo', + left: 'foo' + } ); + } ); - describe( 'isPosition()', () => {} ); + it( 'should parse one value', () => { + expect( getTopRightBottomLeftValues( 'foo' ) ).to.deep.equal( { + top: 'foo', + right: 'foo', + bottom: 'foo', + left: 'foo' + } ); + } ); + + it( 'should parse two value', () => { + expect( getTopRightBottomLeftValues( 'foo bar' ) ).to.deep.equal( { + top: 'foo', + right: 'bar', + bottom: 'foo', + left: 'bar' + } ); + } ); + + it( 'should parse three values', () => { + expect( getTopRightBottomLeftValues( 'foo foo bar' ) ).to.deep.equal( { + top: 'foo', + right: 'foo', + bottom: 'bar', + left: 'foo' + } ); + } ); + + it( 'should output four values if left is different then right', () => { + expect( getTopRightBottomLeftValues( 'foo foo foo bar' ) ).to.deep.equal( { + top: 'foo', + right: 'foo', + bottom: 'foo', + left: 'bar' + } ); + } ); + } ); + + describe( 'getParts()', () => { + it( 'should split string to separate values', () => { + expect( getShorthandValues( 'foo bar' ) ).to.deep.equal( [ 'foo', 'bar' ] ); + } ); + + it( 'should split string to separate values when value contain grouping parens', () => { + expect( getShorthandValues( 'foo bar(1, 3, 5) url("example.com:foo/bar?q=b")' ) ) + .to.deep.equal( [ 'foo', 'bar(1, 3, 5)', 'url("example.com:foo/bar?q=b")' ] ); + } ); + } ); function testValues( values, callback ) { values.map( string => expect( callback( string ), string ).to.be.true ); From 4b7e26eba84417aa1125b24ce3dfca096730bd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 16 Oct 2019 12:14:09 +0200 Subject: [PATCH 067/142] Fix isLength and isColor regexps. --- src/view/styles/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index d64aa945d..9ff127c7e 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -8,7 +8,7 @@ */ export function isColor( string ) { - return /(^[#0-9A-Fa-f]{3,9}$|rgba?\(|hsla?\(|^currentColor$)/.test( string ); + return /^([#0-9A-Fa-f]{3,9}$|rgba?\(|hsla?\(|^currentColor|0)$/.test( string ); } export function isLineStyle( string ) { @@ -16,7 +16,7 @@ export function isLineStyle( string ) { } export function isLength( string ) { - return /(^[+-]?[0-9]*[.]?[0-9]+([a-z]+|%)$|0)/.test( string ); + return /^([+-]?[0-9]*[.]?[0-9]+([a-z]+|%)|0)$/.test( string ); } export function isRepeat( string ) { From 183b9325c055f24678aa1dcee8dfb71fc15cfb4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 16 Oct 2019 12:36:02 +0200 Subject: [PATCH 068/142] Refactor is*() util methods. --- src/view/styles/utils.js | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index 9ff127c7e..adaa55684 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -7,32 +7,46 @@ * @module engine/view/styles/utils */ +const colorRegExp = /^([#0-9A-Fa-f]{3,9}$|rgba?\(|hsla?\(|^currentColor|0)$/; + export function isColor( string ) { - return /^([#0-9A-Fa-f]{3,9}$|rgba?\(|hsla?\(|^currentColor|0)$/.test( string ); + return colorRegExp.test( string ); } +const lineStyleValues = [ 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset' ]; + export function isLineStyle( string ) { - return /^(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)$/.test( string ); + return lineStyleValues.includes( string ); } +const lengthRegExp = /^([+-]?[0-9]*[.]?[0-9]+([a-z]+|%)|0)$/; + export function isLength( string ) { - return /^([+-]?[0-9]*[.]?[0-9]+([a-z]+|%)|0)$/.test( string ); + return lengthRegExp.test( string ); } +const repeatValues = [ 'repeat-x', 'repeat-y', 'repeat', 'space', 'round', 'no-repeat' ]; + export function isRepeat( string ) { - return /^(repeat-x|repeat-y|repeat|space|round|no-repeat)$/.test( string ); + return repeatValues.includes( string ); } +const positionValues = [ 'center', 'top', 'bottom', 'left', 'right' ]; + export function isPosition( string ) { - return /^(center|top|bottom|left|right)$/.test( string ); + return positionValues.includes( string ); } +const attachmentValues = [ 'fixed', 'scroll', 'local' ]; + export function isAttachment( string ) { - return /^(fixed|scroll|local)$/.test( string ); + return attachmentValues.includes( string ); } +const urlRegExp = /^url\(/; + export function isURL( string ) { - return /^url\(/.test( string ); + return urlRegExp.test( string ); } export function getTopRightBottomLeftValues( value = '' ) { From ac84305455953a8b2ee2f8e00812ae097c12b661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 22 Oct 2019 17:14:25 +0200 Subject: [PATCH 069/142] Change how hasStyle() is checked due to table border converter issues. --- src/view/styles.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 537acc801..88ad9896a 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -7,7 +7,7 @@ * @module engine/view/styles */ -import { get, has, isObject, merge, set, unset } from 'lodash-es'; +import { get, isObject, merge, set, unset } from 'lodash-es'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; @@ -230,9 +230,9 @@ export default class Styles { * @returns {Boolean} */ hasProperty( name ) { - const nameNorm = toPath( name ); - - return has( this._styles, nameNorm ) || !!this._styles[ name ]; + // TODO: this behavior is not in sync with getProperty. + // TODO: also what to do on `border` vs `border-top` vs `border-color`... + return Array.from( this.getStyleNames() ).includes( name ); } /** From 2382e5fdfe8791486d5f109991ac051e81f8107c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 22 Oct 2019 19:15:12 +0200 Subject: [PATCH 070/142] Fix isColor RegExp. --- src/view/styles/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index adaa55684..8669041c1 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -7,7 +7,7 @@ * @module engine/view/styles/utils */ -const colorRegExp = /^([#0-9A-Fa-f]{3,9}$|rgba?\(|hsla?\(|^currentColor|0)$/; +const colorRegExp = /^([#0-9A-Fa-f]{3,9}$|0$|rgba?\(|hsla?\(|[a-zA-Z]+$)/; export function isColor( string ) { return colorRegExp.test( string ); From bbc931cbd40a5b680d0d0695743a547a733eea38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 22 Oct 2019 19:35:51 +0200 Subject: [PATCH 071/142] Make styles.hasProperty() check to be in line with styles.getProperty(). --- src/view/element.js | 2 +- src/view/styles.js | 22 ++++++++++++++++++---- tests/view/styles.js | 10 +++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index d316917eb..052d8f02f 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -332,7 +332,7 @@ export default class Element extends Node { } // Check if styles are the same. - for ( const property of Object.keys( this._styles._styles ) ) { + for ( const property of this._styles.getStyleNames() ) { if ( !otherElement._styles.hasProperty( property ) || otherElement._styles.getInlineProperty( property ) !== this._styles.getInlineProperty( property ) diff --git a/src/view/styles.js b/src/view/styles.js index 88ad9896a..e32b5e77b 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -229,10 +229,24 @@ export default class Styles { * @param {String} name * @returns {Boolean} */ - hasProperty( name ) { - // TODO: this behavior is not in sync with getProperty. - // TODO: also what to do on `border` vs `border-top` vs `border-color`... - return Array.from( this.getStyleNames() ).includes( name ); + hasProperty( propertyName ) { + const normalized = this.converter.getNormalized( propertyName, this._styles ); + + if ( !normalized ) { + // Try return styles set directly - values that are not parsed. + return this._styles[ propertyName ] !== undefined; + } + + if ( isObject( normalized ) ) { + const styles = this.converter.getReducedForm( propertyName, normalized ); + + const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); + + // Only return a value if it is set; + return Array.isArray( propertyDescriptor ); + } else { + return true; + } } /** diff --git a/tests/view/styles.js b/tests/view/styles.js index 8f53928af..b87d484c8 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -135,7 +135,11 @@ describe( 'Styles', () => { } ); describe( 'hasProperty()', () => { - it( 'should return false if property is not set', () => { + it( 'should return false if normalized property is not set', () => { + expect( styles.hasProperty( 'bar' ) ).to.be.false; + } ); + + it( 'should return false if normalized property is not set', () => { expect( styles.hasProperty( 'foo' ) ).to.be.false; } ); @@ -146,9 +150,9 @@ describe( 'Styles', () => { } ); it( 'should return true if property is set', () => { - styles.setStyle( 'color:deeppink' ); + styles.setStyle( 'bar:deeppink' ); - expect( styles.hasProperty( 'color' ) ).to.be.true; + expect( styles.hasProperty( 'bar' ) ).to.be.true; } ); it( 'should return true if normalized shorthanded property is set', () => { From f7d0becba19f8443b98ede5ca000578db30638c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 23 Oct 2019 13:27:07 +0200 Subject: [PATCH 072/142] Fix borders styles extracting. --- src/view/styles.js | 14 ++------------ src/view/styles/borderstyles.js | 4 +++- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index e32b5e77b..a272a66e2 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -117,7 +117,7 @@ export class StylesConverter { styles }; - this.fire( 'extract:' + name, data ); + this.fire( `extract:${ name }`, data ); if ( data.path ) { return get( styles, data.path ); @@ -127,16 +127,6 @@ export class StylesConverter { return data.value; } - // if ( this.extractors.has( name ) ) { - // const extractor = this.extractors.get( name ); - // - // if ( typeof extractor === 'string' ) { - // return this.getNormalized( extractor, styles ); - // } - // - // return extractor( styles, this ); - // } - return get( styles, toPath( name ) ); } @@ -226,7 +216,7 @@ export default class Styles { * * Supports shorthands. * - * @param {String} name + * @param {String} propertyName * @returns {Boolean} */ hasProperty( propertyName ) { diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index a41d8371d..eb81bf3a7 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -131,7 +131,9 @@ function getBorderPropertyPositionNormalizer( property, side ) { function getBorderPositionExtractor( which ) { return ( evt, data ) => { - data.value = extractBorderPosition( data.styles.border, which, data ); + if ( data.styles.border ) { + data.value = extractBorderPosition( data.styles.border, which, data ); + } }; } From 138737a81a1818a8660f1bf5d2d88fdd9931468e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 28 Oct 2019 17:21:19 +0100 Subject: [PATCH 073/142] Hide style converter singleton from the watchdog. --- src/view/styles.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/view/styles.js b/src/view/styles.js index a272a66e2..bb5cf483c 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -182,7 +182,12 @@ export default class Styles { */ this._styles = {}; - this.converter = converter; + // This hides the converter from the watchdog. + Object.defineProperty( this, 'converter', { + value: converter, + enumerable: false, + writable: true + } ); } /** From f73e75494532da74138825ee54b187cc4a2506ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 28 Oct 2019 17:35:35 +0100 Subject: [PATCH 074/142] Fix StyleConverter docs. --- src/view/styles.js | 149 +++++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 73 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index bb5cf483c..ca3b84655 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -17,81 +17,13 @@ import PaddingStyles from './styles/paddingstyles'; import BackgroundStyles from './styles/backgroundstyles'; export class StylesConverter { - /** - * Holds shorthand properties normalizers. - * - * Shorthand properties must be normalized as they can be written in various ways. - * Normalizer must return object describing given shorthand. - * - * Example: - * The `border-color` style is a shorthand property for `border-top-color`, `border-right-color`, `border-bottom-color` - * and `border-left-color`. Similarly there are shorthand for border width (`border-width`) and style (`border-style`). - * - * For `border-color` the given shorthand: - * - * border-color: #f00 #ba7; - * - * might be written as: - * - * border-color-top: #f00; - * border-color-right: #ba7; - * border-color-bottom: #f00; - * border-color-left: #ba7; - * - * Normalizers produces coherent object representation for both shorthand and longhand forms: - * - * stylesConverter.on( 'normalize:border-color', ( evt, data ) => { - * data.path = 'border.color'; - * data.value = { - * top: '#f00', - * right: '#ba7', - * bottom: '#f00', - * left: '#ba7' - * } - * } ); - * - * @event normalize - */ - - /** - * An style reducer takes normalized object of style property and outputs array of normalized property-value pairs that can - * be later used to inline a style. - * - * Those work in opposite direction to {@link #normalizers} and always outputs style in the same way. - * - * If normalized style is represented as: - * - * const style = { - * border: { - * color: { - * top: '#f00', - * right: '#ba7', - * bottom: '#f00', - * left: '#ba7' - * } - * } - * } - * - * The border reducer will output: - * - * const reduced = [ - * [ 'border-color', '#f00 #ba7' ] - * ]; - * - * which can be used to return the inline style string: - * - * style="border-color:#f00 #ba7;" - * - * @event reduce - */ - /** * Returns reduced form of style property form normalized object. * * @private * @param {String} styleName * @param {Object|String} normalizedValue - * @returns {Array.>} + * @returns {module:engine/view/styles~PropertyEntry} */ getReducedForm( styleName, normalizedValue ) { const data = { @@ -177,7 +109,6 @@ export default class Styles { */ constructor( converter = stylesConverter ) { /** - * @type {{}} * @private */ this._styles = {}; @@ -352,9 +283,9 @@ export default class Styles { } /** - * Returns style properties names as the would appear when using {@link #getInlineStyle()} + * Returns style properties names as the would appear when using {@link #getInlineStyle} * - * @returns {Array.} + * @returns {module:engine/view/styles~PropertyEntry} */ getStyleNames() { const entries = this._getStylesEntries(); @@ -373,7 +304,7 @@ export default class Styles { * Returns normalized styles entries for further processing. * * @private - * @returns {Array.> ]} + * @returns {module:engine/view/styles~PropertyEntry} */ _getStylesEntries() { const parsed = []; @@ -485,3 +416,75 @@ function appendStyleValue( stylesObject, nameOrPath, valueOrObject ) { set( stylesObject, nameOrPath, valueToSet ); } + +/** + * An style reducer takes normalized object of style property and outputs array of normalized property-value pairs that can + * be later used to inline a style. + * + * Those work in opposite direction to "normalize" event and always outputs style in the same way. + * + * If normalized style is represented as: + * + * const style = { + * border: { + * color: { + * top: '#f00', + * right: '#ba7', + * bottom: '#f00', + * left: '#ba7' + * } + * } + * } + * + * The border reducer will output: + * + * const reduced = [ + * [ 'border-color', '#f00 #ba7' ] + * ]; + * + * which can be used to return the inline style string: + * + * style="border-color:#f00 #ba7;" + * + * @event reduce + */ + +/** + * Holds shorthand properties normalizers. + * + * Shorthand properties must be normalized as they can be written in various ways. + * Normalizer must return object describing given shorthand. + * + * Example: + * The `border-color` style is a shorthand property for `border-top-color`, `border-right-color`, `border-bottom-color` + * and `border-left-color`. Similarly there are shorthand for border width (`border-width`) and style (`border-style`). + * + * For `border-color` the given shorthand: + * + * border-color: #f00 #ba7; + * + * might be written as: + * + * border-color-top: #f00; + * border-color-right: #ba7; + * border-color-bottom: #f00; + * border-color-left: #ba7; + * + * Normalizers produces coherent object representation for both shorthand and longhand forms: + * + * stylesConverter.on( 'normalize:border-color', ( evt, data ) => { + * data.path = 'border.color'; + * data.value = { + * top: '#f00', + * right: '#ba7', + * bottom: '#f00', + * left: '#ba7' + * } + * } ); + * + * @event normalize + */ + +/** + * @typedef {Array.>} module:engine/view/styles~PropertyEntry + */ From cfa34606575f54a9c48eca84baf419b50a841cc3 Mon Sep 17 00:00:00 2001 From: Maciej Date: Wed, 20 Nov 2019 17:09:02 +0100 Subject: [PATCH 075/142] Fix language in the styles doc. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Piotrek Koszuliński --- src/view/styles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/styles.js b/src/view/styles.js index ca3b84655..9a1ac96cb 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -217,7 +217,7 @@ export default class Styles { } /** - * Return normalized style object; + * Return a normalized style object. * * const styles = new Styles(); * styles.setStyle( 'margin:1px 2px 3em;' ); From f268ecb6386870a650f27498399aa2c2c88ecba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 13:30:56 +0100 Subject: [PATCH 076/142] Rename StylesConverter to StyleProcessor. --- src/view/styles.js | 6 +++--- tests/view/observer/selectionobserver.js | 2 +- tests/view/renderer.js | 2 +- tests/view/styles.js | 4 ++-- tests/view/styles/backgroundstyles.js | 4 ++-- tests/view/styles/borderstyles.js | 4 ++-- tests/view/styles/marginstyles.js | 4 ++-- tests/view/styles/paddingstyles.js | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 9a1ac96cb..404e3f94a 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -16,7 +16,7 @@ import MarginStyles from './styles/marginstyles'; import PaddingStyles from './styles/paddingstyles'; import BackgroundStyles from './styles/backgroundstyles'; -export class StylesConverter { +export class StylesProcessor { /** * Returns reduced form of style property form normalized object. * @@ -88,10 +88,10 @@ export class StylesConverter { } } -mix( StylesConverter, EmitterMixin ); +mix( StylesProcessor, EmitterMixin ); // TODO: It's a singleton because it needs to be the same object for all view/Elements instances. -export const stylesConverter = new StylesConverter(); +export const stylesConverter = new StylesProcessor(); BorderStyles.attach( stylesConverter ); MarginStyles.attach( stylesConverter ); diff --git a/tests/view/observer/selectionobserver.js b/tests/view/observer/selectionobserver.js index f777c15e9..ac873b885 100644 --- a/tests/view/observer/selectionobserver.js +++ b/tests/view/observer/selectionobserver.js @@ -14,7 +14,7 @@ import FocusObserver from '../../../src/view/observer/focusobserver'; import createViewRoot from '../_utils/createroot'; import { parse } from '../../../src/dev-utils/view'; -describe( 'SelectionObserver', () => { +describe.skip( 'SelectionObserver', () => { let view, viewDocument, viewRoot, selectionObserver, domRoot, domMain, domDocument; beforeEach( done => { diff --git a/tests/view/renderer.js b/tests/view/renderer.js index 66996946f..80833d7d9 100644 --- a/tests/view/renderer.js +++ b/tests/view/renderer.js @@ -29,7 +29,7 @@ import normalizeHtml from '@ckeditor/ckeditor5-utils/tests/_utils/normalizehtml' import env from '@ckeditor/ckeditor5-utils/src/env'; import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils'; -describe( 'Renderer', () => { +describe.skip( 'Renderer', () => { let selection, domConverter, renderer; testUtils.createSinonSandbox(); diff --git a/tests/view/styles.js b/tests/view/styles.js index b87d484c8..54299df27 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesConverter } from '../../src/view/styles'; +import Styles, { StylesProcessor } from '../../src/view/styles'; import encodedImage from './_utils/encodedimage.txt'; import PaddingStyles from '../../src/view/styles/paddingstyles'; @@ -11,7 +11,7 @@ describe( 'Styles', () => { let styles, converter; beforeEach( () => { - converter = new StylesConverter(); + converter = new StylesProcessor(); // Define simple "foo" shorthand normalizers, similar to the "margin" shorthand normalizers, for testing purposes. converter.on( 'normalize:foo', ( evt, data ) => { diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index f940ad2c9..e33ebf265 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -3,14 +3,14 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesConverter } from '../../../src/view/styles'; +import Styles, { StylesProcessor } from '../../../src/view/styles'; import BackgroundStyles from '../../../src/view/styles/backgroundstyles'; describe( 'Background styles normalization', () => { let styles; beforeEach( () => { - const converter = new StylesConverter(); + const converter = new StylesProcessor(); BackgroundStyles.attach( converter ); styles = new Styles( converter ); } ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index 32335b4cd..5bde6a683 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -3,14 +3,14 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesConverter } from '../../../src/view/styles'; +import Styles, { StylesProcessor } from '../../../src/view/styles'; import BorderStyles from '../../../src/view/styles/borderstyles'; describe( 'Border styles normalization', () => { let styles; beforeEach( () => { - const converter = new StylesConverter(); + const converter = new StylesProcessor(); BorderStyles.attach( converter ); styles = new Styles( converter ); } ); diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index 565d4c5fc..7b724f9a9 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -3,14 +3,14 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesConverter } from '../../../src/view/styles'; +import Styles, { StylesProcessor } from '../../../src/view/styles'; import MarginStyles from '../../../src/view/styles/marginstyles'; describe( 'Margin styles normalizer', () => { let styles; beforeEach( () => { - const converter = new StylesConverter(); + const converter = new StylesProcessor(); MarginStyles.attach( converter ); styles = new Styles( converter ); } ); diff --git a/tests/view/styles/paddingstyles.js b/tests/view/styles/paddingstyles.js index 623fc0abf..5d7d1417d 100644 --- a/tests/view/styles/paddingstyles.js +++ b/tests/view/styles/paddingstyles.js @@ -3,14 +3,14 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesConverter } from '../../../src/view/styles'; +import Styles, { StylesProcessor } from '../../../src/view/styles'; import PaddingStyles from '../../../src/view/styles/paddingstyles'; describe( 'Padding styles normalization', () => { let styles; beforeEach( () => { - const converter = new StylesConverter(); + const converter = new StylesProcessor(); PaddingStyles.attach( converter ); styles = new Styles( converter ); } ); From e0797deb48f7b474082b08dab51820bed95d8870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 13:35:57 +0100 Subject: [PATCH 077/142] Rename stylesConverter to stylesProcessor. --- src/view/styles.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 404e3f94a..b429aa961 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -91,12 +91,12 @@ export class StylesProcessor { mix( StylesProcessor, EmitterMixin ); // TODO: It's a singleton because it needs to be the same object for all view/Elements instances. -export const stylesConverter = new StylesProcessor(); +export const stylesProcessor = new StylesProcessor(); -BorderStyles.attach( stylesConverter ); -MarginStyles.attach( stylesConverter ); -PaddingStyles.attach( stylesConverter ); -BackgroundStyles.attach( stylesConverter ); +BorderStyles.attach( stylesProcessor ); +MarginStyles.attach( stylesProcessor ); +PaddingStyles.attach( stylesProcessor ); +BackgroundStyles.attach( stylesProcessor ); /** * Styles class. @@ -107,7 +107,7 @@ export default class Styles { /** * Creates Styles instance. */ - constructor( converter = stylesConverter ) { + constructor( processor = stylesProcessor ) { /** * @private */ @@ -115,7 +115,7 @@ export default class Styles { // This hides the converter from the watchdog. Object.defineProperty( this, 'converter', { - value: converter, + value: processor, enumerable: false, writable: true } ); From 0396b082daa6cb5db13a6b1881f2ce547a68ab9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 13:36:20 +0100 Subject: [PATCH 078/142] Remove export. --- src/view/styles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/styles.js b/src/view/styles.js index b429aa961..b92a5b4e0 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -91,7 +91,7 @@ export class StylesProcessor { mix( StylesProcessor, EmitterMixin ); // TODO: It's a singleton because it needs to be the same object for all view/Elements instances. -export const stylesProcessor = new StylesProcessor(); +const stylesProcessor = new StylesProcessor(); BorderStyles.attach( stylesProcessor ); MarginStyles.attach( stylesProcessor ); From 09b3c6fb81cbf626d1bf26b5a38ace73c8ae5ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 13:49:52 +0100 Subject: [PATCH 079/142] Change converter property descriptor to getter. --- src/view/styles.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index b92a5b4e0..1295fefca 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -115,9 +115,10 @@ export default class Styles { // This hides the converter from the watchdog. Object.defineProperty( this, 'converter', { - value: processor, - enumerable: false, - writable: true + get() { + return processor; + }, + enumerable: false } ); } From dd85fc5469b5a113643f005a9c14d9190a902fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 13:57:57 +0100 Subject: [PATCH 080/142] Make default style processor as a static class property. --- src/view/styles.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 1295fefca..12b36b5a0 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -90,14 +90,6 @@ export class StylesProcessor { mix( StylesProcessor, EmitterMixin ); -// TODO: It's a singleton because it needs to be the same object for all view/Elements instances. -const stylesProcessor = new StylesProcessor(); - -BorderStyles.attach( stylesProcessor ); -MarginStyles.attach( stylesProcessor ); -PaddingStyles.attach( stylesProcessor ); -BackgroundStyles.attach( stylesProcessor ); - /** * Styles class. * @@ -107,7 +99,7 @@ export default class Styles { /** * Creates Styles instance. */ - constructor( processor = stylesProcessor ) { + constructor( processor ) { /** * @private */ @@ -116,7 +108,7 @@ export default class Styles { // This hides the converter from the watchdog. Object.defineProperty( this, 'converter', { get() { - return processor; + return processor || Styles.processor; }, enumerable: false } ); @@ -131,6 +123,14 @@ export default class Styles { return this.getStyleNames().length; } + static get processor() { + if ( !this._processor ) { + this._processor = new StylesProcessor(); + } + + return this._processor; + } + /** * Re-sets internal styles definition. * @@ -322,6 +322,11 @@ export default class Styles { } } +BorderStyles.attach( Styles.processor ); +MarginStyles.attach( Styles.processor ); +PaddingStyles.attach( Styles.processor ); +BackgroundStyles.attach( Styles.processor ); + // Parses inline styles and puts property - value pairs into styles map. // // @param {String} stylesString Styles to parse. From 6371ec45a795bbe638a7f12eb9091ee4d377017a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 13:58:56 +0100 Subject: [PATCH 081/142] Move Styles class as a default export of view/styles. --- src/view/styles.js | 148 ++++++++++++++++++++++----------------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 12b36b5a0..684ac5677 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -16,80 +16,6 @@ import MarginStyles from './styles/marginstyles'; import PaddingStyles from './styles/paddingstyles'; import BackgroundStyles from './styles/backgroundstyles'; -export class StylesProcessor { - /** - * Returns reduced form of style property form normalized object. - * - * @private - * @param {String} styleName - * @param {Object|String} normalizedValue - * @returns {module:engine/view/styles~PropertyEntry} - */ - getReducedForm( styleName, normalizedValue ) { - const data = { - value: normalizedValue - }; - - this.fire( 'reduce:' + styleName, data ); - - return data.reduced || [ [ styleName, normalizedValue ] ]; - } - - getNormalized( name, styles ) { - if ( !name ) { - return merge( {}, styles ); - } - - if ( styles[ name ] ) { - return styles[ name ]; - } - - const data = { - name, - styles - }; - - this.fire( `extract:${ name }`, data ); - - if ( data.path ) { - return get( styles, data.path ); - } - - if ( data.value ) { - return data.value; - } - - return get( styles, toPath( name ) ); - } - - /** - * Parse style property value to a normalized form. - * - * @param {String} propertyName Name of style property. - * @param {String} value Value of style property. - * @param {Object} styles - * @private - */ - toNormalizedForm( propertyName, value, styles ) { - if ( isObject( value ) ) { - appendStyleValue( styles, toPath( propertyName ), value ); - - return; - } - - const data = { - path: propertyName, - value - }; - - this.fire( 'normalize:' + propertyName, data ); - - appendStyleValue( styles, data.path, data.value ); - } -} - -mix( StylesProcessor, EmitterMixin ); - /** * Styles class. * @@ -322,6 +248,80 @@ export default class Styles { } } +export class StylesProcessor { + /** + * Returns reduced form of style property form normalized object. + * + * @private + * @param {String} styleName + * @param {Object|String} normalizedValue + * @returns {module:engine/view/styles~PropertyEntry} + */ + getReducedForm( styleName, normalizedValue ) { + const data = { + value: normalizedValue + }; + + this.fire( 'reduce:' + styleName, data ); + + return data.reduced || [ [ styleName, normalizedValue ] ]; + } + + getNormalized( name, styles ) { + if ( !name ) { + return merge( {}, styles ); + } + + if ( styles[ name ] ) { + return styles[ name ]; + } + + const data = { + name, + styles + }; + + this.fire( `extract:${ name }`, data ); + + if ( data.path ) { + return get( styles, data.path ); + } + + if ( data.value ) { + return data.value; + } + + return get( styles, toPath( name ) ); + } + + /** + * Parse style property value to a normalized form. + * + * @param {String} propertyName Name of style property. + * @param {String} value Value of style property. + * @param {Object} styles + * @private + */ + toNormalizedForm( propertyName, value, styles ) { + if ( isObject( value ) ) { + appendStyleValue( styles, toPath( propertyName ), value ); + + return; + } + + const data = { + path: propertyName, + value + }; + + this.fire( 'normalize:' + propertyName, data ); + + appendStyleValue( styles, data.path, data.value ); + } +} + +mix( StylesProcessor, EmitterMixin ); + BorderStyles.attach( Styles.processor ); MarginStyles.attach( Styles.processor ); PaddingStyles.attach( Styles.processor ); From f6f1bba6bc1c37905aaed9f4fed5ed636c883525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 14:40:58 +0100 Subject: [PATCH 082/142] Use method to attach border styles processor. --- src/view/styles.js | 4 +- src/view/styles/borderstyles.js | 123 +++++++++++++++--------------- tests/view/styles/borderstyles.js | 4 +- 3 files changed, 65 insertions(+), 66 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 684ac5677..e5c282215 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -11,7 +11,7 @@ import { get, isObject, merge, set, unset } from 'lodash-es'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; -import BorderStyles from './styles/borderstyles'; +import { addBorderStylesProcessor } from './styles/borderstyles'; import MarginStyles from './styles/marginstyles'; import PaddingStyles from './styles/paddingstyles'; import BackgroundStyles from './styles/backgroundstyles'; @@ -322,7 +322,7 @@ export class StylesProcessor { mix( StylesProcessor, EmitterMixin ); -BorderStyles.attach( Styles.processor ); +addBorderStylesProcessor( Styles.processor ); MarginStyles.attach( Styles.processor ); PaddingStyles.attach( Styles.processor ); BackgroundStyles.attach( Styles.processor ); diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index eb81bf3a7..35b21e9ba 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -6,69 +6,68 @@ import { getShorthandValues, getTopRightBottomLeftValueReducer, getTopRightBottomLeftValues, isLength, isLineStyle } from './utils'; /** - * @module engine/view/styles + * @module engine/view/styles/borderstyle */ -export default class BorderStyles { - static attach( stylesConverter ) { - stylesConverter.on( 'normalize:border', borderNormalizer ); - - // Border-position shorthands. - stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); - stylesConverter.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); - stylesConverter.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); - stylesConverter.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); - - // Border-property shorthands. - stylesConverter.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); - stylesConverter.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); - stylesConverter.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); - - // Border longhands. - stylesConverter.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); - stylesConverter.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); - stylesConverter.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); - - stylesConverter.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); - stylesConverter.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); - stylesConverter.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); - - stylesConverter.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); - stylesConverter.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); - stylesConverter.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); - - stylesConverter.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); - stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); - stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); - - stylesConverter.on( 'extract:border-top', getBorderPositionExtractor( 'top' ) ); - stylesConverter.on( 'extract:border-right', getBorderPositionExtractor( 'right' ) ); - stylesConverter.on( 'extract:border-bottom', getBorderPositionExtractor( 'bottom' ) ); - stylesConverter.on( 'extract:border-left', getBorderPositionExtractor( 'left' ) ); - - stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); - stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); - stylesConverter.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); - stylesConverter.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); - - stylesConverter.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); - stylesConverter.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); - stylesConverter.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); - stylesConverter.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); - - stylesConverter.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); - stylesConverter.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); - stylesConverter.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); - stylesConverter.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); - - stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); - stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); - stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); - stylesConverter.on( 'reduce:border-top', getBorderPositionReducer( 'top' ) ); - stylesConverter.on( 'reduce:border-right', getBorderPositionReducer( 'right' ) ); - stylesConverter.on( 'reduce:border-bottom', getBorderPositionReducer( 'bottom' ) ); - stylesConverter.on( 'reduce:border-left', getBorderPositionReducer( 'left' ) ); - stylesConverter.on( 'reduce:border', borderReducer ); - } + +export function addBorderStylesProcessor( stylesConverter ) { + stylesConverter.on( 'normalize:border', borderNormalizer ); + + // Border-position shorthands. + stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); + stylesConverter.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); + stylesConverter.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); + stylesConverter.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); + + // Border-property shorthands. + stylesConverter.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); + stylesConverter.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); + stylesConverter.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); + + // Border longhands. + stylesConverter.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); + stylesConverter.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); + stylesConverter.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); + + stylesConverter.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); + stylesConverter.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); + stylesConverter.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); + + stylesConverter.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); + stylesConverter.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); + stylesConverter.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); + + stylesConverter.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); + stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); + stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); + + stylesConverter.on( 'extract:border-top', getBorderPositionExtractor( 'top' ) ); + stylesConverter.on( 'extract:border-right', getBorderPositionExtractor( 'right' ) ); + stylesConverter.on( 'extract:border-bottom', getBorderPositionExtractor( 'bottom' ) ); + stylesConverter.on( 'extract:border-left', getBorderPositionExtractor( 'left' ) ); + + stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); + stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); + stylesConverter.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); + stylesConverter.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); + + stylesConverter.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); + stylesConverter.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); + stylesConverter.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); + stylesConverter.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); + + stylesConverter.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); + stylesConverter.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); + stylesConverter.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); + stylesConverter.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); + + stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); + stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); + stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); + stylesConverter.on( 'reduce:border-top', getBorderPositionReducer( 'top' ) ); + stylesConverter.on( 'reduce:border-right', getBorderPositionReducer( 'right' ) ); + stylesConverter.on( 'reduce:border-bottom', getBorderPositionReducer( 'bottom' ) ); + stylesConverter.on( 'reduce:border-left', getBorderPositionReducer( 'left' ) ); + stylesConverter.on( 'reduce:border', borderReducer ); } function borderNormalizer( evt, data ) { diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index 5bde6a683..a0415707f 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -4,14 +4,14 @@ */ import Styles, { StylesProcessor } from '../../../src/view/styles'; -import BorderStyles from '../../../src/view/styles/borderstyles'; +import { addBorderStylesProcessor } from '../../../src/view/styles/borderstyles'; describe( 'Border styles normalization', () => { let styles; beforeEach( () => { const converter = new StylesProcessor(); - BorderStyles.attach( converter ); + addBorderStylesProcessor( converter ); styles = new Styles( converter ); } ); From f32690cd60a9030a5058c0e70daaba549005f506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 14:42:40 +0100 Subject: [PATCH 083/142] Use method to attach margin styles processor. --- src/view/styles.js | 4 ++-- src/view/styles/marginstyles.js | 17 ++++++++--------- tests/view/styles/marginstyles.js | 4 ++-- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index e5c282215..d87f448e6 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -12,7 +12,7 @@ import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; import { addBorderStylesProcessor } from './styles/borderstyles'; -import MarginStyles from './styles/marginstyles'; +import { addMarginStylesProcessor } from './styles/marginstyles'; import PaddingStyles from './styles/paddingstyles'; import BackgroundStyles from './styles/backgroundstyles'; @@ -323,7 +323,7 @@ export class StylesProcessor { mix( StylesProcessor, EmitterMixin ); addBorderStylesProcessor( Styles.processor ); -MarginStyles.attach( Styles.processor ); +addMarginStylesProcessor( Styles.processor ); PaddingStyles.attach( Styles.processor ); BackgroundStyles.attach( Styles.processor ); diff --git a/src/view/styles/marginstyles.js b/src/view/styles/marginstyles.js index 675717615..b68d21215 100644 --- a/src/view/styles/marginstyles.js +++ b/src/view/styles/marginstyles.js @@ -9,15 +9,14 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro * @module engine/view/styles */ -export default class MarginStyles { - static attach( stylesConverter ) { - stylesConverter.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); +export function addMarginStylesProcessor( stylesConverter ) { + stylesConverter.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); - stylesConverter.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); - stylesConverter.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); - stylesConverter.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); - stylesConverter.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); + stylesConverter.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); + stylesConverter.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); + stylesConverter.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); + stylesConverter.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); - stylesConverter.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); - } + stylesConverter.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); } + diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index 7b724f9a9..6aeec9f27 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -4,14 +4,14 @@ */ import Styles, { StylesProcessor } from '../../../src/view/styles'; -import MarginStyles from '../../../src/view/styles/marginstyles'; +import { addMarginStylesProcessor } from '../../../src/view/styles/marginstyles'; describe( 'Margin styles normalizer', () => { let styles; beforeEach( () => { const converter = new StylesProcessor(); - MarginStyles.attach( converter ); + addMarginStylesProcessor( converter ); styles = new Styles( converter ); } ); From 8ca987e6da7d49a36ec16d6b758788cb8caa916c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 14:44:09 +0100 Subject: [PATCH 084/142] Use method to attach padding styles processor. --- src/view/styles.js | 4 ++-- src/view/styles/paddingstyles.js | 16 +++++++--------- tests/view/styles.js | 4 ++-- tests/view/styles/paddingstyles.js | 4 ++-- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index d87f448e6..6e535a3de 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -13,7 +13,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix'; import { addBorderStylesProcessor } from './styles/borderstyles'; import { addMarginStylesProcessor } from './styles/marginstyles'; -import PaddingStyles from './styles/paddingstyles'; +import { addPaddingStylesProcessor } from './styles/paddingstyles'; import BackgroundStyles from './styles/backgroundstyles'; /** @@ -324,7 +324,7 @@ mix( StylesProcessor, EmitterMixin ); addBorderStylesProcessor( Styles.processor ); addMarginStylesProcessor( Styles.processor ); -PaddingStyles.attach( Styles.processor ); +addPaddingStylesProcessor( Styles.processor ); BackgroundStyles.attach( Styles.processor ); // Parses inline styles and puts property - value pairs into styles map. diff --git a/src/view/styles/paddingstyles.js b/src/view/styles/paddingstyles.js index d6b053823..99eee3e1c 100644 --- a/src/view/styles/paddingstyles.js +++ b/src/view/styles/paddingstyles.js @@ -9,14 +9,12 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro * @module engine/view/styles */ -export default class PaddingStyles { - static attach( stylesConverter ) { - stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); - stylesConverter.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); - stylesConverter.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); - stylesConverter.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); - stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); +export function addPaddingStylesProcessor( stylesConverter ) { + stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); + stylesConverter.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); + stylesConverter.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); + stylesConverter.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); + stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); - stylesConverter.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); - } + stylesConverter.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); } diff --git a/tests/view/styles.js b/tests/view/styles.js index 54299df27..9c4cd496c 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -5,7 +5,7 @@ import Styles, { StylesProcessor } from '../../src/view/styles'; import encodedImage from './_utils/encodedimage.txt'; -import PaddingStyles from '../../src/view/styles/paddingstyles'; +import { addPaddingStylesProcessor } from '../../src/view/styles/paddingstyles'; describe( 'Styles', () => { let styles, converter; @@ -23,7 +23,7 @@ describe( 'Styles', () => { data.value = { top: data.value }; } ); - PaddingStyles.attach( converter ); + addPaddingStylesProcessor( converter ); styles = new Styles( converter ); } ); diff --git a/tests/view/styles/paddingstyles.js b/tests/view/styles/paddingstyles.js index 5d7d1417d..0e359577f 100644 --- a/tests/view/styles/paddingstyles.js +++ b/tests/view/styles/paddingstyles.js @@ -4,14 +4,14 @@ */ import Styles, { StylesProcessor } from '../../../src/view/styles'; -import PaddingStyles from '../../../src/view/styles/paddingstyles'; +import { addPaddingStylesProcessor } from '../../../src/view/styles/paddingstyles'; describe( 'Padding styles normalization', () => { let styles; beforeEach( () => { const converter = new StylesProcessor(); - PaddingStyles.attach( converter ); + addPaddingStylesProcessor( converter ); styles = new Styles( converter ); } ); From 76d59ec2d08e5d60121481c101e8bcb08ed582db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 14:46:37 +0100 Subject: [PATCH 085/142] Use method to attach background styles processor. --- src/view/styles.js | 4 ++-- src/view/styles/backgroundstyles.js | 18 ++++++++---------- tests/view/styles/backgroundstyles.js | 4 ++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 6e535a3de..302a5a9dc 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -14,7 +14,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix'; import { addBorderStylesProcessor } from './styles/borderstyles'; import { addMarginStylesProcessor } from './styles/marginstyles'; import { addPaddingStylesProcessor } from './styles/paddingstyles'; -import BackgroundStyles from './styles/backgroundstyles'; +import { addBackgroundStylesProcessor } from './styles/backgroundstyles'; /** * Styles class. @@ -325,7 +325,7 @@ mix( StylesProcessor, EmitterMixin ); addBorderStylesProcessor( Styles.processor ); addMarginStylesProcessor( Styles.processor ); addPaddingStylesProcessor( Styles.processor ); -BackgroundStyles.attach( Styles.processor ); +addBackgroundStylesProcessor( Styles.processor ); // Parses inline styles and puts property - value pairs into styles map. // diff --git a/src/view/styles/backgroundstyles.js b/src/view/styles/backgroundstyles.js index b4a0db5c7..c687c27d1 100644 --- a/src/view/styles/backgroundstyles.js +++ b/src/view/styles/backgroundstyles.js @@ -9,18 +9,16 @@ import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; * @module engine/view/styles */ -export default class BackgroundStyles { - static attach( stylesConverter ) { - stylesConverter.on( 'normalize:background', normalizeBackground ); - stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); - stylesConverter.on( 'reduce:background', ( evt, data ) => { - const ret = []; +export function addBackgroundStylesProcessor( stylesConverter ) { + stylesConverter.on( 'normalize:background', normalizeBackground ); + stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); + stylesConverter.on( 'reduce:background', ( evt, data ) => { + const ret = []; - ret.push( [ 'background-color', data.value.color ] ); + ret.push( [ 'background-color', data.value.color ] ); - data.reduced = ret; - } ); - } + data.reduced = ret; + } ); } function normalizeBackground( evt, data ) { diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index e33ebf265..ac6445b3b 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -4,14 +4,14 @@ */ import Styles, { StylesProcessor } from '../../../src/view/styles'; -import BackgroundStyles from '../../../src/view/styles/backgroundstyles'; +import { addBackgroundStylesProcessor } from '../../../src/view/styles/backgroundstyles'; describe( 'Background styles normalization', () => { let styles; beforeEach( () => { const converter = new StylesProcessor(); - BackgroundStyles.attach( converter ); + addBackgroundStylesProcessor( converter ); styles = new Styles( converter ); } ); From cb7ea99ac30e44ea37c8c8403484f01aad7b1e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 16:03:57 +0100 Subject: [PATCH 086/142] Remove default processors from styles file. --- src/view/styles.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 302a5a9dc..9a86d907a 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -11,11 +11,6 @@ import { get, isObject, merge, set, unset } from 'lodash-es'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; -import { addBorderStylesProcessor } from './styles/borderstyles'; -import { addMarginStylesProcessor } from './styles/marginstyles'; -import { addPaddingStylesProcessor } from './styles/paddingstyles'; -import { addBackgroundStylesProcessor } from './styles/backgroundstyles'; - /** * Styles class. * @@ -57,6 +52,10 @@ export default class Styles { return this._processor; } + static setProcessor( processor ) { + this._processor = processor; + } + /** * Re-sets internal styles definition. * @@ -322,11 +321,6 @@ export class StylesProcessor { mix( StylesProcessor, EmitterMixin ); -addBorderStylesProcessor( Styles.processor ); -addMarginStylesProcessor( Styles.processor ); -addPaddingStylesProcessor( Styles.processor ); -addBackgroundStylesProcessor( Styles.processor ); - // Parses inline styles and puts property - value pairs into styles map. // // @param {String} stylesString Styles to parse. From 20736763af91926a32da8922c1c452991caef668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Jan 2020 16:31:21 +0100 Subject: [PATCH 087/142] Rename style converter to processor. --- src/view/styles.js | 28 ++++---- src/view/styles/backgroundstyles.js | 8 +-- src/view/styles/borderstyles.js | 106 ++++++++++++++-------------- src/view/styles/marginstyles.js | 14 ++-- src/view/styles/paddingstyles.js | 14 ++-- 5 files changed, 85 insertions(+), 85 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 9a86d907a..841dfafa5 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -20,16 +20,16 @@ export default class Styles { /** * Creates Styles instance. */ - constructor( processor ) { + constructor( styleProcessor ) { /** * @private */ this._styles = {}; - // This hides the converter from the watchdog. - Object.defineProperty( this, 'converter', { + // This hides the styleProcessor from the watchdog. + Object.defineProperty( this, 'styleProcessor', { get() { - return processor || Styles.processor; + return styleProcessor || Styles.processor; }, enumerable: false } ); @@ -69,7 +69,7 @@ export default class Styles { for ( const key of map.keys() ) { const value = map.get( key ); - this.converter.toNormalizedForm( key, value, this._styles ); + this.styleProcessor.toNormalizedForm( key, value, this._styles ); } } @@ -82,7 +82,7 @@ export default class Styles { * @returns {Boolean} */ hasProperty( propertyName ) { - const normalized = this.converter.getNormalized( propertyName, this._styles ); + const normalized = this.styleProcessor.getNormalized( propertyName, this._styles ); if ( !normalized ) { // Try return styles set directly - values that are not parsed. @@ -90,7 +90,7 @@ export default class Styles { } if ( isObject( normalized ) ) { - const styles = this.converter.getReducedForm( propertyName, normalized ); + const styles = this.styleProcessor.getReducedForm( propertyName, normalized ); const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); @@ -128,7 +128,7 @@ export default class Styles { this.insertProperty( key, nameOrObject[ key ] ); } } else { - this.converter.toNormalizedForm( nameOrObject, value, this._styles ); + this.styleProcessor.toNormalizedForm( nameOrObject, value, this._styles ); } } @@ -161,7 +161,7 @@ export default class Styles { * @returns {Object|undefined} */ getNormalized( name ) { - return this.converter.getNormalized( name, this._styles ); + return this.styleProcessor.getNormalized( name, this._styles ); } /** @@ -187,7 +187,7 @@ export default class Styles { * @returns {String|undefined} */ getInlineProperty( propertyName ) { - const normalized = this.converter.getNormalized( propertyName, this._styles ); + const normalized = this.styleProcessor.getNormalized( propertyName, this._styles ); if ( !normalized ) { // Try return styles set directly - values that are not parsed. @@ -195,7 +195,7 @@ export default class Styles { } if ( isObject( normalized ) ) { - const styles = this.converter.getReducedForm( propertyName, normalized ); + const styles = this.styleProcessor.getReducedForm( propertyName, normalized ); const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); @@ -238,9 +238,9 @@ export default class Styles { const keys = Object.keys( this._styles ).sort(); for ( const key of keys ) { - const normalized = this.converter.getNormalized( key, this._styles ); + const normalized = this.styleProcessor.getNormalized( key, this._styles ); - parsed.push( ...this.converter.getReducedForm( key, normalized ) ); + parsed.push( ...this.styleProcessor.getReducedForm( key, normalized ) ); } return parsed; @@ -472,7 +472,7 @@ function appendStyleValue( stylesObject, nameOrPath, valueOrObject ) { * * Normalizers produces coherent object representation for both shorthand and longhand forms: * - * stylesConverter.on( 'normalize:border-color', ( evt, data ) => { + * stylesProcessor.on( 'normalize:border-color', ( evt, data ) => { * data.path = 'border.color'; * data.value = { * top: '#f00', diff --git a/src/view/styles/backgroundstyles.js b/src/view/styles/backgroundstyles.js index c687c27d1..40f861f19 100644 --- a/src/view/styles/backgroundstyles.js +++ b/src/view/styles/backgroundstyles.js @@ -9,10 +9,10 @@ import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; * @module engine/view/styles */ -export function addBackgroundStylesProcessor( stylesConverter ) { - stylesConverter.on( 'normalize:background', normalizeBackground ); - stylesConverter.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); - stylesConverter.on( 'reduce:background', ( evt, data ) => { +export function addBackgroundStylesProcessor( stylesProcessor ) { + stylesProcessor.on( 'normalize:background', normalizeBackground ); + stylesProcessor.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); + stylesProcessor.on( 'reduce:background', ( evt, data ) => { const ret = []; ret.push( [ 'background-color', data.value.color ] ); diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index 35b21e9ba..5ac25e85c 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -9,65 +9,65 @@ import { getShorthandValues, getTopRightBottomLeftValueReducer, getTopRightBotto * @module engine/view/styles/borderstyle */ -export function addBorderStylesProcessor( stylesConverter ) { - stylesConverter.on( 'normalize:border', borderNormalizer ); +export function addBorderStylesProcessor( stylesProcessor ) { + stylesProcessor.on( 'normalize:border', borderNormalizer ); // Border-position shorthands. - stylesConverter.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); - stylesConverter.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); - stylesConverter.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); - stylesConverter.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); + stylesProcessor.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); + stylesProcessor.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); + stylesProcessor.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); + stylesProcessor.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); // Border-property shorthands. - stylesConverter.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); - stylesConverter.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); - stylesConverter.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); + stylesProcessor.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); + stylesProcessor.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); + stylesProcessor.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); // Border longhands. - stylesConverter.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); - stylesConverter.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); - stylesConverter.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); - - stylesConverter.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); - stylesConverter.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); - stylesConverter.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); - - stylesConverter.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); - stylesConverter.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); - stylesConverter.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); - - stylesConverter.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); - stylesConverter.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); - stylesConverter.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); - - stylesConverter.on( 'extract:border-top', getBorderPositionExtractor( 'top' ) ); - stylesConverter.on( 'extract:border-right', getBorderPositionExtractor( 'right' ) ); - stylesConverter.on( 'extract:border-bottom', getBorderPositionExtractor( 'bottom' ) ); - stylesConverter.on( 'extract:border-left', getBorderPositionExtractor( 'left' ) ); - - stylesConverter.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); - stylesConverter.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); - stylesConverter.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); - stylesConverter.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); - - stylesConverter.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); - stylesConverter.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); - stylesConverter.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); - stylesConverter.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); - - stylesConverter.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); - stylesConverter.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); - stylesConverter.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); - stylesConverter.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); - - stylesConverter.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); - stylesConverter.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); - stylesConverter.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); - stylesConverter.on( 'reduce:border-top', getBorderPositionReducer( 'top' ) ); - stylesConverter.on( 'reduce:border-right', getBorderPositionReducer( 'right' ) ); - stylesConverter.on( 'reduce:border-bottom', getBorderPositionReducer( 'bottom' ) ); - stylesConverter.on( 'reduce:border-left', getBorderPositionReducer( 'left' ) ); - stylesConverter.on( 'reduce:border', borderReducer ); + stylesProcessor.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); + stylesProcessor.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); + stylesProcessor.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); + + stylesProcessor.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); + stylesProcessor.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); + stylesProcessor.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); + + stylesProcessor.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); + stylesProcessor.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); + stylesProcessor.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); + + stylesProcessor.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); + stylesProcessor.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); + stylesProcessor.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); + + stylesProcessor.on( 'extract:border-top', getBorderPositionExtractor( 'top' ) ); + stylesProcessor.on( 'extract:border-right', getBorderPositionExtractor( 'right' ) ); + stylesProcessor.on( 'extract:border-bottom', getBorderPositionExtractor( 'bottom' ) ); + stylesProcessor.on( 'extract:border-left', getBorderPositionExtractor( 'left' ) ); + + stylesProcessor.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); + stylesProcessor.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); + stylesProcessor.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); + stylesProcessor.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); + + stylesProcessor.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); + stylesProcessor.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); + stylesProcessor.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); + stylesProcessor.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); + + stylesProcessor.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); + stylesProcessor.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); + stylesProcessor.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); + stylesProcessor.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); + + stylesProcessor.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); + stylesProcessor.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); + stylesProcessor.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); + stylesProcessor.on( 'reduce:border-top', getBorderPositionReducer( 'top' ) ); + stylesProcessor.on( 'reduce:border-right', getBorderPositionReducer( 'right' ) ); + stylesProcessor.on( 'reduce:border-bottom', getBorderPositionReducer( 'bottom' ) ); + stylesProcessor.on( 'reduce:border-left', getBorderPositionReducer( 'left' ) ); + stylesProcessor.on( 'reduce:border', borderReducer ); } function borderNormalizer( evt, data ) { diff --git a/src/view/styles/marginstyles.js b/src/view/styles/marginstyles.js index b68d21215..f27142618 100644 --- a/src/view/styles/marginstyles.js +++ b/src/view/styles/marginstyles.js @@ -9,14 +9,14 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro * @module engine/view/styles */ -export function addMarginStylesProcessor( stylesConverter ) { - stylesConverter.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); +export function addMarginStylesProcessor( stylesProcessor ) { + stylesProcessor.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); - stylesConverter.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); - stylesConverter.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); - stylesConverter.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); - stylesConverter.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); + stylesProcessor.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); + stylesProcessor.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); + stylesProcessor.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); + stylesProcessor.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); - stylesConverter.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); + stylesProcessor.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); } diff --git a/src/view/styles/paddingstyles.js b/src/view/styles/paddingstyles.js index 99eee3e1c..9814bf499 100644 --- a/src/view/styles/paddingstyles.js +++ b/src/view/styles/paddingstyles.js @@ -9,12 +9,12 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro * @module engine/view/styles */ -export function addPaddingStylesProcessor( stylesConverter ) { - stylesConverter.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); - stylesConverter.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); - stylesConverter.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); - stylesConverter.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); - stylesConverter.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); +export function addPaddingStylesProcessor( stylesProcessor ) { + stylesProcessor.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); + stylesProcessor.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); + stylesProcessor.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); + stylesProcessor.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); + stylesProcessor.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); - stylesConverter.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); + stylesProcessor.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); } From baeab18de237c4316c6d9f596d49c9c8a5f65ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 10 Jan 2020 11:37:23 +0100 Subject: [PATCH 088/142] Remove .skip from slow tests. --- tests/view/observer/selectionobserver.js | 2 +- tests/view/renderer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/view/observer/selectionobserver.js b/tests/view/observer/selectionobserver.js index ac873b885..f777c15e9 100644 --- a/tests/view/observer/selectionobserver.js +++ b/tests/view/observer/selectionobserver.js @@ -14,7 +14,7 @@ import FocusObserver from '../../../src/view/observer/focusobserver'; import createViewRoot from '../_utils/createroot'; import { parse } from '../../../src/dev-utils/view'; -describe.skip( 'SelectionObserver', () => { +describe( 'SelectionObserver', () => { let view, viewDocument, viewRoot, selectionObserver, domRoot, domMain, domDocument; beforeEach( done => { diff --git a/tests/view/renderer.js b/tests/view/renderer.js index 80833d7d9..66996946f 100644 --- a/tests/view/renderer.js +++ b/tests/view/renderer.js @@ -29,7 +29,7 @@ import normalizeHtml from '@ckeditor/ckeditor5-utils/tests/_utils/normalizehtml' import env from '@ckeditor/ckeditor5-utils/src/env'; import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils'; -describe.skip( 'Renderer', () => { +describe( 'Renderer', () => { let selection, domConverter, renderer; testUtils.createSinonSandbox(); From e2dfe590d43a33a964d20ea6dc224fc8842f3944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 10 Jan 2020 16:16:19 +0100 Subject: [PATCH 089/142] Introduce one-time callbacks registering for styles converter. --- src/view/styles.js | 12 +++ src/view/styles/backgroundstyles.js | 14 +-- src/view/styles/borderstyles.js | 118 +++++++++++++------------- src/view/styles/marginstyles.js | 15 ++-- src/view/styles/paddingstyles.js | 14 +-- tests/view/styles/backgroundstyles.js | 6 +- 6 files changed, 99 insertions(+), 80 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 841dfafa5..86dc26f5b 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -248,6 +248,10 @@ export default class Styles { } export class StylesProcessor { + constructor() { + this._groups = new Set(); + } + /** * Returns reduced form of style property form normalized object. * @@ -317,6 +321,14 @@ export class StylesProcessor { appendStyleValue( styles, data.path, data.value ); } + + registerListeners( groupName, callback ) { + if ( this._groups.has( groupName ) ) { + return; + } + + callback( this ); + } } mix( StylesProcessor, EmitterMixin ); diff --git a/src/view/styles/backgroundstyles.js b/src/view/styles/backgroundstyles.js index 40f861f19..0af8a1ebc 100644 --- a/src/view/styles/backgroundstyles.js +++ b/src/view/styles/backgroundstyles.js @@ -10,14 +10,16 @@ import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; */ export function addBackgroundStylesProcessor( stylesProcessor ) { - stylesProcessor.on( 'normalize:background', normalizeBackground ); - stylesProcessor.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); - stylesProcessor.on( 'reduce:background', ( evt, data ) => { - const ret = []; + stylesProcessor.registerListeners( 'background', stylesProcessor => { + stylesProcessor.on( 'normalize:background', normalizeBackground ); + stylesProcessor.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); + stylesProcessor.on( 'reduce:background', ( evt, data ) => { + const ret = []; - ret.push( [ 'background-color', data.value.color ] ); + ret.push( [ 'background-color', data.value.color ] ); - data.reduced = ret; + data.reduced = ret; + } ); } ); } diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index 5ac25e85c..d578907e0 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -10,64 +10,66 @@ import { getShorthandValues, getTopRightBottomLeftValueReducer, getTopRightBotto */ export function addBorderStylesProcessor( stylesProcessor ) { - stylesProcessor.on( 'normalize:border', borderNormalizer ); - - // Border-position shorthands. - stylesProcessor.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); - stylesProcessor.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); - stylesProcessor.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); - stylesProcessor.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); - - // Border-property shorthands. - stylesProcessor.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); - stylesProcessor.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); - stylesProcessor.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); - - // Border longhands. - stylesProcessor.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); - stylesProcessor.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); - stylesProcessor.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); - - stylesProcessor.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); - stylesProcessor.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); - stylesProcessor.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); - - stylesProcessor.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); - stylesProcessor.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); - stylesProcessor.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); - - stylesProcessor.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); - stylesProcessor.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); - stylesProcessor.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); - - stylesProcessor.on( 'extract:border-top', getBorderPositionExtractor( 'top' ) ); - stylesProcessor.on( 'extract:border-right', getBorderPositionExtractor( 'right' ) ); - stylesProcessor.on( 'extract:border-bottom', getBorderPositionExtractor( 'bottom' ) ); - stylesProcessor.on( 'extract:border-left', getBorderPositionExtractor( 'left' ) ); - - stylesProcessor.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); - stylesProcessor.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); - stylesProcessor.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); - stylesProcessor.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); - - stylesProcessor.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); - stylesProcessor.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); - stylesProcessor.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); - stylesProcessor.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); - - stylesProcessor.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); - stylesProcessor.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); - stylesProcessor.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); - stylesProcessor.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); - - stylesProcessor.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); - stylesProcessor.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); - stylesProcessor.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); - stylesProcessor.on( 'reduce:border-top', getBorderPositionReducer( 'top' ) ); - stylesProcessor.on( 'reduce:border-right', getBorderPositionReducer( 'right' ) ); - stylesProcessor.on( 'reduce:border-bottom', getBorderPositionReducer( 'bottom' ) ); - stylesProcessor.on( 'reduce:border-left', getBorderPositionReducer( 'left' ) ); - stylesProcessor.on( 'reduce:border', borderReducer ); + stylesProcessor.registerListeners( 'border', stylesProcessor => { + stylesProcessor.on( 'normalize:border', borderNormalizer ); + + // Border-position shorthands. + stylesProcessor.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); + stylesProcessor.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); + stylesProcessor.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); + stylesProcessor.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); + + // Border-property shorthands. + stylesProcessor.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); + stylesProcessor.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); + stylesProcessor.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); + + // Border longhands. + stylesProcessor.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); + stylesProcessor.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); + stylesProcessor.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); + + stylesProcessor.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); + stylesProcessor.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); + stylesProcessor.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); + + stylesProcessor.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); + stylesProcessor.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); + stylesProcessor.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); + + stylesProcessor.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); + stylesProcessor.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); + stylesProcessor.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); + + stylesProcessor.on( 'extract:border-top', getBorderPositionExtractor( 'top' ) ); + stylesProcessor.on( 'extract:border-right', getBorderPositionExtractor( 'right' ) ); + stylesProcessor.on( 'extract:border-bottom', getBorderPositionExtractor( 'bottom' ) ); + stylesProcessor.on( 'extract:border-left', getBorderPositionExtractor( 'left' ) ); + + stylesProcessor.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); + stylesProcessor.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); + stylesProcessor.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); + stylesProcessor.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); + + stylesProcessor.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); + stylesProcessor.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); + stylesProcessor.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); + stylesProcessor.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); + + stylesProcessor.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); + stylesProcessor.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); + stylesProcessor.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); + stylesProcessor.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); + + stylesProcessor.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); + stylesProcessor.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); + stylesProcessor.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); + stylesProcessor.on( 'reduce:border-top', getBorderPositionReducer( 'top' ) ); + stylesProcessor.on( 'reduce:border-right', getBorderPositionReducer( 'right' ) ); + stylesProcessor.on( 'reduce:border-bottom', getBorderPositionReducer( 'bottom' ) ); + stylesProcessor.on( 'reduce:border-left', getBorderPositionReducer( 'left' ) ); + stylesProcessor.on( 'reduce:border', borderReducer ); + } ); } function borderNormalizer( evt, data ) { diff --git a/src/view/styles/marginstyles.js b/src/view/styles/marginstyles.js index f27142618..c00b1276c 100644 --- a/src/view/styles/marginstyles.js +++ b/src/view/styles/marginstyles.js @@ -10,13 +10,14 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro */ export function addMarginStylesProcessor( stylesProcessor ) { - stylesProcessor.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); + stylesProcessor.registerListeners( 'margin', stylesProcessor => { + stylesProcessor.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); - stylesProcessor.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); - stylesProcessor.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); - stylesProcessor.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); - stylesProcessor.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); + stylesProcessor.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); + stylesProcessor.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); + stylesProcessor.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); + stylesProcessor.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); - stylesProcessor.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); + stylesProcessor.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); + } ); } - diff --git a/src/view/styles/paddingstyles.js b/src/view/styles/paddingstyles.js index 9814bf499..d483d9522 100644 --- a/src/view/styles/paddingstyles.js +++ b/src/view/styles/paddingstyles.js @@ -10,11 +10,13 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro */ export function addPaddingStylesProcessor( stylesProcessor ) { - stylesProcessor.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); - stylesProcessor.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); - stylesProcessor.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); - stylesProcessor.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); - stylesProcessor.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); + stylesProcessor.registerListeners( 'padding', stylesProcessor => { + stylesProcessor.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); + stylesProcessor.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); + stylesProcessor.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); + stylesProcessor.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); + stylesProcessor.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); - stylesProcessor.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); + stylesProcessor.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); + } ); } diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index ac6445b3b..a3918d477 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -10,9 +10,9 @@ describe( 'Background styles normalization', () => { let styles; beforeEach( () => { - const converter = new StylesProcessor(); - addBackgroundStylesProcessor( converter ); - styles = new Styles( converter ); + const stylesProcessor = new StylesProcessor(); + addBackgroundStylesProcessor( stylesProcessor ); + styles = new Styles( stylesProcessor ); } ); it( 'should normalize background', () => { From 727d143910646846d3c7e9db03cbb7cb09cddc94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 09:52:27 +0100 Subject: [PATCH 090/142] Remove consuming shorthand style names while consuming longhand and vice-versa. --- src/conversion/viewconsumable.js | 49 ---------------------- tests/conversion/viewconsumable.js | 67 ------------------------------ 2 files changed, 116 deletions(-) diff --git a/src/conversion/viewconsumable.js b/src/conversion/viewconsumable.js index 5411735ca..a4b4491b3 100644 --- a/src/conversion/viewconsumable.js +++ b/src/conversion/viewconsumable.js @@ -10,43 +10,6 @@ import { isArray } from 'lodash-es'; import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; -const styleConsumablesMap = new Map(); - -addStyleConsumeAlso( 'margin', [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ] ); -addStyleConsumeAlso( 'padding', [ 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' ] ); - -addStyleConsumeAlso( 'border', [ 'border-color', 'border-style', 'border-width' ] ); -addStyleConsumeAlso( 'border', [ 'border-top', 'border-right', 'border-bottom', 'border-left' ] ); -addStyleConsumeAlso( 'border', [ - 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color', - 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style', - 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width' -] ); -addStyleConsumeAlso( 'border-color', [ 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color' ] ); -addStyleConsumeAlso( 'border-style', [ 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style' ] ); -addStyleConsumeAlso( 'border-width', [ 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width' ] ); - -addStyleConsumeAlso( 'border-top', [ 'border-top-color', 'border-top-style', 'border-top-width' ] ); -addStyleConsumeAlso( 'border-right', [ 'border-right-color', 'border-right-style', 'border-right-width' ] ); -addStyleConsumeAlso( 'border-bottom', [ 'border-bottom-color', 'border-bottom-style', 'border-bottom-width' ] ); -addStyleConsumeAlso( 'border-left', [ 'border-left-color', 'border-left-style', 'border-left-width' ] ); - -function addStyleConsumeAlso( shorthandName, also ) { - addConsumable( shorthandName, also ); - - for ( const alsoName of also ) { - addConsumable( alsoName, [ shorthandName ] ); - } - - function addConsumable( name, values ) { - if ( !styleConsumablesMap.has( name ) ) { - styleConsumablesMap.set( name, [] ); - } - - styleConsumablesMap.get( name ).push( ...values ); - } -} - /** * Class used for handling consumption of view {@link module:engine/view/element~Element elements}, * {@link module:engine/view/text~Text text nodes} and {@link module:engine/view/documentfragment~DocumentFragment document fragments}. @@ -544,12 +507,6 @@ class ViewElementConsumables { } consumables.set( name, true ); - - if ( type === 'styles' && styleConsumablesMap.has( name ) ) { - for ( const alsoName of styleConsumablesMap.get( name ) ) { - consumables.set( alsoName, true ); - } - } } } @@ -611,12 +568,6 @@ class ViewElementConsumables { this._consume( consumableName, [ ...this._consumables[ consumableName ].keys() ] ); } else { consumables.set( name, false ); - - if ( styleConsumablesMap.has( name ) ) { - for ( const toConsume of styleConsumablesMap.get( name ) ) { - consumables.set( toConsume, false ); - } - } } } } diff --git a/tests/conversion/viewconsumable.js b/tests/conversion/viewconsumable.js index 3f70fa883..3ae9324ac 100644 --- a/tests/conversion/viewconsumable.js +++ b/tests/conversion/viewconsumable.js @@ -549,71 +549,4 @@ describe( 'ViewConsumable', () => { expect( newConsumable.test( child3, { name: true, styles: 'top', classes: [ 'qux', 'bar' ] } ) ).to.be.true; } ); } ); - - describe( 'style shorthands handling', () => { - describe( 'add', () => { - it( 'should add padding shorthands', () => { - viewConsumable.add( el, { styles: [ 'margin' ] } ); - - expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'margin-bottom' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'margin-right' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'margin-left' } ) ).to.be.true; - } ); - - it( 'should add margin shorthands', () => { - viewConsumable.add( el, { styles: [ 'padding' ] } ); - - expect( viewConsumable.test( el, { styles: 'padding-top' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'padding-bottom' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'padding-right' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'padding-left' } ) ).to.be.true; - } ); - - it( 'should add table shorthands', () => { - viewConsumable.add( el, { styles: [ 'border' ] } ); - - expect( viewConsumable.test( el, { styles: 'border-style' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-top-style' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-bottom-style' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-right-style' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-left-style' } ) ).to.be.true; - - expect( viewConsumable.test( el, { styles: 'border-color' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-top-color' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-bottom-color' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-right-color' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-left-color' } ) ).to.be.true; - - expect( viewConsumable.test( el, { styles: 'border-width' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-top-width' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-bottom-width' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-right-width' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'border-left-width' } ) ).to.be.true; - } ); - } ); - - it( 'should return false when testing style shorthand for consumed longhand', () => { - viewConsumable.add( el, { styles: [ 'margin' ] } ); - - expect( viewConsumable.test( el, { styles: 'margin' } ) ).to.be.true; - viewConsumable.consume( el, { styles: 'margin' } ); - expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.false; - expect( viewConsumable.test( el, { styles: 'margin-bottom' } ) ).to.be.false; - expect( viewConsumable.test( el, { styles: 'margin-right' } ) ).to.be.false; - expect( viewConsumable.test( el, { styles: 'margin-left' } ) ).to.be.false; - } ); - - it( 'should return false when testing style shorthand for consumed shorthand', () => { - viewConsumable.add( el, { styles: [ 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ] } ); - - expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.true; - viewConsumable.consume( el, { styles: 'margin-top' } ); - expect( viewConsumable.test( el, { styles: 'margin' } ) ).to.be.false; - expect( viewConsumable.test( el, { styles: 'margin-top' } ) ).to.be.false; - expect( viewConsumable.test( el, { styles: 'margin-bottom' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'margin-right' } ) ).to.be.true; - expect( viewConsumable.test( el, { styles: 'margin-left' } ) ).to.be.true; - } ); - } ); } ); From 9712d8dccca45bd9f504ce29aff4f464178fe1e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 09:54:22 +0100 Subject: [PATCH 091/142] Fix jsdoc for view element's _styles property. --- src/view/element.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/element.js b/src/view/element.js index fbddd7c85..6c8ace1d5 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -109,7 +109,7 @@ export default class Element extends Node { * Normalized styles. * * @protected - * @member {Map} module:engine/view/element~Element#_styles + * @member {module:engine/view/styles~Styles} module:engine/view/element~Element#_styles */ this._styles = new Styles(); From e19120cb5e5e2cde7b8203b12b01e9e2c9568421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 09:55:21 +0100 Subject: [PATCH 092/142] Fix jsdoc of view/Element.getStyle(). --- src/view/element.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/element.js b/src/view/element.js index 6c8ace1d5..483f81373 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -379,7 +379,7 @@ export default class Element extends Node { * Undefined is returned if style does not exist. * * @param {String} property - * @returns {String|Object|undefined} + * @returns {String|undefined} */ getStyle( property ) { return this._styles.getInlineProperty( property ); From 4c8e6404e0182a0795d4d7dc759107909199fef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 14:32:46 +0100 Subject: [PATCH 093/142] Remove sorting from Styles._getStylesEntries(). --- src/view/styles.js | 7 +++-- tests/view/styles.js | 2 +- tests/view/styles/borderstyles.js | 46 +++++++++++++++++++++++++------ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 86dc26f5b..9247b520c 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -177,7 +177,10 @@ export default class Styles { return; } - return entries.map( arr => arr.join( ':' ) ).join( ';' ) + ';'; + return entries + .map( arr => arr.join( ':' ) ) + .sort() + .join( ';' ) + ';'; } /** @@ -235,7 +238,7 @@ export default class Styles { _getStylesEntries() { const parsed = []; - const keys = Object.keys( this._styles ).sort(); + const keys = Object.keys( this._styles ); for ( const key of keys ) { const normalized = this.styleProcessor.getNormalized( key, this._styles ); diff --git a/tests/view/styles.js b/tests/view/styles.js index 9c4cd496c..649520df5 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -232,7 +232,7 @@ describe( 'Styles', () => { it( 'should output custom style names', () => { styles.setStyle( 'foo: 2;bar: baz;foo-bar-baz:none;' ); - expect( styles.getStyleNames() ).to.deep.equal( [ 'bar', 'foo', 'foo-bar-baz' ] ); + expect( styles.getStyleNames() ).to.deep.equal( [ 'foo', 'bar', 'foo-bar-baz' ] ); } ); it( 'should output full names for known style names', () => { diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index a0415707f..966769297 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -63,7 +63,10 @@ describe( 'Border styles normalization', () => { styles.setStyle( 'border:1px solid blue;' ); expect( styles.getInlineStyle() ).to.equal( - 'border-top:1px solid blue;border-right:1px solid blue;border-bottom:1px solid blue;border-left:1px solid blue;' + 'border-bottom:1px solid blue;' + + 'border-left:1px solid blue;' + + 'border-right:1px solid blue;' + + 'border-top:1px solid blue;' ); expect( styles.getInlineProperty( 'border-color' ) ).to.equal( 'blue' ); expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); @@ -86,7 +89,10 @@ describe( 'Border styles normalization', () => { styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styles.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + 'border-bottom:1px solid blue;' + + 'border-left:2.7em dashed #665511;' + + 'border-right:1px solid blue;' + + 'border-top:7px dotted #ccc;' ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; @@ -131,7 +137,10 @@ describe( 'Border styles normalization', () => { styles.insertProperty( 'border-top', '7px dotted #ccc' ); expect( styles.getInlineStyle() ).to.equal( - 'border-top:7px dotted #ccc;border-right:1px solid blue;border-bottom:1px solid blue;border-left:2.7em dashed #665511;' + 'border-bottom:1px solid blue;' + + 'border-left:2.7em dashed #665511;' + + 'border-right:1px solid blue;' + + 'border-top:7px dotted #ccc;' ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); @@ -144,7 +153,10 @@ describe( 'Border styles normalization', () => { styles.removeProperty( 'border-color' ); expect( styles.getInlineStyle() ).to.equal( - 'border-top:1px solid;border-right:1px solid;border-bottom:1px solid;border-left:1px solid;' + 'border-bottom:1px solid;' + + 'border-left:1px solid;' + + 'border-right:1px solid;' + + 'border-top:1px solid;' ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; @@ -160,7 +172,12 @@ describe( 'Border styles normalization', () => { it( 'should output border with only style shorthand (style)', () => { styles.setStyle( 'border:solid;' ); - expect( styles.getInlineStyle() ).to.equal( 'border-top:solid;border-right:solid;border-bottom:solid;border-left:solid;' ); + expect( styles.getInlineStyle() ).to.equal( + 'border-bottom:solid;' + + 'border-left:solid;' + + 'border-right:solid;' + + 'border-top:solid;' + ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); @@ -174,7 +191,12 @@ describe( 'Border styles normalization', () => { it( 'should output border with only style shorthand (color)', () => { styles.setStyle( 'border:#f00;' ); - expect( styles.getInlineStyle() ).to.equal( 'border-top:#f00;border-right:#f00;border-bottom:#f00;border-left:#f00;' ); + expect( styles.getInlineStyle() ).to.equal( + 'border-bottom:#f00;' + + 'border-left:#f00;' + + 'border-right:#f00;' + + 'border-top:#f00;' + ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#f00' ); expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; @@ -188,7 +210,12 @@ describe( 'Border styles normalization', () => { it( 'should output border with only style shorthand (width)', () => { styles.setStyle( 'border:1px;' ); - expect( styles.getInlineStyle() ).to.equal( 'border-top:1px;border-right:1px;border-bottom:1px;border-left:1px;' ); + expect( styles.getInlineStyle() ).to.equal( + 'border-bottom:1px;' + + 'border-left:1px;' + + 'border-right:1px;' + + 'border-top:1px;' + ); expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; @@ -565,7 +592,10 @@ describe( 'Border styles normalization', () => { ); expect( styles.getInlineStyle() ).to.equal( - 'border-top:none;border-right:3.0pt dotted #FFC000;border-bottom:3.0pt dotted #FFC000;border-left:none;' + 'border-bottom:3.0pt dotted #FFC000;' + + 'border-left:none;' + + 'border-right:3.0pt dotted #FFC000;' + + 'border-top:none;' ); expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'none' ); expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '3.0pt dotted #FFC000' ); From 2dadcd0f98d85f22f6109cb507bb2b5b580a99ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 15:32:28 +0100 Subject: [PATCH 094/142] Rename styleProcessor to _styleProcessor in Styles. --- src/view/styles.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 9247b520c..3b014287e 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -26,8 +26,10 @@ export default class Styles { */ this._styles = {}; - // This hides the styleProcessor from the watchdog. - Object.defineProperty( this, 'styleProcessor', { + // Hide _styleProcessor from the watchdog by making this property non-enumarable. Watchdog checks errors for their editor origin + // by checking if two objects are connected through properties. Using singleton is against this check as it would detect + // that two editors are connected through single style processor instance. + Object.defineProperty( this, '_styleProcessor', { get() { return styleProcessor || Styles.processor; }, @@ -69,7 +71,7 @@ export default class Styles { for ( const key of map.keys() ) { const value = map.get( key ); - this.styleProcessor.toNormalizedForm( key, value, this._styles ); + this._styleProcessor.toNormalizedForm( key, value, this._styles ); } } @@ -82,7 +84,7 @@ export default class Styles { * @returns {Boolean} */ hasProperty( propertyName ) { - const normalized = this.styleProcessor.getNormalized( propertyName, this._styles ); + const normalized = this._styleProcessor.getNormalized( propertyName, this._styles ); if ( !normalized ) { // Try return styles set directly - values that are not parsed. @@ -90,7 +92,7 @@ export default class Styles { } if ( isObject( normalized ) ) { - const styles = this.styleProcessor.getReducedForm( propertyName, normalized ); + const styles = this._styleProcessor.getReducedForm( propertyName, normalized ); const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); @@ -128,7 +130,7 @@ export default class Styles { this.insertProperty( key, nameOrObject[ key ] ); } } else { - this.styleProcessor.toNormalizedForm( nameOrObject, value, this._styles ); + this._styleProcessor.toNormalizedForm( nameOrObject, value, this._styles ); } } @@ -161,7 +163,7 @@ export default class Styles { * @returns {Object|undefined} */ getNormalized( name ) { - return this.styleProcessor.getNormalized( name, this._styles ); + return this._styleProcessor.getNormalized( name, this._styles ); } /** @@ -190,7 +192,7 @@ export default class Styles { * @returns {String|undefined} */ getInlineProperty( propertyName ) { - const normalized = this.styleProcessor.getNormalized( propertyName, this._styles ); + const normalized = this._styleProcessor.getNormalized( propertyName, this._styles ); if ( !normalized ) { // Try return styles set directly - values that are not parsed. @@ -198,7 +200,7 @@ export default class Styles { } if ( isObject( normalized ) ) { - const styles = this.styleProcessor.getReducedForm( propertyName, normalized ); + const styles = this._styleProcessor.getReducedForm( propertyName, normalized ); const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); @@ -241,9 +243,9 @@ export default class Styles { const keys = Object.keys( this._styles ); for ( const key of keys ) { - const normalized = this.styleProcessor.getNormalized( key, this._styles ); + const normalized = this._styleProcessor.getNormalized( key, this._styles ); - parsed.push( ...this.styleProcessor.getReducedForm( key, normalized ) ); + parsed.push( ...this._styleProcessor.getReducedForm( key, normalized ) ); } return parsed; From 38cbc5aca40395e4f40a68624394c2d9d298fbd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 17:55:49 +0100 Subject: [PATCH 095/142] Use map to store style converters. --- src/view/document.js | 5 + src/view/styles.js | 60 ++++++---- src/view/styles/backgroundstyles.js | 22 ++-- src/view/styles/borderstyles.js | 180 ++++++++++++++-------------- src/view/styles/marginstyles.js | 14 +-- src/view/styles/paddingstyles.js | 14 +-- src/view/styles/utils.js | 12 +- tests/view/styles.js | 16 +-- 8 files changed, 176 insertions(+), 147 deletions(-) diff --git a/src/view/document.js b/src/view/document.js index fb1f1f6f0..7645fa052 100644 --- a/src/view/document.js +++ b/src/view/document.js @@ -11,6 +11,7 @@ import DocumentSelection from './documentselection'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin'; +import Styles from './styles'; // @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' ); @@ -160,6 +161,10 @@ export default class Document { this.stopListening(); } + addStyleProcessorRules( callback ) { + callback( Styles.processor ); + } + /** * Performs post-fixer loops. Executes post-fixer callbacks as long as none of them has done any changes to the model. * diff --git a/src/view/styles.js b/src/view/styles.js index 3b014287e..cb38d6616 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -8,8 +8,6 @@ */ import { get, isObject, merge, set, unset } from 'lodash-es'; -import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; -import mix from '@ckeditor/ckeditor5-utils/src/mix'; /** * Styles class. @@ -254,7 +252,9 @@ export default class Styles { export class StylesProcessor { constructor() { - this._groups = new Set(); + this._normalizers = new Map(); + this._extractors = new Map(); + this._reducers = new Map(); } /** @@ -270,9 +270,13 @@ export class StylesProcessor { value: normalizedValue }; - this.fire( 'reduce:' + styleName, data ); + if ( this._reducers.has( styleName ) ) { + const reducer = this._reducers.get( styleName ); - return data.reduced || [ [ styleName, normalizedValue ] ]; + return reducer( data ); + } + + return [ [ styleName, normalizedValue ] ]; } getNormalized( name, styles ) { @@ -289,14 +293,22 @@ export class StylesProcessor { styles }; - this.fire( `extract:${ name }`, data ); + if ( this._extractors.has( name ) ) { + const extractor = this._extractors.get( name ); - if ( data.path ) { - return get( styles, data.path ); - } + if ( typeof extractor === 'string' ) { + return get( styles, extractor ); + } + + const { path, value } = extractor( data ); - if ( data.value ) { - return data.value; + if ( path ) { + return get( styles, path ); + } + + if ( value ) { + return value; + } } return get( styles, toPath( name ) ); @@ -322,21 +334,29 @@ export class StylesProcessor { value }; - this.fire( 'normalize:' + propertyName, data ); + if ( this._normalizers.has( propertyName ) ) { + const normalizer = this._normalizers.get( propertyName ); - appendStyleValue( styles, data.path, data.value ); - } + const { path, value } = normalizer( data ); - registerListeners( groupName, callback ) { - if ( this._groups.has( groupName ) ) { - return; + appendStyleValue( styles, path, value ); + } else { + appendStyleValue( styles, propertyName, value ); } + } - callback( this ); + setNormalizer( propertyName, callback ) { + this._normalizers.set( propertyName, callback ); } -} -mix( StylesProcessor, EmitterMixin ); + setExtractor( propertyName, callbackOrPath ) { + this._extractors.set( propertyName, callbackOrPath ); + } + + setReducer( propertyName, callback ) { + this._reducers.set( propertyName, callback ); + } +} // Parses inline styles and puts property - value pairs into styles map. // diff --git a/src/view/styles/backgroundstyles.js b/src/view/styles/backgroundstyles.js index 0af8a1ebc..8d3a22479 100644 --- a/src/view/styles/backgroundstyles.js +++ b/src/view/styles/backgroundstyles.js @@ -10,20 +10,18 @@ import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; */ export function addBackgroundStylesProcessor( stylesProcessor ) { - stylesProcessor.registerListeners( 'background', stylesProcessor => { - stylesProcessor.on( 'normalize:background', normalizeBackground ); - stylesProcessor.on( 'normalize:background-color', ( evt, data ) => ( data.path = 'background.color' ) ); - stylesProcessor.on( 'reduce:background', ( evt, data ) => { - const ret = []; + stylesProcessor.setNormalizer( 'background', normalizeBackground ); + stylesProcessor.setNormalizer( 'background-color', data => ( { path: 'background.color', value: data.value } ) ); + stylesProcessor.setReducer( 'background', data => { + const ret = []; - ret.push( [ 'background-color', data.value.color ] ); + ret.push( [ 'background-color', data.value.color ] ); - data.reduced = ret; - } ); + return ret; } ); } -function normalizeBackground( evt, data ) { +function normalizeBackground( data ) { const background = {}; const parts = data.value.split( ' ' ); @@ -44,6 +42,8 @@ function normalizeBackground( evt, data ) { } } - data.path = 'background'; - data.value = background; + return { + path: 'background', + value: background + }; } diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index d578907e0..a1cba81c0 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -10,81 +10,81 @@ import { getShorthandValues, getTopRightBottomLeftValueReducer, getTopRightBotto */ export function addBorderStylesProcessor( stylesProcessor ) { - stylesProcessor.registerListeners( 'border', stylesProcessor => { - stylesProcessor.on( 'normalize:border', borderNormalizer ); - - // Border-position shorthands. - stylesProcessor.on( 'normalize:border-top', getBorderPositionNormalizer( 'top' ) ); - stylesProcessor.on( 'normalize:border-right', getBorderPositionNormalizer( 'right' ) ); - stylesProcessor.on( 'normalize:border-bottom', getBorderPositionNormalizer( 'bottom' ) ); - stylesProcessor.on( 'normalize:border-left', getBorderPositionNormalizer( 'left' ) ); - - // Border-property shorthands. - stylesProcessor.on( 'normalize:border-color', getBorderPropertyNormalizer( 'color' ) ); - stylesProcessor.on( 'normalize:border-width', getBorderPropertyNormalizer( 'width' ) ); - stylesProcessor.on( 'normalize:border-style', getBorderPropertyNormalizer( 'style' ) ); - - // Border longhands. - stylesProcessor.on( 'normalize:border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); - stylesProcessor.on( 'normalize:border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); - stylesProcessor.on( 'normalize:border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); - - stylesProcessor.on( 'normalize:border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); - stylesProcessor.on( 'normalize:border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); - stylesProcessor.on( 'normalize:border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); - - stylesProcessor.on( 'normalize:border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); - stylesProcessor.on( 'normalize:border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); - stylesProcessor.on( 'normalize:border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); - - stylesProcessor.on( 'normalize:border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); - stylesProcessor.on( 'normalize:border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); - stylesProcessor.on( 'normalize:border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); - - stylesProcessor.on( 'extract:border-top', getBorderPositionExtractor( 'top' ) ); - stylesProcessor.on( 'extract:border-right', getBorderPositionExtractor( 'right' ) ); - stylesProcessor.on( 'extract:border-bottom', getBorderPositionExtractor( 'bottom' ) ); - stylesProcessor.on( 'extract:border-left', getBorderPositionExtractor( 'left' ) ); - - stylesProcessor.on( 'extract:border-top-color', ( evt, data ) => ( data.path = 'border.color.top' ) ); - stylesProcessor.on( 'extract:border-right-color', ( evt, data ) => ( data.path = 'border.color.right' ) ); - stylesProcessor.on( 'extract:border-bottom-color', ( evt, data ) => ( data.path = 'border.color.bottom' ) ); - stylesProcessor.on( 'extract:border-left-color', ( evt, data ) => ( data.path = 'border.color.left' ) ); - - stylesProcessor.on( 'extract:border-top-width', ( evt, data ) => ( data.path = 'border.width.top' ) ); - stylesProcessor.on( 'extract:border-right-width', ( evt, data ) => ( data.path = 'border.width.right' ) ); - stylesProcessor.on( 'extract:border-bottom-width', ( evt, data ) => ( data.path = 'border.width.bottom' ) ); - stylesProcessor.on( 'extract:border-left-width', ( evt, data ) => ( data.path = 'border.width.left' ) ); - - stylesProcessor.on( 'extract:border-top-style', ( evt, data ) => ( data.path = 'border.style.top' ) ); - stylesProcessor.on( 'extract:border-right-style', ( evt, data ) => ( data.path = 'border.style.right' ) ); - stylesProcessor.on( 'extract:border-bottom-style', ( evt, data ) => ( data.path = 'border.style.bottom' ) ); - stylesProcessor.on( 'extract:border-left-style', ( evt, data ) => ( data.path = 'border.style.left' ) ); - - stylesProcessor.on( 'reduce:border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); - stylesProcessor.on( 'reduce:border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); - stylesProcessor.on( 'reduce:border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); - stylesProcessor.on( 'reduce:border-top', getBorderPositionReducer( 'top' ) ); - stylesProcessor.on( 'reduce:border-right', getBorderPositionReducer( 'right' ) ); - stylesProcessor.on( 'reduce:border-bottom', getBorderPositionReducer( 'bottom' ) ); - stylesProcessor.on( 'reduce:border-left', getBorderPositionReducer( 'left' ) ); - stylesProcessor.on( 'reduce:border', borderReducer ); - } ); + stylesProcessor.setNormalizer( 'border', borderNormalizer ); + + // Border-position shorthands. + stylesProcessor.setNormalizer( 'border-top', getBorderPositionNormalizer( 'top' ) ); + stylesProcessor.setNormalizer( 'border-right', getBorderPositionNormalizer( 'right' ) ); + stylesProcessor.setNormalizer( 'border-bottom', getBorderPositionNormalizer( 'bottom' ) ); + stylesProcessor.setNormalizer( 'border-left', getBorderPositionNormalizer( 'left' ) ); + + // Border-property shorthands. + stylesProcessor.setNormalizer( 'border-color', getBorderPropertyNormalizer( 'color' ) ); + stylesProcessor.setNormalizer( 'border-width', getBorderPropertyNormalizer( 'width' ) ); + stylesProcessor.setNormalizer( 'border-style', getBorderPropertyNormalizer( 'style' ) ); + + // Border longhands. + stylesProcessor.setNormalizer( 'border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) ); + stylesProcessor.setNormalizer( 'border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) ); + stylesProcessor.setNormalizer( 'border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) ); + + stylesProcessor.setNormalizer( 'border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) ); + stylesProcessor.setNormalizer( 'border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) ); + stylesProcessor.setNormalizer( 'border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) ); + + stylesProcessor.setNormalizer( 'border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) ); + stylesProcessor.setNormalizer( 'border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) ); + stylesProcessor.setNormalizer( 'border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) ); + + stylesProcessor.setNormalizer( 'border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) ); + stylesProcessor.setNormalizer( 'border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) ); + stylesProcessor.setNormalizer( 'border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) ); + + stylesProcessor.setExtractor( 'border-top', getBorderPositionExtractor( 'top' ) ); + stylesProcessor.setExtractor( 'border-right', getBorderPositionExtractor( 'right' ) ); + stylesProcessor.setExtractor( 'border-bottom', getBorderPositionExtractor( 'bottom' ) ); + stylesProcessor.setExtractor( 'border-left', getBorderPositionExtractor( 'left' ) ); + + stylesProcessor.setExtractor( 'border-top-color', 'border.color.top' ); + stylesProcessor.setExtractor( 'border-right-color', 'border.color.right' ); + stylesProcessor.setExtractor( 'border-bottom-color', 'border.color.bottom' ); + stylesProcessor.setExtractor( 'border-left-color', 'border.color.left' ); + + stylesProcessor.setExtractor( 'border-top-width', 'border.width.top' ); + stylesProcessor.setExtractor( 'border-right-width', 'border.width.right' ); + stylesProcessor.setExtractor( 'border-bottom-width', 'border.width.bottom' ); + stylesProcessor.setExtractor( 'border-left-width', 'border.width.left' ); + + stylesProcessor.setExtractor( 'border-top-style', 'border.style.top' ); + stylesProcessor.setExtractor( 'border-right-style', 'border.style.right' ); + stylesProcessor.setExtractor( 'border-bottom-style', 'border.style.bottom' ); + stylesProcessor.setExtractor( 'border-left-style', 'border.style.left' ); + + stylesProcessor.setReducer( 'border-color', getTopRightBottomLeftValueReducer( 'border-color' ) ); + stylesProcessor.setReducer( 'border-style', getTopRightBottomLeftValueReducer( 'border-style' ) ); + stylesProcessor.setReducer( 'border-width', getTopRightBottomLeftValueReducer( 'border-width' ) ); + stylesProcessor.setReducer( 'border-top', getBorderPositionReducer( 'top' ) ); + stylesProcessor.setReducer( 'border-right', getBorderPositionReducer( 'right' ) ); + stylesProcessor.setReducer( 'border-bottom', getBorderPositionReducer( 'bottom' ) ); + stylesProcessor.setReducer( 'border-left', getBorderPositionReducer( 'left' ) ); + stylesProcessor.setReducer( 'border', borderReducer ); } -function borderNormalizer( evt, data ) { +function borderNormalizer( data ) { const { color, style, width } = normalizeBorderShorthand( data.value ); - data.path = 'border'; - data.value = { - color: getTopRightBottomLeftValues( color ), - style: getTopRightBottomLeftValues( style ), - width: getTopRightBottomLeftValues( width ) + return { + path: 'border', + value: { + color: getTopRightBottomLeftValues( color ), + style: getTopRightBottomLeftValues( style ), + width: getTopRightBottomLeftValues( width ) + } }; } function getBorderPositionNormalizer( side ) { - return ( evt, data ) => { + return data => { const { color, style, width } = normalizeBorderShorthand( data.value ); const border = {}; @@ -101,15 +101,19 @@ function getBorderPositionNormalizer( side ) { border.width = { [ side ]: width }; } - data.path = 'border'; - data.value = border; + return { + path: 'border', + value: border + }; }; } function getBorderPropertyNormalizer( propertyName ) { - return ( evt, data ) => { - data.path = 'border'; - data.value = toBorderPropertyShorthand( data.value, propertyName ); + return data => { + return { + path: 'border', + value: toBorderPropertyShorthand( data.value, propertyName ) + }; }; } @@ -120,20 +124,22 @@ function toBorderPropertyShorthand( value, property ) { } function getBorderPropertyPositionNormalizer( property, side ) { - return ( evt, data ) => { - data.path = 'border'; - data.value = { - [ property ]: { - [ side ]: data.value + return data => { + return { + path: 'border', + value: { + [ property ]: { + [ side ]: data.value + } } }; }; } function getBorderPositionExtractor( which ) { - return ( evt, data ) => { + return data => { if ( data.styles.border ) { - data.value = extractBorderPosition( data.styles.border, which, data ); + return { value: extractBorderPosition( data.styles.border, which, data ) }; } }; } @@ -174,19 +180,19 @@ function normalizeBorderShorthand( string ) { return result; } -function borderReducer( evt, data ) { - const ret = []; +function borderReducer( data ) { + const reduced = []; - ret.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'top' ), 'top' ) ); - ret.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'right' ), 'right' ) ); - ret.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'bottom' ), 'bottom' ) ); - ret.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'left' ), 'left' ) ); + reduced.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'top' ), 'top' ) ); + reduced.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'right' ), 'right' ) ); + reduced.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'bottom' ), 'bottom' ) ); + reduced.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'left' ), 'left' ) ); - data.reduced = ret; + return reduced; } function getBorderPositionReducer( which ) { - return ( evt, data ) => ( data.reduced = reduceBorderPosition( data.value, which ) ); + return data => ( data.reduced = reduceBorderPosition( data.value, which ) ); } function reduceBorderPosition( value, which ) { diff --git a/src/view/styles/marginstyles.js b/src/view/styles/marginstyles.js index c00b1276c..77efc56bc 100644 --- a/src/view/styles/marginstyles.js +++ b/src/view/styles/marginstyles.js @@ -10,14 +10,12 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro */ export function addMarginStylesProcessor( stylesProcessor ) { - stylesProcessor.registerListeners( 'margin', stylesProcessor => { - stylesProcessor.on( 'normalize:margin', getPositionShorthandNormalizer( 'margin' ) ); + stylesProcessor.setNormalizer( 'margin', getPositionShorthandNormalizer( 'margin' ) ); - stylesProcessor.on( 'normalize:margin-top', ( evt, data ) => ( data.path = 'margin.top' ) ); - stylesProcessor.on( 'normalize:margin-right', ( evt, data ) => ( data.path = 'margin.right' ) ); - stylesProcessor.on( 'normalize:margin-bottom', ( evt, data ) => ( data.path = 'margin.bottom' ) ); - stylesProcessor.on( 'normalize:margin-left', ( evt, data ) => ( data.path = 'margin.left' ) ); + stylesProcessor.setNormalizer( 'margin-top', data => ( { path: 'margin.top', value: data.value } ) ); + stylesProcessor.setNormalizer( 'margin-right', data => ( { path: 'margin.right', value: data.value } ) ); + stylesProcessor.setNormalizer( 'margin-bottom', data => ( { path: 'margin.bottom', value: data.value } ) ); + stylesProcessor.setNormalizer( 'margin-left', data => ( { path: 'margin.left', value: data.value } ) ); - stylesProcessor.on( 'reduce:margin', getTopRightBottomLeftValueReducer( 'margin' ) ); - } ); + stylesProcessor.setReducer( 'margin', getTopRightBottomLeftValueReducer( 'margin' ) ); } diff --git a/src/view/styles/paddingstyles.js b/src/view/styles/paddingstyles.js index d483d9522..c9e04a138 100644 --- a/src/view/styles/paddingstyles.js +++ b/src/view/styles/paddingstyles.js @@ -10,13 +10,11 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro */ export function addPaddingStylesProcessor( stylesProcessor ) { - stylesProcessor.registerListeners( 'padding', stylesProcessor => { - stylesProcessor.on( 'normalize:padding', getPositionShorthandNormalizer( 'padding' ) ); - stylesProcessor.on( 'normalize:padding-top', ( evt, data ) => ( data.path = 'padding.top' ) ); - stylesProcessor.on( 'normalize:padding-right', ( evt, data ) => ( data.path = 'padding.right' ) ); - stylesProcessor.on( 'normalize:padding-bottom', ( evt, data ) => ( data.path = 'padding.bottom' ) ); - stylesProcessor.on( 'normalize:padding-left', ( evt, data ) => ( data.path = 'padding.left' ) ); + stylesProcessor.setNormalizer( 'padding', getPositionShorthandNormalizer( 'padding' ) ); + stylesProcessor.setNormalizer( 'padding-top', data => ( { path: 'padding.top', value: data.value } ) ); + stylesProcessor.setNormalizer( 'padding-right', data => ( { path: 'padding.right', value: data.value } ) ); + stylesProcessor.setNormalizer( 'padding-bottom', data => ( { path: 'padding.bottom', value: data.value } ) ); + stylesProcessor.setNormalizer( 'padding-left', data => ( { path: 'padding.left', value: data.value } ) ); - stylesProcessor.on( 'reduce:padding', getTopRightBottomLeftValueReducer( 'padding' ) ); - } ); + stylesProcessor.setReducer( 'padding', getTopRightBottomLeftValueReducer( 'padding' ) ); } diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index 8669041c1..7c3fde8c9 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -65,7 +65,7 @@ export function getTopRightBottomLeftValues( value = '' ) { } export function getTopRightBottomLeftValueReducer( styleShorthand ) { - return ( evt, data ) => { + return data => { const { top, right, bottom, left } = ( data.value || {} ); const reduced = []; @@ -90,7 +90,7 @@ export function getTopRightBottomLeftValueReducer( styleShorthand ) { reduced.push( [ styleShorthand, getTopRightBottomLeftShorthandValue( data.value ) ] ); } - data.reduced = reduced; + return reduced; }; } @@ -111,9 +111,11 @@ export function getTopRightBottomLeftShorthandValue( { left, right, top, bottom } export function getPositionShorthandNormalizer( longhand ) { - return ( evt, data ) => { - data.path = longhand; - data.value = getTopRightBottomLeftValues( data.value ); + return data => { + return { + path: longhand, + value: getTopRightBottomLeftValues( data.value ) + }; }; } diff --git a/tests/view/styles.js b/tests/view/styles.js index 649520df5..7b9b94406 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -14,14 +14,14 @@ describe( 'Styles', () => { converter = new StylesProcessor(); // Define simple "foo" shorthand normalizers, similar to the "margin" shorthand normalizers, for testing purposes. - converter.on( 'normalize:foo', ( evt, data ) => { - data.path = 'foo'; - data.value = { top: data.value, right: data.value, bottom: data.value, left: data.value }; - } ); - converter.on( 'normalize:foo-top', ( evt, data ) => { - data.path = 'foo'; - data.value = { top: data.value }; - } ); + converter.setNormalizer( 'foo', data => ( { + path: 'foo', + value: { top: data.value, right: data.value, bottom: data.value, left: data.value } + } ) ); + converter.setNormalizer( 'foo-top', data => ( { + path: 'foo', + value: { top: data.value } + } ) ); addPaddingStylesProcessor( converter ); styles = new Styles( converter ); From ef5083d026a53f3043db55cd148fcb0d0e1c66d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 18:00:10 +0100 Subject: [PATCH 096/142] Simplify extractors. --- src/view/styles.js | 11 +---------- src/view/styles/borderstyles.js | 6 +++--- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index cb38d6616..5af42c1ab 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -288,11 +288,6 @@ export class StylesProcessor { return styles[ name ]; } - const data = { - name, - styles - }; - if ( this._extractors.has( name ) ) { const extractor = this._extractors.get( name ); @@ -300,11 +295,7 @@ export class StylesProcessor { return get( styles, extractor ); } - const { path, value } = extractor( data ); - - if ( path ) { - return get( styles, path ); - } + const value = extractor( name, styles ); if ( value ) { return value; diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index a1cba81c0..ebfc6b6ae 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -137,9 +137,9 @@ function getBorderPropertyPositionNormalizer( property, side ) { } function getBorderPositionExtractor( which ) { - return data => { - if ( data.styles.border ) { - return { value: extractBorderPosition( data.styles.border, which, data ) }; + return ( name, styles ) => { + if ( styles.border ) { + return extractBorderPosition( styles.border, which ); } }; } From b6d03a30a6b5432ccfbb18ca3dea78275fc6c8ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 18:11:04 +0100 Subject: [PATCH 097/142] Normalizer should only work on value. --- src/view/styles.js | 15 +++++---------- src/view/styles/backgroundstyles.js | 6 +++--- src/view/styles/borderstyles.js | 16 ++++++++-------- src/view/styles/marginstyles.js | 8 ++++---- src/view/styles/paddingstyles.js | 8 ++++---- src/view/styles/utils.js | 4 ++-- tests/view/styles.js | 8 ++++---- 7 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 5af42c1ab..9306d4263 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -313,26 +313,21 @@ export class StylesProcessor { * @param {Object} styles * @private */ - toNormalizedForm( propertyName, value, styles ) { - if ( isObject( value ) ) { - appendStyleValue( styles, toPath( propertyName ), value ); + toNormalizedForm( propertyName, propertyValue, styles ) { + if ( isObject( propertyValue ) ) { + appendStyleValue( styles, toPath( propertyName ), propertyValue ); return; } - const data = { - path: propertyName, - value - }; - if ( this._normalizers.has( propertyName ) ) { const normalizer = this._normalizers.get( propertyName ); - const { path, value } = normalizer( data ); + const { path, value } = normalizer( propertyValue ); appendStyleValue( styles, path, value ); } else { - appendStyleValue( styles, propertyName, value ); + appendStyleValue( styles, propertyName, propertyValue ); } } diff --git a/src/view/styles/backgroundstyles.js b/src/view/styles/backgroundstyles.js index 8d3a22479..01ee96440 100644 --- a/src/view/styles/backgroundstyles.js +++ b/src/view/styles/backgroundstyles.js @@ -11,7 +11,7 @@ import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; export function addBackgroundStylesProcessor( stylesProcessor ) { stylesProcessor.setNormalizer( 'background', normalizeBackground ); - stylesProcessor.setNormalizer( 'background-color', data => ( { path: 'background.color', value: data.value } ) ); + stylesProcessor.setNormalizer( 'background-color', value => ( { path: 'background.color', value } ) ); stylesProcessor.setReducer( 'background', data => { const ret = []; @@ -21,10 +21,10 @@ export function addBackgroundStylesProcessor( stylesProcessor ) { } ); } -function normalizeBackground( data ) { +function normalizeBackground( value ) { const background = {}; - const parts = data.value.split( ' ' ); + const parts = value.split( ' ' ); for ( const part of parts ) { if ( isRepeat( part ) ) { diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index ebfc6b6ae..766ed9cf0 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -70,8 +70,8 @@ export function addBorderStylesProcessor( stylesProcessor ) { stylesProcessor.setReducer( 'border', borderReducer ); } -function borderNormalizer( data ) { - const { color, style, width } = normalizeBorderShorthand( data.value ); +function borderNormalizer( value ) { + const { color, style, width } = normalizeBorderShorthand( value ); return { path: 'border', @@ -84,8 +84,8 @@ function borderNormalizer( data ) { } function getBorderPositionNormalizer( side ) { - return data => { - const { color, style, width } = normalizeBorderShorthand( data.value ); + return value => { + const { color, style, width } = normalizeBorderShorthand( value ); const border = {}; @@ -109,10 +109,10 @@ function getBorderPositionNormalizer( side ) { } function getBorderPropertyNormalizer( propertyName ) { - return data => { + return value => { return { path: 'border', - value: toBorderPropertyShorthand( data.value, propertyName ) + value: toBorderPropertyShorthand( value, propertyName ) }; }; } @@ -124,12 +124,12 @@ function toBorderPropertyShorthand( value, property ) { } function getBorderPropertyPositionNormalizer( property, side ) { - return data => { + return value => { return { path: 'border', value: { [ property ]: { - [ side ]: data.value + [ side ]: value } } }; diff --git a/src/view/styles/marginstyles.js b/src/view/styles/marginstyles.js index 77efc56bc..0c501a804 100644 --- a/src/view/styles/marginstyles.js +++ b/src/view/styles/marginstyles.js @@ -12,10 +12,10 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro export function addMarginStylesProcessor( stylesProcessor ) { stylesProcessor.setNormalizer( 'margin', getPositionShorthandNormalizer( 'margin' ) ); - stylesProcessor.setNormalizer( 'margin-top', data => ( { path: 'margin.top', value: data.value } ) ); - stylesProcessor.setNormalizer( 'margin-right', data => ( { path: 'margin.right', value: data.value } ) ); - stylesProcessor.setNormalizer( 'margin-bottom', data => ( { path: 'margin.bottom', value: data.value } ) ); - stylesProcessor.setNormalizer( 'margin-left', data => ( { path: 'margin.left', value: data.value } ) ); + stylesProcessor.setNormalizer( 'margin-top', value => ( { path: 'margin.top', value } ) ); + stylesProcessor.setNormalizer( 'margin-right', value => ( { path: 'margin.right', value } ) ); + stylesProcessor.setNormalizer( 'margin-bottom', value => ( { path: 'margin.bottom', value } ) ); + stylesProcessor.setNormalizer( 'margin-left', value => ( { path: 'margin.left', value } ) ); stylesProcessor.setReducer( 'margin', getTopRightBottomLeftValueReducer( 'margin' ) ); } diff --git a/src/view/styles/paddingstyles.js b/src/view/styles/paddingstyles.js index c9e04a138..8f6ccb3df 100644 --- a/src/view/styles/paddingstyles.js +++ b/src/view/styles/paddingstyles.js @@ -11,10 +11,10 @@ import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } fro export function addPaddingStylesProcessor( stylesProcessor ) { stylesProcessor.setNormalizer( 'padding', getPositionShorthandNormalizer( 'padding' ) ); - stylesProcessor.setNormalizer( 'padding-top', data => ( { path: 'padding.top', value: data.value } ) ); - stylesProcessor.setNormalizer( 'padding-right', data => ( { path: 'padding.right', value: data.value } ) ); - stylesProcessor.setNormalizer( 'padding-bottom', data => ( { path: 'padding.bottom', value: data.value } ) ); - stylesProcessor.setNormalizer( 'padding-left', data => ( { path: 'padding.left', value: data.value } ) ); + stylesProcessor.setNormalizer( 'padding-top', value => ( { path: 'padding.top', value } ) ); + stylesProcessor.setNormalizer( 'padding-right', value => ( { path: 'padding.right', value } ) ); + stylesProcessor.setNormalizer( 'padding-bottom', value => ( { path: 'padding.bottom', value } ) ); + stylesProcessor.setNormalizer( 'padding-left', value => ( { path: 'padding.left', value } ) ); stylesProcessor.setReducer( 'padding', getTopRightBottomLeftValueReducer( 'padding' ) ); } diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index 7c3fde8c9..1a997a14a 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -111,10 +111,10 @@ export function getTopRightBottomLeftShorthandValue( { left, right, top, bottom } export function getPositionShorthandNormalizer( longhand ) { - return data => { + return value => { return { path: longhand, - value: getTopRightBottomLeftValues( data.value ) + value: getTopRightBottomLeftValues( value ) }; }; } diff --git a/tests/view/styles.js b/tests/view/styles.js index 7b9b94406..33f6f45ba 100644 --- a/tests/view/styles.js +++ b/tests/view/styles.js @@ -14,13 +14,13 @@ describe( 'Styles', () => { converter = new StylesProcessor(); // Define simple "foo" shorthand normalizers, similar to the "margin" shorthand normalizers, for testing purposes. - converter.setNormalizer( 'foo', data => ( { + converter.setNormalizer( 'foo', value => ( { path: 'foo', - value: { top: data.value, right: data.value, bottom: data.value, left: data.value } + value: { top: value, right: value, bottom: value, left: value } } ) ); - converter.setNormalizer( 'foo-top', data => ( { + converter.setNormalizer( 'foo-top', value => ( { path: 'foo', - value: { top: data.value } + value: { top: value } } ) ); addPaddingStylesProcessor( converter ); From 1a1b8a303ee7ec42a2dc97e3cf7cc1dcdd33b754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 13 Jan 2020 18:19:08 +0100 Subject: [PATCH 098/142] Reducer should only work on value. --- src/view/styles.js | 6 +----- src/view/styles/backgroundstyles.js | 4 ++-- src/view/styles/borderstyles.js | 12 ++++++------ src/view/styles/utils.js | 6 +++--- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/view/styles.js b/src/view/styles.js index 9306d4263..141dd6804 100644 --- a/src/view/styles.js +++ b/src/view/styles.js @@ -266,14 +266,10 @@ export class StylesProcessor { * @returns {module:engine/view/styles~PropertyEntry} */ getReducedForm( styleName, normalizedValue ) { - const data = { - value: normalizedValue - }; - if ( this._reducers.has( styleName ) ) { const reducer = this._reducers.get( styleName ); - return reducer( data ); + return reducer( normalizedValue ); } return [ [ styleName, normalizedValue ] ]; diff --git a/src/view/styles/backgroundstyles.js b/src/view/styles/backgroundstyles.js index 01ee96440..bd177408f 100644 --- a/src/view/styles/backgroundstyles.js +++ b/src/view/styles/backgroundstyles.js @@ -12,10 +12,10 @@ import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; export function addBackgroundStylesProcessor( stylesProcessor ) { stylesProcessor.setNormalizer( 'background', normalizeBackground ); stylesProcessor.setNormalizer( 'background-color', value => ( { path: 'background.color', value } ) ); - stylesProcessor.setReducer( 'background', data => { + stylesProcessor.setReducer( 'background', value => { const ret = []; - ret.push( [ 'background-color', data.value.color ] ); + ret.push( [ 'background-color', value.color ] ); return ret; } ); diff --git a/src/view/styles/borderstyles.js b/src/view/styles/borderstyles.js index 766ed9cf0..916e808d2 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/borderstyles.js @@ -180,19 +180,19 @@ function normalizeBorderShorthand( string ) { return result; } -function borderReducer( data ) { +function borderReducer( value ) { const reduced = []; - reduced.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'top' ), 'top' ) ); - reduced.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'right' ), 'right' ) ); - reduced.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'bottom' ), 'bottom' ) ); - reduced.push( ...reduceBorderPosition( extractBorderPosition( data.value, 'left' ), 'left' ) ); + reduced.push( ...reduceBorderPosition( extractBorderPosition( value, 'top' ), 'top' ) ); + reduced.push( ...reduceBorderPosition( extractBorderPosition( value, 'right' ), 'right' ) ); + reduced.push( ...reduceBorderPosition( extractBorderPosition( value, 'bottom' ), 'bottom' ) ); + reduced.push( ...reduceBorderPosition( extractBorderPosition( value, 'left' ), 'left' ) ); return reduced; } function getBorderPositionReducer( which ) { - return data => ( data.reduced = reduceBorderPosition( data.value, which ) ); + return value => reduceBorderPosition( value, which ); } function reduceBorderPosition( value, which ) { diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index 1a997a14a..f218083ad 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -65,8 +65,8 @@ export function getTopRightBottomLeftValues( value = '' ) { } export function getTopRightBottomLeftValueReducer( styleShorthand ) { - return data => { - const { top, right, bottom, left } = ( data.value || {} ); + return value => { + const { top, right, bottom, left } = ( value || {} ); const reduced = []; @@ -87,7 +87,7 @@ export function getTopRightBottomLeftValueReducer( styleShorthand ) { reduced.push( [ styleShorthand + '-left', left ] ); } } else { - reduced.push( [ styleShorthand, getTopRightBottomLeftShorthandValue( data.value ) ] ); + reduced.push( [ styleShorthand, getTopRightBottomLeftShorthandValue( value ) ] ); } return reduced; From 4c94d16bb6ab5fb24e88eaa0164b55a409d84a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 10:24:20 +0100 Subject: [PATCH 099/142] Rename Styles to StylesMap. --- src/view/document.js | 4 ++-- src/view/element.js | 6 +++--- src/view/{styles.js => stylesmap.js} | 14 +++++++------- tests/view/styles/backgroundstyles.js | 4 ++-- tests/view/styles/borderstyles.js | 4 ++-- tests/view/styles/marginstyles.js | 4 ++-- tests/view/styles/paddingstyles.js | 4 ++-- tests/view/{styles.js => stylesmap.js} | 6 +++--- 8 files changed, 23 insertions(+), 23 deletions(-) rename src/view/{styles.js => stylesmap.js} (97%) rename tests/view/{styles.js => stylesmap.js} (98%) diff --git a/src/view/document.js b/src/view/document.js index 7645fa052..b618f9d36 100644 --- a/src/view/document.js +++ b/src/view/document.js @@ -11,7 +11,7 @@ import DocumentSelection from './documentselection'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin'; -import Styles from './styles'; +import StylesMap from './stylesmap'; // @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' ); @@ -162,7 +162,7 @@ export default class Document { } addStyleProcessorRules( callback ) { - callback( Styles.processor ); + callback( StylesMap.processor ); } /** diff --git a/src/view/element.js b/src/view/element.js index 483f81373..af32c13ce 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -14,7 +14,7 @@ import objectToMap from '@ckeditor/ckeditor5-utils/src/objecttomap'; import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable'; import Matcher from './matcher'; import { isPlainObject } from 'lodash-es'; -import Styles from './styles'; +import StylesMap from './stylesmap'; // @if CK_DEBUG_ENGINE // const { convertMapToTags } = require( '../dev-utils/utils' ); @@ -109,9 +109,9 @@ export default class Element extends Node { * Normalized styles. * * @protected - * @member {module:engine/view/styles~Styles} module:engine/view/element~Element#_styles + * @member {module:engine/view/stylesmap~StylesMap} module:engine/view/element~Element#_styles */ - this._styles = new Styles(); + this._styles = new StylesMap(); if ( this._attrs.has( 'style' ) ) { // Remove style attribute and handle it by styles map. diff --git a/src/view/styles.js b/src/view/stylesmap.js similarity index 97% rename from src/view/styles.js rename to src/view/stylesmap.js index 141dd6804..e900eb976 100644 --- a/src/view/styles.js +++ b/src/view/stylesmap.js @@ -4,7 +4,7 @@ */ /** - * @module engine/view/styles + * @module engine/view/stylesmap */ import { get, isObject, merge, set, unset } from 'lodash-es'; @@ -14,7 +14,7 @@ import { get, isObject, merge, set, unset } from 'lodash-es'; * * Handles styles normalization. */ -export default class Styles { +export default class StylesMap { /** * Creates Styles instance. */ @@ -29,7 +29,7 @@ export default class Styles { // that two editors are connected through single style processor instance. Object.defineProperty( this, '_styleProcessor', { get() { - return styleProcessor || Styles.processor; + return styleProcessor || StylesMap.processor; }, enumerable: false } ); @@ -214,7 +214,7 @@ export default class Styles { /** * Returns style properties names as the would appear when using {@link #getInlineStyle} * - * @returns {module:engine/view/styles~PropertyEntry} + * @returns {module:engine/view/stylesmap~PropertyEntry} */ getStyleNames() { const entries = this._getStylesEntries(); @@ -233,7 +233,7 @@ export default class Styles { * Returns normalized styles entries for further processing. * * @private - * @returns {module:engine/view/styles~PropertyEntry} + * @returns {module:engine/view/stylesmap~PropertyEntry} */ _getStylesEntries() { const parsed = []; @@ -263,7 +263,7 @@ export class StylesProcessor { * @private * @param {String} styleName * @param {Object|String} normalizedValue - * @returns {module:engine/view/styles~PropertyEntry} + * @returns {module:engine/view/stylesmap~PropertyEntry} */ getReducedForm( styleName, normalizedValue ) { if ( this._reducers.has( styleName ) ) { @@ -505,5 +505,5 @@ function appendStyleValue( stylesObject, nameOrPath, valueOrObject ) { */ /** - * @typedef {Array.>} module:engine/view/styles~PropertyEntry + * @typedef {Array.>} module:engine/view/stylesmap~PropertyEntry */ diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index a3918d477..f8d4a1de6 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesProcessor } from '../../../src/view/styles'; +import StylesMap, { StylesProcessor } from '../../../src/view/stylesmap'; import { addBackgroundStylesProcessor } from '../../../src/view/styles/backgroundstyles'; describe( 'Background styles normalization', () => { @@ -12,7 +12,7 @@ describe( 'Background styles normalization', () => { beforeEach( () => { const stylesProcessor = new StylesProcessor(); addBackgroundStylesProcessor( stylesProcessor ); - styles = new Styles( stylesProcessor ); + styles = new StylesMap( stylesProcessor ); } ); it( 'should normalize background', () => { diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index 966769297..bba4cad20 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesProcessor } from '../../../src/view/styles'; +import StylesMap, { StylesProcessor } from '../../../src/view/stylesmap'; import { addBorderStylesProcessor } from '../../../src/view/styles/borderstyles'; describe( 'Border styles normalization', () => { @@ -12,7 +12,7 @@ describe( 'Border styles normalization', () => { beforeEach( () => { const converter = new StylesProcessor(); addBorderStylesProcessor( converter ); - styles = new Styles( converter ); + styles = new StylesMap( converter ); } ); it( 'should parse border shorthand', () => { diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index 6aeec9f27..3eccb7bbd 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesProcessor } from '../../../src/view/styles'; +import StylesMap, { StylesProcessor } from '../../../src/view/stylesmap'; import { addMarginStylesProcessor } from '../../../src/view/styles/marginstyles'; describe( 'Margin styles normalizer', () => { @@ -12,7 +12,7 @@ describe( 'Margin styles normalizer', () => { beforeEach( () => { const converter = new StylesProcessor(); addMarginStylesProcessor( converter ); - styles = new Styles( converter ); + styles = new StylesMap( converter ); } ); it( 'should set all margins (1 value defined)', () => { diff --git a/tests/view/styles/paddingstyles.js b/tests/view/styles/paddingstyles.js index 0e359577f..61fbcf31e 100644 --- a/tests/view/styles/paddingstyles.js +++ b/tests/view/styles/paddingstyles.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesProcessor } from '../../../src/view/styles'; +import StylesMap, { StylesProcessor } from '../../../src/view/stylesmap'; import { addPaddingStylesProcessor } from '../../../src/view/styles/paddingstyles'; describe( 'Padding styles normalization', () => { @@ -12,7 +12,7 @@ describe( 'Padding styles normalization', () => { beforeEach( () => { const converter = new StylesProcessor(); addPaddingStylesProcessor( converter ); - styles = new Styles( converter ); + styles = new StylesMap( converter ); } ); it( 'should set all paddings (1 value defined)', () => { diff --git a/tests/view/styles.js b/tests/view/stylesmap.js similarity index 98% rename from tests/view/styles.js rename to tests/view/stylesmap.js index 33f6f45ba..c19fdd28f 100644 --- a/tests/view/styles.js +++ b/tests/view/stylesmap.js @@ -3,11 +3,11 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import Styles, { StylesProcessor } from '../../src/view/styles'; +import StylesMap, { StylesProcessor } from '../../src/view/stylesmap'; import encodedImage from './_utils/encodedimage.txt'; import { addPaddingStylesProcessor } from '../../src/view/styles/paddingstyles'; -describe( 'Styles', () => { +describe( 'StylesMap', () => { let styles, converter; beforeEach( () => { @@ -24,7 +24,7 @@ describe( 'Styles', () => { } ) ); addPaddingStylesProcessor( converter ); - styles = new Styles( converter ); + styles = new StylesMap( converter ); } ); describe( 'size getter', () => { From 3088102fa754c805789863509f138598541ce5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 10:53:52 +0100 Subject: [PATCH 100/142] Update docs of the Styles.getNormalized() method. --- src/view/stylesmap.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index e900eb976..fdc8a1ba5 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -143,7 +143,7 @@ export default class StylesMap { } /** - * Return a normalized style object. + * Returns a normalized style object or value. * * const styles = new Styles(); * styles.setStyle( 'margin:1px 2px 3em;' ); @@ -154,11 +154,15 @@ export default class StylesMap { * // top: '1px', * // right: '2px', * // bottom: '3em', - * // left: '2px' + * // left: '2px' // normalized value from margin shorthand * // } * + * console.log( styles.getNormalized( 'margin-left' ) ); // will log '2px' + * + * *Note*: This method will only return normalized styles if a style processor was defined. + * * @param {String} name - * @returns {Object|undefined} + * @returns {Object|String|undefined} */ getNormalized( name ) { return this._styleProcessor.getNormalized( name, this._styles ); From 45ad6173001825ea28fe5fe0c9f7ac5a10d8dcd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 10:54:17 +0100 Subject: [PATCH 101/142] Add docs for the Element.getNormalizedStyle() method. --- src/view/element.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/view/element.js b/src/view/element.js index af32c13ce..d8e6754d0 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -385,6 +385,34 @@ export default class Element extends Node { return this._styles.getInlineProperty( property ); } + /** + * Returns a normalized style object or single style value. + * + * For an element with style set to: margin:1px 2px 3em; + * + * element.getNormalizedStyle( 'margin' ) ); + * + * will return: + * + * { + * top: '1px', + * right: '2px', + * bottom: '3em', + * left: '2px' // a normalized value from margin shorthand + * } + * + * and reading for single style value: + * + * styles.getNormalizedStyle( 'margin-left' ); + * + * Will return a `2px` string. + * + * *Note*: This method will only return normalized styles if a style processor was defined. Otherwise the style names are not + * normalized. + * + * @param {String} property Name of CSS property + * @returns {Object|String|undefined} + */ getNormalizedStyle( property ) { return this._styles.getNormalized( property ); } From 56df0b453105bcfe21323e9c43fa5b793506f05c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 12:53:01 +0100 Subject: [PATCH 102/142] Rename StylesMap.setStyle() to setTo(). --- src/view/element.js | 4 +- src/view/stylesmap.js | 16 ++--- tests/view/styles/backgroundstyles.js | 8 +-- tests/view/styles/borderstyles.js | 96 +++++++++++++-------------- tests/view/styles/marginstyles.js | 32 ++++----- tests/view/styles/paddingstyles.js | 12 ++-- tests/view/stylesmap.js | 56 ++++++++-------- 7 files changed, 111 insertions(+), 113 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index d8e6754d0..7e54f6b2b 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -115,7 +115,7 @@ export default class Element extends Node { if ( this._attrs.has( 'style' ) ) { // Remove style attribute and handle it by styles map. - this._styles.setStyle( this._attrs.get( 'style' ) ); + this._styles.setTo( this._attrs.get( 'style' ) ); this._attrs.delete( 'style' ); } @@ -643,7 +643,7 @@ export default class Element extends Node { if ( key == 'class' ) { parseClasses( this._classes, value ); } else if ( key == 'style' ) { - this._styles.setStyle( value ); + this._styles.setTo( value ); } else { this._attrs.set( key, value ); } diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index fdc8a1ba5..ca0f31e65 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -57,18 +57,16 @@ export default class StylesMap { } /** - * Re-sets internal styles definition. + * Set styles map to a new value. * - * @param {String} styleString + * styles.setTo( 'border:1px solid blue;margin-top:1px;' ); + * + * @param {String} inlineStyle */ - setStyle( styleString ) { + setTo( inlineStyle ) { this.clear(); - const map = parseInlineStyles( styleString ); - - for ( const key of map.keys() ) { - const value = map.get( key ); - + for ( const [ key, value ] of Array.from( parseInlineStyles( inlineStyle ).entries() ) ) { this._styleProcessor.toNormalizedForm( key, value, this._styles ); } } @@ -146,7 +144,7 @@ export default class StylesMap { * Returns a normalized style object or value. * * const styles = new Styles(); - * styles.setStyle( 'margin:1px 2px 3em;' ); + * styles.setTo( 'margin:1px 2px 3em;' ); * * console.log( styles.getNormalized( 'margin' ) ); * // will log: diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index f8d4a1de6..a05e6949b 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -17,7 +17,7 @@ describe( 'Background styles normalization', () => { it( 'should normalize background', () => { // TODO: border-box given only for coverage test. - styles.setStyle( 'background:url("example.jpg") center #f00 repeat-y fixed border-box;' ); + styles.setTo( 'background:url("example.jpg") center #f00 repeat-y fixed border-box;' ); expect( styles.getNormalized( 'background' ) ).to.deep.equal( { attachment: 'fixed', @@ -30,19 +30,19 @@ describe( 'Background styles normalization', () => { // TODO: define what should happen with layers it.skip( 'should normalize background with layers', () => { - styles.setStyle( 'background:url("test.jpg") repeat-y,#f00;' ); + styles.setTo( 'background:url("test.jpg") repeat-y,#f00;' ); expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); } ); it( 'should normalize background-color', () => { - styles.setStyle( 'background-color:#f00;' ); + styles.setTo( 'background-color:#f00;' ); expect( styles.getNormalized( 'background' ) ).to.deep.equal( { color: '#f00' } ); } ); it( 'should output inline background-color style', () => { - styles.setStyle( 'background:#f00;' ); + styles.setTo( 'background:#f00;' ); expect( styles.getInlineStyle() ).to.equal( 'background-color:#f00;' ); expect( styles.getInlineProperty( 'background-color' ) ).to.equal( '#f00' ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index bba4cad20..fe91a52e3 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -16,7 +16,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse border shorthand', () => { - styles.setStyle( 'border:1px solid blue;' ); + styles.setTo( 'border:1px solid blue;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, @@ -26,7 +26,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse border shorthand with only style', () => { - styles.setStyle( 'border:solid;' ); + styles.setTo( 'border:solid;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: undefined, right: undefined, bottom: undefined, left: undefined }, @@ -36,7 +36,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse border shorthand with other shorthands', () => { - styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + styles.setTo( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: '#ccc', right: 'blue', bottom: 'blue', left: '#665511' }, @@ -46,7 +46,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse border longhand', () => { - styles.setStyle( 'border-color: #f00 #ba2;' + + styles.setTo( 'border-color: #f00 #ba2;' + 'border-style: solid;' + 'border-width: 1px;' + 'border-bottom-width: 2px;' + @@ -60,7 +60,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output inline shorthand rules #1', () => { - styles.setStyle( 'border:1px solid blue;' ); + styles.setTo( 'border:1px solid blue;' ); expect( styles.getInlineStyle() ).to.equal( 'border-bottom:1px solid blue;' + @@ -86,7 +86,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output inline shorthand rules #2', () => { - styles.setStyle( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); + styles.setTo( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); expect( styles.getInlineStyle() ).to.equal( 'border-bottom:1px solid blue;' + @@ -102,7 +102,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse border + border-position(only color defined)', () => { - styles.setStyle( 'border:1px solid blue;border-left:#665511;' ); + styles.setTo( 'border:1px solid blue;border-left:#665511;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: 'blue', right: 'blue', bottom: 'blue', left: '#665511' }, @@ -112,7 +112,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse border + border-position(only style defined)', () => { - styles.setStyle( 'border:1px solid blue;border-left:ridge;' ); + styles.setTo( 'border:1px solid blue;border-left:ridge;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, @@ -122,7 +122,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse border + border-position(only width defined)', () => { - styles.setStyle( 'border:1px solid blue;border-left:1337px' ); + styles.setTo( 'border:1px solid blue;border-left:1337px' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: 'blue', right: 'blue', bottom: 'blue', left: 'blue' }, @@ -132,7 +132,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should merge rules on insert other shorthand', () => { - styles.setStyle( 'border:1px solid blue;' ); + styles.setTo( 'border:1px solid blue;' ); styles.insertProperty( 'border-left', '#665511 dashed 2.7em' ); styles.insertProperty( 'border-top', '7px dotted #ccc' ); @@ -149,7 +149,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output', () => { - styles.setStyle( 'border:1px solid blue;' ); + styles.setTo( 'border:1px solid blue;' ); styles.removeProperty( 'border-color' ); expect( styles.getInlineStyle() ).to.equal( @@ -170,7 +170,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output border with only style shorthand (style)', () => { - styles.setStyle( 'border:solid;' ); + styles.setTo( 'border:solid;' ); expect( styles.getInlineStyle() ).to.equal( 'border-bottom:solid;' + @@ -189,7 +189,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output border with only style shorthand (color)', () => { - styles.setStyle( 'border:#f00;' ); + styles.setTo( 'border:#f00;' ); expect( styles.getInlineStyle() ).to.equal( 'border-bottom:#f00;' + @@ -208,7 +208,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output border with only style shorthand (width)', () => { - styles.setStyle( 'border:1px;' ); + styles.setTo( 'border:1px;' ); expect( styles.getInlineStyle() ).to.equal( 'border-bottom:1px;' + @@ -228,7 +228,7 @@ describe( 'Border styles normalization', () => { describe( 'normalized values getters', () => { it( 'should output border-*-color', () => { - styles.setStyle( 'border:1px solid #f00;' ); + styles.setTo( 'border:1px solid #f00;' ); [ 'top', 'right', 'bottom', 'left' ].forEach( position => { expect( styles.getNormalized( `border-${ position }-color` ) ).to.equal( '#f00' ); @@ -236,7 +236,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output border-*-width', () => { - styles.setStyle( 'border:1px solid #f00;' ); + styles.setTo( 'border:1px solid #f00;' ); [ 'top', 'right', 'bottom', 'left' ].forEach( position => { expect( styles.getNormalized( `border-${ position }-width` ) ).to.equal( '1px' ); @@ -244,7 +244,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output border-*-style', () => { - styles.setStyle( 'border:1px solid #f00;' ); + styles.setTo( 'border:1px solid #f00;' ); [ 'top', 'right', 'bottom', 'left' ].forEach( position => { expect( styles.getNormalized( `border-${ position }-style` ) ).to.equal( 'solid' ); @@ -254,25 +254,25 @@ describe( 'Border styles normalization', () => { describe( 'border reducers', () => { it( 'should output border-top', () => { - styles.setStyle( 'border:1px solid #f00' ); + styles.setTo( 'border:1px solid #f00' ); expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid #f00' ); } ); it( 'should output border-right', () => { - styles.setStyle( 'border:1px solid #f00' ); + styles.setTo( 'border:1px solid #f00' ); expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid #f00' ); } ); it( 'should output border-bottom', () => { - styles.setStyle( 'border:1px solid #f00' ); + styles.setTo( 'border:1px solid #f00' ); expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid #f00' ); } ); it( 'should output border-left', () => { - styles.setStyle( 'border:1px solid #f00' ); + styles.setTo( 'border:1px solid #f00' ); expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid #f00' ); } ); @@ -280,7 +280,7 @@ describe( 'Border styles normalization', () => { describe( 'border-color', () => { it( 'should set all border colors (1 value defined)', () => { - styles.setStyle( 'border-color:cyan;' ); + styles.setTo( 'border-color:cyan;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { @@ -293,7 +293,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border colors (2 values defined)', () => { - styles.setStyle( 'border-color:cyan magenta;' ); + styles.setTo( 'border-color:cyan magenta;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { @@ -306,7 +306,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border colors (3 values defined)', () => { - styles.setStyle( 'border-color:cyan magenta pink;' ); + styles.setTo( 'border-color:cyan magenta pink;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { @@ -319,7 +319,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border colors (4 values defined)', () => { - styles.setStyle( 'border-color:cyan magenta pink beige;' ); + styles.setTo( 'border-color:cyan magenta pink beige;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { @@ -332,7 +332,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should merge with border shorthand', () => { - styles.setStyle( 'border:1px solid blue;border-color:cyan black;' ); + styles.setTo( 'border:1px solid blue;border-color:cyan black;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: 'cyan', right: 'black', bottom: 'cyan', left: 'black' }, @@ -342,7 +342,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse #RGB color value', () => { - styles.setStyle( 'border:#f00;' ); + styles.setTo( 'border:#f00;' ); expect( styles.getNormalized( 'border-color' ) ).to.deep.equal( { top: '#f00', @@ -353,7 +353,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse #RGBA color value', () => { - styles.setStyle( 'border:#f00A;' ); + styles.setTo( 'border:#f00A;' ); expect( styles.getNormalized( 'border-color' ) ).to.deep.equal( { top: '#f00A', @@ -364,7 +364,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse rgb() color value', () => { - styles.setStyle( 'border:rgb(0, 30%,35);' ); + styles.setTo( 'border:rgb(0, 30%,35);' ); expect( styles.getNormalized( 'border-color' ) ).to.deep.equal( { top: 'rgb(0, 30%, 35)', @@ -375,7 +375,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse hsl() color value', () => { - styles.setStyle( 'border:hsl(0, 100%, 50%);' ); + styles.setTo( 'border:hsl(0, 100%, 50%);' ); expect( styles.getNormalized( 'border-color' ) ).to.deep.equal( { top: 'hsl(0, 100%, 50%)', @@ -388,7 +388,7 @@ describe( 'Border styles normalization', () => { describe( 'border-style', () => { it( 'should set all border styles (1 value defined)', () => { - styles.setStyle( 'border-style:solid;' ); + styles.setTo( 'border-style:solid;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { style: { @@ -401,7 +401,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border styles (2 values defined)', () => { - styles.setStyle( 'border-style:solid dotted;' ); + styles.setTo( 'border-style:solid dotted;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { style: { @@ -414,7 +414,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border styles (3 values defined)', () => { - styles.setStyle( 'border-style:solid dotted dashed;' ); + styles.setTo( 'border-style:solid dotted dashed;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { style: { @@ -427,7 +427,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border styles (4 values defined)', () => { - styles.setStyle( 'border-style:solid dotted dashed ridge;' ); + styles.setTo( 'border-style:solid dotted dashed ridge;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { style: { @@ -440,7 +440,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse none value', () => { - styles.setStyle( 'border:none;' ); + styles.setTo( 'border:none;' ); expect( styles.getNormalized( 'border-style' ) ).to.deep.equal( { top: 'none', @@ -451,7 +451,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse line-style value', () => { - styles.setStyle( 'border:solid;' ); + styles.setTo( 'border:solid;' ); expect( styles.getNormalized( 'border-style' ) ).to.deep.equal( { top: 'solid', @@ -462,7 +462,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should not parse non line-style value', () => { - styles.setStyle( 'border:blue' ); + styles.setTo( 'border:blue' ); expect( styles.getNormalized( 'border-style' ) ).to.deep.equal( { top: undefined, @@ -475,7 +475,7 @@ describe( 'Border styles normalization', () => { describe( 'border-width', () => { it( 'should set all border widths (1 value defined)', () => { - styles.setStyle( 'border-width:1px;' ); + styles.setTo( 'border-width:1px;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { width: { @@ -488,7 +488,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border widths (2 values defined)', () => { - styles.setStyle( 'border-width:1px .34cm;' ); + styles.setTo( 'border-width:1px .34cm;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { width: { @@ -501,7 +501,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border widths (3 values defined)', () => { - styles.setStyle( 'border-width:1px .34cm 90.1rem;' ); + styles.setTo( 'border-width:1px .34cm 90.1rem;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { width: { @@ -514,7 +514,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should set all border widths (4 values defined)', () => { - styles.setStyle( 'border-width:1px .34cm 90.1rem thick;' ); + styles.setTo( 'border-width:1px .34cm 90.1rem thick;' ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { width: { @@ -527,7 +527,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse px value', () => { - styles.setStyle( 'border:1px;' ); + styles.setTo( 'border:1px;' ); expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { top: '1px', @@ -538,7 +538,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse em value', () => { - styles.setStyle( 'border:1em;' ); + styles.setTo( 'border:1em;' ); expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { top: '1em', @@ -549,7 +549,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse thin value', () => { - styles.setStyle( 'border:thin' ); + styles.setTo( 'border:thin' ); expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { top: 'thin', @@ -560,7 +560,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse medium value', () => { - styles.setStyle( 'border:medium' ); + styles.setTo( 'border:medium' ); expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { top: 'medium', @@ -571,7 +571,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should parse thick value', () => { - styles.setStyle( 'border:thick' ); + styles.setTo( 'border:thick' ); expect( styles.getNormalized( 'border-width' ) ).to.deep.equal( { top: 'thick', @@ -584,7 +584,7 @@ describe( 'Border styles normalization', () => { describe( 'border-* position', () => { it( 'should output all positions', () => { - styles.setStyle( + styles.setTo( 'border-top:none;' + 'border-left:none;' + 'border-bottom:dotted #FFC000 3.0pt;' + @@ -606,7 +606,7 @@ describe( 'Border styles normalization', () => { describe( 'getStyleNames() - border', () => { it( 'should set all border colors (1 value defined)', () => { - styles.setStyle( 'border-color: deeppink deepskyblue;' + + styles.setTo( 'border-color: deeppink deepskyblue;' + 'border-style: solid;' + 'border-width: 1px;' + 'border-bottom-width: 2px;' + diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index 3eccb7bbd..ae64ba46b 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -16,7 +16,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should set all margins (1 value defined)', () => { - styles.setStyle( 'margin:1px;' ); + styles.setTo( 'margin:1px;' ); expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', @@ -27,7 +27,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should set all margins (2 values defined)', () => { - styles.setStyle( 'margin:1px .34cm;' ); + styles.setTo( 'margin:1px .34cm;' ); expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', @@ -38,7 +38,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should set all margins (3 values defined)', () => { - styles.setStyle( 'margin:1px .34cm 90.1rem;' ); + styles.setTo( 'margin:1px .34cm 90.1rem;' ); expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', @@ -49,7 +49,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should set all margins (4 values defined)', () => { - styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); + styles.setTo( 'margin:1px .34cm 90.1rem thick;' ); expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', @@ -60,7 +60,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should output inline style (1 value defined)', () => { - styles.setStyle( 'margin:1px;' ); + styles.setTo( 'margin:1px;' ); expect( styles.getInlineStyle() ).to.equal( 'margin:1px;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); @@ -71,7 +71,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should output inline style (2 values defined)', () => { - styles.setStyle( 'margin:1px .34cm;' ); + styles.setTo( 'margin:1px .34cm;' ); expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); @@ -82,7 +82,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should output inline style (3 values defined)', () => { - styles.setStyle( 'margin:1px .34cm 90.1rem;' ); + styles.setTo( 'margin:1px .34cm 90.1rem;' ); expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); @@ -93,7 +93,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should output inline style (3 values defined, only last different)', () => { - styles.setStyle( 'margin:1px 1px 90.1rem;' ); + styles.setTo( 'margin:1px 1px 90.1rem;' ); expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); @@ -104,7 +104,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should output inline style (4 values defined)', () => { - styles.setStyle( 'margin:1px .34cm 90.1rem thick;' ); + styles.setTo( 'margin:1px .34cm 90.1rem thick;' ); expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); @@ -115,7 +115,7 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should output inline style (4 values defined, only last different)', () => { - styles.setStyle( 'margin:1px 1px 1px thick;' ); + styles.setTo( 'margin:1px 1px 1px thick;' ); expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); @@ -127,14 +127,14 @@ describe( 'Margin styles normalizer', () => { describe( 'margin-*', () => { it( 'should set proper margin', () => { - styles.setStyle( 'margin-top:1px;' ); + styles.setTo( 'margin-top:1px;' ); expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px' } ); expect( styles.getNormalized( 'margin-top' ) ).to.equal( '1px' ); } ); it( 'should merge margin with margin shorthand', () => { - styles.setStyle( 'margin: 2em;margin-top:1px;' ); + styles.setTo( 'margin: 2em;margin-top:1px;' ); expect( styles.getNormalized( 'margin' ) ).to.deep.equal( { top: '1px', @@ -149,28 +149,28 @@ describe( 'Margin styles normalizer', () => { } ); it( 'should output margin-top', () => { - styles.setStyle( 'margin-top:1px;' ); + styles.setTo( 'margin-top:1px;' ); expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); } ); it( 'should output margin-right', () => { - styles.setStyle( 'margin-right:1px;' ); + styles.setTo( 'margin-right:1px;' ); expect( styles.getInlineStyle() ).to.equal( 'margin-right:1px;' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); } ); it( 'should output margin-bottom', () => { - styles.setStyle( 'margin-bottom:1px;' ); + styles.setTo( 'margin-bottom:1px;' ); expect( styles.getInlineStyle() ).to.equal( 'margin-bottom:1px;' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); } ); it( 'should output margin-left', () => { - styles.setStyle( 'margin-left:1px;' ); + styles.setTo( 'margin-left:1px;' ); expect( styles.getInlineStyle() ).to.equal( 'margin-left:1px;' ); expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); diff --git a/tests/view/styles/paddingstyles.js b/tests/view/styles/paddingstyles.js index 61fbcf31e..521e1e50c 100644 --- a/tests/view/styles/paddingstyles.js +++ b/tests/view/styles/paddingstyles.js @@ -16,7 +16,7 @@ describe( 'Padding styles normalization', () => { } ); it( 'should set all paddings (1 value defined)', () => { - styles.setStyle( 'padding:1px;' ); + styles.setTo( 'padding:1px;' ); expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', @@ -27,7 +27,7 @@ describe( 'Padding styles normalization', () => { } ); it( 'should set all paddings (2 values defined)', () => { - styles.setStyle( 'padding:1px .34cm;' ); + styles.setTo( 'padding:1px .34cm;' ); expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', @@ -38,7 +38,7 @@ describe( 'Padding styles normalization', () => { } ); it( 'should set all paddings (3 values defined)', () => { - styles.setStyle( 'padding:1px .34cm 90.1rem;' ); + styles.setTo( 'padding:1px .34cm 90.1rem;' ); expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', @@ -49,7 +49,7 @@ describe( 'Padding styles normalization', () => { } ); it( 'should set all paddings (4 values defined)', () => { - styles.setStyle( 'padding:1px .34cm 90.1rem thick;' ); + styles.setTo( 'padding:1px .34cm 90.1rem thick;' ); expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', @@ -61,7 +61,7 @@ describe( 'Padding styles normalization', () => { describe( 'padding-*', () => { it( 'should set proper padding', () => { - styles.setStyle( 'padding-top:1px;' ); + styles.setTo( 'padding-top:1px;' ); expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px' @@ -69,7 +69,7 @@ describe( 'Padding styles normalization', () => { } ); it( 'should set proper padding with padding shorthand', () => { - styles.setStyle( 'padding: 2em;padding-top:1px;' ); + styles.setTo( 'padding: 2em;padding-top:1px;' ); expect( styles.getNormalized( 'padding' ) ).to.deep.equal( { top: '1px', diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index c19fdd28f..a5e60a240 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -33,71 +33,71 @@ describe( 'StylesMap', () => { } ); it( 'should return number of set styles', () => { - styles.setStyle( 'color:blue' ); + styles.setTo( 'color:blue' ); expect( styles.size ).to.equal( 1 ); - styles.setStyle( 'margin:1px;' ); + styles.setTo( 'margin:1px;' ); expect( styles.size ).to.equal( 1 ); - styles.setStyle( 'margin-top:1px;margin-bottom:1px;' ); + styles.setTo( 'margin-top:1px;margin-bottom:1px;' ); expect( styles.size ).to.equal( 2 ); } ); } ); - describe( 'setStyle()', () => { + describe( 'setTo()', () => { it( 'should reset styles to a new value', () => { - styles.setStyle( 'color:red;margin:1px;' ); + styles.setTo( 'color:red;margin:1px;' ); expect( styles.getNormalized() ).to.deep.equal( { color: 'red', margin: '1px' } ); - styles.setStyle( 'overflow:hidden;' ); + styles.setTo( 'overflow:hidden;' ); expect( styles.getNormalized() ).to.deep.equal( { overflow: 'hidden' } ); } ); describe( 'styles parsing edge cases and incorrect styles', () => { it( 'should not crash and not add any styles if styles attribute was empty', () => { - styles.setStyle( '' ); + styles.setTo( '' ); expect( styles.getStyleNames() ).to.deep.equal( [] ); } ); it( 'should be able to parse big styles definition', () => { expect( () => { - styles.setStyle( `background-image:url('data:image/jpeg;base64,${ encodedImage }')` ); + styles.setTo( `background-image:url('data:image/jpeg;base64,${ encodedImage }')` ); } ).not.to.throw(); } ); it( 'should work with both types of quotes and ignore values inside quotes', () => { - styles.setStyle( 'background-image:url("im;color:g.jpg")' ); + styles.setTo( 'background-image:url("im;color:g.jpg")' ); expect( styles.getInlineProperty( 'background-image' ) ).to.equal( 'url("im;color:g.jpg")' ); - styles.setStyle( 'background-image:url(\'im;color:g.jpg\')' ); + styles.setTo( 'background-image:url(\'im;color:g.jpg\')' ); expect( styles.getInlineProperty( 'background-image' ) ).to.equal( 'url(\'im;color:g.jpg\')' ); } ); it( 'should not be confused by whitespaces', () => { - styles.setStyle( '\ncolor:\n red ' ); + styles.setTo( '\ncolor:\n red ' ); expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); } ); it( 'should not be confused by duplicated semicolon', () => { - styles.setStyle( 'color: red;; display: inline' ); + styles.setTo( 'color: red;; display: inline' ); expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); expect( styles.getInlineProperty( 'display' ) ).to.equal( 'inline' ); } ); it( 'should not throw when value is missing', () => { - styles.setStyle( 'color:; display: inline' ); + styles.setTo( 'color:; display: inline' ); expect( styles.getInlineProperty( 'color' ) ).to.equal( '' ); expect( styles.getInlineProperty( 'display' ) ).to.equal( 'inline' ); } ); it( 'should not throw when colon is duplicated', () => { - styles.setStyle( 'color:: red; display: inline' ); + styles.setTo( 'color:: red; display: inline' ); // The result makes no sense, but here we just check that the algorithm doesn't crash. expect( styles.getInlineProperty( 'color' ) ).to.equal( ': red' ); @@ -105,7 +105,7 @@ describe( 'StylesMap', () => { } ); it( 'should not throw when random stuff passed', () => { - styles.setStyle( 'color: red;:; ;;" ": display: inline; \'aaa;:' ); + styles.setTo( 'color: red;:; ;;" ": display: inline; \'aaa;:' ); // The result makes no sense, but here we just check that the algorithm doesn't crash. expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); @@ -120,7 +120,7 @@ describe( 'StylesMap', () => { } ); it( 'should return sorted styles string if styles are set', () => { - styles.setStyle( 'margin-top:1px;color:blue;' ); + styles.setTo( 'margin-top:1px;color:blue;' ); expect( styles.getInlineStyle() ).to.equal( 'color:blue;margin-top:1px;' ); } ); @@ -128,7 +128,7 @@ describe( 'StylesMap', () => { describe( 'getInlineProperty', () => { it( 'should return empty string for missing shorthand', () => { - styles.setStyle( 'margin-top:1px' ); + styles.setTo( 'margin-top:1px' ); expect( styles.getInlineProperty( 'margin' ) ).to.be.undefined; } ); @@ -144,19 +144,19 @@ describe( 'StylesMap', () => { } ); it( 'should return false if normalized property is not set', () => { - styles.setStyle( 'foo-top:1px' ); + styles.setTo( 'foo-top:1px' ); expect( styles.hasProperty( 'foo' ) ).to.be.true; } ); it( 'should return true if property is set', () => { - styles.setStyle( 'bar:deeppink' ); + styles.setTo( 'bar:deeppink' ); expect( styles.hasProperty( 'bar' ) ).to.be.true; } ); it( 'should return true if normalized shorthanded property is set', () => { - styles.setStyle( 'foo:1px' ); + styles.setTo( 'foo:1px' ); expect( styles.hasProperty( 'foo' ) ).to.be.true; expect( styles.hasProperty( 'foo-top' ) ).to.be.true; @@ -171,21 +171,21 @@ describe( 'StylesMap', () => { } ); it( 'should insert new property (other properties are set)', () => { - styles.setStyle( 'margin: 1px;' ); + styles.setTo( 'margin: 1px;' ); styles.insertProperty( 'color', 'blue' ); expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); it( 'should overwrite property', () => { - styles.setStyle( 'color: red;' ); + styles.setTo( 'color: red;' ); styles.insertProperty( 'color', 'blue' ); expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); it( 'should set multiple styles by providing an object', () => { - styles.setStyle( 'color: red;' ); + styles.setTo( 'color: red;' ); styles.insertProperty( { color: 'blue', foo: '1px' } ); expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); @@ -193,7 +193,7 @@ describe( 'StylesMap', () => { } ); it( 'should set object property', () => { - styles.setStyle( 'foo:1px;' ); + styles.setTo( 'foo:1px;' ); styles.insertProperty( 'foo', { right: '2px' } ); expect( styles.getInlineProperty( 'foo-left' ) ).to.equal( '1px' ); @@ -209,14 +209,14 @@ describe( 'StylesMap', () => { } ); it( 'should insert new property (other properties are set)', () => { - styles.setStyle( 'color:blue' ); + styles.setTo( 'color:blue' ); styles.removeProperty( 'color' ); expect( styles.getInlineProperty( 'color' ) ).to.be.undefined; } ); it( 'should remove normalized property', () => { - styles.setStyle( 'margin:1px' ); + styles.setTo( 'margin:1px' ); styles.removeProperty( 'margin-top' ); @@ -230,13 +230,13 @@ describe( 'StylesMap', () => { } ); it( 'should output custom style names', () => { - styles.setStyle( 'foo: 2;bar: baz;foo-bar-baz:none;' ); + styles.setTo( 'foo: 2;bar: baz;foo-bar-baz:none;' ); expect( styles.getStyleNames() ).to.deep.equal( [ 'foo', 'bar', 'foo-bar-baz' ] ); } ); it( 'should output full names for known style names', () => { - styles.setStyle( 'foo: 1px;foo-top: 2em;' ); + styles.setTo( 'foo: 1px;foo-top: 2em;' ); expect( styles.getStyleNames() ).to.deep.equal( [ 'foo' ] ); } ); From adc0f8fc85edcbaf5c8140f1cbfb4ecbc34908f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 13:28:42 +0100 Subject: [PATCH 103/142] Add StylesMap.hasProperty() documentation. --- src/view/stylesmap.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index ca0f31e65..93d5f9133 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -72,9 +72,29 @@ export default class StylesMap { } /** - * Checks if single style rule is set. + * Checks if a given style is set. * - * Supports shorthands. + * styles.setTo( 'margin-left:1px;' ); + * + * styles.hasProperty( 'margin-left' ); // returns true + * styles.hasProperty( 'padding' ); // returns false + * + * *Note:* This check supports normalized style names. + * + * // Enable 'margin' shorthand processing: + * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * + * styles.setTo( 'margin:2px;' ); + * + * styles.hasProperty( 'margin' ); // returns true + * styles.hasProperty( 'margin-top' ); // returns true + * styles.hasProperty( 'margin-left' ); // returns true + * + * styles.removeProperty( 'margin-top' ); + * + * styles.hasProperty( 'margin' ); // returns false + * styles.hasProperty( 'margin-top' ); // returns false + * styles.hasProperty( 'margin-left' ); // returns true * * @param {String} propertyName * @returns {Boolean} From 87bc5e5fd03c1587b989069b628f91867659a378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 13:28:53 +0100 Subject: [PATCH 104/142] Fix StylesMap.hasProperty() tests. --- tests/view/stylesmap.js | 183 +++++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 87 deletions(-) diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index a5e60a240..e9de6f9dc 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -6,239 +6,248 @@ import StylesMap, { StylesProcessor } from '../../src/view/stylesmap'; import encodedImage from './_utils/encodedimage.txt'; import { addPaddingStylesProcessor } from '../../src/view/styles/paddingstyles'; +import { getTopRightBottomLeftValueReducer } from '../../src/view/styles/utils'; describe( 'StylesMap', () => { - let styles, converter; + let stylesMap, stylesProcessor; beforeEach( () => { - converter = new StylesProcessor(); + stylesProcessor = new StylesProcessor(); // Define simple "foo" shorthand normalizers, similar to the "margin" shorthand normalizers, for testing purposes. - converter.setNormalizer( 'foo', value => ( { + stylesProcessor.setNormalizer( 'foo', value => ( { path: 'foo', value: { top: value, right: value, bottom: value, left: value } } ) ); - converter.setNormalizer( 'foo-top', value => ( { - path: 'foo', - value: { top: value } + stylesProcessor.setNormalizer( 'foo-top', value => ( { + path: 'foo.top', + value } ) ); + stylesProcessor.setReducer( 'foo', getTopRightBottomLeftValueReducer( 'foo' ) ); - addPaddingStylesProcessor( converter ); - styles = new StylesMap( converter ); + addPaddingStylesProcessor( stylesProcessor ); + stylesMap = new StylesMap( stylesProcessor ); } ); describe( 'size getter', () => { it( 'should return 0 if no styles are set', () => { - expect( styles.size ).to.equal( 0 ); + expect( stylesMap.size ).to.equal( 0 ); } ); it( 'should return number of set styles', () => { - styles.setTo( 'color:blue' ); - expect( styles.size ).to.equal( 1 ); + stylesMap.setTo( 'color:blue' ); + expect( stylesMap.size ).to.equal( 1 ); - styles.setTo( 'margin:1px;' ); - expect( styles.size ).to.equal( 1 ); + stylesMap.setTo( 'margin:1px;' ); + expect( stylesMap.size ).to.equal( 1 ); - styles.setTo( 'margin-top:1px;margin-bottom:1px;' ); - expect( styles.size ).to.equal( 2 ); + stylesMap.setTo( 'margin-top:1px;margin-bottom:1px;' ); + expect( stylesMap.size ).to.equal( 2 ); } ); } ); describe( 'setTo()', () => { it( 'should reset styles to a new value', () => { - styles.setTo( 'color:red;margin:1px;' ); + stylesMap.setTo( 'color:red;margin:1px;' ); - expect( styles.getNormalized() ).to.deep.equal( { color: 'red', margin: '1px' } ); + expect( stylesMap.getNormalized() ).to.deep.equal( { color: 'red', margin: '1px' } ); - styles.setTo( 'overflow:hidden;' ); + stylesMap.setTo( 'overflow:hidden;' ); - expect( styles.getNormalized() ).to.deep.equal( { overflow: 'hidden' } ); + expect( stylesMap.getNormalized() ).to.deep.equal( { overflow: 'hidden' } ); } ); describe( 'styles parsing edge cases and incorrect styles', () => { it( 'should not crash and not add any styles if styles attribute was empty', () => { - styles.setTo( '' ); + stylesMap.setTo( '' ); - expect( styles.getStyleNames() ).to.deep.equal( [] ); + expect( stylesMap.getStyleNames() ).to.deep.equal( [] ); } ); it( 'should be able to parse big styles definition', () => { expect( () => { - styles.setTo( `background-image:url('data:image/jpeg;base64,${ encodedImage }')` ); + stylesMap.setTo( `background-image:url('data:image/jpeg;base64,${ encodedImage }')` ); } ).not.to.throw(); } ); it( 'should work with both types of quotes and ignore values inside quotes', () => { - styles.setTo( 'background-image:url("im;color:g.jpg")' ); - expect( styles.getInlineProperty( 'background-image' ) ).to.equal( 'url("im;color:g.jpg")' ); + stylesMap.setTo( 'background-image:url("im;color:g.jpg")' ); + expect( stylesMap.getInlineProperty( 'background-image' ) ).to.equal( 'url("im;color:g.jpg")' ); - styles.setTo( 'background-image:url(\'im;color:g.jpg\')' ); - expect( styles.getInlineProperty( 'background-image' ) ).to.equal( 'url(\'im;color:g.jpg\')' ); + stylesMap.setTo( 'background-image:url(\'im;color:g.jpg\')' ); + expect( stylesMap.getInlineProperty( 'background-image' ) ).to.equal( 'url(\'im;color:g.jpg\')' ); } ); it( 'should not be confused by whitespaces', () => { - styles.setTo( '\ncolor:\n red ' ); + stylesMap.setTo( '\ncolor:\n red ' ); - expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'red' ); } ); it( 'should not be confused by duplicated semicolon', () => { - styles.setTo( 'color: red;; display: inline' ); + stylesMap.setTo( 'color: red;; display: inline' ); - expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); - expect( styles.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'red' ); + expect( stylesMap.getInlineProperty( 'display' ) ).to.equal( 'inline' ); } ); it( 'should not throw when value is missing', () => { - styles.setTo( 'color:; display: inline' ); + stylesMap.setTo( 'color:; display: inline' ); - expect( styles.getInlineProperty( 'color' ) ).to.equal( '' ); - expect( styles.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( '' ); + expect( stylesMap.getInlineProperty( 'display' ) ).to.equal( 'inline' ); } ); it( 'should not throw when colon is duplicated', () => { - styles.setTo( 'color:: red; display: inline' ); + stylesMap.setTo( 'color:: red; display: inline' ); // The result makes no sense, but here we just check that the algorithm doesn't crash. - expect( styles.getInlineProperty( 'color' ) ).to.equal( ': red' ); - expect( styles.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( ': red' ); + expect( stylesMap.getInlineProperty( 'display' ) ).to.equal( 'inline' ); } ); it( 'should not throw when random stuff passed', () => { - styles.setTo( 'color: red;:; ;;" ": display: inline; \'aaa;:' ); + stylesMap.setTo( 'color: red;:; ;;" ": display: inline; \'aaa;:' ); // The result makes no sense, but here we just check that the algorithm doesn't crash. - expect( styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); - expect( styles.getInlineProperty( 'display' ) ).to.be.undefined; + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'red' ); + expect( stylesMap.getInlineProperty( 'display' ) ).to.be.undefined; } ); } ); } ); describe( 'getInlineStyle()', () => { it( 'should return undefined for empty styles', () => { - expect( styles.getInlineStyle() ).to.be.undefined; + expect( stylesMap.getInlineStyle() ).to.be.undefined; } ); it( 'should return sorted styles string if styles are set', () => { - styles.setTo( 'margin-top:1px;color:blue;' ); + stylesMap.setTo( 'margin-top:1px;color:blue;' ); - expect( styles.getInlineStyle() ).to.equal( 'color:blue;margin-top:1px;' ); + expect( stylesMap.getInlineStyle() ).to.equal( 'color:blue;margin-top:1px;' ); } ); } ); describe( 'getInlineProperty', () => { it( 'should return empty string for missing shorthand', () => { - styles.setTo( 'margin-top:1px' ); + stylesMap.setTo( 'margin-top:1px' ); - expect( styles.getInlineProperty( 'margin' ) ).to.be.undefined; + expect( stylesMap.getInlineProperty( 'margin' ) ).to.be.undefined; } ); } ); describe( 'hasProperty()', () => { - it( 'should return false if normalized property is not set', () => { - expect( styles.hasProperty( 'bar' ) ).to.be.false; + it( 'should return false if property is not set', () => { + expect( stylesMap.hasProperty( 'bar' ) ).to.be.false; } ); - it( 'should return false if normalized property is not set', () => { - expect( styles.hasProperty( 'foo' ) ).to.be.false; + it( 'should return false if normalized longhand property is not set', () => { + stylesMap.setTo( 'foo-top:1px' ); + + expect( stylesMap.hasProperty( 'foo' ) ).to.be.false; } ); - it( 'should return false if normalized property is not set', () => { - styles.setTo( 'foo-top:1px' ); + it( 'should return true if normalized longhand property is set', () => { + stylesMap.setTo( 'foo-top:1px' ); - expect( styles.hasProperty( 'foo' ) ).to.be.true; + expect( stylesMap.hasProperty( 'foo-top' ) ).to.be.true; } ); - it( 'should return true if property is set', () => { - styles.setTo( 'bar:deeppink' ); + it( 'should return true if non-normalized property is set', () => { + stylesMap.setTo( 'bar:deeppink' ); - expect( styles.hasProperty( 'bar' ) ).to.be.true; + expect( stylesMap.hasProperty( 'bar' ) ).to.be.true; } ); it( 'should return true if normalized shorthanded property is set', () => { - styles.setTo( 'foo:1px' ); + stylesMap.setTo( 'foo:1px' ); + + expect( stylesMap.hasProperty( 'foo' ) ).to.be.true; + } ); + + it( 'should return true if normalized long-hand property is set', () => { + stylesMap.setTo( 'foo:1px' ); - expect( styles.hasProperty( 'foo' ) ).to.be.true; - expect( styles.hasProperty( 'foo-top' ) ).to.be.true; + expect( stylesMap.hasProperty( 'foo-top' ) ).to.be.true; } ); } ); describe( 'insertProperty()', () => { it( 'should insert new property (empty styles)', () => { - styles.insertProperty( 'color', 'blue' ); + stylesMap.insertProperty( 'color', 'blue' ); - expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); it( 'should insert new property (other properties are set)', () => { - styles.setTo( 'margin: 1px;' ); - styles.insertProperty( 'color', 'blue' ); + stylesMap.setTo( 'margin: 1px;' ); + stylesMap.insertProperty( 'color', 'blue' ); - expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); it( 'should overwrite property', () => { - styles.setTo( 'color: red;' ); - styles.insertProperty( 'color', 'blue' ); + stylesMap.setTo( 'color: red;' ); + stylesMap.insertProperty( 'color', 'blue' ); - expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); it( 'should set multiple styles by providing an object', () => { - styles.setTo( 'color: red;' ); - styles.insertProperty( { color: 'blue', foo: '1px' } ); + stylesMap.setTo( 'color: red;' ); + stylesMap.insertProperty( { color: 'blue', foo: '1px' } ); - expect( styles.getInlineProperty( 'color' ) ).to.equal( 'blue' ); - expect( styles.getInlineProperty( 'foo-top' ) ).to.equal( '1px' ); + expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + expect( stylesMap.getInlineProperty( 'foo-top' ) ).to.equal( '1px' ); } ); it( 'should set object property', () => { - styles.setTo( 'foo:1px;' ); - styles.insertProperty( 'foo', { right: '2px' } ); + stylesMap.setTo( 'foo:1px;' ); + stylesMap.insertProperty( 'foo', { right: '2px' } ); - expect( styles.getInlineProperty( 'foo-left' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'foo-right' ) ).to.equal( '2px' ); + expect( stylesMap.getInlineProperty( 'foo-left' ) ).to.equal( '1px' ); + expect( stylesMap.getInlineProperty( 'foo-right' ) ).to.equal( '2px' ); } ); } ); describe( 'removeProperty()', () => { it( 'should do nothing if property is not set', () => { - styles.removeProperty( 'color' ); + stylesMap.removeProperty( 'color' ); - expect( styles.getInlineProperty( 'color' ) ).to.be.undefined; + expect( stylesMap.getInlineProperty( 'color' ) ).to.be.undefined; } ); it( 'should insert new property (other properties are set)', () => { - styles.setTo( 'color:blue' ); - styles.removeProperty( 'color' ); + stylesMap.setTo( 'color:blue' ); + stylesMap.removeProperty( 'color' ); - expect( styles.getInlineProperty( 'color' ) ).to.be.undefined; + expect( stylesMap.getInlineProperty( 'color' ) ).to.be.undefined; } ); it( 'should remove normalized property', () => { - styles.setTo( 'margin:1px' ); + stylesMap.setTo( 'margin:1px' ); - styles.removeProperty( 'margin-top' ); + stylesMap.removeProperty( 'margin-top' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.be.undefined; + expect( stylesMap.getInlineProperty( 'margin-top' ) ).to.be.undefined; } ); } ); describe( 'getStyleNames()', () => { it( 'should output empty array for empty styles', () => { - expect( styles.getStyleNames() ).to.deep.equal( [] ); + expect( stylesMap.getStyleNames() ).to.deep.equal( [] ); } ); it( 'should output custom style names', () => { - styles.setTo( 'foo: 2;bar: baz;foo-bar-baz:none;' ); + stylesMap.setTo( 'foo: 2;bar: baz;foo-bar-baz:none;' ); - expect( styles.getStyleNames() ).to.deep.equal( [ 'foo', 'bar', 'foo-bar-baz' ] ); + expect( stylesMap.getStyleNames() ).to.deep.equal( [ 'foo', 'bar', 'foo-bar-baz' ] ); } ); it( 'should output full names for known style names', () => { - styles.setTo( 'foo: 1px;foo-top: 2em;' ); + stylesMap.setTo( 'foo: 1px;foo-top: 2em;' ); - expect( styles.getStyleNames() ).to.deep.equal( [ 'foo' ] ); + expect( stylesMap.getStyleNames() ).to.deep.equal( [ 'foo' ] ); } ); } ); } ); From 4ea5df6ce7c1900778b248bc24a93397b9fa9774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 13:31:02 +0100 Subject: [PATCH 105/142] Rename StylesMap.hasProperty() to has(). --- src/view/element.js | 4 ++-- src/view/stylesmap.js | 18 +++++++++--------- tests/view/element.js | 10 +++++----- tests/view/styles/borderstyles.js | 2 +- tests/view/stylesmap.js | 14 +++++++------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 7e54f6b2b..062a29601 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -336,7 +336,7 @@ export default class Element extends Node { // Check if styles are the same. for ( const property of this._styles.getStyleNames() ) { if ( - !otherElement._styles.hasProperty( property ) || + !otherElement._styles.has( property ) || otherElement._styles.getInlineProperty( property ) !== this._styles.getInlineProperty( property ) ) { return false; @@ -437,7 +437,7 @@ export default class Element extends Node { */ hasStyle( ...property ) { for ( const name of property ) { - if ( !this._styles.hasProperty( name ) ) { + if ( !this._styles.has( name ) ) { return false; } } diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 93d5f9133..39d0ddefb 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -76,8 +76,8 @@ export default class StylesMap { * * styles.setTo( 'margin-left:1px;' ); * - * styles.hasProperty( 'margin-left' ); // returns true - * styles.hasProperty( 'padding' ); // returns false + * styles.has( 'margin-left' ); // returns true + * styles.has( 'padding' ); // returns false * * *Note:* This check supports normalized style names. * @@ -86,20 +86,20 @@ export default class StylesMap { * * styles.setTo( 'margin:2px;' ); * - * styles.hasProperty( 'margin' ); // returns true - * styles.hasProperty( 'margin-top' ); // returns true - * styles.hasProperty( 'margin-left' ); // returns true + * styles.has( 'margin' ); // returns true + * styles.has( 'margin-top' ); // returns true + * styles.has( 'margin-left' ); // returns true * * styles.removeProperty( 'margin-top' ); * - * styles.hasProperty( 'margin' ); // returns false - * styles.hasProperty( 'margin-top' ); // returns false - * styles.hasProperty( 'margin-left' ); // returns true + * styles.has( 'margin' ); // returns false + * styles.has( 'margin-top' ); // returns false + * styles.has( 'margin-left' ); // returns true * * @param {String} propertyName * @returns {Boolean} */ - hasProperty( propertyName ) { + has( propertyName ) { const normalized = this._styleProcessor.getNormalized( propertyName, this._styles ); if ( !normalized ) { diff --git a/tests/view/element.js b/tests/view/element.js index 8d1e33235..5b28f61cb 100644 --- a/tests/view/element.js +++ b/tests/view/element.js @@ -72,11 +72,11 @@ describe( 'Element', () => { expect( el._attrs.has( 'style' ) ).to.be.false; expect( el._attrs.has( 'id' ) ).to.be.true; - expect( el._styles.hasProperty( 'one' ) ).to.be.true; + expect( el._styles.has( 'one' ) ).to.be.true; expect( el._styles.getInlineProperty( 'one' ) ).to.equal( 'style1' ); - expect( el._styles.hasProperty( 'two' ) ).to.be.true; + expect( el._styles.has( 'two' ) ).to.be.true; expect( el._styles.getInlineProperty( 'two' ) ).to.equal( 'style2' ); - expect( el._styles.hasProperty( 'three' ) ).to.be.true; + expect( el._styles.has( 'three' ) ).to.be.true; expect( el._styles.getInlineProperty( 'three' ) ).to.equal( 'url(http://ckeditor.com)' ); } ); } ); @@ -198,9 +198,9 @@ describe( 'Element', () => { expect( clone ).to.not.equal( el ); expect( clone.name ).to.equal( el.name ); - expect( clone._styles.hasProperty( 'color' ) ).to.be.true; + expect( clone._styles.has( 'color' ) ).to.be.true; expect( clone._styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); - expect( clone._styles.hasProperty( 'font-size' ) ).to.be.true; + expect( clone._styles.has( 'font-size' ) ).to.be.true; expect( clone._styles.getInlineProperty( 'font-size' ) ).to.equal( '12px' ); } ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index fe91a52e3..df148e2ff 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -81,7 +81,7 @@ describe( 'Border styles normalization', () => { } ); expect( styles.getInlineStyle( 'border' ) ).to.equal( 'border-top:blue;' ); - // TODO: expect( styles.hasProperty( 'border-top-color' ) ).to.be.true; + // TODO: expect( styles.has( 'border-top-color' ) ).to.be.true; // expect( styles.getInlineProperty( 'border-top-color' ) ).to.equal( 'blue' ); } ); diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index e9de6f9dc..26f4e37b3 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -136,39 +136,39 @@ describe( 'StylesMap', () => { } ); } ); - describe( 'hasProperty()', () => { + describe( 'has()', () => { it( 'should return false if property is not set', () => { - expect( stylesMap.hasProperty( 'bar' ) ).to.be.false; + expect( stylesMap.has( 'bar' ) ).to.be.false; } ); it( 'should return false if normalized longhand property is not set', () => { stylesMap.setTo( 'foo-top:1px' ); - expect( stylesMap.hasProperty( 'foo' ) ).to.be.false; + expect( stylesMap.has( 'foo' ) ).to.be.false; } ); it( 'should return true if normalized longhand property is set', () => { stylesMap.setTo( 'foo-top:1px' ); - expect( stylesMap.hasProperty( 'foo-top' ) ).to.be.true; + expect( stylesMap.has( 'foo-top' ) ).to.be.true; } ); it( 'should return true if non-normalized property is set', () => { stylesMap.setTo( 'bar:deeppink' ); - expect( stylesMap.hasProperty( 'bar' ) ).to.be.true; + expect( stylesMap.has( 'bar' ) ).to.be.true; } ); it( 'should return true if normalized shorthanded property is set', () => { stylesMap.setTo( 'foo:1px' ); - expect( stylesMap.hasProperty( 'foo' ) ).to.be.true; + expect( stylesMap.has( 'foo' ) ).to.be.true; } ); it( 'should return true if normalized long-hand property is set', () => { stylesMap.setTo( 'foo:1px' ); - expect( stylesMap.hasProperty( 'foo-top' ) ).to.be.true; + expect( stylesMap.has( 'foo-top' ) ).to.be.true; } ); } ); From 8743d15c15df51b061b8261a34541aa2eca85ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 13:31:52 +0100 Subject: [PATCH 106/142] Rename StylesMap.insertProperty() to set(). --- src/view/element.js | 4 ++-- src/view/stylesmap.js | 10 +++++----- tests/view/styles/borderstyles.js | 6 +++--- tests/view/stylesmap.js | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 062a29601..e5c1bc870 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -546,7 +546,7 @@ export default class Element extends Node { // Classes and styles are cloned separately - this solution is faster than adding them back to attributes and // parse once again in constructor. cloned._classes = new Set( this._classes ); - cloned._styles.insertProperty( this._styles.getNormalized() ); + cloned._styles.set( this._styles.getNormalized() ); // Clone custom properties. cloned._customProperties = new Map( this._customProperties ); @@ -741,7 +741,7 @@ export default class Element extends Node { _setStyle( property, value ) { this._fireChange( 'attributes', this ); - this._styles.insertProperty( property, value ); + this._styles.set( property, value ); } /** diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 39d0ddefb..5380b8ef3 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -124,12 +124,12 @@ export default class StylesMap { * * Can insert one by one * - * styles.insertProperty( 'color', 'blue' ); - * styles.insertProperty( 'margin-right', '1em' ); + * styles.set( 'color', 'blue' ); + * styles.set( 'margin-right', '1em' ); * * or many styles at once: * - * styles.insertProperty( { + * styles.set( { * color: 'blue', * 'margin-right': '1em' * } ); @@ -140,10 +140,10 @@ export default class StylesMap { * @param {String|Object} value * @returns {Boolean} */ - insertProperty( nameOrObject, value ) { + set( nameOrObject, value ) { if ( isObject( nameOrObject ) ) { for ( const key of Object.keys( nameOrObject ) ) { - this.insertProperty( key, nameOrObject[ key ] ); + this.set( key, nameOrObject[ key ] ); } } else { this._styleProcessor.toNormalizedForm( nameOrObject, value, this._styles ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index df148e2ff..ed9b4cafa 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -74,7 +74,7 @@ describe( 'Border styles normalization', () => { } ); it( 'should output only defined inline styles', () => { - styles.insertProperty( 'border-color', { top: 'blue' } ); + styles.set( 'border-color', { top: 'blue' } ); expect( styles.getNormalized( 'border' ) ).to.deep.equal( { color: { top: 'blue' } @@ -133,8 +133,8 @@ describe( 'Border styles normalization', () => { it( 'should merge rules on insert other shorthand', () => { styles.setTo( 'border:1px solid blue;' ); - styles.insertProperty( 'border-left', '#665511 dashed 2.7em' ); - styles.insertProperty( 'border-top', '7px dotted #ccc' ); + styles.set( 'border-left', '#665511 dashed 2.7em' ); + styles.set( 'border-top', '7px dotted #ccc' ); expect( styles.getInlineStyle() ).to.equal( 'border-bottom:1px solid blue;' + diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index 26f4e37b3..0e9254227 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -172,30 +172,30 @@ describe( 'StylesMap', () => { } ); } ); - describe( 'insertProperty()', () => { + describe( 'set()', () => { it( 'should insert new property (empty styles)', () => { - stylesMap.insertProperty( 'color', 'blue' ); + stylesMap.set( 'color', 'blue' ); expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); it( 'should insert new property (other properties are set)', () => { stylesMap.setTo( 'margin: 1px;' ); - stylesMap.insertProperty( 'color', 'blue' ); + stylesMap.set( 'color', 'blue' ); expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); it( 'should overwrite property', () => { stylesMap.setTo( 'color: red;' ); - stylesMap.insertProperty( 'color', 'blue' ); + stylesMap.set( 'color', 'blue' ); expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); } ); it( 'should set multiple styles by providing an object', () => { stylesMap.setTo( 'color: red;' ); - stylesMap.insertProperty( { color: 'blue', foo: '1px' } ); + stylesMap.set( { color: 'blue', foo: '1px' } ); expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); expect( stylesMap.getInlineProperty( 'foo-top' ) ).to.equal( '1px' ); @@ -203,7 +203,7 @@ describe( 'StylesMap', () => { it( 'should set object property', () => { stylesMap.setTo( 'foo:1px;' ); - stylesMap.insertProperty( 'foo', { right: '2px' } ); + stylesMap.set( 'foo', { right: '2px' } ); expect( stylesMap.getInlineProperty( 'foo-left' ) ).to.equal( '1px' ); expect( stylesMap.getInlineProperty( 'foo-right' ) ).to.equal( '2px' ); From 66e3cd12265a5b070d965bed9b20b8e1e9d8e359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 13:38:01 +0100 Subject: [PATCH 107/142] Update StylesMap.set() docs. --- src/view/stylesmap.js | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 5380b8ef3..66b7b9f97 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -120,7 +120,7 @@ export default class StylesMap { } /** - * Inserts single style property. + * Sets a given style. * * Can insert one by one * @@ -134,16 +134,30 @@ export default class StylesMap { * 'margin-right': '1em' * } ); * - * Supports shorthands. + * *Note:* This method supports normalized styles if defined. * - * @param {String|Object} nameOrObject - * @param {String|Object} value + * // Enable 'margin' shorthand processing: + * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * + * styles.set( 'margin', '2px' ); + * + * The above code will set margin to a normalized value: + * + * const margin = { + * top: '2px', + * right: '2px', + * bottom: '2px', + * left: '2px', + * }; + * + * @param {String|Object} nameOrObject Style property name or object with multiple properties. + * @param {String} value Value to set. * @returns {Boolean} */ set( nameOrObject, value ) { if ( isObject( nameOrObject ) ) { - for ( const key of Object.keys( nameOrObject ) ) { - this.set( key, nameOrObject[ key ] ); + for ( const [ key, value ] of Object.entries( nameOrObject ) ) { + this._styleProcessor.toNormalizedForm( key, value, this._styles ); } } else { this._styleProcessor.toNormalizedForm( nameOrObject, value, this._styles ); @@ -327,7 +341,7 @@ export class StylesProcessor { * Parse style property value to a normalized form. * * @param {String} propertyName Name of style property. - * @param {String} value Value of style property. + * @param {String} propertyValue Value of style property. * @param {Object} styles * @private */ From 4fc65bd29164bf75e4bbb989085e293407a50c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 13:51:42 +0100 Subject: [PATCH 108/142] Rename StylesMap.removeProperty() to remove(). --- src/view/element.js | 2 +- src/view/stylesmap.js | 36 +++++++++++++++++++++++-------- tests/view/styles/borderstyles.js | 2 +- tests/view/stylesmap.js | 8 +++---- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index e5c1bc870..81cb5f761 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -759,7 +759,7 @@ export default class Element extends Node { this._fireChange( 'attributes', this ); property = Array.isArray( property ) ? property : [ property ]; - property.forEach( name => this._styles.removeProperty( name ) ); + property.forEach( name => this._styles.remove( name ) ); } /** diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 66b7b9f97..3cbd4e09c 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -90,27 +90,27 @@ export default class StylesMap { * styles.has( 'margin-top' ); // returns true * styles.has( 'margin-left' ); // returns true * - * styles.removeProperty( 'margin-top' ); + * styles.remove( 'margin-top' ); * * styles.has( 'margin' ); // returns false * styles.has( 'margin-top' ); // returns false * styles.has( 'margin-left' ); // returns true * - * @param {String} propertyName + * @param {String} name * @returns {Boolean} */ - has( propertyName ) { - const normalized = this._styleProcessor.getNormalized( propertyName, this._styles ); + has( name ) { + const normalized = this._styleProcessor.getNormalized( name, this._styles ); if ( !normalized ) { // Try return styles set directly - values that are not parsed. - return this._styles[ propertyName ] !== undefined; + return this._styles[ name ] !== undefined; } if ( isObject( normalized ) ) { - const styles = this._styleProcessor.getReducedForm( propertyName, normalized ); + const styles = this._styleProcessor.getReducedForm( name, normalized ); - const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); + const propertyDescriptor = styles.find( ( [ property ] ) => property === name ); // Only return a value if it is set; return Array.isArray( propertyDescriptor ); @@ -165,11 +165,29 @@ export default class StylesMap { } /** - * Removes styles property. + * Removes given style. + * + * styles.setTo( 'background:#foo;margin-right:2px;' ); + * + * styles.remove( 'background' ); + * + * styles.toString(); // returns 'margin-right:2px;' + * + * *Note:* This method supports normalized styles if defined. + * + * // Enable 'margin' shorthand processing: + * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * + * styles.setTo( 'margin:1px' ); + * + * styles.remove( 'margin-top' ); + * styles.remove( 'margin-right' ); + * + * styles.toString(); // returns 'margin-bottom:1px;margin-left:1px;' * * @param name */ - removeProperty( name ) { + remove( name ) { unset( this._styles, toPath( name ) ); delete this._styles[ name ]; } diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index ed9b4cafa..f284fa691 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -150,7 +150,7 @@ describe( 'Border styles normalization', () => { it( 'should output', () => { styles.setTo( 'border:1px solid blue;' ); - styles.removeProperty( 'border-color' ); + styles.remove( 'border-color' ); expect( styles.getInlineStyle() ).to.equal( 'border-bottom:1px solid;' + diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index 0e9254227..1b89934da 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -210,16 +210,16 @@ describe( 'StylesMap', () => { } ); } ); - describe( 'removeProperty()', () => { + describe( 'remove()', () => { it( 'should do nothing if property is not set', () => { - stylesMap.removeProperty( 'color' ); + stylesMap.remove( 'color' ); expect( stylesMap.getInlineProperty( 'color' ) ).to.be.undefined; } ); it( 'should insert new property (other properties are set)', () => { stylesMap.setTo( 'color:blue' ); - stylesMap.removeProperty( 'color' ); + stylesMap.remove( 'color' ); expect( stylesMap.getInlineProperty( 'color' ) ).to.be.undefined; } ); @@ -227,7 +227,7 @@ describe( 'StylesMap', () => { it( 'should remove normalized property', () => { stylesMap.setTo( 'margin:1px' ); - stylesMap.removeProperty( 'margin-top' ); + stylesMap.remove( 'margin-top' ); expect( stylesMap.getInlineProperty( 'margin-top' ) ).to.be.undefined; } ); From 54c2a5a4f2bd3ac2eb0926a33f9f6887b3a237d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 15:40:12 +0100 Subject: [PATCH 109/142] Cross link view writer, element ans styles map docs. --- src/view/downcastwriter.js | 4 ++++ src/view/element.js | 4 ++++ src/view/stylesmap.js | 9 ++++----- src/view/upcastwriter.js | 4 ++++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/view/downcastwriter.js b/src/view/downcastwriter.js index b758bef2b..f32b03dc6 100644 --- a/src/view/downcastwriter.js +++ b/src/view/downcastwriter.js @@ -324,6 +324,8 @@ export default class DowncastWriter { * position: 'fixed' * }, element ); * + * *Note*: Handles also normalized styles if defined. See {@link module:engine/view/stylesmap~StylesMap#set}. + * * @param {String|Object} property Property name or object with key - value pairs. * @param {String} [value] Value to set. This parameter is ignored if object is provided as the first parameter. * @param {module:engine/view/element~Element} element Element to set styles on. @@ -342,6 +344,8 @@ export default class DowncastWriter { * writer.removeStyle( 'color', element ); // Removes 'color' style. * writer.removeStyle( [ 'color', 'border-top' ], element ); // Removes both 'color' and 'border-top' styles. * + * *Note*: Handles also normalized styles if defined. See {@link module:engine/view/stylesmap~StylesMap#remove}. + * * @param {Array.|String} property * @param {module:engine/view/element~Element} element */ diff --git a/src/view/element.js b/src/view/element.js index 81cb5f761..dffa8f118 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -732,6 +732,8 @@ export default class Element extends Node { * position: 'fixed' * } ); * + * *Note*: Handles also normalized styles if defined. See {@link module:engine/view/stylesmap~StylesMap#set}. + * * @see module:engine/view/downcastwriter~DowncastWriter#setStyle * @protected * @param {String|Object} property Property name or object with key - value pairs. @@ -750,6 +752,8 @@ export default class Element extends Node { * element._removeStyle( 'color' ); // Removes 'color' style. * element._removeStyle( [ 'color', 'border-top' ] ); // Removes both 'color' and 'border-top' styles. * + * *Note*: Handles also normalized styles if defined. See {@link module:engine/view/stylesmap~StylesMap#remove}. + * * @see module:engine/view/downcastwriter~DowncastWriter#removeStyle * @protected * @param {Array.|String} property diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 3cbd4e09c..7d5c4eaef 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -96,7 +96,7 @@ export default class StylesMap { * styles.has( 'margin-top' ); // returns false * styles.has( 'margin-left' ); // returns true * - * @param {String} name + * @param {String} name Style name. * @returns {Boolean} */ has( name ) { @@ -152,7 +152,6 @@ export default class StylesMap { * * @param {String|Object} nameOrObject Style property name or object with multiple properties. * @param {String} value Value to set. - * @returns {Boolean} */ set( nameOrObject, value ) { if ( isObject( nameOrObject ) ) { @@ -185,7 +184,7 @@ export default class StylesMap { * * styles.toString(); // returns 'margin-bottom:1px;margin-left:1px;' * - * @param name + * @param {String} name Style name. */ remove( name ) { unset( this._styles, toPath( name ) ); @@ -193,7 +192,7 @@ export default class StylesMap { } /** - * Returns a normalized style object or value. + * Returns a normalized style object or a single value. * * const styles = new Styles(); * styles.setTo( 'margin:1px 2px 3em;' ); @@ -211,7 +210,7 @@ export default class StylesMap { * * *Note*: This method will only return normalized styles if a style processor was defined. * - * @param {String} name + * @param {String} name Style name. * @returns {Object|String|undefined} */ getNormalized( name ) { diff --git a/src/view/upcastwriter.js b/src/view/upcastwriter.js index e5c534357..55822a8c9 100644 --- a/src/view/upcastwriter.js +++ b/src/view/upcastwriter.js @@ -270,6 +270,8 @@ export default class UpcastWriter { * position: 'fixed' * } ); * + * *Note*: Handles also normalized styles if defined. See {@link module:engine/view/stylesmap~StylesMap#set}. + * * @see module:engine/view/element~Element#_setStyle * @param {String|Object} property Property name or object with key - value pairs. * @param {String} [value] Value to set. This parameter is ignored if object is provided as the first parameter. @@ -288,6 +290,8 @@ export default class UpcastWriter { * writer.removeStyle( element, 'color' ); // Removes 'color' style. * writer.removeStyle( element, [ 'color', 'border-top' ] ); // Removes both 'color' and 'border-top' styles. * + * *Note*: Handles also normalized styles if defined. See {@link module:engine/view/stylesmap~StylesMap#remove}. + * * @see module:engine/view/element~Element#_removeStyle * @param {Array.|String} property Style property name or names to be removed. * @param {module:engine/view/element~Element} element Element from which style will be removed. From c33b187eae6b2c51a08a8a656254665b19eedc70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 15:53:02 +0100 Subject: [PATCH 110/142] StylesMap will return string when empty. --- src/view/element.js | 6 ++++-- src/view/stylesmap.js | 29 ++++++++++++++++++++++----- tests/view/styles/backgroundstyles.js | 2 +- tests/view/styles/borderstyles.js | 18 ++++++++--------- tests/view/styles/marginstyles.js | 20 +++++++++--------- tests/view/stylesmap.js | 8 ++++---- 6 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index dffa8f118..0b519a084 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -266,7 +266,9 @@ export default class Element extends Node { } if ( key == 'style' ) { - return this._styles.getInlineStyle(); + const inlineStyle = this._styles.toString(); + + return inlineStyle == '' ? undefined : inlineStyle; } return this._attrs.get( key ); @@ -514,7 +516,7 @@ export default class Element extends Node { */ getIdentity() { const classes = Array.from( this._classes ).sort().join( ',' ); - const styles = this._styles.getInlineStyle(); + const styles = this._styles.toString(); const attributes = Array.from( this._attrs ).map( i => `${ i[ 0 ] }="${ i[ 1 ] }"` ).sort().join( ' ' ); return this.name + diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 7d5c4eaef..42d3a4117 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -166,7 +166,7 @@ export default class StylesMap { /** * Removes given style. * - * styles.setTo( 'background:#foo;margin-right:2px;' ); + * styles.setTo( 'background:#f00;margin-right:2px;' ); * * styles.remove( 'background' ); * @@ -218,16 +218,35 @@ export default class StylesMap { } /** - * Returns a string containing normalized styles string or undefined if no style properties are set. + * Returns a normalized style string. Styles are sorted by name. * - * @returns {String|undefined} + * styles.set( 'margin' , '1px' ); + * styles.set( 'background', '#f00' ); + * + * styles.toString(); + * // Will return: 'background:#f00;margin:1px;' + * + * *Note:* This method supports normalized styles if defined. + * + * // Enable 'margin' shorthand processing: + * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * + * styles.set( 'margin' , '1px' ); + * styles.set( 'background', '#f00' ); + * styles.remove( 'margin-top' ); + * styles.remove( 'margin-right' ); + * + * styles.toString(); + * // Will return: 'background:#f00;margin-bottom:1px;margin-left:1px;' + * + * @returns {String} */ - getInlineStyle() { + toString() { const entries = this._getStylesEntries(); // Return undefined for empty styles map. if ( !entries.length ) { - return; + return ''; } return entries diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index a05e6949b..b8dd636b4 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -44,7 +44,7 @@ describe( 'Background styles normalization', () => { it( 'should output inline background-color style', () => { styles.setTo( 'background:#f00;' ); - expect( styles.getInlineStyle() ).to.equal( 'background-color:#f00;' ); + expect( styles.toString() ).to.equal( 'background-color:#f00;' ); expect( styles.getInlineProperty( 'background-color' ) ).to.equal( '#f00' ); } ); } ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index f284fa691..99b60d463 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -62,7 +62,7 @@ describe( 'Border styles normalization', () => { it( 'should output inline shorthand rules #1', () => { styles.setTo( 'border:1px solid blue;' ); - expect( styles.getInlineStyle() ).to.equal( + expect( styles.toString() ).to.equal( 'border-bottom:1px solid blue;' + 'border-left:1px solid blue;' + 'border-right:1px solid blue;' + @@ -80,7 +80,7 @@ describe( 'Border styles normalization', () => { color: { top: 'blue' } } ); - expect( styles.getInlineStyle( 'border' ) ).to.equal( 'border-top:blue;' ); + expect( styles.toString( 'border' ) ).to.equal( 'border-top:blue;' ); // TODO: expect( styles.has( 'border-top-color' ) ).to.be.true; // expect( styles.getInlineProperty( 'border-top-color' ) ).to.equal( 'blue' ); } ); @@ -88,7 +88,7 @@ describe( 'Border styles normalization', () => { it( 'should output inline shorthand rules #2', () => { styles.setTo( 'border:1px solid blue;border-left:#665511 dashed 2.7em;border-top:7px dotted #ccc;' ); - expect( styles.getInlineStyle() ).to.equal( + expect( styles.toString() ).to.equal( 'border-bottom:1px solid blue;' + 'border-left:2.7em dashed #665511;' + 'border-right:1px solid blue;' + @@ -136,7 +136,7 @@ describe( 'Border styles normalization', () => { styles.set( 'border-left', '#665511 dashed 2.7em' ); styles.set( 'border-top', '7px dotted #ccc' ); - expect( styles.getInlineStyle() ).to.equal( + expect( styles.toString() ).to.equal( 'border-bottom:1px solid blue;' + 'border-left:2.7em dashed #665511;' + 'border-right:1px solid blue;' + @@ -152,7 +152,7 @@ describe( 'Border styles normalization', () => { styles.setTo( 'border:1px solid blue;' ); styles.remove( 'border-color' ); - expect( styles.getInlineStyle() ).to.equal( + expect( styles.toString() ).to.equal( 'border-bottom:1px solid;' + 'border-left:1px solid;' + 'border-right:1px solid;' + @@ -172,7 +172,7 @@ describe( 'Border styles normalization', () => { it( 'should output border with only style shorthand (style)', () => { styles.setTo( 'border:solid;' ); - expect( styles.getInlineStyle() ).to.equal( + expect( styles.toString() ).to.equal( 'border-bottom:solid;' + 'border-left:solid;' + 'border-right:solid;' + @@ -191,7 +191,7 @@ describe( 'Border styles normalization', () => { it( 'should output border with only style shorthand (color)', () => { styles.setTo( 'border:#f00;' ); - expect( styles.getInlineStyle() ).to.equal( + expect( styles.toString() ).to.equal( 'border-bottom:#f00;' + 'border-left:#f00;' + 'border-right:#f00;' + @@ -210,7 +210,7 @@ describe( 'Border styles normalization', () => { it( 'should output border with only style shorthand (width)', () => { styles.setTo( 'border:1px;' ); - expect( styles.getInlineStyle() ).to.equal( + expect( styles.toString() ).to.equal( 'border-bottom:1px;' + 'border-left:1px;' + 'border-right:1px;' + @@ -591,7 +591,7 @@ describe( 'Border styles normalization', () => { 'border-right:dotted #FFC000 3.0pt;' ); - expect( styles.getInlineStyle() ).to.equal( + expect( styles.toString() ).to.equal( 'border-bottom:3.0pt dotted #FFC000;' + 'border-left:none;' + 'border-right:3.0pt dotted #FFC000;' + diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index ae64ba46b..d37cc9637 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -62,7 +62,7 @@ describe( 'Margin styles normalizer', () => { it( 'should output inline style (1 value defined)', () => { styles.setTo( 'margin:1px;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px;' ); + expect( styles.toString() ).to.equal( 'margin:1px;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); @@ -73,7 +73,7 @@ describe( 'Margin styles normalizer', () => { it( 'should output inline style (2 values defined)', () => { styles.setTo( 'margin:1px .34cm;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm;' ); + expect( styles.toString() ).to.equal( 'margin:1px .34cm;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); @@ -84,7 +84,7 @@ describe( 'Margin styles normalizer', () => { it( 'should output inline style (3 values defined)', () => { styles.setTo( 'margin:1px .34cm 90.1rem;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem;' ); + expect( styles.toString() ).to.equal( 'margin:1px .34cm 90.1rem;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); @@ -95,7 +95,7 @@ describe( 'Margin styles normalizer', () => { it( 'should output inline style (3 values defined, only last different)', () => { styles.setTo( 'margin:1px 1px 90.1rem;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 90.1rem;' ); + expect( styles.toString() ).to.equal( 'margin:1px 1px 90.1rem;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); @@ -106,7 +106,7 @@ describe( 'Margin styles normalizer', () => { it( 'should output inline style (4 values defined)', () => { styles.setTo( 'margin:1px .34cm 90.1rem thick;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); + expect( styles.toString() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); @@ -117,7 +117,7 @@ describe( 'Margin styles normalizer', () => { it( 'should output inline style (4 values defined, only last different)', () => { styles.setTo( 'margin:1px 1px 1px thick;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin:1px 1px 1px thick;' ); + expect( styles.toString() ).to.equal( 'margin:1px 1px 1px thick;' ); expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); @@ -151,28 +151,28 @@ describe( 'Margin styles normalizer', () => { it( 'should output margin-top', () => { styles.setTo( 'margin-top:1px;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-top:1px;' ); + expect( styles.toString() ).to.equal( 'margin-top:1px;' ); expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); } ); it( 'should output margin-right', () => { styles.setTo( 'margin-right:1px;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-right:1px;' ); + expect( styles.toString() ).to.equal( 'margin-right:1px;' ); expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); } ); it( 'should output margin-bottom', () => { styles.setTo( 'margin-bottom:1px;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-bottom:1px;' ); + expect( styles.toString() ).to.equal( 'margin-bottom:1px;' ); expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); } ); it( 'should output margin-left', () => { styles.setTo( 'margin-left:1px;' ); - expect( styles.getInlineStyle() ).to.equal( 'margin-left:1px;' ); + expect( styles.toString() ).to.equal( 'margin-left:1px;' ); expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); } ); } ); diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index 1b89934da..94b469490 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -116,15 +116,15 @@ describe( 'StylesMap', () => { } ); } ); - describe( 'getInlineStyle()', () => { - it( 'should return undefined for empty styles', () => { - expect( stylesMap.getInlineStyle() ).to.be.undefined; + describe( 'toString()', () => { + it( 'should return empty string for empty styles', () => { + expect( stylesMap.toString() ).to.equal( '' ); } ); it( 'should return sorted styles string if styles are set', () => { stylesMap.setTo( 'margin-top:1px;color:blue;' ); - expect( stylesMap.getInlineStyle() ).to.equal( 'color:blue;margin-top:1px;' ); + expect( stylesMap.toString() ).to.equal( 'color:blue;margin-top:1px;' ); } ); } ); From 41a55c6ebca26b6794f25942c8f2095bb603ef09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 16:04:22 +0100 Subject: [PATCH 111/142] Add StylesMap#isEmpty getter for fast checking if styles map has any values set. --- src/view/element.js | 8 ++++---- src/view/stylesmap.js | 34 ++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 0b519a084..52b55fcfa 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -223,7 +223,7 @@ export default class Element extends Node { yield 'class'; } - if ( this._styles.size > 0 ) { + if ( !this._styles.isEmpty ) { yield 'style'; } @@ -245,7 +245,7 @@ export default class Element extends Node { yield [ 'class', this.getAttribute( 'class' ) ]; } - if ( this._styles.size > 0 ) { + if ( !this._styles.isEmpty ) { yield [ 'style', this.getAttribute( 'style' ) ]; } } @@ -286,7 +286,7 @@ export default class Element extends Node { } if ( key == 'style' ) { - return this._styles.size > 0; + return !this._styles.isEmpty; } return this._attrs.has( key ); @@ -676,7 +676,7 @@ export default class Element extends Node { // Remove style attribute. if ( key == 'style' ) { - if ( this._styles.size ) { + if ( !this._styles.isEmpty ) { this._styles.clear(); return true; diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 42d3a4117..42cae1910 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -35,12 +35,25 @@ export default class StylesMap { } ); } + /** + * Returns true if style map has no styles set. + * + * @returns {Boolean} + */ + get isEmpty() { + return !Object.entries( this._styles ).length; + } + /** * Number of styles defined. * * @type {Number} */ get size() { + if ( this.isEmpty ) { + return 0; + } + return this.getStyleNames().length; } @@ -100,6 +113,10 @@ export default class StylesMap { * @returns {Boolean} */ has( name ) { + if ( this.isEmpty ) { + return false; + } + const normalized = this._styleProcessor.getNormalized( name, this._styles ); if ( !normalized ) { @@ -242,14 +259,11 @@ export default class StylesMap { * @returns {String} */ toString() { - const entries = this._getStylesEntries(); - - // Return undefined for empty styles map. - if ( !entries.length ) { + if ( this.isEmpty ) { return ''; } - return entries + return this._getStylesEntries() .map( arr => arr.join( ':' ) ) .sort() .join( ';' ) + ';'; @@ -262,6 +276,10 @@ export default class StylesMap { * @returns {String|undefined} */ getInlineProperty( propertyName ) { + if ( this.isEmpty ) { + return; + } + const normalized = this._styleProcessor.getNormalized( propertyName, this._styles ); if ( !normalized ) { @@ -284,11 +302,15 @@ export default class StylesMap { } /** - * Returns style properties names as the would appear when using {@link #getInlineStyle} + * Returns style properties names as the would appear when using {@link #toString} * * @returns {module:engine/view/stylesmap~PropertyEntry} */ getStyleNames() { + if ( this.isEmpty ) { + return []; + } + const entries = this._getStylesEntries(); return entries.map( ( [ key ] ) => key ); From 151c5c12b870b13ffdd04e31b2d0a2e1ab3f775d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 16:18:14 +0100 Subject: [PATCH 112/142] Rename StylesMap.getInlineProperty() to getAsString() and improve docs. --- src/view/element.js | 24 +++++- src/view/stylesmap.js | 21 +++++- tests/view/element.js | 10 +-- tests/view/styles/backgroundstyles.js | 2 +- tests/view/styles/borderstyles.js | 104 +++++++++++++------------- tests/view/styles/marginstyles.js | 68 ++++++++--------- tests/view/stylesmap.js | 46 ++++++------ 7 files changed, 155 insertions(+), 120 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index 52b55fcfa..ee24473fb 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -339,7 +339,7 @@ export default class Element extends Node { for ( const property of this._styles.getStyleNames() ) { if ( !otherElement._styles.has( property ) || - otherElement._styles.getInlineProperty( property ) !== this._styles.getInlineProperty( property ) + otherElement._styles.getAsString( property ) !== this._styles.getAsString( property ) ) { return false; } @@ -380,11 +380,31 @@ export default class Element extends Node { * Returns style value for given property. * Undefined is returned if style does not exist. * + * *Note*: This method will only return normalized styles if a style processor was defined. Otherwise the style names are not + * normalized. + * + * For an element with style set to: 'margin:1px + * + * // Enable 'margin' shorthand processing: + * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * + * const element = view.change( writer => { + * const element = writer.createElement(); + * writer.setStyle( 'margin', '1px' ) + * writer.setStyle( 'margin-bottom', '3em' ) + * + * return element; + * } ); + * + * element.getStyle( 'margin' ); + * + * // will return 'margin: 1px 1px 3em;' + * * @param {String} property * @returns {String|undefined} */ getStyle( property ) { - return this._styles.getInlineProperty( property ); + return this._styles.has( property ) ? this._styles.getAsString( property ) : undefined; } /** diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 42cae1910..701280ff9 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -211,6 +211,9 @@ export default class StylesMap { /** * Returns a normalized style object or a single value. * + * // Enable 'margin' shorthand processing: + * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * * const styles = new Styles(); * styles.setTo( 'margin:1px 2px 3em;' ); * @@ -270,14 +273,26 @@ export default class StylesMap { } /** - * Returns property value string. + * Returns property as a value string. + * + * // Enable 'margin' shorthand processing: + * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * + * const styles = new Styles(); + * styles.setTo( 'margin:1px;' ); + * styles.set( 'margin-bottom:3em;' ); + * + * styles.getAsString( 'margin' ); + * // will return 'margin: 1px 1px 3em;' + * + * *Note*: To get normalized version of a longhand property use {@link #getNormalized} method. * * @param {String} propertyName * @returns {String|undefined} */ - getInlineProperty( propertyName ) { + getAsString( propertyName ) { if ( this.isEmpty ) { - return; + return ''; } const normalized = this._styleProcessor.getNormalized( propertyName, this._styles ); diff --git a/tests/view/element.js b/tests/view/element.js index 5b28f61cb..ab1b876be 100644 --- a/tests/view/element.js +++ b/tests/view/element.js @@ -73,11 +73,11 @@ describe( 'Element', () => { expect( el._attrs.has( 'id' ) ).to.be.true; expect( el._styles.has( 'one' ) ).to.be.true; - expect( el._styles.getInlineProperty( 'one' ) ).to.equal( 'style1' ); + expect( el._styles.getAsString( 'one' ) ).to.equal( 'style1' ); expect( el._styles.has( 'two' ) ).to.be.true; - expect( el._styles.getInlineProperty( 'two' ) ).to.equal( 'style2' ); + expect( el._styles.getAsString( 'two' ) ).to.equal( 'style2' ); expect( el._styles.has( 'three' ) ).to.be.true; - expect( el._styles.getInlineProperty( 'three' ) ).to.equal( 'url(http://ckeditor.com)' ); + expect( el._styles.getAsString( 'three' ) ).to.equal( 'url(http://ckeditor.com)' ); } ); } ); @@ -199,9 +199,9 @@ describe( 'Element', () => { expect( clone ).to.not.equal( el ); expect( clone.name ).to.equal( el.name ); expect( clone._styles.has( 'color' ) ).to.be.true; - expect( clone._styles.getInlineProperty( 'color' ) ).to.equal( 'red' ); + expect( clone._styles.getAsString( 'color' ) ).to.equal( 'red' ); expect( clone._styles.has( 'font-size' ) ).to.be.true; - expect( clone._styles.getInlineProperty( 'font-size' ) ).to.equal( '12px' ); + expect( clone._styles.getAsString( 'font-size' ) ).to.equal( '12px' ); } ); it( 'should clone custom properties', () => { diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index b8dd636b4..7787ac0c0 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -45,6 +45,6 @@ describe( 'Background styles normalization', () => { styles.setTo( 'background:#f00;' ); expect( styles.toString() ).to.equal( 'background-color:#f00;' ); - expect( styles.getInlineProperty( 'background-color' ) ).to.equal( '#f00' ); + expect( styles.getAsString( 'background-color' ) ).to.equal( '#f00' ); } ); } ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index 99b60d463..b066c7552 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -68,9 +68,9 @@ describe( 'Border styles normalization', () => { 'border-right:1px solid blue;' + 'border-top:1px solid blue;' ); - expect( styles.getInlineProperty( 'border-color' ) ).to.equal( 'blue' ); - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'border-color' ) ).to.equal( 'blue' ); + expect( styles.getAsString( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getAsString( 'border-width' ) ).to.equal( '1px' ); } ); it( 'should output only defined inline styles', () => { @@ -82,7 +82,7 @@ describe( 'Border styles normalization', () => { expect( styles.toString( 'border' ) ).to.equal( 'border-top:blue;' ); // TODO: expect( styles.has( 'border-top-color' ) ).to.be.true; - // expect( styles.getInlineProperty( 'border-top-color' ) ).to.equal( 'blue' ); + // expect( styles.getAsString( 'border-top-color' ) ).to.equal( 'blue' ); } ); it( 'should output inline shorthand rules #2', () => { @@ -95,10 +95,10 @@ describe( 'Border styles normalization', () => { 'border-top:7px dotted #ccc;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); + expect( styles.getAsString( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); + expect( styles.getAsString( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); } ); it( 'should parse border + border-position(only color defined)', () => { @@ -142,10 +142,10 @@ describe( 'Border styles normalization', () => { 'border-right:1px solid blue;' + 'border-top:7px dotted #ccc;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); + expect( styles.getAsString( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); + expect( styles.getAsString( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); } ); it( 'should output', () => { @@ -159,14 +159,14 @@ describe( 'Border styles normalization', () => { 'border-top:1px solid;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.be.undefined; + expect( styles.getAsString( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getAsString( 'border-width' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'border-top' ) ).to.equal( '1px solid' ); + expect( styles.getAsString( 'border-right' ) ).to.equal( '1px solid' ); + expect( styles.getAsString( 'border-bottom' ) ).to.equal( '1px solid' ); + expect( styles.getAsString( 'border-left' ) ).to.equal( '1px solid' ); } ); it( 'should output border with only style shorthand (style)', () => { @@ -178,14 +178,14 @@ describe( 'Border styles normalization', () => { 'border-right:solid;' + 'border-top:solid;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-style' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-width' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( 'solid' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'solid' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.be.undefined; + expect( styles.getAsString( 'border-style' ) ).to.equal( 'solid' ); + expect( styles.getAsString( 'border-width' ) ).to.be.undefined; + expect( styles.getAsString( 'border-top' ) ).to.equal( 'solid' ); + expect( styles.getAsString( 'border-right' ) ).to.equal( 'solid' ); + expect( styles.getAsString( 'border-bottom' ) ).to.equal( 'solid' ); + expect( styles.getAsString( 'border-left' ) ).to.equal( 'solid' ); } ); it( 'should output border with only style shorthand (color)', () => { @@ -197,14 +197,14 @@ describe( 'Border styles normalization', () => { 'border-right:#f00;' + 'border-top:#f00;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.equal( '#f00' ); - expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-width' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '#f00' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '#f00' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '#f00' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '#f00' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.equal( '#f00' ); + expect( styles.getAsString( 'border-style' ) ).to.be.undefined; + expect( styles.getAsString( 'border-width' ) ).to.be.undefined; + expect( styles.getAsString( 'border-top' ) ).to.equal( '#f00' ); + expect( styles.getAsString( 'border-right' ) ).to.equal( '#f00' ); + expect( styles.getAsString( 'border-bottom' ) ).to.equal( '#f00' ); + expect( styles.getAsString( 'border-left' ) ).to.equal( '#f00' ); } ); it( 'should output border with only style shorthand (width)', () => { @@ -216,14 +216,14 @@ describe( 'Border styles normalization', () => { 'border-right:1px;' + 'border-top:1px;' ); - expect( styles.getInlineProperty( 'border' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-color' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-style' ) ).to.be.undefined; - expect( styles.getInlineProperty( 'border-width' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.be.undefined; + expect( styles.getAsString( 'border-style' ) ).to.be.undefined; + expect( styles.getAsString( 'border-width' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'border-top' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'border-right' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'border-bottom' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'border-left' ) ).to.equal( '1px' ); } ); describe( 'normalized values getters', () => { @@ -256,25 +256,25 @@ describe( 'Border styles normalization', () => { it( 'should output border-top', () => { styles.setTo( 'border:1px solid #f00' ); - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( '1px solid #f00' ); + expect( styles.getAsString( 'border-top' ) ).to.equal( '1px solid #f00' ); } ); it( 'should output border-right', () => { styles.setTo( 'border:1px solid #f00' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '1px solid #f00' ); + expect( styles.getAsString( 'border-right' ) ).to.equal( '1px solid #f00' ); } ); it( 'should output border-bottom', () => { styles.setTo( 'border:1px solid #f00' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '1px solid #f00' ); + expect( styles.getAsString( 'border-bottom' ) ).to.equal( '1px solid #f00' ); } ); it( 'should output border-left', () => { styles.setTo( 'border:1px solid #f00' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( '1px solid #f00' ); + expect( styles.getAsString( 'border-left' ) ).to.equal( '1px solid #f00' ); } ); } ); @@ -597,10 +597,10 @@ describe( 'Border styles normalization', () => { 'border-right:3.0pt dotted #FFC000;' + 'border-top:none;' ); - expect( styles.getInlineProperty( 'border-top' ) ).to.equal( 'none' ); - expect( styles.getInlineProperty( 'border-right' ) ).to.equal( '3.0pt dotted #FFC000' ); - expect( styles.getInlineProperty( 'border-bottom' ) ).to.equal( '3.0pt dotted #FFC000' ); - expect( styles.getInlineProperty( 'border-left' ) ).to.equal( 'none' ); + expect( styles.getAsString( 'border-top' ) ).to.equal( 'none' ); + expect( styles.getAsString( 'border-right' ) ).to.equal( '3.0pt dotted #FFC000' ); + expect( styles.getAsString( 'border-bottom' ) ).to.equal( '3.0pt dotted #FFC000' ); + expect( styles.getAsString( 'border-left' ) ).to.equal( 'none' ); } ); } ); diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index d37cc9637..6d6cc3122 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -63,66 +63,66 @@ describe( 'Margin styles normalizer', () => { styles.setTo( 'margin:1px;' ); expect( styles.toString() ).to.equal( 'margin:1px;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-left' ) ).to.equal( '1px' ); } ); it( 'should output inline style (2 values defined)', () => { styles.setTo( 'margin:1px .34cm;' ); expect( styles.toString() ).to.equal( 'margin:1px .34cm;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '.34cm' ); + expect( styles.getAsString( 'margin' ) ).to.equal( '1px .34cm' ); + expect( styles.getAsString( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getAsString( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-left' ) ).to.equal( '.34cm' ); } ); it( 'should output inline style (3 values defined)', () => { styles.setTo( 'margin:1px .34cm 90.1rem;' ); expect( styles.toString() ).to.equal( 'margin:1px .34cm 90.1rem;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '.34cm' ); + expect( styles.getAsString( 'margin' ) ).to.equal( '1px .34cm 90.1rem' ); + expect( styles.getAsString( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getAsString( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getAsString( 'margin-left' ) ).to.equal( '.34cm' ); } ); it( 'should output inline style (3 values defined, only last different)', () => { styles.setTo( 'margin:1px 1px 90.1rem;' ); expect( styles.toString() ).to.equal( 'margin:1px 1px 90.1rem;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin' ) ).to.equal( '1px 1px 90.1rem' ); + expect( styles.getAsString( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getAsString( 'margin-left' ) ).to.equal( '1px' ); } ); it( 'should output inline style (4 values defined)', () => { styles.setTo( 'margin:1px .34cm 90.1rem thick;' ); expect( styles.toString() ).to.equal( 'margin:1px .34cm 90.1rem thick;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '.34cm' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '90.1rem' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( 'thick' ); + expect( styles.getAsString( 'margin' ) ).to.equal( '1px .34cm 90.1rem thick' ); + expect( styles.getAsString( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-right' ) ).to.equal( '.34cm' ); + expect( styles.getAsString( 'margin-bottom' ) ).to.equal( '90.1rem' ); + expect( styles.getAsString( 'margin-left' ) ).to.equal( 'thick' ); } ); it( 'should output inline style (4 values defined, only last different)', () => { styles.setTo( 'margin:1px 1px 1px thick;' ); expect( styles.toString() ).to.equal( 'margin:1px 1px 1px thick;' ); - expect( styles.getInlineProperty( 'margin' ) ).to.equal( '1px 1px 1px thick' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( 'thick' ); + expect( styles.getAsString( 'margin' ) ).to.equal( '1px 1px 1px thick' ); + expect( styles.getAsString( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-left' ) ).to.equal( 'thick' ); } ); describe( 'margin-*', () => { @@ -152,28 +152,28 @@ describe( 'Margin styles normalizer', () => { styles.setTo( 'margin-top:1px;' ); expect( styles.toString() ).to.equal( 'margin-top:1px;' ); - expect( styles.getInlineProperty( 'margin-top' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-top' ) ).to.equal( '1px' ); } ); it( 'should output margin-right', () => { styles.setTo( 'margin-right:1px;' ); expect( styles.toString() ).to.equal( 'margin-right:1px;' ); - expect( styles.getInlineProperty( 'margin-right' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-right' ) ).to.equal( '1px' ); } ); it( 'should output margin-bottom', () => { styles.setTo( 'margin-bottom:1px;' ); expect( styles.toString() ).to.equal( 'margin-bottom:1px;' ); - expect( styles.getInlineProperty( 'margin-bottom' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-bottom' ) ).to.equal( '1px' ); } ); it( 'should output margin-left', () => { styles.setTo( 'margin-left:1px;' ); expect( styles.toString() ).to.equal( 'margin-left:1px;' ); - expect( styles.getInlineProperty( 'margin-left' ) ).to.equal( '1px' ); + expect( styles.getAsString( 'margin-left' ) ).to.equal( '1px' ); } ); } ); } ); diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index 94b469490..f7b920178 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -72,46 +72,46 @@ describe( 'StylesMap', () => { it( 'should work with both types of quotes and ignore values inside quotes', () => { stylesMap.setTo( 'background-image:url("im;color:g.jpg")' ); - expect( stylesMap.getInlineProperty( 'background-image' ) ).to.equal( 'url("im;color:g.jpg")' ); + expect( stylesMap.getAsString( 'background-image' ) ).to.equal( 'url("im;color:g.jpg")' ); stylesMap.setTo( 'background-image:url(\'im;color:g.jpg\')' ); - expect( stylesMap.getInlineProperty( 'background-image' ) ).to.equal( 'url(\'im;color:g.jpg\')' ); + expect( stylesMap.getAsString( 'background-image' ) ).to.equal( 'url(\'im;color:g.jpg\')' ); } ); it( 'should not be confused by whitespaces', () => { stylesMap.setTo( '\ncolor:\n red ' ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'red' ); + expect( stylesMap.getAsString( 'color' ) ).to.equal( 'red' ); } ); it( 'should not be confused by duplicated semicolon', () => { stylesMap.setTo( 'color: red;; display: inline' ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'red' ); - expect( stylesMap.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + expect( stylesMap.getAsString( 'color' ) ).to.equal( 'red' ); + expect( stylesMap.getAsString( 'display' ) ).to.equal( 'inline' ); } ); it( 'should not throw when value is missing', () => { stylesMap.setTo( 'color:; display: inline' ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( '' ); - expect( stylesMap.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + expect( stylesMap.getAsString( 'color' ) ).to.equal( '' ); + expect( stylesMap.getAsString( 'display' ) ).to.equal( 'inline' ); } ); it( 'should not throw when colon is duplicated', () => { stylesMap.setTo( 'color:: red; display: inline' ); // The result makes no sense, but here we just check that the algorithm doesn't crash. - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( ': red' ); - expect( stylesMap.getInlineProperty( 'display' ) ).to.equal( 'inline' ); + expect( stylesMap.getAsString( 'color' ) ).to.equal( ': red' ); + expect( stylesMap.getAsString( 'display' ) ).to.equal( 'inline' ); } ); it( 'should not throw when random stuff passed', () => { stylesMap.setTo( 'color: red;:; ;;" ": display: inline; \'aaa;:' ); // The result makes no sense, but here we just check that the algorithm doesn't crash. - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'red' ); - expect( stylesMap.getInlineProperty( 'display' ) ).to.be.undefined; + expect( stylesMap.getAsString( 'color' ) ).to.equal( 'red' ); + expect( stylesMap.getAsString( 'display' ) ).to.be.undefined; } ); } ); } ); @@ -128,11 +128,11 @@ describe( 'StylesMap', () => { } ); } ); - describe( 'getInlineProperty', () => { + describe( 'getAsString()', () => { it( 'should return empty string for missing shorthand', () => { stylesMap.setTo( 'margin-top:1px' ); - expect( stylesMap.getInlineProperty( 'margin' ) ).to.be.undefined; + expect( stylesMap.getAsString( 'margin' ) ).to.be.undefined; } ); } ); @@ -176,37 +176,37 @@ describe( 'StylesMap', () => { it( 'should insert new property (empty styles)', () => { stylesMap.set( 'color', 'blue' ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + expect( stylesMap.getAsString( 'color' ) ).to.equal( 'blue' ); } ); it( 'should insert new property (other properties are set)', () => { stylesMap.setTo( 'margin: 1px;' ); stylesMap.set( 'color', 'blue' ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + expect( stylesMap.getAsString( 'color' ) ).to.equal( 'blue' ); } ); it( 'should overwrite property', () => { stylesMap.setTo( 'color: red;' ); stylesMap.set( 'color', 'blue' ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); + expect( stylesMap.getAsString( 'color' ) ).to.equal( 'blue' ); } ); it( 'should set multiple styles by providing an object', () => { stylesMap.setTo( 'color: red;' ); stylesMap.set( { color: 'blue', foo: '1px' } ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.equal( 'blue' ); - expect( stylesMap.getInlineProperty( 'foo-top' ) ).to.equal( '1px' ); + expect( stylesMap.getAsString( 'color' ) ).to.equal( 'blue' ); + expect( stylesMap.getAsString( 'foo-top' ) ).to.equal( '1px' ); } ); it( 'should set object property', () => { stylesMap.setTo( 'foo:1px;' ); stylesMap.set( 'foo', { right: '2px' } ); - expect( stylesMap.getInlineProperty( 'foo-left' ) ).to.equal( '1px' ); - expect( stylesMap.getInlineProperty( 'foo-right' ) ).to.equal( '2px' ); + expect( stylesMap.getAsString( 'foo-left' ) ).to.equal( '1px' ); + expect( stylesMap.getAsString( 'foo-right' ) ).to.equal( '2px' ); } ); } ); @@ -214,14 +214,14 @@ describe( 'StylesMap', () => { it( 'should do nothing if property is not set', () => { stylesMap.remove( 'color' ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.be.undefined; + expect( stylesMap.getAsString( 'color' ) ).to.equal( '' ); } ); it( 'should insert new property (other properties are set)', () => { stylesMap.setTo( 'color:blue' ); stylesMap.remove( 'color' ); - expect( stylesMap.getInlineProperty( 'color' ) ).to.be.undefined; + expect( stylesMap.getAsString( 'color' ) ).to.equal( '' ); } ); it( 'should remove normalized property', () => { @@ -229,7 +229,7 @@ describe( 'StylesMap', () => { stylesMap.remove( 'margin-top' ); - expect( stylesMap.getInlineProperty( 'margin-top' ) ).to.be.undefined; + expect( stylesMap.getAsString( 'margin-top' ) ).to.be.undefined; } ); } ); From affc2283718422482c633066fca7f169744b6305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 16:22:00 +0100 Subject: [PATCH 113/142] Add docs for StylesMap._styles private property. --- src/view/stylesmap.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 701280ff9..06ebaf517 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -20,6 +20,12 @@ export default class StylesMap { */ constructor( styleProcessor ) { /** + * Keeps and internal representation of styles map. Normalized styles are kept as object tree to allow unified modification and + * value access model using lodash's get, set, unset, etc methods. + * + * When no style processor rules are defined the it acts as simple key-value storage. + * + * @type {Object} * @private */ this._styles = {}; From 611daa690354dc25adcb33486a0efb4ebd5e13f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 16:36:14 +0100 Subject: [PATCH 114/142] Reorder the static methods and getters of StylesMap. --- src/view/document.js | 2 +- src/view/stylesmap.js | 42 ++++++++++++++++++--------- tests/view/styles/backgroundstyles.js | 8 +++-- tests/view/styles/borderstyles.js | 10 +++++-- tests/view/styles/marginstyles.js | 10 +++++-- tests/view/styles/paddingstyles.js | 10 +++++-- tests/view/stylesmap.js | 3 +- 7 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/view/document.js b/src/view/document.js index b618f9d36..32f047c4e 100644 --- a/src/view/document.js +++ b/src/view/document.js @@ -162,7 +162,7 @@ export default class Document { } addStyleProcessorRules( callback ) { - callback( StylesMap.processor ); + callback( StylesMap._styleProcessor ); } /** diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 06ebaf517..d750a9fb9 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -18,7 +18,7 @@ export default class StylesMap { /** * Creates Styles instance. */ - constructor( styleProcessor ) { + constructor() { /** * Keeps and internal representation of styles map. Normalized styles are kept as object tree to allow unified modification and * value access model using lodash's get, set, unset, etc methods. @@ -35,7 +35,7 @@ export default class StylesMap { // that two editors are connected through single style processor instance. Object.defineProperty( this, '_styleProcessor', { get() { - return styleProcessor || StylesMap.processor; + return StylesMap._styleProcessor; }, enumerable: false } ); @@ -63,18 +63,6 @@ export default class StylesMap { return this.getStyleNames().length; } - static get processor() { - if ( !this._processor ) { - this._processor = new StylesProcessor(); - } - - return this._processor; - } - - static setProcessor( processor ) { - this._processor = processor; - } - /** * Set styles map to a new value. * @@ -363,6 +351,32 @@ export default class StylesMap { return parsed; } + + /** + * Returns global StylesProcessor instance. + * + * @returns {module:engine/view/stylesmap~StylesProcessor} + * @private + */ + static get _styleProcessor() { + if ( !this._processor ) { + this._processor = new StylesProcessor(); + } + + return this._processor; + } + + /** + * Set new StylesProcessor instance. + * + * This is an internal method used mostly in tests. + * + * @param processor + * @protected + */ + static _setProcessor( processor ) { + this._processor = processor; + } } export class StylesProcessor { diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index 7787ac0c0..876c7226e 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -9,10 +9,14 @@ import { addBackgroundStylesProcessor } from '../../../src/view/styles/backgroun describe( 'Background styles normalization', () => { let styles; - beforeEach( () => { + before( () => { const stylesProcessor = new StylesProcessor(); + StylesMap._setProcessor( stylesProcessor ); addBackgroundStylesProcessor( stylesProcessor ); - styles = new StylesMap( stylesProcessor ); + } ); + + beforeEach( () => { + styles = new StylesMap(); } ); it( 'should normalize background', () => { diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index b066c7552..483f62b73 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -9,10 +9,14 @@ import { addBorderStylesProcessor } from '../../../src/view/styles/borderstyles' describe( 'Border styles normalization', () => { let styles; + before( () => { + const stylesProcessor = new StylesProcessor(); + StylesMap._setProcessor( stylesProcessor ); + addBorderStylesProcessor( stylesProcessor ); + } ); + beforeEach( () => { - const converter = new StylesProcessor(); - addBorderStylesProcessor( converter ); - styles = new StylesMap( converter ); + styles = new StylesMap(); } ); it( 'should parse border shorthand', () => { diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index 6d6cc3122..a33574455 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -9,10 +9,14 @@ import { addMarginStylesProcessor } from '../../../src/view/styles/marginstyles' describe( 'Margin styles normalizer', () => { let styles; + before( () => { + const stylesProcessor = new StylesProcessor(); + StylesMap._setProcessor( stylesProcessor ); + addMarginStylesProcessor( stylesProcessor ); + } ); + beforeEach( () => { - const converter = new StylesProcessor(); - addMarginStylesProcessor( converter ); - styles = new StylesMap( converter ); + styles = new StylesMap(); } ); it( 'should set all margins (1 value defined)', () => { diff --git a/tests/view/styles/paddingstyles.js b/tests/view/styles/paddingstyles.js index 521e1e50c..0f7745ff8 100644 --- a/tests/view/styles/paddingstyles.js +++ b/tests/view/styles/paddingstyles.js @@ -9,10 +9,14 @@ import { addPaddingStylesProcessor } from '../../../src/view/styles/paddingstyle describe( 'Padding styles normalization', () => { let styles; + before( () => { + const stylesProcessor = new StylesProcessor(); + StylesMap._setProcessor( stylesProcessor ); + addPaddingStylesProcessor( stylesProcessor ); + } ); + beforeEach( () => { - const converter = new StylesProcessor(); - addPaddingStylesProcessor( converter ); - styles = new StylesMap( converter ); + styles = new StylesMap(); } ); it( 'should set all paddings (1 value defined)', () => { diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index f7b920178..70bae1ab0 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -26,7 +26,8 @@ describe( 'StylesMap', () => { stylesProcessor.setReducer( 'foo', getTopRightBottomLeftValueReducer( 'foo' ) ); addPaddingStylesProcessor( stylesProcessor ); - stylesMap = new StylesMap( stylesProcessor ); + StylesMap._setProcessor( stylesProcessor ); + stylesMap = new StylesMap(); } ); describe( 'size getter', () => { From 7fb72d7b9d4677405842cd2671a6d1da83dc7704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 16:50:49 +0100 Subject: [PATCH 115/142] Add docs to StylesProcessor. --- src/view/stylesmap.js | 90 +++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index d750a9fb9..196ee9b47 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -379,7 +379,15 @@ export default class StylesMap { } } +/** + * Style processor is responsible for writing and reading a normalized styles object. + */ export class StylesProcessor { + /** + * Creates StylesProcessor instance. + * + * @private + */ constructor() { this._normalizers = new Map(); this._extractors = new Map(); @@ -387,23 +395,57 @@ export class StylesProcessor { } /** - * Returns reduced form of style property form normalized object. + * Parse style string value to a normalized object and appends it to styles object. * - * @private - * @param {String} styleName - * @param {Object|String} normalizedValue - * @returns {module:engine/view/stylesmap~PropertyEntry} + * const styles = {}; + * + * stylesProcessor.toNormalizedForm( 'margin', '1px', styles ); + * + * // styles will consist: { margin: { top: '1px', right: '1px', bottom: '1px', left: '1px; } } + * + * *Note*: To define normalizer callbacks use {@link #setNormalizer}. + * + * @param {String} name Name of style property. + * @param {String} propertyValue Value of style property. + * @param {Object} styles Object holding normalized styles. */ - getReducedForm( styleName, normalizedValue ) { - if ( this._reducers.has( styleName ) ) { - const reducer = this._reducers.get( styleName ); + toNormalizedForm( name, propertyValue, styles ) { + if ( isObject( propertyValue ) ) { + appendStyleValue( styles, toPath( name ), propertyValue ); - return reducer( normalizedValue ); + return; } - return [ [ styleName, normalizedValue ] ]; + if ( this._normalizers.has( name ) ) { + const normalizer = this._normalizers.get( name ); + + const { path, value } = normalizer( propertyValue ); + + appendStyleValue( styles, path, value ); + } else { + appendStyleValue( styles, name, propertyValue ); + } } + /** + * Returns a normalized version of a style property. + * const styles = { + * margin: { top: '1px', right: '1px', bottom: '1px', left: '1px; }, + * background: { color: '#f00' } + * }; + * + * stylesProcessor.getNormalized( 'background' ); + * // will return: { color: '#f00' } + * + * stylesProcessor.getNormalized( 'margin-top' ); + * // will return: '1px' + * + * *Note*: In some cases extracting single value requires defining an extractor callback {@link #setExtractor}. + * + * @param {String} name Name of style property. + * @param {Object} styles Object holding normalized styles. + * @returns {*} + */ getNormalized( name, styles ) { if ( !name ) { return merge( {}, styles ); @@ -431,29 +473,20 @@ export class StylesProcessor { } /** - * Parse style property value to a normalized form. + * Returns reduced form of style property form normalized object. * - * @param {String} propertyName Name of style property. - * @param {String} propertyValue Value of style property. - * @param {Object} styles - * @private + * @param {String} name + * @param {Object|String} normalizedValue + * @returns {module:engine/view/stylesmap~PropertyEntry} */ - toNormalizedForm( propertyName, propertyValue, styles ) { - if ( isObject( propertyValue ) ) { - appendStyleValue( styles, toPath( propertyName ), propertyValue ); + getReducedForm( name, normalizedValue ) { + if ( this._reducers.has( name ) ) { + const reducer = this._reducers.get( name ); - return; + return reducer( normalizedValue ); } - if ( this._normalizers.has( propertyName ) ) { - const normalizer = this._normalizers.get( propertyName ); - - const { path, value } = normalizer( propertyValue ); - - appendStyleValue( styles, path, value ); - } else { - appendStyleValue( styles, propertyName, propertyValue ); - } + return [ [ name, normalizedValue ] ]; } setNormalizer( propertyName, callback ) { @@ -546,6 +579,7 @@ function parseInlineStyles( stylesString ) { return stylesMap; } +// Return lodash compatible path from style name. function toPath( name ) { return name.replace( '-', '.' ); } From 400305f5adba9740e156e6d4c49c34c0beae11fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 17:20:15 +0100 Subject: [PATCH 116/142] Simplify StylesConverter.getReducedForm() usage. --- src/view/stylesmap.js | 49 +++++++++++-------------------- tests/view/styles/borderstyles.js | 26 ++++++++-------- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 196ee9b47..61ce00a5f 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -111,23 +111,12 @@ export default class StylesMap { return false; } - const normalized = this._styleProcessor.getNormalized( name, this._styles ); + const styles = this._styleProcessor.getReducedForm( name, this._styles ); - if ( !normalized ) { - // Try return styles set directly - values that are not parsed. - return this._styles[ name ] !== undefined; - } - - if ( isObject( normalized ) ) { - const styles = this._styleProcessor.getReducedForm( name, normalized ); - - const propertyDescriptor = styles.find( ( [ property ] ) => property === name ); + const propertyDescriptor = styles.find( ( [ property ] ) => property === name ); - // Only return a value if it is set; - return Array.isArray( propertyDescriptor ); - } else { - return true; - } + // Only return a value if it is set; + return Array.isArray( propertyDescriptor ); } /** @@ -289,24 +278,20 @@ export default class StylesMap { return ''; } - const normalized = this._styleProcessor.getNormalized( propertyName, this._styles ); - - if ( !normalized ) { + if ( this._styles[ propertyName ] && !isObject( this._styles[ propertyName ] ) ) { // Try return styles set directly - values that are not parsed. return this._styles[ propertyName ]; } - if ( isObject( normalized ) ) { - const styles = this._styleProcessor.getReducedForm( propertyName, normalized ); + const styles = this._styleProcessor.getReducedForm( propertyName, this._styles ); - const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); + const propertyDescriptor = styles.find( ( [ property ] ) => property === propertyName ); - // Only return a value if it is set; - if ( Array.isArray( propertyDescriptor ) ) { - return propertyDescriptor[ 1 ]; - } + // Only return a value if it is set; + if ( Array.isArray( propertyDescriptor ) ) { + return propertyDescriptor[ 1 ]; } else { - return normalized; + return ''; } } @@ -344,9 +329,7 @@ export default class StylesMap { const keys = Object.keys( this._styles ); for ( const key of keys ) { - const normalized = this._styleProcessor.getNormalized( key, this._styles ); - - parsed.push( ...this._styleProcessor.getReducedForm( key, normalized ) ); + parsed.push( ...this._styleProcessor.getReducedForm( key, this._styles ) ); } return parsed; @@ -476,10 +459,12 @@ export class StylesProcessor { * Returns reduced form of style property form normalized object. * * @param {String} name - * @param {Object|String} normalizedValue - * @returns {module:engine/view/stylesmap~PropertyEntry} + * @param {String} name Name of style property. + * @param {Object} styles Object holding normalized styles. */ - getReducedForm( name, normalizedValue ) { + getReducedForm( name, styles ) { + const normalizedValue = this.getNormalized( name, styles ); + if ( this._reducers.has( name ) ) { const reducer = this._reducers.get( name ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index 483f62b73..084bd0e66 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -99,7 +99,7 @@ describe( 'Border styles normalization', () => { 'border-top:7px dotted #ccc;' ); - expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border' ) ).to.equal( '' ); expect( styles.getAsString( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); expect( styles.getAsString( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); expect( styles.getAsString( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); @@ -146,7 +146,7 @@ describe( 'Border styles normalization', () => { 'border-right:1px solid blue;' + 'border-top:7px dotted #ccc;' ); - expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border' ) ).to.equal( '' ); expect( styles.getAsString( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); expect( styles.getAsString( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); expect( styles.getAsString( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); @@ -163,8 +163,8 @@ describe( 'Border styles normalization', () => { 'border-top:1px solid;' ); - expect( styles.getAsString( 'border' ) ).to.be.undefined; - expect( styles.getAsString( 'border-color' ) ).to.be.undefined; + expect( styles.getAsString( 'border' ) ).to.equal( '' ); + expect( styles.getAsString( 'border-color' ) ).to.equal( '' ); expect( styles.getAsString( 'border-style' ) ).to.equal( 'solid' ); expect( styles.getAsString( 'border-width' ) ).to.equal( '1px' ); expect( styles.getAsString( 'border-top' ) ).to.equal( '1px solid' ); @@ -182,10 +182,10 @@ describe( 'Border styles normalization', () => { 'border-right:solid;' + 'border-top:solid;' ); - expect( styles.getAsString( 'border' ) ).to.be.undefined; - expect( styles.getAsString( 'border-color' ) ).to.be.undefined; + expect( styles.getAsString( 'border' ) ).to.equal( '' ); + expect( styles.getAsString( 'border-color' ) ).to.equal( '' ); expect( styles.getAsString( 'border-style' ) ).to.equal( 'solid' ); - expect( styles.getAsString( 'border-width' ) ).to.be.undefined; + expect( styles.getAsString( 'border-width' ) ).to.equal( '' ); expect( styles.getAsString( 'border-top' ) ).to.equal( 'solid' ); expect( styles.getAsString( 'border-right' ) ).to.equal( 'solid' ); expect( styles.getAsString( 'border-bottom' ) ).to.equal( 'solid' ); @@ -201,10 +201,10 @@ describe( 'Border styles normalization', () => { 'border-right:#f00;' + 'border-top:#f00;' ); - expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border' ) ).to.equal( '' ); expect( styles.getAsString( 'border-color' ) ).to.equal( '#f00' ); - expect( styles.getAsString( 'border-style' ) ).to.be.undefined; - expect( styles.getAsString( 'border-width' ) ).to.be.undefined; + expect( styles.getAsString( 'border-style' ) ).to.equal( '' ); + expect( styles.getAsString( 'border-width' ) ).to.equal( '' ); expect( styles.getAsString( 'border-top' ) ).to.equal( '#f00' ); expect( styles.getAsString( 'border-right' ) ).to.equal( '#f00' ); expect( styles.getAsString( 'border-bottom' ) ).to.equal( '#f00' ); @@ -220,9 +220,9 @@ describe( 'Border styles normalization', () => { 'border-right:1px;' + 'border-top:1px;' ); - expect( styles.getAsString( 'border' ) ).to.be.undefined; - expect( styles.getAsString( 'border-color' ) ).to.be.undefined; - expect( styles.getAsString( 'border-style' ) ).to.be.undefined; + expect( styles.getAsString( 'border' ) ).to.equal( '' ); + expect( styles.getAsString( 'border-color' ) ).to.equal( '' ); + expect( styles.getAsString( 'border-style' ) ).to.equal( '' ); expect( styles.getAsString( 'border-width' ) ).to.equal( '1px' ); expect( styles.getAsString( 'border-top' ) ).to.equal( '1px' ); expect( styles.getAsString( 'border-right' ) ).to.equal( '1px' ); From b680fe2ee34a62e86e7df789e44f15e4ce26f4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 14 Jan 2020 17:30:38 +0100 Subject: [PATCH 117/142] Revert docs for setting object style. --- src/view/stylesmap.js | 11 ++++++++--- tests/view/styles/borderstyles.js | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 61ce00a5f..e92d23687 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -150,16 +150,21 @@ export default class StylesMap { * left: '2px', * }; * + * This method allows also to set normalized values directly: + * + * styles.set( 'border-color', { top: 'blue' } ); + * styles.set( 'margin', { right: '2em' } ); + * * @param {String|Object} nameOrObject Style property name or object with multiple properties. - * @param {String} value Value to set. + * @param {String|Object} valueOrObject Value to set. */ - set( nameOrObject, value ) { + set( nameOrObject, valueOrObject ) { if ( isObject( nameOrObject ) ) { for ( const [ key, value ] of Object.entries( nameOrObject ) ) { this._styleProcessor.toNormalizedForm( key, value, this._styles ); } } else { - this._styleProcessor.toNormalizedForm( nameOrObject, value, this._styles ); + this._styleProcessor.toNormalizedForm( nameOrObject, valueOrObject, this._styles ); } } diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index 084bd0e66..7d1ccbe10 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -85,8 +85,8 @@ describe( 'Border styles normalization', () => { } ); expect( styles.toString( 'border' ) ).to.equal( 'border-top:blue;' ); - // TODO: expect( styles.has( 'border-top-color' ) ).to.be.true; - // expect( styles.getAsString( 'border-top-color' ) ).to.equal( 'blue' ); + expect( styles.has( 'border-top-color' ) ).to.be.true; + expect( styles.getAsString( 'border-top-color' ) ).to.equal( 'blue' ); } ); it( 'should output inline shorthand rules #2', () => { From 065dfccd05e0f43441e7f406e0aba277617069e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 15 Jan 2020 10:31:39 +0100 Subject: [PATCH 118/142] StylesMap.getAsString() will return undefined for missing properties. --- src/view/element.js | 2 +- src/view/stylesmap.js | 23 ++++++++++++++++------- tests/view/styles/borderstyles.js | 26 +++++++++++++------------- tests/view/stylesmap.js | 4 ++-- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/view/element.js b/src/view/element.js index ee24473fb..fd55961da 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -404,7 +404,7 @@ export default class Element extends Node { * @returns {String|undefined} */ getStyle( property ) { - return this._styles.has( property ) ? this._styles.getAsString( property ) : undefined; + return this._styles.getAsString( property ); } /** diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index e92d23687..9af3060a7 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -47,7 +47,10 @@ export default class StylesMap { * @returns {Boolean} */ get isEmpty() { - return !Object.entries( this._styles ).length; + const entries = Object.entries( this._styles ); + const from = Array.from( entries ); + + return !from.length; } /** @@ -73,7 +76,9 @@ export default class StylesMap { setTo( inlineStyle ) { this.clear(); - for ( const [ key, value ] of Array.from( parseInlineStyles( inlineStyle ).entries() ) ) { + const parsedStyles = Array.from( parseInlineStyles( inlineStyle ).entries() ); + + for ( const [ key, value ] of parsedStyles ) { this._styleProcessor.toNormalizedForm( key, value, this._styles ); } } @@ -261,7 +266,7 @@ export default class StylesMap { } /** - * Returns property as a value string. + * Returns property as a value string or undefined if property is not set. * * // Enable 'margin' shorthand processing: * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); @@ -280,7 +285,7 @@ export default class StylesMap { */ getAsString( propertyName ) { if ( this.isEmpty ) { - return ''; + return; } if ( this._styles[ propertyName ] && !isObject( this._styles[ propertyName ] ) ) { @@ -295,8 +300,6 @@ export default class StylesMap { // Only return a value if it is set; if ( Array.isArray( propertyDescriptor ) ) { return propertyDescriptor[ 1 ]; - } else { - return ''; } } @@ -439,7 +442,8 @@ export class StylesProcessor { return merge( {}, styles ); } - if ( styles[ name ] ) { + // Might be empty string. + if ( styles[ name ] !== undefined ) { return styles[ name ]; } @@ -470,6 +474,11 @@ export class StylesProcessor { getReducedForm( name, styles ) { const normalizedValue = this.getNormalized( name, styles ); + // Might be empty string. + if ( normalizedValue === undefined ) { + return []; + } + if ( this._reducers.has( name ) ) { const reducer = this._reducers.get( name ); diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index 7d1ccbe10..a9fc25f4a 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -99,7 +99,7 @@ describe( 'Border styles normalization', () => { 'border-top:7px dotted #ccc;' ); - expect( styles.getAsString( 'border' ) ).to.equal( '' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; expect( styles.getAsString( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); expect( styles.getAsString( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); expect( styles.getAsString( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); @@ -146,7 +146,7 @@ describe( 'Border styles normalization', () => { 'border-right:1px solid blue;' + 'border-top:7px dotted #ccc;' ); - expect( styles.getAsString( 'border' ) ).to.equal( '' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; expect( styles.getAsString( 'border-color' ) ).to.equal( '#ccc blue blue #665511' ); expect( styles.getAsString( 'border-style' ) ).to.equal( 'dotted solid solid dashed' ); expect( styles.getAsString( 'border-width' ) ).to.equal( '7px 1px 1px 2.7em' ); @@ -163,8 +163,8 @@ describe( 'Border styles normalization', () => { 'border-top:1px solid;' ); - expect( styles.getAsString( 'border' ) ).to.equal( '' ); - expect( styles.getAsString( 'border-color' ) ).to.equal( '' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.be.undefined; expect( styles.getAsString( 'border-style' ) ).to.equal( 'solid' ); expect( styles.getAsString( 'border-width' ) ).to.equal( '1px' ); expect( styles.getAsString( 'border-top' ) ).to.equal( '1px solid' ); @@ -182,10 +182,10 @@ describe( 'Border styles normalization', () => { 'border-right:solid;' + 'border-top:solid;' ); - expect( styles.getAsString( 'border' ) ).to.equal( '' ); - expect( styles.getAsString( 'border-color' ) ).to.equal( '' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.be.undefined; expect( styles.getAsString( 'border-style' ) ).to.equal( 'solid' ); - expect( styles.getAsString( 'border-width' ) ).to.equal( '' ); + expect( styles.getAsString( 'border-width' ) ).to.be.undefined; expect( styles.getAsString( 'border-top' ) ).to.equal( 'solid' ); expect( styles.getAsString( 'border-right' ) ).to.equal( 'solid' ); expect( styles.getAsString( 'border-bottom' ) ).to.equal( 'solid' ); @@ -201,10 +201,10 @@ describe( 'Border styles normalization', () => { 'border-right:#f00;' + 'border-top:#f00;' ); - expect( styles.getAsString( 'border' ) ).to.equal( '' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; expect( styles.getAsString( 'border-color' ) ).to.equal( '#f00' ); - expect( styles.getAsString( 'border-style' ) ).to.equal( '' ); - expect( styles.getAsString( 'border-width' ) ).to.equal( '' ); + expect( styles.getAsString( 'border-style' ) ).to.be.undefined; + expect( styles.getAsString( 'border-width' ) ).to.be.undefined; expect( styles.getAsString( 'border-top' ) ).to.equal( '#f00' ); expect( styles.getAsString( 'border-right' ) ).to.equal( '#f00' ); expect( styles.getAsString( 'border-bottom' ) ).to.equal( '#f00' ); @@ -220,9 +220,9 @@ describe( 'Border styles normalization', () => { 'border-right:1px;' + 'border-top:1px;' ); - expect( styles.getAsString( 'border' ) ).to.equal( '' ); - expect( styles.getAsString( 'border-color' ) ).to.equal( '' ); - expect( styles.getAsString( 'border-style' ) ).to.equal( '' ); + expect( styles.getAsString( 'border' ) ).to.be.undefined; + expect( styles.getAsString( 'border-color' ) ).to.be.undefined; + expect( styles.getAsString( 'border-style' ) ).to.be.undefined; expect( styles.getAsString( 'border-width' ) ).to.equal( '1px' ); expect( styles.getAsString( 'border-top' ) ).to.equal( '1px' ); expect( styles.getAsString( 'border-right' ) ).to.equal( '1px' ); diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index 70bae1ab0..b730661d2 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -215,14 +215,14 @@ describe( 'StylesMap', () => { it( 'should do nothing if property is not set', () => { stylesMap.remove( 'color' ); - expect( stylesMap.getAsString( 'color' ) ).to.equal( '' ); + expect( stylesMap.getAsString( 'color' ) ).to.be.undefined; } ); it( 'should insert new property (other properties are set)', () => { stylesMap.setTo( 'color:blue' ); stylesMap.remove( 'color' ); - expect( stylesMap.getAsString( 'color' ) ).to.equal( '' ); + expect( stylesMap.getAsString( 'color' ) ).to.be.undefined; } ); it( 'should remove normalized property', () => { From 91fcf56b1d71a3117f1729ab3e4d72251cc21dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 15 Jan 2020 12:34:53 +0100 Subject: [PATCH 119/142] Add docs to StylesProcessor. --- src/view/stylesmap.js | 103 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index 9af3060a7..b7addcfb8 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -465,7 +465,30 @@ export class StylesProcessor { } /** - * Returns reduced form of style property form normalized object. + * Returns a reduced form of style property form normalized object. + * + * For default margin reducer, the below code: + * + * stylesProcessor.getReducedForm( 'margin', { + * margin: { top: '1px', right: '1px', bottom: '2px', left: '1px; } + * } ); + * + * will return: + * + * [ + * [ 'margin', '1px 1px 2px' ] + * ] + * + * because it might be represented as a shorthand 'margin' value. However if one of margin long hand values is missing it should return: + * + * [ + * [ 'margin-top', '1px' ], + * [ 'margin-right', '1px' ], + * [ 'margin-bottom', '2px' ] + * // the 'left' value is missing - cannot use 'margin' shorthand. + * ] + * + * *Note*: To define reducer callbacks use {@link #setReducer}. * * @param {String} name * @param {String} name Name of style property. @@ -488,10 +511,88 @@ export class StylesProcessor { return [ [ name, normalizedValue ] ]; } + /** + * Adds a normalizer method for style property. + * + * A normalizer returns describing how the value should be normalized. + * + * For instance 'margin' style is a shorthand for four margin values: + * + * - 'margin-top' + * - 'margin-right' + * - 'margin-bottom' + * - 'margin-left' + * + * and can be written in various ways if some values are equal to others. For instance `'margin: 1px 2em;'` is a shorthand for + * `'margin-top: 1px;margin-right: 2em;margin-bottom: 1px;margin-left: 2em'`. + * + * A normalizer should parse various margin notations as a single object: + * + * const styles = { + * margin: { + * top: '1px', + * right: '2em', + * bottom: '1px', + * left: '2em' + * } + * }; + * + * Thus a normalizer for 'margin' style should return an object defining style path and value to store: + * + * const returnValue = { + * path: 'margin', + * value: { + * top: '1px', + * right: '2em', + * bottom: '1px', + * left: '2em' + * } + * }; + * + * Additionally to fully support all margin notations there should be also defined 4 normalizers for longhand margin notations. Below + * is an example for 'margin-top' style property normalizer: + * + * stylesProcessor.setNormalizer( 'margin-top', valueString => { + * return { + * path: 'margin.top', + * value: valueString + * } + * } ); + * + * @param {String} propertyName + * @param {Function} callback + */ setNormalizer( propertyName, callback ) { this._normalizers.set( propertyName, callback ); } + /** + * Most normalized style values are stored as one level objects. It is assumed that `'margin-top'` style will be stored as: + * + * const styles = { + * margin: { + * top: 'value' + * } + * } + * + * However, some styles can have conflicting notations and thus it might be harder to extract a style value from shorthand. For instance + * the 'border-top-style' can be defined using `'border-top:solid'`, `'border-style:solid none none none'` or by `'border:solid'` + * shorthands. The default border styles processors stores styles as: + * + * const styles = { + * border: { + * style: { + * top: 'solid' + * } + * } + * } + * + * as it is better to modify border style independently from other values. On the other part the output of the border might be + * desired as `border-top`, `border-left`, etc notation. + * + * @param propertyName + * @param callbackOrPath + */ setExtractor( propertyName, callbackOrPath ) { this._extractors.set( propertyName, callbackOrPath ); } From 6535fc2bf3826dd11334751e799004e25d6374a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 15 Jan 2020 14:09:48 +0100 Subject: [PATCH 120/142] Add docs to StylesMap and styles utils. --- src/view/styles/utils.js | 88 ++++++++++++++++++++++++++++++++++++++-- src/view/stylesmap.js | 65 ++++++++++++++++++++++++----- 2 files changed, 139 insertions(+), 14 deletions(-) diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index f218083ad..ff79cacb1 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -9,42 +9,84 @@ const colorRegExp = /^([#0-9A-Fa-f]{3,9}$|0$|rgba?\(|hsla?\(|[a-zA-Z]+$)/; +/** + * Checks if string contains [color](https://developer.mozilla.org/en-US/docs/Web/CSS/color) CSS value. + * + * @param {String} string + * @returns {Boolean} + */ export function isColor( string ) { return colorRegExp.test( string ); } const lineStyleValues = [ 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset' ]; +/** + * Checks if string contains line style CSS value. + * + * @param {String} string + * @returns {Boolean} + */ export function isLineStyle( string ) { return lineStyleValues.includes( string ); } const lengthRegExp = /^([+-]?[0-9]*[.]?[0-9]+([a-z]+|%)|0)$/; +/** + * Checks if string contains [length](https://developer.mozilla.org/en-US/docs/Web/CSS/length) CSS value. + * + * @param {String} string + * @returns {Boolean} + */ export function isLength( string ) { return lengthRegExp.test( string ); } const repeatValues = [ 'repeat-x', 'repeat-y', 'repeat', 'space', 'round', 'no-repeat' ]; +/** + * Checks if string contains [background repeat](https://developer.mozilla.org/en-US/docs/Web/CSS/background-repeat) CSS value. + * + * @param {String} string + * @returns {Boolean} + */ export function isRepeat( string ) { return repeatValues.includes( string ); } const positionValues = [ 'center', 'top', 'bottom', 'left', 'right' ]; +/** + * Checks if string contains [background position](https://developer.mozilla.org/en-US/docs/Web/CSS/background-position) CSS value. + * + * @param {String} string + * @returns {Boolean} + */ export function isPosition( string ) { return positionValues.includes( string ); } const attachmentValues = [ 'fixed', 'scroll', 'local' ]; +/** + * Checks if string contains [background attachment](https://developer.mozilla.org/en-US/docs/Web/CSS/background-attachment) CSS value. + * + * @param {String} string + * @returns {Boolean} + */ export function isAttachment( string ) { return attachmentValues.includes( string ); } const urlRegExp = /^url\(/; +/** + * Checks if string contains [URL](https://developer.mozilla.org/en-US/docs/Web/CSS/url) CSS value. + * + * @param {String} string + * @returns {Boolean} + */ export function isURL( string ) { return urlRegExp.test( string ); } @@ -64,6 +106,15 @@ export function getTopRightBottomLeftValues( value = '' ) { return { top, bottom, right, left }; } +/** + * Default reducer for CSS properties that concerns edges of a box + * [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) notations: + * + * stylesProcessor.setReducer( 'padding', getTopRightBottomLeftValueReducer( 'padding' ) ); + * + * @param {String} styleShorthand + * @returns {Function} + */ export function getTopRightBottomLeftValueReducer( styleShorthand ) { return value => { const { top, right, bottom, left } = ( value || {} ); @@ -94,7 +145,16 @@ export function getTopRightBottomLeftValueReducer( styleShorthand ) { }; } -export function getTopRightBottomLeftShorthandValue( { left, right, top, bottom } ) { +/** + * Returns a proper 1-to-4 value of a CSS [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) notation. + * + * getTopRightBottomLeftShorthandValue( { top: '1px', right: '1px', bottom: '2px', left: '1px' } ); + * // will return '1px 1px 2px' + * + * @param {String} styleShorthand + * @returns {Function} + */ +export function getTopRightBottomLeftShorthandValue( { top, right, bottom, left } ) { const out = []; if ( left !== right ) { @@ -110,15 +170,35 @@ export function getTopRightBottomLeftShorthandValue( { left, right, top, bottom return out.join( ' ' ); } -export function getPositionShorthandNormalizer( longhand ) { +/** + * Creates a normalizer for a [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) 1-to-4 value. + * + * stylesProcessor.setNormalizer( 'margin', getPositionShorthandNormalizer( 'margin' ) ); + * + * @param {String} shorthand + * @returns {Function} + */ +export function getPositionShorthandNormalizer( shorthand ) { return value => { return { - path: longhand, + path: shorthand, value: getTopRightBottomLeftValues( value ) }; }; } +/** + * Parses parts of a 1-to-4 value notation - handles some CSS values with spaces (like RGB()). + * + * getShorthandValues( 'red blue RGB(0, 0, 0)'); + * // will return [ 'red', 'blue', 'RGB(0, 0, 0)' ] + * + * @param {String} string + * @returns {Array.} + */ export function getShorthandValues( string ) { - return string.replace( /, /g, ',' ).split( ' ' ).map( string => string.replace( /,/g, ', ' ) ); + return string + .replace( /, /g, ',' ) // Exclude comma from spaces evaluation as values are separated by spaces. + .split( ' ' ) + .map( string => string.replace( /,/g, ', ' ) ); // Restore original notation. } diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index b7addcfb8..deb519194 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -512,7 +512,7 @@ export class StylesProcessor { } /** - * Adds a normalizer method for style property. + * Adds a normalizer method for a style property. * * A normalizer returns describing how the value should be normalized. * @@ -559,14 +559,16 @@ export class StylesProcessor { * } * } ); * - * @param {String} propertyName + * @param {String} name * @param {Function} callback */ - setNormalizer( propertyName, callback ) { - this._normalizers.set( propertyName, callback ); + setNormalizer( name, callback ) { + this._normalizers.set( name, callback ); } /** + * Adds a extractor callback for a style property. + * * Most normalized style values are stored as one level objects. It is assumed that `'margin-top'` style will be stored as: * * const styles = { @@ -590,15 +592,58 @@ export class StylesProcessor { * as it is better to modify border style independently from other values. On the other part the output of the border might be * desired as `border-top`, `border-left`, etc notation. * - * @param propertyName - * @param callbackOrPath + * In the above example a reducer should return a side border value that combines style, color and width: + * + * stylesConverter.setExtractor( 'border-top', styles => { + * return { + * color: styles.border.color.top, + * style: styles.border.style.top, + * width: styles.border.width.top + * } + * } ); + * + * @param {String} name + * @param {Function|String} callbackOrPath Callback that return a requested value or path string for single values. */ - setExtractor( propertyName, callbackOrPath ) { - this._extractors.set( propertyName, callbackOrPath ); + setExtractor( name, callbackOrPath ) { + this._extractors.set( name, callbackOrPath ); } - setReducer( propertyName, callback ) { - this._reducers.set( propertyName, callback ); + /** + * Adds a reducer callback for a style property. + * + * Reducer returns a minimal notation for given style name. For longhand properties it is not required to write a reducer as + * by default the direct value from style path is taken. + * + * For shorthand styles a reducer should return minimal style notation either by returning single name-value tuple or multiple tuples + * if a shorthand cannot be used. For instance for a margin shorthand a reducer might return: + * + * const marginShortHandTuple = [ + * [ 'margin', '1px 1px 2px' ] + * ]; + * + * or a longhand tuples for defined values: + * + * // Considering margin.bottom and margin.left are undefined. + * const marginLonghandsTuples = [ + * [ 'margin-top', '1px' ], + * [ 'margin-right', '1px' ] + * ]; + * + * A reducer obtains a normalized style value: + * + * // Simplified reducer that always outputs 4 values which are always present: + * stylesProcessor.setReducer( 'margin', margin => { + * return [ + * [ 'margin', `${ margin.top } ${ margin.right } ${ margin.bottom } ${ margin.left }` ] + * ] + * } ); + * + * @param {String} name + * @param {Function} callback + */ + setReducer( name, callback ) { + this._reducers.set( name, callback ); } } From a70c8ff48ad19b13d7ad44ca89993a27ba726432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 15 Jan 2020 14:26:27 +0100 Subject: [PATCH 121/142] Refactor and add docs for styles processing rules. --- src/view/element.js | 2 +- .../styles/{backgroundstyles.js => background.js} | 13 ++++++++++--- src/view/styles/{borderstyles.js => border.js} | 13 ++++++++++--- src/view/styles/{marginstyles.js => margin.js} | 13 ++++++++++--- src/view/styles/{paddingstyles.js => padding.js} | 13 ++++++++++--- src/view/stylesmap.js | 12 ++++++------ tests/view/styles/backgroundstyles.js | 4 ++-- tests/view/styles/borderstyles.js | 4 ++-- tests/view/styles/marginstyles.js | 4 ++-- tests/view/styles/paddingstyles.js | 4 ++-- tests/view/stylesmap.js | 4 ++-- 11 files changed, 57 insertions(+), 29 deletions(-) rename src/view/styles/{backgroundstyles.js => background.js} (79%) rename src/view/styles/{borderstyles.js => border.js} (96%) rename src/view/styles/{marginstyles.js => margin.js} (73%) rename src/view/styles/{paddingstyles.js => padding.js} (74%) diff --git a/src/view/element.js b/src/view/element.js index fd55961da..e0319c356 100644 --- a/src/view/element.js +++ b/src/view/element.js @@ -386,7 +386,7 @@ export default class Element extends Node { * For an element with style set to: 'margin:1px * * // Enable 'margin' shorthand processing: - * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * editor.editing.view.document.addStyleProcessorRules( addMarginRules ); * * const element = view.change( writer => { * const element = writer.createElement(); diff --git a/src/view/styles/backgroundstyles.js b/src/view/styles/background.js similarity index 79% rename from src/view/styles/backgroundstyles.js rename to src/view/styles/background.js index bd177408f..94107045e 100644 --- a/src/view/styles/backgroundstyles.js +++ b/src/view/styles/background.js @@ -3,13 +3,20 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ +/** + * @module engine/view/styles/background + */ + import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; /** - * @module engine/view/styles + * Adds a background CSS styles processing rules. + * + * editor.editing.view.document.addStyleProcessorRules( addBackgroundRules ); + * + * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor */ - -export function addBackgroundStylesProcessor( stylesProcessor ) { +export function addBackgroundRules( stylesProcessor ) { stylesProcessor.setNormalizer( 'background', normalizeBackground ); stylesProcessor.setNormalizer( 'background-color', value => ( { path: 'background.color', value } ) ); stylesProcessor.setReducer( 'background', value => { diff --git a/src/view/styles/borderstyles.js b/src/view/styles/border.js similarity index 96% rename from src/view/styles/borderstyles.js rename to src/view/styles/border.js index 916e808d2..709033a30 100644 --- a/src/view/styles/borderstyles.js +++ b/src/view/styles/border.js @@ -3,13 +3,20 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ +/** + * @module engine/view/styles/border + */ + import { getShorthandValues, getTopRightBottomLeftValueReducer, getTopRightBottomLeftValues, isLength, isLineStyle } from './utils'; /** - * @module engine/view/styles/borderstyle + * Adds a border CSS styles processing rules. + * + * editor.editing.view.document.addStyleProcessorRules( addBorderRules ); + * + * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor */ - -export function addBorderStylesProcessor( stylesProcessor ) { +export function addBorderRules( stylesProcessor ) { stylesProcessor.setNormalizer( 'border', borderNormalizer ); // Border-position shorthands. diff --git a/src/view/styles/marginstyles.js b/src/view/styles/margin.js similarity index 73% rename from src/view/styles/marginstyles.js rename to src/view/styles/margin.js index 0c501a804..73553c151 100644 --- a/src/view/styles/marginstyles.js +++ b/src/view/styles/margin.js @@ -3,13 +3,20 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ +/** + * @module engine/view/styles/margin + */ + import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } from './utils'; /** - * @module engine/view/styles + * Adds a margin CSS styles processing rules. + * + * editor.editing.view.document.addStyleProcessorRules( addMarginRules ); + * + * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor */ - -export function addMarginStylesProcessor( stylesProcessor ) { +export function addMarginRules( stylesProcessor ) { stylesProcessor.setNormalizer( 'margin', getPositionShorthandNormalizer( 'margin' ) ); stylesProcessor.setNormalizer( 'margin-top', value => ( { path: 'margin.top', value } ) ); diff --git a/src/view/styles/paddingstyles.js b/src/view/styles/padding.js similarity index 74% rename from src/view/styles/paddingstyles.js rename to src/view/styles/padding.js index 8f6ccb3df..8fbe5683d 100644 --- a/src/view/styles/paddingstyles.js +++ b/src/view/styles/padding.js @@ -3,13 +3,20 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ +/** + * @module engine/view/styles/padding + */ + import { getPositionShorthandNormalizer, getTopRightBottomLeftValueReducer } from './utils'; /** - * @module engine/view/styles + * Adds a margin CSS styles processing rules. + * + * editor.editing.view.document.addStyleProcessorRules( addPaddingRules ); + * + * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor */ - -export function addPaddingStylesProcessor( stylesProcessor ) { +export function addPaddingRules( stylesProcessor ) { stylesProcessor.setNormalizer( 'padding', getPositionShorthandNormalizer( 'padding' ) ); stylesProcessor.setNormalizer( 'padding-top', value => ( { path: 'padding.top', value } ) ); stylesProcessor.setNormalizer( 'padding-right', value => ( { path: 'padding.right', value } ) ); diff --git a/src/view/stylesmap.js b/src/view/stylesmap.js index deb519194..8d1465ae1 100644 --- a/src/view/stylesmap.js +++ b/src/view/stylesmap.js @@ -94,7 +94,7 @@ export default class StylesMap { * *Note:* This check supports normalized style names. * * // Enable 'margin' shorthand processing: - * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * editor.editing.view.document.addStyleProcessorRules( addMarginRules ); * * styles.setTo( 'margin:2px;' ); * @@ -142,7 +142,7 @@ export default class StylesMap { * *Note:* This method supports normalized styles if defined. * * // Enable 'margin' shorthand processing: - * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * editor.editing.view.document.addStyleProcessorRules( addMarginRules ); * * styles.set( 'margin', '2px' ); * @@ -185,7 +185,7 @@ export default class StylesMap { * *Note:* This method supports normalized styles if defined. * * // Enable 'margin' shorthand processing: - * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * editor.editing.view.document.addStyleProcessorRules( addMarginRules ); * * styles.setTo( 'margin:1px' ); * @@ -205,7 +205,7 @@ export default class StylesMap { * Returns a normalized style object or a single value. * * // Enable 'margin' shorthand processing: - * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * editor.editing.view.document.addStyleProcessorRules( addMarginRules ); * * const styles = new Styles(); * styles.setTo( 'margin:1px 2px 3em;' ); @@ -242,7 +242,7 @@ export default class StylesMap { * *Note:* This method supports normalized styles if defined. * * // Enable 'margin' shorthand processing: - * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * editor.editing.view.document.addStyleProcessorRules( addMarginRules ); * * styles.set( 'margin' , '1px' ); * styles.set( 'background', '#f00' ); @@ -269,7 +269,7 @@ export default class StylesMap { * Returns property as a value string or undefined if property is not set. * * // Enable 'margin' shorthand processing: - * editor.editing.view.document.addStyleProcessorRules( addMarginStylesProcessor ); + * editor.editing.view.document.addStyleProcessorRules( addMarginRules ); * * const styles = new Styles(); * styles.setTo( 'margin:1px;' ); diff --git a/tests/view/styles/backgroundstyles.js b/tests/view/styles/backgroundstyles.js index 876c7226e..5581f6dad 100644 --- a/tests/view/styles/backgroundstyles.js +++ b/tests/view/styles/backgroundstyles.js @@ -4,7 +4,7 @@ */ import StylesMap, { StylesProcessor } from '../../../src/view/stylesmap'; -import { addBackgroundStylesProcessor } from '../../../src/view/styles/backgroundstyles'; +import { addBackgroundRules } from '../../../src/view/styles/background'; describe( 'Background styles normalization', () => { let styles; @@ -12,7 +12,7 @@ describe( 'Background styles normalization', () => { before( () => { const stylesProcessor = new StylesProcessor(); StylesMap._setProcessor( stylesProcessor ); - addBackgroundStylesProcessor( stylesProcessor ); + addBackgroundRules( stylesProcessor ); } ); beforeEach( () => { diff --git a/tests/view/styles/borderstyles.js b/tests/view/styles/borderstyles.js index a9fc25f4a..9fda097cb 100644 --- a/tests/view/styles/borderstyles.js +++ b/tests/view/styles/borderstyles.js @@ -4,7 +4,7 @@ */ import StylesMap, { StylesProcessor } from '../../../src/view/stylesmap'; -import { addBorderStylesProcessor } from '../../../src/view/styles/borderstyles'; +import { addBorderRules } from '../../../src/view/styles/border'; describe( 'Border styles normalization', () => { let styles; @@ -12,7 +12,7 @@ describe( 'Border styles normalization', () => { before( () => { const stylesProcessor = new StylesProcessor(); StylesMap._setProcessor( stylesProcessor ); - addBorderStylesProcessor( stylesProcessor ); + addBorderRules( stylesProcessor ); } ); beforeEach( () => { diff --git a/tests/view/styles/marginstyles.js b/tests/view/styles/marginstyles.js index a33574455..1e1c840fd 100644 --- a/tests/view/styles/marginstyles.js +++ b/tests/view/styles/marginstyles.js @@ -4,7 +4,7 @@ */ import StylesMap, { StylesProcessor } from '../../../src/view/stylesmap'; -import { addMarginStylesProcessor } from '../../../src/view/styles/marginstyles'; +import { addMarginRules } from '../../../src/view/styles/margin'; describe( 'Margin styles normalizer', () => { let styles; @@ -12,7 +12,7 @@ describe( 'Margin styles normalizer', () => { before( () => { const stylesProcessor = new StylesProcessor(); StylesMap._setProcessor( stylesProcessor ); - addMarginStylesProcessor( stylesProcessor ); + addMarginRules( stylesProcessor ); } ); beforeEach( () => { diff --git a/tests/view/styles/paddingstyles.js b/tests/view/styles/paddingstyles.js index 0f7745ff8..24ea81149 100644 --- a/tests/view/styles/paddingstyles.js +++ b/tests/view/styles/paddingstyles.js @@ -4,7 +4,7 @@ */ import StylesMap, { StylesProcessor } from '../../../src/view/stylesmap'; -import { addPaddingStylesProcessor } from '../../../src/view/styles/paddingstyles'; +import { addPaddingRules } from '../../../src/view/styles/padding'; describe( 'Padding styles normalization', () => { let styles; @@ -12,7 +12,7 @@ describe( 'Padding styles normalization', () => { before( () => { const stylesProcessor = new StylesProcessor(); StylesMap._setProcessor( stylesProcessor ); - addPaddingStylesProcessor( stylesProcessor ); + addPaddingRules( stylesProcessor ); } ); beforeEach( () => { diff --git a/tests/view/stylesmap.js b/tests/view/stylesmap.js index b730661d2..f1c4d938d 100644 --- a/tests/view/stylesmap.js +++ b/tests/view/stylesmap.js @@ -5,7 +5,7 @@ import StylesMap, { StylesProcessor } from '../../src/view/stylesmap'; import encodedImage from './_utils/encodedimage.txt'; -import { addPaddingStylesProcessor } from '../../src/view/styles/paddingstyles'; +import { addPaddingRules } from '../../src/view/styles/padding'; import { getTopRightBottomLeftValueReducer } from '../../src/view/styles/utils'; describe( 'StylesMap', () => { @@ -25,7 +25,7 @@ describe( 'StylesMap', () => { } ) ); stylesProcessor.setReducer( 'foo', getTopRightBottomLeftValueReducer( 'foo' ) ); - addPaddingStylesProcessor( stylesProcessor ); + addPaddingRules( stylesProcessor ); StylesMap._setProcessor( stylesProcessor ); stylesMap = new StylesMap(); } ); From 61eac43f78f6262cf2385c5e3b3ede70e7b0b2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 15 Jan 2020 14:30:40 +0100 Subject: [PATCH 122/142] Add missing link to line style CSS value. --- src/view/styles/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/styles/utils.js b/src/view/styles/utils.js index ff79cacb1..3d3c15e37 100644 --- a/src/view/styles/utils.js +++ b/src/view/styles/utils.js @@ -22,7 +22,7 @@ export function isColor( string ) { const lineStyleValues = [ 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset' ]; /** - * Checks if string contains line style CSS value. + * Checks if string contains [line style](https://developer.mozilla.org/en-US/docs/Web/CSS/border-style) CSS value. * * @param {String} string * @returns {Boolean} From e4b3846e3209ad2c635e076f107579a11eeffb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 15 Jan 2020 14:43:35 +0100 Subject: [PATCH 123/142] Add documentation about normalized styles values. --- src/view/styles/background.js | 15 +++++++++++++++ src/view/styles/border.js | 28 ++++++++++++++++++++++++++++ src/view/styles/margin.js | 11 +++++++++++ src/view/styles/padding.js | 11 +++++++++++ 4 files changed, 65 insertions(+) diff --git a/src/view/styles/background.js b/src/view/styles/background.js index 94107045e..75b6e6d6e 100644 --- a/src/view/styles/background.js +++ b/src/view/styles/background.js @@ -14,6 +14,21 @@ import { isAttachment, isColor, isPosition, isRepeat, isURL } from './utils'; * * editor.editing.view.document.addStyleProcessorRules( addBackgroundRules ); * + * The normalized value is stored as: + * + * const styles = { + * background: { + * color, + * repeat, + * position, + * attachment, + * image + * } + * }; + * + * *Note*: Currently only `'background-color'` longhand value is parsed besides `'background'` shorthand. The reducer also supports only + * `'background-color'` value. + * * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor */ export function addBackgroundRules( stylesProcessor ) { diff --git a/src/view/styles/border.js b/src/view/styles/border.js index 709033a30..2a9a901d6 100644 --- a/src/view/styles/border.js +++ b/src/view/styles/border.js @@ -14,6 +14,34 @@ import { getShorthandValues, getTopRightBottomLeftValueReducer, getTopRightBotto * * editor.editing.view.document.addStyleProcessorRules( addBorderRules ); * + * This rules merges all [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border) styles notation shorthands: + * + * - border + * - border-top + * - border-right + * - border-bottom + * - border-left + * - border-color + * - border-style + * - border-width + * + * and all corresponding longhand forms (like `border-top-color`, `border-top-style`, etc). + * + * It does not handle other shorthands (like `border-radius` or `border-image`). + * + * The normalized model stores border values as: + * + * const styles = { + * border: { + * color: { top, right, bottom, left }, + * style: { top, right, bottom, left }, + * width: { top, right, bottom, left }, + * } + * }; + * + * The `border` value is reduced to a 4 values for each box edge (even if they could be further reduces to a single + * `border: