Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

I/3287: Styles normalization (part of table styles) #1807

Merged
merged 148 commits into from
Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from 77 commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
5403671
Add style normalization stub for borders.
jodator Sep 25, 2019
34c5687
Extract stylenormalizer.js.
jodator Sep 26, 2019
1e4d5aa
Define StyleProxy methods.
jodator Sep 26, 2019
893519e
Rewrite Element class to use Styles normalization class.
jodator Sep 26, 2019
6396311
Add style retrieval for model storage.
jodator Sep 26, 2019
d65236e
Add rule should accept object.
jodator Sep 26, 2019
eb9ff1d
Always add trailing semicolon to styles output.
jodator Sep 26, 2019
54b24ad
Organize tests for styles normalizer.
jodator Sep 26, 2019
8a65d99
Add support for border-color shorthand.
jodator Sep 26, 2019
3a9ee3e
Add support for border-style shorthand.
jodator Sep 26, 2019
dbd70bf
Add support for border-width shorthand.
jodator Sep 26, 2019
69167b9
Properly merge values in styles normalizer.
jodator Sep 26, 2019
b8138a6
Make tests compact.
jodator Sep 26, 2019
ca4bda8
Add support for margin shorthand.
jodator Sep 26, 2019
029efbd
Add support for padding shorthand.
jodator Sep 26, 2019
f7420b3
Add support for margin-* and padding-* shorthands.
jodator Sep 26, 2019
a78136c
Add margin output.
jodator Sep 27, 2019
28a6440
Add tests for margin style output.
jodator Sep 27, 2019
008c90d
Add get style names logic.
jodator Sep 27, 2019
a16afe6
Refactor Styles class to more instance based.
jodator Sep 27, 2019
5ac8b42
Add direct properties support.
jodator Sep 30, 2019
4cc9f15
Refactor internal parsing to object.
jodator Sep 30, 2019
7476ed0
Fix tests.
jodator Sep 30, 2019
de8f1bc
Fix output of undefined value for margin.
jodator Sep 30, 2019
0b710f1
Consume shorthand styles together with their counterpart properties.
jodator Sep 30, 2019
2eb54c2
Remove .only from tests.
jodator Sep 30, 2019
a3cfc0c
Add support for background-color in styles normalization.
jodator Oct 1, 2019
2cd6cb5
Refactor styles method names.
jodator Oct 1, 2019
197e99a
Refactor inline style parsing to be more declarative.
jodator Oct 2, 2019
45cdfd1
Remove private _parseStyle() method.
jodator Oct 2, 2019
7da4282
Add getNormalizedStyle to view/element.
jodator Oct 2, 2019
b88f747
Check how to stringify normalized model value.
jodator Oct 2, 2019
1bc367e
Change border normalized version.
jodator Oct 2, 2019
e2b793f
Remove unneeded styles normalizations.
jodator Oct 2, 2019
293b089
Fix tests.
jodator Oct 2, 2019
98df9e4
Stub proper styles signifying from normalized object.
jodator Oct 3, 2019
e0ee293
Refactor styles outputting.
jodator Oct 3, 2019
a380904
Add tests for styles class methods.
jodator Oct 3, 2019
50df1cb
Add more tests to the styles class.
jodator Oct 3, 2019
edf5e6b
Clarify naming of internal style normalization mechanisms.
jodator Oct 3, 2019
4acd303
Remove unused code.
jodator Oct 7, 2019
76edc1c
Add tests for background color.
jodator Oct 7, 2019
d95959b
Add more background shorthand values parsing.
jodator Oct 7, 2019
3455103
Add more tests for top-right-bottom-left shorthands.
jodator Oct 7, 2019
15de90c
Simplify code.
jodator Oct 7, 2019
26a616d
Remove unused code, add more tests, cleanup API.
jodator Oct 7, 2019
52dbcfe
View element clone should use normalized object.
jodator Oct 7, 2019
3227d86
Add tests for parsing top right bottom left empty value.
jodator Oct 7, 2019
68a2ddd
Add border-top,right,bottom,left as a default border output.
jodator Oct 8, 2019
5c3957d
Add padding reducer.
jodator Oct 8, 2019
ba78816
Add test for inline border position style.
jodator Oct 9, 2019
cbb220c
Convert borders to three separate values.
jodator Oct 14, 2019
986eba9
Create styles converter singleton.
jodator Oct 14, 2019
2760f89
Refactor styles extracting to events.
jodator Oct 14, 2019
8f08fda
Refactor styles reducing to events.
jodator Oct 14, 2019
0404fcf
Refactor styles extracting to events.
jodator Oct 15, 2019
365af42
Organize styles normalization to classes.
jodator Oct 15, 2019
0b9cb11
Extract BorderStyles normalizer.
jodator Oct 15, 2019
cec8026
Extract MarginStyles normalizer.
jodator Oct 15, 2019
98874d4
Extract PaddingStyles normalizer.
jodator Oct 15, 2019
e2d44b9
Extract BackgroundStyles normalizer.
jodator Oct 15, 2019
7eec7b2
Extract particular styles tests to own files.
jodator Oct 15, 2019
dcf0f1c
Parse styles independently.
jodator Oct 15, 2019
b63d527
Generalize tests for Styles class.
jodator Oct 15, 2019
43ea44e
Fix border styles normalization code. Add tests for utils methods.
jodator Oct 15, 2019
80f1a6b
Add more styles utils tests.
jodator Oct 15, 2019
88f83b5
Merge branch 'master' into i/3287
jodator Oct 16, 2019
4b7e26e
Fix isLength and isColor regexps.
jodator Oct 16, 2019
183b932
Refactor is*() util methods.
jodator Oct 16, 2019
ac84305
Change how hasStyle() is checked due to table border converter issues.
jodator Oct 22, 2019
2382e5f
Fix isColor RegExp.
jodator Oct 22, 2019
bbc931c
Make styles.hasProperty() check to be in line with styles.getProperty().
jodator Oct 22, 2019
64820f6
Merge branch 'master' into i/3287
jodator Oct 23, 2019
f7d0bec
Fix borders styles extracting.
jodator Oct 23, 2019
1e13aff
Merge branch 'master' into i/3287
jodator Oct 28, 2019
138737a
Hide style converter singleton from the watchdog.
jodator Oct 28, 2019
f73e754
Fix StyleConverter docs.
jodator Oct 28, 2019
544a9b1
Merge branch 'master' into i/3287
Reinmar Nov 18, 2019
cfa3460
Fix language in the styles doc.
jodator Nov 20, 2019
649f354
Merge branch 'master' into i/3287
jodator Jan 7, 2020
c2c6364
Merge remote-tracking branch 'origin/i/3287' into i/3287
jodator Jan 7, 2020
f268ecb
Rename StylesConverter to StyleProcessor.
jodator Jan 9, 2020
e0797de
Rename stylesConverter to stylesProcessor.
jodator Jan 9, 2020
0396b08
Remove export.
jodator Jan 9, 2020
09b3c6f
Change converter property descriptor to getter.
jodator Jan 9, 2020
dd85fc5
Make default style processor as a static class property.
jodator Jan 9, 2020
6371ec4
Move Styles class as a default export of view/styles.
jodator Jan 9, 2020
f6f1bba
Use method to attach border styles processor.
jodator Jan 9, 2020
f32690c
Use method to attach margin styles processor.
jodator Jan 9, 2020
8ca987e
Use method to attach padding styles processor.
jodator Jan 9, 2020
76d59ec
Use method to attach background styles processor.
jodator Jan 9, 2020
cb7ea99
Remove default processors from styles file.
jodator Jan 9, 2020
2073676
Rename style converter to processor.
jodator Jan 9, 2020
baeab18
Remove .skip from slow tests.
jodator Jan 10, 2020
e2dfe59
Introduce one-time callbacks registering for styles converter.
jodator Jan 10, 2020
727d143
Remove consuming shorthand style names while consuming longhand and v…
jodator Jan 13, 2020
9712d8d
Fix jsdoc for view element's _styles property.
jodator Jan 13, 2020
e19120c
Fix jsdoc of view/Element.getStyle().
jodator Jan 13, 2020
4c8e640
Remove sorting from Styles._getStylesEntries().
jodator Jan 13, 2020
2dadcd0
Rename styleProcessor to _styleProcessor in Styles.
jodator Jan 13, 2020
38cbc5a
Use map to store style converters.
jodator Jan 13, 2020
ef5083d
Simplify extractors.
jodator Jan 13, 2020
b6d03a3
Normalizer should only work on value.
jodator Jan 13, 2020
1a1b8a3
Reducer should only work on value.
jodator Jan 13, 2020
4c94d16
Rename Styles to StylesMap.
jodator Jan 14, 2020
3088102
Update docs of the Styles.getNormalized() method.
jodator Jan 14, 2020
45ad617
Add docs for the Element.getNormalizedStyle() method.
jodator Jan 14, 2020
56df0b4
Rename StylesMap.setStyle() to setTo().
jodator Jan 14, 2020
adc0f8f
Add StylesMap.hasProperty() documentation.
jodator Jan 14, 2020
87bc5e5
Fix StylesMap.hasProperty() tests.
jodator Jan 14, 2020
4ea5df6
Rename StylesMap.hasProperty() to has().
jodator Jan 14, 2020
8743d15
Rename StylesMap.insertProperty() to set().
jodator Jan 14, 2020
66e3cd1
Update StylesMap.set() docs.
jodator Jan 14, 2020
4fc65bd
Rename StylesMap.removeProperty() to remove().
jodator Jan 14, 2020
54c2a5a
Cross link view writer, element ans styles map docs.
jodator Jan 14, 2020
c33b187
StylesMap will return string when empty.
jodator Jan 14, 2020
41a55c6
Add StylesMap#isEmpty getter for fast checking if styles map has any …
jodator Jan 14, 2020
151c5c1
Rename StylesMap.getInlineProperty() to getAsString() and improve docs.
jodator Jan 14, 2020
affc228
Add docs for StylesMap._styles private property.
jodator Jan 14, 2020
611daa6
Reorder the static methods and getters of StylesMap.
jodator Jan 14, 2020
7fb72d7
Add docs to StylesProcessor.
jodator Jan 14, 2020
400305f
Simplify StylesConverter.getReducedForm() usage.
jodator Jan 14, 2020
b680fe2
Revert docs for setting object style.
jodator Jan 14, 2020
065dfcc
StylesMap.getAsString() will return undefined for missing properties.
jodator Jan 15, 2020
91fcf56
Add docs to StylesProcessor.
jodator Jan 15, 2020
6535fc2
Add docs to StylesMap and styles utils.
jodator Jan 15, 2020
a70c8ff
Refactor and add docs for styles processing rules.
jodator Jan 15, 2020
61eac43
Add missing link to line style CSS value.
jodator Jan 15, 2020
e4b3846
Add documentation about normalized styles values.
jodator Jan 15, 2020
3699473
Remove obsolete docs.
jodator Jan 15, 2020
2836d24
Add test for view/Document.addStyleProcessorRules().
jodator Jan 15, 2020
fef22a7
Add tests for view/Element.getNormalizedStyle().
jodator Jan 15, 2020
a391dbc
Add StylesMap._styleProcessor static getter tests.
jodator Jan 15, 2020
79b013d
Match test file names to source file names.
jodator Jan 15, 2020
391f960
Add tests for other padding values (increase coverage).
jodator Jan 15, 2020
87570d8
Add tests for border styles processing.
jodator Jan 15, 2020
c1f54be
Removing empty statement.
jodator Jan 15, 2020
b145b3b
Bump year in the PR files.
jodator Jan 16, 2020
e405be5
Fix docs links.
jodator Jan 16, 2020
546fd5b
Docs, docs, docs.
Reinmar Jan 16, 2020
8cd6ca9
Docs, docs, docs.
Reinmar Jan 16, 2020
a636db8
Docs, docs, docs.
Reinmar Jan 16, 2020
9d8df6d
Docs, docs, docs.
Reinmar Jan 16, 2020
709bbb2
Docs, docs, docs.
Reinmar Jan 16, 2020
589fb34
Docs, docs, docs.
Reinmar Jan 16, 2020
254203f
Introduce smart view style consuming.
jodator Jan 17, 2020
b8bb94c
Fix link in docs.
jodator Jan 20, 2020
13e5f7f
Docs.
Reinmar Jan 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/conversion/viewconsumable.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,43 @@
import { isArray } from 'lodash-es';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';

const styleConsumablesMap = new Map();
jodator marked this conversation as resolved.
Show resolved Hide resolved

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}.
Expand Down Expand Up @@ -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 );
}
}
}
}

Expand Down Expand Up @@ -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 );
}
}
}
}
}
Expand Down
137 changes: 26 additions & 111 deletions src/view/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 Styles from './styles';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When reviewing this file I skipped a lot of places where the naming or behavior was unclear to me. Since normalization was added, it must remain clear what do particular methods do here. Whether they care about the raw style or only about normalized styles. Also, there are some places where you can get surprised – e.g. assuming that getStyleNames() do optimize the names, you may set one set of styles on an element, and get something else back. We must make it extremely clear what's happening. Both, by having tests and docs.


// @if CK_DEBUG_ENGINE // const { convertMapToTags } = require( '../dev-utils/utils' );

Expand Down Expand Up @@ -105,16 +106,17 @@ export default class Element extends Node {
}

/**
* Map of styles.
* Normalized styles.
*
* @protected
* @member {Map} module:engine/view/element~Element#_styles
jodator marked this conversation as resolved.
Show resolved Hide resolved
*/
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._styles.setStyle( this._attrs.get( 'style' ) );

this._attrs.delete( 'style' );
}

Expand Down Expand Up @@ -264,17 +266,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 );
Expand Down Expand Up @@ -342,8 +334,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 this._styles.getStyleNames() ) {
jodator marked this conversation as resolved.
Show resolved Hide resolved
if (
!otherElement._styles.hasProperty( property ) ||
otherElement._styles.getInlineProperty( property ) !== this._styles.getInlineProperty( property )
) {
return false;
}
}
Expand Down Expand Up @@ -384,10 +379,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}
jodator marked this conversation as resolved.
Show resolved Hide resolved
*/
getStyle( property ) {
return this._styles.get( property );
return this._styles.getInlineProperty( property );
}

getNormalizedStyle( property ) {
jodator marked this conversation as resolved.
Show resolved Hide resolved
return this._styles.getNormalized( property );
}

/**
Expand All @@ -396,7 +395,7 @@ export default class Element extends Node {
* @returns {Iterable.<String>}
*/
getStyleNames() {
return this._styles.keys();
return this._styles.getStyleNames();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to be sure (test needed) whether this method's behavior is changing in any way now.

}

/**
Expand All @@ -410,7 +409,7 @@ export default class Element extends Node {
*/
hasStyle( ...property ) {
for ( const name of property ) {
if ( !this._styles.has( name ) ) {
if ( !this._styles.hasProperty( name ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to be sure (test needed) whether this method's behavior is changing in any way now.

return false;
}
}
Expand Down Expand Up @@ -487,12 +486,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 }` );
}

Expand All @@ -519,7 +518,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.insertProperty( this._styles.getNormalized() );

// Clone custom properties.
cloned._customProperties = new Map( this._customProperties );
Expand Down Expand Up @@ -616,7 +615,7 @@ 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 );
}
Expand Down Expand Up @@ -647,7 +646,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;
Expand Down Expand Up @@ -714,15 +713,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.insertProperty( property, value );
}

/**
Expand All @@ -740,7 +731,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.removeProperty( name ) );
}

/**
Expand Down Expand Up @@ -826,82 +817,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.<String, String>} 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;
}
}
}

// Parses class attribute and puts all classes into classes set.
// Classes set s cleared before insertion.
//
Expand Down
Loading