From 869ede05ed16af994c89ab2a140fae3185bd288d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 29 Nov 2017 15:10:17 +0100 Subject: [PATCH 001/101] Feature: Add Font & FontSize plugins stubs. --- src/font.js | 35 +++++++++++++++++++++++++++++++++++ src/fontsize.js | 31 +++++++++++++++++++++++++++++++ tests/font.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 src/font.js create mode 100644 src/fontsize.js create mode 100644 tests/font.js diff --git a/src/font.js b/src/font.js new file mode 100644 index 0000000..aa884d2 --- /dev/null +++ b/src/font.js @@ -0,0 +1,35 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/font + */ + +import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; + +import FontSize from './fontsize'; + +/** + * The Font plugin. + * + * It requires {@link module:font/fontsize~FontSize} plugin. + * + * @extends module:core/plugin~Plugin + */ +export default class Font extends Plugin { + /** + * @inheritDoc + */ + static get requires() { + return [ FontSize ]; + } + + /** + * @inheritDoc + */ + static get pluginName() { + return 'Font'; + } +} diff --git a/src/fontsize.js b/src/fontsize.js new file mode 100644 index 0000000..356f5eb --- /dev/null +++ b/src/fontsize.js @@ -0,0 +1,31 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontsize + */ + +import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; + +/** + * The Font Size plugin. + * + * @extends module:core/plugin~Plugin + */ +export default class FontSize extends Plugin { + /** + * @inheritDoc + */ + static get requires() { + return []; + } + + /** + * @inheritDoc + */ + static get pluginName() { + return 'FontSize'; + } +} diff --git a/tests/font.js b/tests/font.js new file mode 100644 index 0000000..636f0cc --- /dev/null +++ b/tests/font.js @@ -0,0 +1,38 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* global document */ + +import Font from './../src/font'; +import FontSize from './../src/fontsize'; + +import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; + +describe( 'Font', () => { + let editor, element; + + beforeEach( () => { + element = document.createElement( 'div' ); + document.body.appendChild( element ); + + return ClassicTestEditor + .create( element, { + plugins: [ Font ] + } ) + .then( newEditor => { + editor = newEditor; + } ); + } ); + + afterEach( () => { + element.remove(); + + return editor.destroy(); + } ); + + it( 'requires FontSize', () => { + expect( Font.requires ).to.deep.equal( [ FontSize ] ); + } ); +} ); From 75ee85f7ba4577c7050ae3919131147b57e8a668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 29 Nov 2017 15:23:50 +0100 Subject: [PATCH 002/101] Feature: Add FontSizeEditing plugins stub. --- src/fontsize.js | 3 ++- src/fontsizeediting.js | 38 +++++++++++++++++++++++++++++++++++ tests/font.js | 29 ++++----------------------- tests/fontsize.js | 17 ++++++++++++++++ tests/fontsizeediting.js | 43 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 26 deletions(-) create mode 100644 src/fontsizeediting.js create mode 100644 tests/fontsize.js create mode 100644 tests/fontsizeediting.js diff --git a/src/fontsize.js b/src/fontsize.js index 356f5eb..10aa34b 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -8,6 +8,7 @@ */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; +import FontSizeEditing from './fontsizeediting'; /** * The Font Size plugin. @@ -19,7 +20,7 @@ export default class FontSize extends Plugin { * @inheritDoc */ static get requires() { - return []; + return [ FontSizeEditing ]; } /** diff --git a/src/fontsizeediting.js b/src/fontsizeediting.js new file mode 100644 index 0000000..087ca68 --- /dev/null +++ b/src/fontsizeediting.js @@ -0,0 +1,38 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontsizeediting + */ + +import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; + +/** + * The Font Size Editing feature. + * + * @extends module:core/plugin~Plugin + */ +export default class FontSizeEditing extends Plugin { + /** + * @inheritDoc + */ + constructor( editor ) { + super( editor ); + + editor.config.define( 'fontSize', { items: [] } ); + } + + /** + * @inheritDoc + */ + init() { + const editor = this.editor; + + // Allow highlight attribute on all elements + editor.document.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); + // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. + editor.document.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ); + } +} diff --git a/tests/font.js b/tests/font.js index 636f0cc..c2eeef5 100644 --- a/tests/font.js +++ b/tests/font.js @@ -3,36 +3,15 @@ * For licensing, see LICENSE.md. */ -/* global document */ - import Font from './../src/font'; import FontSize from './../src/fontsize'; -import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; - describe( 'Font', () => { - let editor, element; - - beforeEach( () => { - element = document.createElement( 'div' ); - document.body.appendChild( element ); - - return ClassicTestEditor - .create( element, { - plugins: [ Font ] - } ) - .then( newEditor => { - editor = newEditor; - } ); - } ); - - afterEach( () => { - element.remove(); - - return editor.destroy(); - } ); - it( 'requires FontSize', () => { expect( Font.requires ).to.deep.equal( [ FontSize ] ); } ); + + it( 'defines plugin name', () => { + expect( Font.pluginName ).to.equal( 'Font' ); + } ); } ); diff --git a/tests/fontsize.js b/tests/fontsize.js new file mode 100644 index 0000000..376c3c7 --- /dev/null +++ b/tests/fontsize.js @@ -0,0 +1,17 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import FontSize from './../src/fontsize'; +import FontSizeEditing from './../src/fontsizeediting'; + +describe( 'FontSize', () => { + it( 'requires FontSizeEditing', () => { + expect( FontSize.requires ).to.deep.equal( [ FontSizeEditing ] ); + } ); + + it( 'defines plugin name', () => { + expect( FontSize.pluginName ).to.equal( 'FontSize' ); + } ); +} ); diff --git a/tests/fontsizeediting.js b/tests/fontsizeediting.js new file mode 100644 index 0000000..91ced80 --- /dev/null +++ b/tests/fontsizeediting.js @@ -0,0 +1,43 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import FontSizeEditing from './../src/fontsizeediting'; + +import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; + +import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; + +describe( 'FontSizeEditing', () => { + let editor, doc; + + beforeEach( () => { + return VirtualTestEditor + .create( { + plugins: [ FontSizeEditing, Paragraph ] + } ) + .then( newEditor => { + editor = newEditor; + + doc = editor.document; + } ); + } ); + + afterEach( () => { + editor.destroy(); + } ); + + it( 'should set proper schema rules', () => { + expect( doc.schema.check( { name: '$inline', attributes: 'fontSize', inside: '$block' } ) ).to.be.true; + expect( doc.schema.check( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ) ).to.be.true; + } ); + + describe( 'config', () => { + describe( 'default value', () => { + it( 'should be set', () => { + expect( editor.config.get( 'fontSize.items' ) ).to.deep.equal( [] ); + } ); + } ); + } ); +} ); From 7eb6bf7c2b404157f870fc2be540d7b08b42bff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 29 Nov 2017 16:12:27 +0100 Subject: [PATCH 003/101] Other: Move FontSizeEditing to font/fontsize module. --- src/fontsize.js | 2 +- src/{ => fontsize}/fontsizeediting.js | 2 +- tests/fontsize.js | 2 +- tests/{ => fontsize}/fontsizeediting.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{ => fontsize}/fontsizeediting.js (95%) rename tests/{ => fontsize}/fontsizeediting.js (94%) diff --git a/src/fontsize.js b/src/fontsize.js index 10aa34b..ae9a377 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -8,7 +8,7 @@ */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; -import FontSizeEditing from './fontsizeediting'; +import FontSizeEditing from './fontsize/fontsizeediting'; /** * The Font Size plugin. diff --git a/src/fontsizeediting.js b/src/fontsize/fontsizeediting.js similarity index 95% rename from src/fontsizeediting.js rename to src/fontsize/fontsizeediting.js index 087ca68..5c1f4f0 100644 --- a/src/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -4,7 +4,7 @@ */ /** - * @module font/fontsizeediting + * @module font/fontsize/fontsizeediting */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; diff --git a/tests/fontsize.js b/tests/fontsize.js index 376c3c7..0e2bdeb 100644 --- a/tests/fontsize.js +++ b/tests/fontsize.js @@ -4,7 +4,7 @@ */ import FontSize from './../src/fontsize'; -import FontSizeEditing from './../src/fontsizeediting'; +import FontSizeEditing from './../src/fontsize/fontsizeediting'; describe( 'FontSize', () => { it( 'requires FontSizeEditing', () => { diff --git a/tests/fontsizeediting.js b/tests/fontsize/fontsizeediting.js similarity index 94% rename from tests/fontsizeediting.js rename to tests/fontsize/fontsizeediting.js index 91ced80..f597c84 100644 --- a/tests/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md. */ -import FontSizeEditing from './../src/fontsizeediting'; +import FontSizeEditing from './../../src/fontsize/fontsizeediting'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; From dbbdb19b9d271c52155cd8516f47d17a4e152a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 29 Nov 2017 16:37:20 +0100 Subject: [PATCH 004/101] Feature: Introduce FontSizeCommand. --- package.json | 1 + src/fontsize/fontsizecommand.js | 72 +++++++++++++++++ tests/fontsize/fontsizecommand.js | 124 ++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 src/fontsize/fontsizecommand.js create mode 100644 tests/fontsize/fontsizecommand.js diff --git a/package.json b/package.json index 57566e1..abc66bd 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@ckeditor/ckeditor5-ui": "^1.0.0-alpha.2" }, "devDependencies": { + "@ckeditor/ckeditor5-paragraph": "^1.0.0-alpha.2", "eslint": "^4.8.0", "eslint-config-ckeditor5": "^1.0.6", "husky": "^0.14.3", diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js new file mode 100644 index 0000000..29e111e --- /dev/null +++ b/src/fontsize/fontsizecommand.js @@ -0,0 +1,72 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontsizecommand + */ + +import Command from '@ckeditor/ckeditor5-core/src/command'; + +/** + * The font size command. It is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing FontSizeEditing feature} + * to apply font size. + * + * @extends module:core/command~Command + */ +export default class FontSizeCommand extends Command { + constructor( editor, fontSize ) { + super( editor ); + + /** + * Name of font size config. + */ + this.fontSize = fontSize; + + /** + * A flag indicating whether the command is active, which means that the selection has fontSize attribute set. + * + * @observable + * @readonly + * @member {Boolean} module:font/fontsizecommand~FontSizeCommand#value + */ + } + + /** + * @inheritDoc + */ + refresh() { + const doc = this.editor.document; + + this.value = doc.selection.getAttribute( 'fontSize' ) === this.fontSize; + this.isEnabled = doc.schema.checkAttributeInSelection( doc.selection, 'fontSize' ); + } + + /** + * Executes the command. + * + * @protected + * @param {Object} [options] Options for the executed command. + * @param {module:engine/model/batch~Batch} [options.batch] A batch to collect all the change steps. + * A new batch will be created if this option is not set. + */ + execute( options = {} ) { + const doc = this.editor.document; + const selection = doc.selection; + + // Do not apply fontSize on collapsed selection. + if ( selection.isCollapsed ) { + return; + } + + doc.enqueueChanges( () => { + const ranges = doc.schema.getValidRanges( selection.getRanges(), 'fontSize' ); + const batch = options.batch || doc.batch(); + + for ( const range of ranges ) { + batch.setAttribute( 'fontSize', this.fontSize, range ); + } + } ); + } +} diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js new file mode 100644 index 0000000..a98fb44 --- /dev/null +++ b/tests/fontsize/fontsizecommand.js @@ -0,0 +1,124 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import FontSizeCommand from '../../src/fontsize/fontsizecommand'; + +import Command from '@ckeditor/ckeditor5-core/src/command'; +import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; +import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; + +describe( 'FontSizeCommand', () => { + let editor, doc, command; + + beforeEach( () => { + return ModelTestEditor.create() + .then( newEditor => { + doc = newEditor.document; + command = new FontSizeCommand( newEditor, 'text-huge' ); + editor = newEditor; + + editor.commands.add( 'fontSize', command ); + + doc.schema.registerItem( 'paragraph', '$block' ); + + doc.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); + } ); + } ); + + afterEach( () => { + editor.destroy(); + } ); + + it( 'is a command', () => { + expect( FontSizeCommand.prototype ).to.be.instanceOf( Command ); + expect( command ).to.be.instanceOf( Command ); + } ); + + describe( 'value', () => { + it( 'is set to true when selection is in text with fontSize attribute', () => { + setData( doc, '<$text fontSize="text-huge">fo[]o' ); + + expect( command ).to.have.property( 'value', true ); + } ); + + it( 'is undefined when selection is not in text with fontSize attribute', () => { + setData( doc, 'fo[]o' ); + + expect( command ).to.have.property( 'value', false ); + } ); + } ); + + describe( 'isEnabled', () => { + it( 'is true when selection is on text which can have fontSize added', () => { + setData( doc, 'fo[]o' ); + + expect( command ).to.have.property( 'isEnabled', true ); + } ); + } ); + + describe( 'execute()', () => { + it( 'should add fontSize attribute on selected text', () => { + setData( doc, 'a[bc<$text fontSize="text-huge">fo]obarxyz' ); + + expect( command.value ).to.be.false; + + command.execute(); + + expect( command.value ).to.be.true; + + expect( getData( doc ) ).to.equal( 'a[<$text fontSize="text-huge">bcfo]obarxyz' ); + } ); + + it( 'should add fontSize attribute on selected nodes (multiple nodes)', () => { + setData( + doc, + 'abcabc[abc' + + 'foofoofoo' + + 'barbar]bar' + ); + + command.execute(); + + expect( command.value ).to.be.true; + + expect( getData( doc ) ).to.equal( + 'abcabc[<$text fontSize="text-huge">abc' + + '<$text fontSize="text-huge">foofoofoo' + + '<$text fontSize="text-huge">barbar]bar' + ); + } ); + + it( 'should change fontSize attribute on selected nodes', () => { + setData( + doc, + 'abc[abc<$text fontSize="text-small">abc' + + '<$text fontSize="text-small">foofoofoo' + + '<$text fontSize="text-small">bar]barbar' + ); + + command.execute(); + + expect( command.value ).to.be.true; + + expect( getData( doc ) ).to.equal( + 'abc[<$text fontSize="text-huge">abcabc' + + '<$text fontSize="text-huge">foofoofoo' + + '<$text fontSize="text-huge">bar]<$text fontSize="text-small">barbar' + ); + } ); + + it( 'should do nothing on collapsed range', () => { + setData( doc, 'abc<$text fontSize="text-huge">foo[]barxyz' ); + + expect( command.value ).to.be.true; + + command.execute(); + + expect( getData( doc ) ).to.equal( 'abc<$text fontSize="text-huge">foo[]barxyz' ); + + expect( command.value ).to.be.true; + } ); + } ); +} ); From 817597e526da8dc2abe524263836b11a9d7214f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 30 Nov 2017 17:48:33 +0100 Subject: [PATCH 005/101] Other: Initial implementation of FontSize configuration and editing pipeline conversion. --- src/fontsize/fontsizeediting.js | 157 +++++++++++++++++++++++++++++- tests/fontsize/fontsizeediting.js | 142 ++++++++++++++++++++++++++- 2 files changed, 297 insertions(+), 2 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 5c1f4f0..6d7c622 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -8,6 +8,8 @@ */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; +import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter'; +import AttributeElement from '@ckeditor/ckeditor5-engine/src/view/attributeelement'; /** * The Font Size Editing feature. @@ -21,7 +23,69 @@ export default class FontSizeEditing extends Plugin { constructor( editor ) { super( editor ); - editor.config.define( 'fontSize', { items: [] } ); + // Define default configuration using named presets + editor.config.define( 'fontSize', { + items: [ + 'tiny', + 'small', + 'normal', + 'big', + 'huge' + ] + } ); + + // Get configuration + const data = editor.data; + const editing = editor.editing; + + // Convert model to view + buildModelConverter().for( data.modelToView, editing.modelToView ) + .fromAttribute( 'fontSize' ) + .toElement( data => { + const definition = this._getDefinition( data ); + + if ( !definition ) { + return; + } + + // TODO: make utitlity class of this? + const viewDefinition = definition.view; + + const attributes = {}; + + if ( viewDefinition.classes ) { + attributes.class = viewDefinition.classes; + } + + if ( viewDefinition.styles ) { + attributes.style = viewDefinition.styles; + } + + return new AttributeElement( viewDefinition.name, attributes ); + } ); + } + + get configuredItems() { + // Cache value + if ( this._cachedItems ) { + return this._cachedItems; + } + + const items = []; + const editor = this.editor; + const config = editor.config; + + const configuredItems = config.get( 'fontSize.items' ); + + for ( const item of configuredItems ) { + const itemDefinition = getItemDefinition( item ); + + if ( itemDefinition ) { + items.push( itemDefinition ); + } + } + + return ( this._cachedItems = items ); } /** @@ -35,4 +99,95 @@ export default class FontSizeEditing extends Plugin { // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. editor.document.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ); } + + _getDefinition( name ) { + const stringName = String( name ); + + for ( const item of this.configuredItems ) { + if ( item.model === stringName ) { + return item; + } + } + } +} + +const namedPresets = { + tiny: { + label: 'Tiny', + model: 'text-tiny', + // stopValue: .7 + // stopUnit: 'em' + view: { + name: 'span', + classes: 'text-tiny' + } + }, + small: { + label: 'Small', + model: 'text-small', + // stopValue: .85 + // stopUnit: 'em', + view: { + name: 'span', + classes: 'text-small' + } + }, + big: { + label: 'Big', + model: 'text-big', + // stopValue: 1.4 + // stopUnit: 'em', + view: { + name: 'span', + classes: 'text-big' + } + }, + huge: { + label: 'Huge', + model: 'text-huge', + // stopValue: 1.8 + // stopUnit: 'em', + view: { + name: 'span', + classes: 'text-huge' + } + } +}; + +// Returns item definition from preset +function getItemDefinition( item ) { + // Named preset exist so return it + if ( namedPresets[ item ] ) { + return namedPresets[ item ]; + } + + // Probably it is full item definition so return it + if ( typeof item === 'object' ) { + return item; + } + + // At this stage we probably have numerical value to generate a preset so parse it's value. + const sizePreset = parseInt( item ); // TODO: Should we parse floats? 🤔 + + // Discard any faulty values. + if ( isNaN( sizePreset ) ) { + return; + } + + return generatePixelPreset( sizePreset ); +} + +// Creates a predefined preset for pixel size. +function generatePixelPreset( size ) { + const sizeName = String( size ); + + return { + label: sizeName, + model: sizeName, + stopValue: size, + view: { + name: 'span', + styles: `font-size: ${ size }px` + } + }; } diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index f597c84..c0d3430 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -8,6 +8,7 @@ import FontSizeEditing from './../../src/fontsize/fontsizeediting'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; +import { setData as setModelData } from '../../../ckeditor5-engine/src/dev-utils/model'; describe( 'FontSizeEditing', () => { let editor, doc; @@ -36,8 +37,147 @@ describe( 'FontSizeEditing', () => { describe( 'config', () => { describe( 'default value', () => { it( 'should be set', () => { - expect( editor.config.get( 'fontSize.items' ) ).to.deep.equal( [] ); + expect( editor.config.get( 'fontSize.items' ) ).to.deep.equal( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); } ); } ); } ); + + describe( 'configuredItems', () => { + it( 'should discard falsy values', () => { + return VirtualTestEditor + .create( { + plugins: [ FontSizeEditing ], + fontSize: { + items: [ () => {}, 'normal', 'unknown' ] + } + } ) + .then( newEditor => { + editor = newEditor; + + const plugin = editor.plugins.get( FontSizeEditing ); + + expect( plugin.configuredItems ).to.deep.equal( [] ); + } ); + } ); + + it( 'should pass through object definition', () => { + return VirtualTestEditor + .create( { + plugins: [ FontSizeEditing ], + fontSize: { + items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', styles: 'font-size: 12em;' } } ] + } + } ) + .then( newEditor => { + editor = newEditor; + + const plugin = editor.plugins.get( FontSizeEditing ); + + expect( plugin.configuredItems ).to.deep.equal( [ + { + label: 'My Size', + model: 'my-size', + view: { name: 'span', styles: 'font-size: 12em;' } + } + ] ); + } ); + } ); + + describe( 'named presets', () => { + it( 'should return defined presets', () => { + return VirtualTestEditor + .create( { + plugins: [ FontSizeEditing ], + fontSize: { + items: [ 'tiny', 'small', 'normal', 'big', 'huge' ] + } + } ) + .then( newEditor => { + editor = newEditor; + + const plugin = editor.plugins.get( FontSizeEditing ); + + expect( plugin.configuredItems ).to.deep.equal( [ + { label: 'Tiny', model: 'text-tiny', view: { name: 'span', classes: 'text-tiny' } }, + { label: 'Small', model: 'text-small', view: { name: 'span', classes: 'text-small' } }, + { label: 'Big', model: 'text-big', view: { name: 'span', classes: 'text-big' } }, + { label: 'Huge', model: 'text-huge', view: { name: 'span', classes: 'text-huge' } } + ] ); + } ); + } ); + } ); + + describe( 'numeric presets', () => { + it( 'should return generated presets', () => { + return VirtualTestEditor + .create( { + plugins: [ FontSizeEditing ], + fontSize: { + items: [ '10', 12, 'normal', '14.1', 18.3 ] + } + } ) + .then( newEditor => { + editor = newEditor; + + const plugin = editor.plugins.get( FontSizeEditing ); + + expect( plugin.configuredItems ).to.deep.equal( [ + { label: '10', model: '10', stopValue: 10, view: { name: 'span', styles: 'font-size: 10px' } }, + { label: '12', model: '12', stopValue: 12, view: { name: 'span', styles: 'font-size: 12px' } }, + { label: '14', model: '14', stopValue: 14, view: { name: 'span', styles: 'font-size: 14px' } }, + { label: '18', model: '18', stopValue: 18, view: { name: 'span', styles: 'font-size: 18px' } } + ] ); + } ); + } ); + } ); + } ); + + describe( 'editing pipeline conversion', () => { + beforeEach( () => { + return VirtualTestEditor + .create( { + plugins: [ FontSizeEditing, Paragraph ], + fontSize: { + items: [ 'tiny', 'normal', 18, { + label: 'My setting', + model: 'my', + view: { + name: 'mark', + styles: 'font-size: 30px', + classes: 'my-style' + } + } ] + } + } ) + .then( newEditor => { + editor = newEditor; + + doc = editor.document; + } ); + } ); + + it( 'should discard unknown fontSize attribute values', () => { + setModelData( doc, 'f<$text fontSize="foo-bar">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + + it( 'should convert fontSize attribute to predefined named preset', () => { + setModelData( doc, 'f<$text fontSize="text-tiny">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + + it( 'should convert fontSize attribute to predefined pixel size preset', () => { + setModelData( doc, 'f<$text fontSize="18">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + + it( 'should convert fontSize attribute from user defined settings', () => { + setModelData( doc, 'f<$text fontSize="my">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + } ); } ); From bd9c78c57a4b475d4d6cd711c068355bcef75f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 30 Nov 2017 18:54:56 +0100 Subject: [PATCH 006/101] Other: Initial implementation of FontSize data pipeline conversion. --- src/fontsize/fontsizeediting.js | 29 +++++++++++++- tests/fontsize/fontsizeediting.js | 65 ++++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 6d7c622..e703502 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -9,6 +9,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter'; +import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter'; import AttributeElement from '@ckeditor/ckeditor5-engine/src/view/attributeelement'; /** @@ -38,6 +39,32 @@ export default class FontSizeEditing extends Plugin { const data = editor.data; const editing = editor.editing; + for ( const item of this.configuredItems ) { + const viewDefinition = item.view; + const element = viewDefinition.name; + const classes = viewDefinition.classes; + const styles = viewDefinition.styles; + + const attribute = {}; + + if ( classes ) { + attribute.class = classes; + } + + // TODO styles are not normalized in parsing - it require better handling + if ( styles ) { + attribute.style = styles; + } + + buildViewConverter() + .for( data.viewToModel ) + .from( { name: element, attribute } ) + .toAttribute( () => ( { + key: 'fontSize', + value: item.model + } ) ); + } + // Convert model to view buildModelConverter().for( data.modelToView, editing.modelToView ) .fromAttribute( 'fontSize' ) @@ -187,7 +214,7 @@ function generatePixelPreset( size ) { stopValue: size, view: { name: 'span', - styles: `font-size: ${ size }px` + styles: `font-size:${ size }px;` } }; } diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index c0d3430..5e219b8 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -8,7 +8,7 @@ import FontSizeEditing from './../../src/fontsize/fontsizeediting'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; -import { setData as setModelData } from '../../../ckeditor5-engine/src/dev-utils/model'; +import { getData as getModelData, setData as setModelData } from '../../../ckeditor5-engine/src/dev-utils/model'; describe( 'FontSizeEditing', () => { let editor, doc; @@ -122,10 +122,10 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: '10', model: '10', stopValue: 10, view: { name: 'span', styles: 'font-size: 10px' } }, - { label: '12', model: '12', stopValue: 12, view: { name: 'span', styles: 'font-size: 12px' } }, - { label: '14', model: '14', stopValue: 14, view: { name: 'span', styles: 'font-size: 14px' } }, - { label: '18', model: '18', stopValue: 18, view: { name: 'span', styles: 'font-size: 18px' } } + { label: '10', model: '10', stopValue: 10, view: { name: 'span', styles: 'font-size:10px;' } }, + { label: '12', model: '12', stopValue: 12, view: { name: 'span', styles: 'font-size:12px;' } }, + { label: '14', model: '14', stopValue: 14, view: { name: 'span', styles: 'font-size:14px;' } }, + { label: '18', model: '18', stopValue: 18, view: { name: 'span', styles: 'font-size:18px;' } } ] ); } ); } ); @@ -180,4 +180,59 @@ describe( 'FontSizeEditing', () => { expect( editor.getData() ).to.equal( '

foo

' ); } ); } ); + + describe( 'data pipeline conversions', () => { + beforeEach( () => { + return VirtualTestEditor + .create( { + plugins: [ FontSizeEditing, Paragraph ], + fontSize: { + items: [ 'tiny', 'normal', 18, { + label: 'My setting', + model: 'my', + view: { + name: 'mark', + styles: 'font-size:30px;', + classes: 'my-style' + } + } ] + } + } ) + .then( newEditor => { + editor = newEditor; + + doc = editor.document; + } ); + } ); + + it( 'should convert from element with defined class', () => { + const data = '

foo

'; + + editor.setData( data ); + + expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="text-tiny">oo' ); + + expect( editor.getData() ).to.equal( data ); + } ); + + it( 'should convert from element with defined style', () => { + const data = '

foo

'; + + editor.setData( data ); + + expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="18">oo' ); + + expect( editor.getData() ).to.equal( data ); + } ); + + it( 'should convert from user defined element', () => { + const data = '

foo

'; + + editor.setData( data ); + + expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="my">oo' ); + + expect( editor.getData() ).to.equal( data ); + } ); + } ); } ); From 345d3497f1485e25460b866c0ebb8a381f52cdda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 1 Dec 2017 09:10:09 +0100 Subject: [PATCH 007/101] Tests: Add font size manual test stub. --- tests/manual/font-size.html | 43 ++++++++++++++++++++++++++++++++++++ tests/manual/font-size.js | 24 ++++++++++++++++++++ tests/manual/font-size.md | 11 +++++++++ tests/manual/sample.jpg | Bin 0 -> 114298 bytes 4 files changed, 78 insertions(+) create mode 100644 tests/manual/font-size.html create mode 100644 tests/manual/font-size.js create mode 100644 tests/manual/font-size.md create mode 100644 tests/manual/sample.jpg diff --git a/tests/manual/font-size.html b/tests/manual/font-size.html new file mode 100644 index 0000000..a757a47 --- /dev/null +++ b/tests/manual/font-size.html @@ -0,0 +1,43 @@ + + +
+

Font Size feature sample.

+ +

+ This is a mixed text with different sizes of text: + tiny, + small, + big and + huge. +

+ +
    +
  • It's a list item with tiny text
  • +
  • It's a list item with small text
  • +
  • It's a list item with big text
  • +
  • It's a list item with huge text
  • +
+ +
+ CKEditor logo +
+ Sample image with crazy caption. +
+
+
diff --git a/tests/manual/font-size.js b/tests/manual/font-size.js new file mode 100644 index 0000000..0ea5767 --- /dev/null +++ b/tests/manual/font-size.js @@ -0,0 +1,24 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals console, window, document */ + +import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'; +import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset'; +import FontSize from '../../src/fontsize'; + +ClassicEditor + .create( document.querySelector( '#editor' ), { + plugins: [ ArticlePluginSet, FontSize ], + toolbar: [ + 'headings', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' + ] + } ) + .then( editor => { + window.editor = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); diff --git a/tests/manual/font-size.md b/tests/manual/font-size.md new file mode 100644 index 0000000..33d6147 --- /dev/null +++ b/tests/manual/font-size.md @@ -0,0 +1,11 @@ +### Loading + +The data should be loaded with: +- heading with big fragment, +- paragraph with fragments with all font sizes (tiny, small, big, huge), +- list with 4 items - each with different font size fragment, +- image with caption with styled word. + +### Testing + +TODO diff --git a/tests/manual/sample.jpg b/tests/manual/sample.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b77d07e7bfff7fed1563fe2cd190c46ade02905f GIT binary patch literal 114298 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 literal 0 HcmV?d00001 From 9b10a3fd90b8b2d8dd4cb059d18991fc9efd8ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 1 Dec 2017 13:59:22 +0100 Subject: [PATCH 008/101] Other: Implement proper use of matchers and converters. --- src/fontsize/fontsizeediting.js | 34 +++++++++++------ tests/fontsize/fontsizeediting.js | 62 ++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index e703502..2712177 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -45,20 +45,19 @@ export default class FontSizeEditing extends Plugin { const classes = viewDefinition.classes; const styles = viewDefinition.styles; - const attribute = {}; + const pattern = { name: element }; if ( classes ) { - attribute.class = classes; + pattern.class = classes; } - // TODO styles are not normalized in parsing - it require better handling if ( styles ) { - attribute.style = styles; + pattern.style = styles; } buildViewConverter() .for( data.viewToModel ) - .from( { name: element, attribute } ) + .from( pattern ) .toAttribute( () => ( { key: 'fontSize', value: item.model @@ -75,17 +74,18 @@ export default class FontSizeEditing extends Plugin { return; } - // TODO: make utitlity class of this? const viewDefinition = definition.view; + const classes = viewDefinition.classes; + const styles = viewDefinition.styles; const attributes = {}; - if ( viewDefinition.classes ) { - attributes.class = viewDefinition.classes; + if ( classes ) { + attributes.class = Array.isArray( classes ) ? classes.join( ' ' ) : classes; } - if ( viewDefinition.styles ) { - attributes.style = viewDefinition.styles; + if ( styles ) { + attributes.style = typeof styles === 'string' ? styles : toStylesString( styles ); } return new AttributeElement( viewDefinition.name, attributes ); @@ -214,7 +214,19 @@ function generatePixelPreset( size ) { stopValue: size, view: { name: 'span', - styles: `font-size:${ size }px;` + styles: { + 'font-size': `${ size }px` + } } }; } + +function toStylesString( stylesObject ) { + const styles = []; + + for ( const key in stylesObject ) { + styles.push( key + ':' + stylesObject[ key ] ); + } + + return styles.join( ';' ); +} diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index 5e219b8..d347646 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -122,10 +122,10 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: '10', model: '10', stopValue: 10, view: { name: 'span', styles: 'font-size:10px;' } }, - { label: '12', model: '12', stopValue: 12, view: { name: 'span', styles: 'font-size:12px;' } }, - { label: '14', model: '14', stopValue: 14, view: { name: 'span', styles: 'font-size:14px;' } }, - { label: '18', model: '18', stopValue: 18, view: { name: 'span', styles: 'font-size:18px;' } } + { label: '10', model: '10', stopValue: 10, view: { name: 'span', styles: { 'font-size': '10px' } } }, + { label: '12', model: '12', stopValue: 12, view: { name: 'span', styles: { 'font-size': '12px' } } }, + { label: '14', model: '14', stopValue: 14, view: { name: 'span', styles: { 'font-size': '14px' } } }, + { label: '18', model: '18', stopValue: 18, view: { name: 'span', styles: { 'font-size': '18px' } } } ] ); } ); } ); @@ -187,15 +187,29 @@ describe( 'FontSizeEditing', () => { .create( { plugins: [ FontSizeEditing, Paragraph ], fontSize: { - items: [ 'tiny', 'normal', 18, { - label: 'My setting', - model: 'my', - view: { - name: 'mark', - styles: 'font-size:30px;', - classes: 'my-style' + items: [ + 'tiny', + 'normal', + 18, + { + label: 'My setting', + model: 'my', + view: { + name: 'mark', + styles: { 'font-size': '30px' }, + classes: 'my-style' + } + }, + { + label: 'Big multiple classes', + model: 'big-multiple', + // TODO: define ViewElementConfigDefinition interface (wheter strings or arrays to use)? + view: { + name: 'span', + classes: [ 'foo', 'foo-big' ] + } } - } ] + ] } } ) .then( newEditor => { @@ -206,13 +220,23 @@ describe( 'FontSizeEditing', () => { } ); it( 'should convert from element with defined class', () => { - const data = '

foo

'; + const data = '

foo

'; editor.setData( data ); expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="text-tiny">oo' ); - expect( editor.getData() ).to.equal( data ); + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + + it( 'should convert from element with defined multiple classes', () => { + const data = '

foo

'; + + editor.setData( data ); + + expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="big-multiple">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); } ); it( 'should convert from element with defined style', () => { @@ -225,6 +249,16 @@ describe( 'FontSizeEditing', () => { expect( editor.getData() ).to.equal( data ); } ); + it( 'should convert from element with defined style when with other styles', () => { + const data = '

foo

'; + + editor.setData( data ); + + expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="18">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + it( 'should convert from user defined element', () => { const data = '

foo

'; From 35430c4c3ab210eac65d9cd69101be8848a00e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 5 Dec 2017 11:28:52 +0100 Subject: [PATCH 009/101] Other: Initial support fo ViewElementDefinition interface. --- src/fontsize/fontsizeediting.js | 195 +++++++++++++++++++----------- tests/fontsize/fontsizeediting.js | 65 +++++++--- 2 files changed, 174 insertions(+), 86 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 2712177..9928d2b 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -39,57 +39,15 @@ export default class FontSizeEditing extends Plugin { const data = editor.data; const editing = editor.editing; + // Covert view to model for ( const item of this.configuredItems ) { - const viewDefinition = item.view; - const element = viewDefinition.name; - const classes = viewDefinition.classes; - const styles = viewDefinition.styles; - - const pattern = { name: element }; - - if ( classes ) { - pattern.class = classes; - } - - if ( styles ) { - pattern.style = styles; - } - - buildViewConverter() - .for( data.viewToModel ) - .from( pattern ) - .toAttribute( () => ( { - key: 'fontSize', - value: item.model - } ) ); + viewToAttribute( item.view, [ data.viewToModel ], 'fontSize', item.model ); } // Convert model to view - buildModelConverter().for( data.modelToView, editing.modelToView ) - .fromAttribute( 'fontSize' ) - .toElement( data => { - const definition = this._getDefinition( data ); - - if ( !definition ) { - return; - } - - const viewDefinition = definition.view; - const classes = viewDefinition.classes; - const styles = viewDefinition.styles; + const items = this.configuredItems; - const attributes = {}; - - if ( classes ) { - attributes.class = Array.isArray( classes ) ? classes.join( ' ' ) : classes; - } - - if ( styles ) { - attributes.style = typeof styles === 'string' ? styles : toStylesString( styles ); - } - - return new AttributeElement( viewDefinition.name, attributes ); - } ); + attributeToView( [ data.modelToView, editing.modelToView ], 'fontSize', items ); } get configuredItems() { @@ -126,57 +84,39 @@ export default class FontSizeEditing extends Plugin { // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. editor.document.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ); } - - _getDefinition( name ) { - const stringName = String( name ); - - for ( const item of this.configuredItems ) { - if ( item.model === stringName ) { - return item; - } - } - } } const namedPresets = { tiny: { label: 'Tiny', model: 'text-tiny', - // stopValue: .7 - // stopUnit: 'em' view: { name: 'span', - classes: 'text-tiny' + class: 'text-tiny' } }, small: { label: 'Small', model: 'text-small', - // stopValue: .85 - // stopUnit: 'em', view: { name: 'span', - classes: 'text-small' + class: 'text-small' } }, big: { label: 'Big', model: 'text-big', - // stopValue: 1.4 - // stopUnit: 'em', view: { name: 'span', - classes: 'text-big' + class: 'text-big' } }, huge: { label: 'Huge', model: 'text-huge', - // stopValue: 1.8 - // stopUnit: 'em', view: { name: 'span', - classes: 'text-huge' + class: 'text-huge' } } }; @@ -211,10 +151,9 @@ function generatePixelPreset( size ) { return { label: sizeName, model: sizeName, - stopValue: size, view: { name: 'span', - styles: { + style: { 'font-size': `${ size }px` } } @@ -230,3 +169,119 @@ function toStylesString( stylesObject ) { return styles.join( ';' ); } + +function viewToAttribute( view, dispatchers, attributeName, attributeValue ) { + const viewDefinitions = view.from ? view.from : [ view ]; + + for ( const viewDefinition of viewDefinitions ) { + const element = viewDefinition.name; + const classes = viewDefinition.class; + const styles = viewDefinition.style; + + const pattern = { name: element }; + + if ( classes ) { + pattern.class = classes; + } + + if ( styles ) { + pattern.style = styles; + } + + buildViewConverter() + .for( ...dispatchers ) + .from( pattern ) + .toAttribute( () => ( { + key: attributeName, + value: attributeValue + } ) ); + } +} + +function attributeToView( dispatchers, attributeName, items ) { + buildModelConverter() + .for( ...dispatchers ) + .fromAttribute( attributeName ) + .toElement( attributeValue => { + const definition = _getDefinition( attributeValue ); + + if ( !definition ) { + return; + } + + const viewDefinition = definition.view.to ? definition.view.to : definition.view; + // TODO: AttributeElement.fromDefinition() ? + + const classes = viewDefinition.class; + const styles = viewDefinition.style; + + const attributes = {}; + + // TODO: AttributeElement does no accept Array + if ( classes ) { + attributes.class = Array.isArray( classes ) ? classes.join( ' ' ) : classes; + } + + // TODO: Attribute element does not accept Object + if ( styles ) { + attributes.style = typeof styles === 'string' ? styles : toStylesString( styles ); + } + + return new AttributeElement( viewDefinition.name, attributes ); + } ); + + function _getDefinition( name ) { + const stringName = String( name ); + + for ( const item of items ) { + if ( item.model === stringName ) { + return item; + } + } + } +} + +/** + * Font size option descriptor. + * + * @typedef {Object} module:font/fontsize/fontsizeediting~FontSizeOption + * + * @property {String} label TODO me + * @property {String} model TODO me + * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. + */ + +/** + * The configuration of the heading feature. Introduced by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. + * + * Read more in {@link module:font/fontsize/fontsizeediting~FontSizeConfig}. + * + * @member {module:font/fontsize/fontsizeediting~FontSizeConfig} module:core/editor/editorconfig~EditorConfig#fontSize + */ + +/** + * The configuration of the font size feature. + * The option is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. + * + * ClassicEditor + * .create( { + * fontSize: ... // Font size feature config. + * } ) + * .then( ... ) + * .catch( ... ); + * + * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. + * + * @interface module:font/fontsize/fontsizeediting~FontSizeConfig + */ + +/** + * Available font size options. Defined either as array of strings. + * + * The default value is + * TODO code + * which configures + * + * @member {Array.} + * module:font/fontsize/fontsizeediting~FontSizeConfig#items + */ diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index d347646..c524372 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -65,7 +65,7 @@ describe( 'FontSizeEditing', () => { .create( { plugins: [ FontSizeEditing ], fontSize: { - items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', styles: 'font-size: 12em;' } } ] + items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', style: 'font-size: 12em;' } } ] } } ) .then( newEditor => { @@ -77,7 +77,7 @@ describe( 'FontSizeEditing', () => { { label: 'My Size', model: 'my-size', - view: { name: 'span', styles: 'font-size: 12em;' } + view: { name: 'span', style: 'font-size: 12em;' } } ] ); } ); @@ -98,10 +98,10 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: 'Tiny', model: 'text-tiny', view: { name: 'span', classes: 'text-tiny' } }, - { label: 'Small', model: 'text-small', view: { name: 'span', classes: 'text-small' } }, - { label: 'Big', model: 'text-big', view: { name: 'span', classes: 'text-big' } }, - { label: 'Huge', model: 'text-huge', view: { name: 'span', classes: 'text-huge' } } + { label: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, + { label: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, + { label: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, + { label: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } ] ); } ); } ); @@ -122,10 +122,10 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: '10', model: '10', stopValue: 10, view: { name: 'span', styles: { 'font-size': '10px' } } }, - { label: '12', model: '12', stopValue: 12, view: { name: 'span', styles: { 'font-size': '12px' } } }, - { label: '14', model: '14', stopValue: 14, view: { name: 'span', styles: { 'font-size': '14px' } } }, - { label: '18', model: '18', stopValue: 18, view: { name: 'span', styles: { 'font-size': '18px' } } } + { label: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, + { label: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, + { label: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, + { label: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } ] ); } ); } ); @@ -143,8 +143,8 @@ describe( 'FontSizeEditing', () => { model: 'my', view: { name: 'mark', - styles: 'font-size: 30px', - classes: 'my-style' + style: 'font-size: 30px', + class: 'my-style' } } ] } @@ -196,17 +196,30 @@ describe( 'FontSizeEditing', () => { model: 'my', view: { name: 'mark', - styles: { 'font-size': '30px' }, - classes: 'my-style' + style: { 'font-size': '30px' }, + class: 'my-style' } }, { label: 'Big multiple classes', model: 'big-multiple', - // TODO: define ViewElementConfigDefinition interface (wheter strings or arrays to use)? view: { name: 'span', - classes: [ 'foo', 'foo-big' ] + class: [ 'foo', 'foo-big' ] + } + }, + { + label: 'Hybrid', + model: 'complex', + view: { + to: { + name: 'span', + class: [ 'text-complex' ] + }, + from: [ + { name: 'span', style: { 'font-size': '77em' } }, + { name: 'span', attribute: { 'data-size': '77em' } } + ] } } ] @@ -268,5 +281,25 @@ describe( 'FontSizeEditing', () => { expect( editor.getData() ).to.equal( data ); } ); + + it( 'should convert from complex definitions', () => { + editor.setData( + '

foo

' + + '

bar

' + + '

baz

' + ); + + expect( getModelData( doc ) ).to.equal( + '[]f<$text fontSize="complex">oo' + + 'b<$text fontSize="complex">ar' + + 'b<$text fontSize="complex">az' + ); + + expect( editor.getData() ).to.equal( + '

foo

' + + '

bar

' + + '

baz

' + ); + } ); } ); } ); From db88e509e5beee458f7c3466ef586078dda3edc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 5 Dec 2017 14:48:07 +0100 Subject: [PATCH 010/101] Other: Extract implementation of unified converters from ViewElementDefinition interface to ckeditor5-engine. --- src/fontsize/fontsizeediting.js | 97 +++---------------------------- tests/fontsize/fontsizeediting.js | 21 ++++--- 2 files changed, 20 insertions(+), 98 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 9928d2b..be23e14 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -8,9 +8,7 @@ */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; -import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter'; -import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter'; -import AttributeElement from '@ckeditor/ckeditor5-engine/src/view/attributeelement'; +import { modelAttributeToView, viewToModelAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/attributeconverters'; /** * The Font Size Editing feature. @@ -39,15 +37,15 @@ export default class FontSizeEditing extends Plugin { const data = editor.data; const editing = editor.editing; - // Covert view to model for ( const item of this.configuredItems ) { - viewToAttribute( item.view, [ data.viewToModel ], 'fontSize', item.model ); - } + // Covert view to model. + viewToModelAttribute( 'fontSize', item.model, item.view, [ data.viewToModel ] ); - // Convert model to view - const items = this.configuredItems; + // Covert model to view. + modelAttributeToView( 'fontSize', item.model, item.view, [ data.modelToView, editing.modelToView ] ); - attributeToView( [ data.modelToView, editing.modelToView ], 'fontSize', items ); + // Add command. + } } get configuredItems() { @@ -160,87 +158,6 @@ function generatePixelPreset( size ) { }; } -function toStylesString( stylesObject ) { - const styles = []; - - for ( const key in stylesObject ) { - styles.push( key + ':' + stylesObject[ key ] ); - } - - return styles.join( ';' ); -} - -function viewToAttribute( view, dispatchers, attributeName, attributeValue ) { - const viewDefinitions = view.from ? view.from : [ view ]; - - for ( const viewDefinition of viewDefinitions ) { - const element = viewDefinition.name; - const classes = viewDefinition.class; - const styles = viewDefinition.style; - - const pattern = { name: element }; - - if ( classes ) { - pattern.class = classes; - } - - if ( styles ) { - pattern.style = styles; - } - - buildViewConverter() - .for( ...dispatchers ) - .from( pattern ) - .toAttribute( () => ( { - key: attributeName, - value: attributeValue - } ) ); - } -} - -function attributeToView( dispatchers, attributeName, items ) { - buildModelConverter() - .for( ...dispatchers ) - .fromAttribute( attributeName ) - .toElement( attributeValue => { - const definition = _getDefinition( attributeValue ); - - if ( !definition ) { - return; - } - - const viewDefinition = definition.view.to ? definition.view.to : definition.view; - // TODO: AttributeElement.fromDefinition() ? - - const classes = viewDefinition.class; - const styles = viewDefinition.style; - - const attributes = {}; - - // TODO: AttributeElement does no accept Array - if ( classes ) { - attributes.class = Array.isArray( classes ) ? classes.join( ' ' ) : classes; - } - - // TODO: Attribute element does not accept Object - if ( styles ) { - attributes.style = typeof styles === 'string' ? styles : toStylesString( styles ); - } - - return new AttributeElement( viewDefinition.name, attributes ); - } ); - - function _getDefinition( name ) { - const stringName = String( name ); - - for ( const item of items ) { - if ( item.model === stringName ) { - return item; - } - } - } -} - /** * Font size option descriptor. * diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index c524372..766fa13 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -138,15 +138,20 @@ describe( 'FontSizeEditing', () => { .create( { plugins: [ FontSizeEditing, Paragraph ], fontSize: { - items: [ 'tiny', 'normal', 18, { - label: 'My setting', - model: 'my', - view: { - name: 'mark', - style: 'font-size: 30px', - class: 'my-style' + items: [ + 'tiny', + 'normal', + 18, + { + label: 'My setting', + model: 'my', + view: { + name: 'mark', + style: 'font-size: 30px', + class: 'my-style' + } } - } ] + ] } } ) .then( newEditor => { From 419c4de4508204fca4351acc0e748df83c4629bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 11 Dec 2017 17:21:32 +0100 Subject: [PATCH 011/101] Other: Unify ViewElement converters for attribute and element. --- src/fontsize/fontsizeediting.js | 4 ++-- tests/fontsize/fontsizeediting.js | 18 ++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index be23e14..ffda39f 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -39,10 +39,10 @@ export default class FontSizeEditing extends Plugin { for ( const item of this.configuredItems ) { // Covert view to model. - viewToModelAttribute( 'fontSize', item.model, item.view, [ data.viewToModel ] ); + viewToModelAttribute( 'fontSize', item, [ data.viewToModel ] ); // Covert model to view. - modelAttributeToView( 'fontSize', item.model, item.view, [ data.modelToView, editing.modelToView ] ); + modelAttributeToView( 'fontSize', item, [ data.modelToView, editing.modelToView ] ); // Add command. } diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index 766fa13..3581788 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -147,7 +147,7 @@ describe( 'FontSizeEditing', () => { model: 'my', view: { name: 'mark', - style: 'font-size: 30px', + style: { 'font-size': '30px' }, class: 'my-style' } } @@ -217,15 +217,13 @@ describe( 'FontSizeEditing', () => { label: 'Hybrid', model: 'complex', view: { - to: { - name: 'span', - class: [ 'text-complex' ] - }, - from: [ - { name: 'span', style: { 'font-size': '77em' } }, - { name: 'span', attribute: { 'data-size': '77em' } } - ] - } + name: 'span', + class: [ 'text-complex' ] + }, + acceptsAlso: [ + { name: 'span', style: { 'font-size': '77em' } }, + { name: 'span', attribute: { 'data-size': '77em' } } + ] } ] } From ebda823772f4d702b731620f739530e83534d1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 11 Dec 2017 17:41:12 +0100 Subject: [PATCH 012/101] Other: Rename ViewElementDefinition attributes to plural names. --- src/fontsize/fontsizeediting.js | 10 ++++----- tests/fontsize/fontsizeediting.js | 36 +++++++++++++++---------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index ffda39f..7a79e9f 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -90,7 +90,7 @@ const namedPresets = { model: 'text-tiny', view: { name: 'span', - class: 'text-tiny' + classes: 'text-tiny' } }, small: { @@ -98,7 +98,7 @@ const namedPresets = { model: 'text-small', view: { name: 'span', - class: 'text-small' + classes: 'text-small' } }, big: { @@ -106,7 +106,7 @@ const namedPresets = { model: 'text-big', view: { name: 'span', - class: 'text-big' + classes: 'text-big' } }, huge: { @@ -114,7 +114,7 @@ const namedPresets = { model: 'text-huge', view: { name: 'span', - class: 'text-huge' + classes: 'text-huge' } } }; @@ -151,7 +151,7 @@ function generatePixelPreset( size ) { model: sizeName, view: { name: 'span', - style: { + styles: { 'font-size': `${ size }px` } } diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index 3581788..9a08fb2 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -65,7 +65,7 @@ describe( 'FontSizeEditing', () => { .create( { plugins: [ FontSizeEditing ], fontSize: { - items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', style: 'font-size: 12em;' } } ] + items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', styles: 'font-size: 12em;' } } ] } } ) .then( newEditor => { @@ -77,7 +77,7 @@ describe( 'FontSizeEditing', () => { { label: 'My Size', model: 'my-size', - view: { name: 'span', style: 'font-size: 12em;' } + view: { name: 'span', styles: 'font-size: 12em;' } } ] ); } ); @@ -98,10 +98,10 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, - { label: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, - { label: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, - { label: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } + { label: 'Tiny', model: 'text-tiny', view: { name: 'span', classes: 'text-tiny' } }, + { label: 'Small', model: 'text-small', view: { name: 'span', classes: 'text-small' } }, + { label: 'Big', model: 'text-big', view: { name: 'span', classes: 'text-big' } }, + { label: 'Huge', model: 'text-huge', view: { name: 'span', classes: 'text-huge' } } ] ); } ); } ); @@ -122,10 +122,10 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, - { label: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, - { label: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, - { label: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } + { label: '10', model: '10', view: { name: 'span', styles: { 'font-size': '10px' } } }, + { label: '12', model: '12', view: { name: 'span', styles: { 'font-size': '12px' } } }, + { label: '14', model: '14', view: { name: 'span', styles: { 'font-size': '14px' } } }, + { label: '18', model: '18', view: { name: 'span', styles: { 'font-size': '18px' } } } ] ); } ); } ); @@ -147,8 +147,8 @@ describe( 'FontSizeEditing', () => { model: 'my', view: { name: 'mark', - style: { 'font-size': '30px' }, - class: 'my-style' + styles: { 'font-size': '30px' }, + classes: 'my-style' } } ] @@ -201,8 +201,8 @@ describe( 'FontSizeEditing', () => { model: 'my', view: { name: 'mark', - style: { 'font-size': '30px' }, - class: 'my-style' + styles: { 'font-size': '30px' }, + classes: 'my-style' } }, { @@ -210,7 +210,7 @@ describe( 'FontSizeEditing', () => { model: 'big-multiple', view: { name: 'span', - class: [ 'foo', 'foo-big' ] + classes: [ 'foo', 'foo-big' ] } }, { @@ -218,11 +218,11 @@ describe( 'FontSizeEditing', () => { model: 'complex', view: { name: 'span', - class: [ 'text-complex' ] + classes: [ 'text-complex' ] }, acceptsAlso: [ - { name: 'span', style: { 'font-size': '77em' } }, - { name: 'span', attribute: { 'data-size': '77em' } } + { name: 'span', styles: { 'font-size': '77em' } }, + { name: 'span', attributes: { 'data-size': '77em' } } ] } ] From 284baf0d42b37daa4d065361921f7b982559eab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 12 Dec 2017 11:18:14 +0100 Subject: [PATCH 013/101] Other: Rename AttributeElement conversion helpers. --- src/fontsize/fontsizeediting.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 7a79e9f..3e846b1 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -8,7 +8,10 @@ */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; -import { modelAttributeToView, viewToModelAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/attributeconverters'; +import { + attributeElementToViewConverter, + viewToAttributeElementConverter +} from '@ckeditor/ckeditor5-engine/src/conversion/attributeelementconverters'; /** * The Font Size Editing feature. @@ -39,10 +42,10 @@ export default class FontSizeEditing extends Plugin { for ( const item of this.configuredItems ) { // Covert view to model. - viewToModelAttribute( 'fontSize', item, [ data.viewToModel ] ); + viewToAttributeElementConverter( 'fontSize', item, [ data.viewToModel ] ); // Covert model to view. - modelAttributeToView( 'fontSize', item, [ data.modelToView, editing.modelToView ] ); + attributeElementToViewConverter( 'fontSize', item, [ data.modelToView, editing.modelToView ] ); // Add command. } From 1b26cb79e3bee059a3e79c8118a426be306d5f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 14 Dec 2017 11:50:51 +0100 Subject: [PATCH 014/101] Other: Merge configuration defined converters into one file. --- src/fontsize/fontsizeediting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 3e846b1..9c28dc4 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -11,7 +11,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import { attributeElementToViewConverter, viewToAttributeElementConverter -} from '@ckeditor/ckeditor5-engine/src/conversion/attributeelementconverters'; +} from '@ckeditor/ckeditor5-engine/src/conversion/configurationdefinedconverters'; /** * The Font Size Editing feature. From 8efb27d4592a97b0a71385c03b375fed32ed02b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Sun, 17 Dec 2017 13:14:16 +0100 Subject: [PATCH 015/101] Other: Update Font Size feature to latest changes in engine. --- src/fontsize/fontsizecommand.js | 20 +++++++++----------- src/fontsize/fontsizeediting.js | 12 ++++++------ tests/fontsize/fontsizecommand.js | 30 +++++++++++++++--------------- tests/fontsize/fontsizeediting.js | 8 ++++---- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index 29e111e..86cc950 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -37,10 +37,10 @@ export default class FontSizeCommand extends Command { * @inheritDoc */ refresh() { - const doc = this.editor.document; + const doc = this.editor.model.document; this.value = doc.selection.getAttribute( 'fontSize' ) === this.fontSize; - this.isEnabled = doc.schema.checkAttributeInSelection( doc.selection, 'fontSize' ); + this.isEnabled = this.editor.model.schema.checkAttributeInSelection( doc.selection, 'fontSize' ); } /** @@ -48,24 +48,22 @@ export default class FontSizeCommand extends Command { * * @protected * @param {Object} [options] Options for the executed command. - * @param {module:engine/model/batch~Batch} [options.batch] A batch to collect all the change steps. - * A new batch will be created if this option is not set. */ - execute( options = {} ) { - const doc = this.editor.document; - const selection = doc.selection; + execute() { + const model = this.editor.model; + const document = model.document; + const selection = document.selection; // Do not apply fontSize on collapsed selection. if ( selection.isCollapsed ) { return; } - doc.enqueueChanges( () => { - const ranges = doc.schema.getValidRanges( selection.getRanges(), 'fontSize' ); - const batch = options.batch || doc.batch(); + model.change( writer => { + const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontSize' ); for ( const range of ranges ) { - batch.setAttribute( 'fontSize', this.fontSize, range ); + writer.setAttribute( 'fontSize', this.fontSize, range ); } } ); } diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 9c28dc4..5f16173 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -9,8 +9,8 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import { - attributeElementToViewConverter, - viewToAttributeElementConverter + modelAttributeToViewAttributeElement, + viewToModelAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/configurationdefinedconverters'; /** @@ -42,10 +42,10 @@ export default class FontSizeEditing extends Plugin { for ( const item of this.configuredItems ) { // Covert view to model. - viewToAttributeElementConverter( 'fontSize', item, [ data.viewToModel ] ); + viewToModelAttribute( 'fontSize', item, [ data.viewToModel ] ); // Covert model to view. - attributeElementToViewConverter( 'fontSize', item, [ data.modelToView, editing.modelToView ] ); + modelAttributeToViewAttributeElement( 'fontSize', item, [ data.modelToView, editing.modelToView ] ); // Add command. } @@ -81,9 +81,9 @@ export default class FontSizeEditing extends Plugin { const editor = this.editor; // Allow highlight attribute on all elements - editor.document.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); + editor.model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. - editor.document.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ); + editor.model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ); } } diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js index a98fb44..c94fadc 100644 --- a/tests/fontsize/fontsizecommand.js +++ b/tests/fontsize/fontsizecommand.js @@ -10,20 +10,20 @@ import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltestedit import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; describe( 'FontSizeCommand', () => { - let editor, doc, command; + let editor, model, command; beforeEach( () => { return ModelTestEditor.create() .then( newEditor => { - doc = newEditor.document; + model = newEditor.model; command = new FontSizeCommand( newEditor, 'text-huge' ); editor = newEditor; editor.commands.add( 'fontSize', command ); - doc.schema.registerItem( 'paragraph', '$block' ); + model.schema.registerItem( 'paragraph', '$block' ); - doc.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); + model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); } ); } ); @@ -38,13 +38,13 @@ describe( 'FontSizeCommand', () => { describe( 'value', () => { it( 'is set to true when selection is in text with fontSize attribute', () => { - setData( doc, '<$text fontSize="text-huge">fo[]o' ); + setData( model, '<$text fontSize="text-huge">fo[]o' ); expect( command ).to.have.property( 'value', true ); } ); it( 'is undefined when selection is not in text with fontSize attribute', () => { - setData( doc, 'fo[]o' ); + setData( model, 'fo[]o' ); expect( command ).to.have.property( 'value', false ); } ); @@ -52,7 +52,7 @@ describe( 'FontSizeCommand', () => { describe( 'isEnabled', () => { it( 'is true when selection is on text which can have fontSize added', () => { - setData( doc, 'fo[]o' ); + setData( model, 'fo[]o' ); expect( command ).to.have.property( 'isEnabled', true ); } ); @@ -60,7 +60,7 @@ describe( 'FontSizeCommand', () => { describe( 'execute()', () => { it( 'should add fontSize attribute on selected text', () => { - setData( doc, 'a[bc<$text fontSize="text-huge">fo]obarxyz' ); + setData( model, 'a[bc<$text fontSize="text-huge">fo]obarxyz' ); expect( command.value ).to.be.false; @@ -68,12 +68,12 @@ describe( 'FontSizeCommand', () => { expect( command.value ).to.be.true; - expect( getData( doc ) ).to.equal( 'a[<$text fontSize="text-huge">bcfo]obarxyz' ); + expect( getData( model ) ).to.equal( 'a[<$text fontSize="text-huge">bcfo]obarxyz' ); } ); it( 'should add fontSize attribute on selected nodes (multiple nodes)', () => { setData( - doc, + model, 'abcabc[abc' + 'foofoofoo' + 'barbar]bar' @@ -83,7 +83,7 @@ describe( 'FontSizeCommand', () => { expect( command.value ).to.be.true; - expect( getData( doc ) ).to.equal( + expect( getData( model ) ).to.equal( 'abcabc[<$text fontSize="text-huge">abc' + '<$text fontSize="text-huge">foofoofoo' + '<$text fontSize="text-huge">barbar]bar' @@ -92,7 +92,7 @@ describe( 'FontSizeCommand', () => { it( 'should change fontSize attribute on selected nodes', () => { setData( - doc, + model, 'abc[abc<$text fontSize="text-small">abc' + '<$text fontSize="text-small">foofoofoo' + '<$text fontSize="text-small">bar]barbar' @@ -102,7 +102,7 @@ describe( 'FontSizeCommand', () => { expect( command.value ).to.be.true; - expect( getData( doc ) ).to.equal( + expect( getData( model ) ).to.equal( 'abc[<$text fontSize="text-huge">abcabc' + '<$text fontSize="text-huge">foofoofoo' + '<$text fontSize="text-huge">bar]<$text fontSize="text-small">barbar' @@ -110,13 +110,13 @@ describe( 'FontSizeCommand', () => { } ); it( 'should do nothing on collapsed range', () => { - setData( doc, 'abc<$text fontSize="text-huge">foo[]barxyz' ); + setData( model, 'abc<$text fontSize="text-huge">foo[]barxyz' ); expect( command.value ).to.be.true; command.execute(); - expect( getData( doc ) ).to.equal( 'abc<$text fontSize="text-huge">foo[]barxyz' ); + expect( getData( model ) ).to.equal( 'abc<$text fontSize="text-huge">foo[]barxyz' ); expect( command.value ).to.be.true; } ); diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index 9a08fb2..e31ddaa 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -30,8 +30,8 @@ describe( 'FontSizeEditing', () => { } ); it( 'should set proper schema rules', () => { - expect( doc.schema.check( { name: '$inline', attributes: 'fontSize', inside: '$block' } ) ).to.be.true; - expect( doc.schema.check( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ) ).to.be.true; + expect( editor.model.schema.check( { name: '$inline', attributes: 'fontSize', inside: '$block' } ) ).to.be.true; + expect( editor.model.schema.check( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ) ).to.be.true; } ); describe( 'config', () => { @@ -157,7 +157,7 @@ describe( 'FontSizeEditing', () => { .then( newEditor => { editor = newEditor; - doc = editor.document; + doc = editor.model; } ); } ); @@ -231,7 +231,7 @@ describe( 'FontSizeEditing', () => { .then( newEditor => { editor = newEditor; - doc = editor.document; + doc = editor.model; } ); } ); From 152e48f5ffbface656636b181c00c46e3f79b8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 18 Dec 2017 11:46:31 +0100 Subject: [PATCH 016/101] Tests: Reorder `FontSizeCommand` test. --- tests/fontsize/fontsizecommand.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js index c94fadc..084a684 100644 --- a/tests/fontsize/fontsizecommand.js +++ b/tests/fontsize/fontsizecommand.js @@ -15,14 +15,13 @@ describe( 'FontSizeCommand', () => { beforeEach( () => { return ModelTestEditor.create() .then( newEditor => { - model = newEditor.model; - command = new FontSizeCommand( newEditor, 'text-huge' ); editor = newEditor; + model = editor.model; + command = new FontSizeCommand( editor, 'text-huge' ); editor.commands.add( 'fontSize', command ); model.schema.registerItem( 'paragraph', '$block' ); - model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); } ); } ); From b8a9311d9a6709395109e0df244363adbf29f683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 19 Dec 2017 10:15:16 +0100 Subject: [PATCH 017/101] Other: Rename configurationdefinedconverters to definition-based-converters. --- src/fontsize/fontsizeediting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 5f16173..a3e9968 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -11,7 +11,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import { modelAttributeToViewAttributeElement, viewToModelAttribute -} from '@ckeditor/ckeditor5-engine/src/conversion/configurationdefinedconverters'; +} from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters'; /** * The Font Size Editing feature. From 174e6ee885385df509dd8611478cc584868adbbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 19 Dec 2017 14:33:55 +0100 Subject: [PATCH 018/101] Refactor: Use singular class and styles in ViewElementDefinition. --- src/fontsize/fontsizeediting.js | 10 ++++----- tests/fontsize/fontsizeediting.js | 34 +++++++++++++++---------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index a3e9968..272f8b0 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -93,7 +93,7 @@ const namedPresets = { model: 'text-tiny', view: { name: 'span', - classes: 'text-tiny' + class: 'text-tiny' } }, small: { @@ -101,7 +101,7 @@ const namedPresets = { model: 'text-small', view: { name: 'span', - classes: 'text-small' + class: 'text-small' } }, big: { @@ -109,7 +109,7 @@ const namedPresets = { model: 'text-big', view: { name: 'span', - classes: 'text-big' + class: 'text-big' } }, huge: { @@ -117,7 +117,7 @@ const namedPresets = { model: 'text-huge', view: { name: 'span', - classes: 'text-huge' + class: 'text-huge' } } }; @@ -154,7 +154,7 @@ function generatePixelPreset( size ) { model: sizeName, view: { name: 'span', - styles: { + style: { 'font-size': `${ size }px` } } diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index e31ddaa..8577898 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -65,7 +65,7 @@ describe( 'FontSizeEditing', () => { .create( { plugins: [ FontSizeEditing ], fontSize: { - items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', styles: 'font-size: 12em;' } } ] + items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', style: 'font-size: 12em;' } } ] } } ) .then( newEditor => { @@ -77,7 +77,7 @@ describe( 'FontSizeEditing', () => { { label: 'My Size', model: 'my-size', - view: { name: 'span', styles: 'font-size: 12em;' } + view: { name: 'span', style: 'font-size: 12em;' } } ] ); } ); @@ -98,10 +98,10 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: 'Tiny', model: 'text-tiny', view: { name: 'span', classes: 'text-tiny' } }, - { label: 'Small', model: 'text-small', view: { name: 'span', classes: 'text-small' } }, - { label: 'Big', model: 'text-big', view: { name: 'span', classes: 'text-big' } }, - { label: 'Huge', model: 'text-huge', view: { name: 'span', classes: 'text-huge' } } + { label: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, + { label: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, + { label: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, + { label: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } ] ); } ); } ); @@ -122,10 +122,10 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: '10', model: '10', view: { name: 'span', styles: { 'font-size': '10px' } } }, - { label: '12', model: '12', view: { name: 'span', styles: { 'font-size': '12px' } } }, - { label: '14', model: '14', view: { name: 'span', styles: { 'font-size': '14px' } } }, - { label: '18', model: '18', view: { name: 'span', styles: { 'font-size': '18px' } } } + { label: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, + { label: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, + { label: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, + { label: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } ] ); } ); } ); @@ -147,8 +147,8 @@ describe( 'FontSizeEditing', () => { model: 'my', view: { name: 'mark', - styles: { 'font-size': '30px' }, - classes: 'my-style' + style: { 'font-size': '30px' }, + class: 'my-style' } } ] @@ -201,8 +201,8 @@ describe( 'FontSizeEditing', () => { model: 'my', view: { name: 'mark', - styles: { 'font-size': '30px' }, - classes: 'my-style' + style: { 'font-size': '30px' }, + class: 'my-style' } }, { @@ -210,7 +210,7 @@ describe( 'FontSizeEditing', () => { model: 'big-multiple', view: { name: 'span', - classes: [ 'foo', 'foo-big' ] + class: [ 'foo', 'foo-big' ] } }, { @@ -218,10 +218,10 @@ describe( 'FontSizeEditing', () => { model: 'complex', view: { name: 'span', - classes: [ 'text-complex' ] + class: [ 'text-complex' ] }, acceptsAlso: [ - { name: 'span', styles: { 'font-size': '77em' } }, + { name: 'span', style: { 'font-size': '77em' } }, { name: 'span', attributes: { 'data-size': '77em' } } ] } From 58a8c857127ee07cc1b58a0e957a50152222cb9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 20 Dec 2017 17:29:30 +0100 Subject: [PATCH 019/101] Added: Create commands in FontSizeEditing plugin. --- src/fontsize/fontsizeediting.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 272f8b0..bc37ec7 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -13,6 +13,8 @@ import { viewToModelAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters'; +import FontSizeCommand from './fontsizecommand'; + /** * The Font Size Editing feature. * @@ -41,6 +43,11 @@ export default class FontSizeEditing extends Plugin { const editing = editor.editing; for ( const item of this.configuredItems ) { + if ( item.model === 'normal' ) { + editor.commands.add( 'font-size:normal', new FontSizeCommand( editor ) ); + continue; + } + // Covert view to model. viewToModelAttribute( 'fontSize', item, [ data.viewToModel ] ); @@ -48,6 +55,7 @@ export default class FontSizeEditing extends Plugin { modelAttributeToViewAttributeElement( 'fontSize', item, [ data.modelToView, editing.modelToView ] ); // Add command. + editor.commands.add( 'font-size:' + item.model, new FontSizeCommand( editor, item.model ) ); } } @@ -89,7 +97,7 @@ export default class FontSizeEditing extends Plugin { const namedPresets = { tiny: { - label: 'Tiny', + title: 'Tiny', model: 'text-tiny', view: { name: 'span', @@ -97,7 +105,7 @@ const namedPresets = { } }, small: { - label: 'Small', + title: 'Small', model: 'text-small', view: { name: 'span', @@ -105,7 +113,7 @@ const namedPresets = { } }, big: { - label: 'Big', + title: 'Big', model: 'text-big', view: { name: 'span', @@ -113,7 +121,7 @@ const namedPresets = { } }, huge: { - label: 'Huge', + title: 'Huge', model: 'text-huge', view: { name: 'span', @@ -134,6 +142,13 @@ function getItemDefinition( item ) { return item; } + if ( item === 'normal' ) { + return { + model: 'normal', + title: 'Normal' + }; + } + // At this stage we probably have numerical value to generate a preset so parse it's value. const sizePreset = parseInt( item ); // TODO: Should we parse floats? 🤔 @@ -150,7 +165,7 @@ function generatePixelPreset( size ) { const sizeName = String( size ); return { - label: sizeName, + title: sizeName, model: sizeName, view: { name: 'span', @@ -166,7 +181,7 @@ function generatePixelPreset( size ) { * * @typedef {Object} module:font/fontsize/fontsizeediting~FontSizeOption * - * @property {String} label TODO me + * @property {String} title TODO me * @property {String} model TODO me * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. */ From 62b2d9bd7d948dc29748653b9c919d09a83af90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 21 Dec 2017 11:37:57 +0100 Subject: [PATCH 020/101] Tests: Add 'normal' setting to tests. --- tests/fontsize/fontsizeediting.js | 34 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index 8577898..b09125b 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -43,7 +43,7 @@ describe( 'FontSizeEditing', () => { } ); describe( 'configuredItems', () => { - it( 'should discard falsy values', () => { + it( 'should discard unsupported values', () => { return VirtualTestEditor .create( { plugins: [ FontSizeEditing ], @@ -56,7 +56,7 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); - expect( plugin.configuredItems ).to.deep.equal( [] ); + expect( plugin.configuredItems ).to.deep.equal( [ { title: 'Normal', model: 'normal' } ] ); } ); } ); @@ -65,7 +65,7 @@ describe( 'FontSizeEditing', () => { .create( { plugins: [ FontSizeEditing ], fontSize: { - items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', style: 'font-size: 12em;' } } ] + items: [ { title: 'My Size', model: 'my-size', view: { name: 'span', style: 'font-size: 12em;' } } ] } } ) .then( newEditor => { @@ -75,7 +75,7 @@ describe( 'FontSizeEditing', () => { expect( plugin.configuredItems ).to.deep.equal( [ { - label: 'My Size', + title: 'My Size', model: 'my-size', view: { name: 'span', style: 'font-size: 12em;' } } @@ -98,10 +98,11 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, - { label: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, - { label: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, - { label: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } + { title: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, + { title: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, + { title: 'Normal', model: 'normal' }, + { title: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, + { title: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } ] ); } ); } ); @@ -122,10 +123,11 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); expect( plugin.configuredItems ).to.deep.equal( [ - { label: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, - { label: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, - { label: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, - { label: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } + { title: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, + { title: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, + { title: 'Normal', model: 'normal' }, + { title: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, + { title: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } ] ); } ); } ); @@ -143,7 +145,7 @@ describe( 'FontSizeEditing', () => { 'normal', 18, { - label: 'My setting', + title: 'My setting', model: 'my', view: { name: 'mark', @@ -197,7 +199,7 @@ describe( 'FontSizeEditing', () => { 'normal', 18, { - label: 'My setting', + title: 'My setting', model: 'my', view: { name: 'mark', @@ -206,7 +208,7 @@ describe( 'FontSizeEditing', () => { } }, { - label: 'Big multiple classes', + title: 'Big multiple classes', model: 'big-multiple', view: { name: 'span', @@ -214,7 +216,7 @@ describe( 'FontSizeEditing', () => { } }, { - label: 'Hybrid', + title: 'Hybrid', model: 'complex', view: { name: 'span', From dc170d42833ef124e98ba17255ba4152c67ca6d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Sun, 17 Dec 2017 17:36:30 +0100 Subject: [PATCH 021/101] Other: Bootstrap FontFamilyEditing and FontFamilyCommand. --- src/fontfamily/fontfamilycommand.js | 72 ++++++++++++ src/fontfamily/fontfamilyediting.js | 159 ++++++++++++++++++++++++++ tests/fontfamily/fontfamilycommand.js | 124 ++++++++++++++++++++ tests/fontfamily/fontfamilyediting.js | 45 ++++++++ 4 files changed, 400 insertions(+) create mode 100644 src/fontfamily/fontfamilycommand.js create mode 100644 src/fontfamily/fontfamilyediting.js create mode 100644 tests/fontfamily/fontfamilycommand.js create mode 100644 tests/fontfamily/fontfamilyediting.js diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js new file mode 100644 index 0000000..8e5a11e --- /dev/null +++ b/src/fontfamily/fontfamilycommand.js @@ -0,0 +1,72 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontfamilycommand + */ + +import Command from '@ckeditor/ckeditor5-core/src/command'; + +/** + * The font family command. It is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} + * to apply font family. + * + * @extends module:core/command~Command + */ +export default class FontFamilyCommand extends Command { + constructor( editor, fontFamily ) { + super( editor ); + + /** + * Name of font family config. + */ + this.fontFamily = fontFamily; + + /** + * A flag indicating whether the command is active, which means that the selection has fontFamily attribute set. + * + * @observable + * @readonly + * @member {Boolean} module:font/fontfamilycommand~FontFamilyCommand#value + */ + } + + /** + * @inheritDoc + */ + refresh() { + const doc = this.editor.model.document; + + this.value = doc.selection.getAttribute( 'fontFamily' ) === this.fontFamily; + this.isEnabled = this.editor.model.schema.checkAttributeInSelection( doc.selection, 'fontFamily' ); + } + + /** + * Executes the command. + * + * @protected + * @param {Object} [options] Options for the executed command. + * @param {module:engine/model/batch~Batch} [options.batch] A batch to collect all the change steps. + * A new batch will be created if this option is not set. + */ + execute() { + const model = this.editor.model; + const document = model.document; + const selection = document.selection; + + // Do not apply fontFamily on collapsed selection. + if ( selection.isCollapsed ) { + return; + } + + model.change( writer => { + const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontFamily' ); + + for ( const range of ranges ) { + writer.setAttribute( 'fontFamily', this.fontFamily, range ); + } + } ); + } +} diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js new file mode 100644 index 0000000..168b0e7 --- /dev/null +++ b/src/fontfamily/fontfamilyediting.js @@ -0,0 +1,159 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontfamily/fontfamilyediting + */ + +import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; +import { + modelAttributeToViewAttributeElement, + viewToModelAttribute +} from '@ckeditor/ckeditor5-engine/src/conversion/configurationdefinedconverters'; + +/** + * The Font Family Editing feature. + * + * @extends module:core/plugin~Plugin + */ +export default class FontFamilyEditing extends Plugin { + /** + * @inheritDoc + */ + constructor( editor ) { + super( editor ); + + // Define default configuration using named presets + editor.config.define( 'fontFamily', { + items: [ + 'default', + 'Arial, Helvetica, sans-serif', + 'Courier New, Courier, monospace' + ] + } ); + + // Get configuration + const data = editor.data; + const editing = editor.editing; + + for ( const item of this.configuredItems ) { + // Covert view to model. + viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] ); + + // Covert model to view. + modelAttributeToViewAttributeElement( 'fontFamily', item, [ data.modelToView, editing.modelToView ] ); + + // Add command. + } + } + + get configuredItems() { + // Cache value + if ( this._cachedItems ) { + return this._cachedItems; + } + + const items = []; + const editor = this.editor; + const config = editor.config; + + const configuredItems = config.get( 'fontFamily.items' ); + + for ( const item of configuredItems ) { + const itemDefinition = getItemDefinition( item ); + + if ( itemDefinition ) { + items.push( itemDefinition ); + } + } + + return ( this._cachedItems = items ); + } + + /** + * @inheritDoc + */ + init() { + const editor = this.editor; + + // Allow highlight attribute on all elements + editor.model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$block' } ); + // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. + editor.model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$clipboardHolder' } ); + } +} + +// Returns item definition from preset +function getItemDefinition( item ) { + // Probably it is full item definition so return it + if ( typeof item === 'object' ) { + return item; + } + + return generateFontPreset( item ); +} + +// Creates a predefined preset for pixel size. +function generateFontPreset( font ) { + const fontNames = font.split( ',' ); + + const cssFontNames = fontNames.join( fontNames ); + + return { + label: fontNames[ 0 ], + model: fontNames[ 0 ], + view: { + name: 'span', + styles: { + 'font-family': cssFontNames + } + } + }; +} + +/** + * Font family option descriptor. + * + * @typedef {Object} module:font/fontfamily/fontfamilyediting~FontFamilyOption + * + * @property {String} label TODO me + * @property {String} model TODO me + * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. + */ + +/** + * The configuration of the heading feature. Introduced by the {@link module:font/fontfamily/fontfamilyediting~FontSizeEditing} feature. + * + * Read more in {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig}. + * + * @member {module:font/fontfamily/fontfamilyediting~FontFamilyConfig} module:core/editor/editorconfig~EditorConfig#fontFamily + */ + +/** + * The configuration of the font family feature. + * The option is used by the {@link module:font/fontfamily/fontfamilyediting~FontSizeEditing} feature. + * + * ClassicEditor + * .create( { + * fontFamily: ... // Font family feature config. + * } ) + * .then( ... ) + * .catch( ... ); + * + * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. + * + * @interface module:font/fontfamily/fontfamilyediting~FontFamilyConfig + */ + +/** + * Available font family options. Defined either as array of strings. + * + * The default value is + * TODO code + * which configures + * + * @member {Array.} + * module:font/fontfamily/fontfamilyediting~FontFamilyConfig#items + */ diff --git a/tests/fontfamily/fontfamilycommand.js b/tests/fontfamily/fontfamilycommand.js new file mode 100644 index 0000000..b53bb18 --- /dev/null +++ b/tests/fontfamily/fontfamilycommand.js @@ -0,0 +1,124 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import FontFamilyCommand from '../../src/fontfamily/fontfamilycommand'; + +import Command from '@ckeditor/ckeditor5-core/src/command'; +import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; +import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; + +describe( 'FontFamilyCommand', () => { + let editor, model, command; + + beforeEach( () => { + return ModelTestEditor.create() + .then( newEditor => { + editor = newEditor; + command = new FontFamilyCommand( editor, 'arial' ); + model = editor.model; + + editor.commands.add( 'fontFamily', command ); + + model.schema.registerItem( 'paragraph', '$block' ); + + model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$block' } ); + } ); + } ); + + afterEach( () => { + editor.destroy(); + } ); + + it( 'is a command', () => { + expect( FontFamilyCommand.prototype ).to.be.instanceOf( Command ); + expect( command ).to.be.instanceOf( Command ); + } ); + + describe( 'value', () => { + it( 'is set to true when selection is in text with fontFamily attribute', () => { + setData( model, '<$text fontFamily="arial">fo[]o' ); + + expect( command ).to.have.property( 'value', true ); + } ); + + it( 'is undefined when selection is not in text with fontFamily attribute', () => { + setData( model, 'fo[]o' ); + + expect( command ).to.have.property( 'value', false ); + } ); + } ); + + describe( 'isEnabled', () => { + it( 'is true when selection is on text which can have fontFamily added', () => { + setData( model, 'fo[]o' ); + + expect( command ).to.have.property( 'isEnabled', true ); + } ); + } ); + + describe( 'execute()', () => { + it( 'should add fontFamily attribute on selected text', () => { + setData( model, 'a[bc<$text fontFamily="arial">fo]obarxyz' ); + + expect( command.value ).to.be.false; + + command.execute(); + + expect( command.value ).to.be.true; + + expect( getData( model ) ).to.equal( 'a[<$text fontFamily="arial">bcfo]obarxyz' ); + } ); + + it( 'should add fontFamily attribute on selected nodes (multiple nodes)', () => { + setData( + model, + 'abcabc[abc' + + 'foofoofoo' + + 'barbar]bar' + ); + + command.execute(); + + expect( command.value ).to.be.true; + + expect( getData( model ) ).to.equal( + 'abcabc[<$text fontFamily="arial">abc' + + '<$text fontFamily="arial">foofoofoo' + + '<$text fontFamily="arial">barbar]bar' + ); + } ); + + it( 'should change fontFamily attribute on selected nodes', () => { + setData( + model, + 'abc[abc<$text fontFamily="text-small">abc' + + '<$text fontFamily="text-small">foofoofoo' + + '<$text fontFamily="text-small">bar]barbar' + ); + + command.execute(); + + expect( command.value ).to.be.true; + + expect( getData( model ) ).to.equal( + 'abc[<$text fontFamily="arial">abcabc' + + '<$text fontFamily="arial">foofoofoo' + + '<$text fontFamily="arial">bar]<$text fontFamily="text-small">barbar' + ); + } ); + + it( 'should do nothing on collapsed range', () => { + setData( model, 'abc<$text fontFamily="arial">foo[]barxyz' ); + + expect( command.value ).to.be.true; + + command.execute(); + + expect( getData( model ) ).to.equal( 'abc<$text fontFamily="arial">foo[]barxyz' ); + + expect( command.value ).to.be.true; + } ); + } ); +} ); diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js new file mode 100644 index 0000000..42179d9 --- /dev/null +++ b/tests/fontfamily/fontfamilyediting.js @@ -0,0 +1,45 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import FontFamilyEditing from './../../src/fontfamily/fontfamilyediting'; + +import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; + +import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; + +describe( 'FontFamilyEditing', () => { + let editor; + + beforeEach( () => { + return VirtualTestEditor + .create( { + plugins: [ FontFamilyEditing, Paragraph ] + } ) + .then( newEditor => { + editor = newEditor; + } ); + } ); + + afterEach( () => { + editor.destroy(); + } ); + + it( 'should set proper schema rules', () => { + expect( editor.model.schema.check( { name: '$inline', attributes: 'fontFamily', inside: '$block' } ) ).to.be.true; + expect( editor.model.schema.check( { name: '$inline', attributes: 'fontFamily', inside: '$clipboardHolder' } ) ).to.be.true; + } ); + + describe( 'config', () => { + describe( 'default value', () => { + it( 'should be set', () => { + expect( editor.config.get( 'fontFamily.items' ) ).to.deep.equal( [ + 'default', + 'Arial, Helvetica, sans-serif', + 'Courier New, Courier, monospace' + ] ); + } ); + } ); + } ); +} ); From 5f3c60cdcc6a47cf501caf189d3421422538307d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 18 Dec 2017 11:45:53 +0100 Subject: [PATCH 022/101] Tests: Add more tests for `FontFamilyEditing` and `FontFamilyCommand`. --- src/fontfamily/fontfamilyediting.js | 27 ++- tests/fontfamily/fontfamilycommand.js | 3 +- tests/fontfamily/fontfamilyediting.js | 253 +++++++++++++++++++++++++- 3 files changed, 274 insertions(+), 9 deletions(-) diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 168b0e7..c8429cb 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -28,7 +28,6 @@ export default class FontFamilyEditing extends Plugin { // Define default configuration using named presets editor.config.define( 'fontFamily', { items: [ - 'default', 'Arial, Helvetica, sans-serif', 'Courier New, Courier, monospace' ] @@ -92,18 +91,26 @@ function getItemDefinition( item ) { return item; } + // Ignore falsy values + if ( typeof item !== 'string' ) { + return; + } + return generateFontPreset( item ); } // Creates a predefined preset for pixel size. function generateFontPreset( font ) { - const fontNames = font.split( ',' ); + // Remove quotes from font names + const fontNames = font.replace( /"/g, '' ).split( ',' ); - const cssFontNames = fontNames.join( fontNames ); + const cssFontNames = fontNames.map( cleanFontName ).join( ', ' ); + + const firstFontName = fontNames[ 0 ]; return { - label: fontNames[ 0 ], - model: fontNames[ 0 ], + label: firstFontName, + model: firstFontName, view: { name: 'span', styles: { @@ -113,6 +120,16 @@ function generateFontPreset( font ) { }; } +function cleanFontName( fontName ) { + fontName = fontName.trim(); + + if ( fontName.indexOf( ' ' ) > 0 ) { + fontName = `"${ fontName }"`; + } + + return fontName; +} + /** * Font family option descriptor. * diff --git a/tests/fontfamily/fontfamilycommand.js b/tests/fontfamily/fontfamilycommand.js index b53bb18..104c20b 100644 --- a/tests/fontfamily/fontfamilycommand.js +++ b/tests/fontfamily/fontfamilycommand.js @@ -16,13 +16,12 @@ describe( 'FontFamilyCommand', () => { return ModelTestEditor.create() .then( newEditor => { editor = newEditor; - command = new FontFamilyCommand( editor, 'arial' ); model = editor.model; + command = new FontFamilyCommand( editor, 'arial' ); editor.commands.add( 'fontFamily', command ); model.schema.registerItem( 'paragraph', '$block' ); - model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$block' } ); } ); } ); diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 42179d9..069b8fd 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -8,9 +8,10 @@ import FontFamilyEditing from './../../src/fontfamily/fontfamilyediting'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; +import { getData as getModelData, setData as setModelData } from '../../../ckeditor5-engine/src/dev-utils/model'; describe( 'FontFamilyEditing', () => { - let editor; + let editor, doc; beforeEach( () => { return VirtualTestEditor @@ -19,6 +20,8 @@ describe( 'FontFamilyEditing', () => { } ) .then( newEditor => { editor = newEditor; + + doc = editor.document; } ); } ); @@ -35,11 +38,257 @@ describe( 'FontFamilyEditing', () => { describe( 'default value', () => { it( 'should be set', () => { expect( editor.config.get( 'fontFamily.items' ) ).to.deep.equal( [ - 'default', 'Arial, Helvetica, sans-serif', 'Courier New, Courier, monospace' ] ); } ); } ); } ); + + describe( 'configuredItems', () => { + it( 'should discard unparsable values', () => { + return VirtualTestEditor + .create( { + plugins: [ FontFamilyEditing ], + fontFamily: { + items: [ () => {}, 0, true ] + } + } ) + .then( newEditor => { + editor = newEditor; + + const plugin = editor.plugins.get( FontFamilyEditing ); + + expect( plugin.configuredItems ).to.deep.equal( [] ); + } ); + } ); + + it( 'should pass through object definition', () => { + return VirtualTestEditor + .create( { + plugins: [ FontFamilyEditing ], + fontFamily: { + items: [ + { + label: 'Comic Sans', + model: 'comic', + view: { + name: 'span', + styles: { + 'font-family': 'Comic Sans' + } + } + } + ] + } + } ) + .then( newEditor => { + editor = newEditor; + + const plugin = editor.plugins.get( FontFamilyEditing ); + + expect( plugin.configuredItems ).to.deep.equal( [ + { + label: 'Comic Sans', + model: 'comic', + view: { + name: 'span', styles: { + 'font-family': 'Comic Sans' + } + } + } + ] ); + } ); + } ); + + describe( 'shorthand presets', () => { + it( 'should return full preset from string presets', () => { + return VirtualTestEditor + .create( { + plugins: [ FontFamilyEditing ], + fontFamily: { + items: [ + 'Arial', + '"Comic Sans MS", sans-serif', + 'Lucida Console, Courier New, Courier, monospace' + ] + } + } ) + .then( newEditor => { + editor = newEditor; + + const plugin = editor.plugins.get( FontFamilyEditing ); + + expect( plugin.configuredItems ).to.deep.equal( [ + { + label: 'Arial', + model: 'Arial', + view: { + name: 'span', + styles: { + 'font-family': 'Arial' + } + } + }, + { + label: 'Comic Sans MS', + model: 'Comic Sans MS', + view: { + name: 'span', + styles: { + 'font-family': '"Comic Sans MS", sans-serif' + } + } + }, + { + label: 'Lucida Console', + model: 'Lucida Console', + view: { + name: 'span', + styles: { + 'font-family': '"Lucida Console", "Courier New", Courier, monospace' + } + } + } + ] ); + } ); + } ); + } ); + } ); + + describe( 'editing pipeline conversion', () => { + beforeEach( () => { + return VirtualTestEditor + .create( { + plugins: [ FontFamilyEditing, Paragraph ], + fontFamily: { + items: [ + 'Arial', + { + label: 'My font', + model: 'my', + view: { + name: 'mark', + classes: 'my-style' + } + } + ] + } + } ) + .then( newEditor => { + editor = newEditor; + + doc = editor.model; + } ); + } ); + + it( 'should discard unknown fontFamily attribute values', () => { + setModelData( doc, 'f<$text fontFamily="foo-bar">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + + it( 'should convert fontFamily attribute to configured preset', () => { + setModelData( doc, 'f<$text fontFamily="Arial">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + + it( 'should convert fontFamily attribute from user defined settings', () => { + setModelData( doc, 'f<$text fontFamily="my">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + } ); + + describe( 'data pipeline conversions', () => { + beforeEach( () => { + return VirtualTestEditor + .create( { + plugins: [ FontFamilyEditing, Paragraph ], + fontFamily: { + items: [ + { + label: 'My other setting', + model: 'my-other', + view: { + name: 'span', + styles: { 'font-family': 'Other' } + } + }, + { + label: 'My setting', + model: 'my', + view: { + name: 'mark', + styles: { 'font-family': 'Verdana' }, + classes: 'my-style' + } + }, + { + label: 'Hybrid', + model: 'complex', + view: { + name: 'span', + classes: [ 'text-complex' ] + }, + acceptsAlso: [ + { name: 'span', styles: { 'font-family': 'Arial' } }, + { name: 'span', styles: { 'font-family': 'Arial,sans-serif' } }, + { name: 'span', attributes: { 'data-font': 'Arial' } } + ] + } + ] + } + } ) + .then( newEditor => { + editor = newEditor; + + doc = editor.model; + } ); + } ); + + it( 'should convert from element with defined style when with other styles', () => { + const data = '

foo

'; + + editor.setData( data ); + + expect( getModelData( doc ) ).to.equal( '[]f<$text fontFamily="my-other">oo' ); + + expect( editor.getData() ).to.equal( '

foo

' ); + } ); + + it( 'should convert from user defined element', () => { + const data = '

foo

'; + + editor.setData( data ); + + expect( getModelData( doc ) ).to.equal( '[]f<$text fontFamily="my">oo' ); + + expect( editor.getData() ).to.equal( data ); + } ); + + it( 'should convert from complex definitions', () => { + editor.setData( + '

foo

' + + '

foo

' + + '

bar

' + + '

baz

' + ); + + expect( getModelData( doc ) ).to.equal( + '[]f<$text fontFamily="complex">oo' + + 'f<$text fontFamily="complex">oo' + + 'b<$text fontFamily="complex">ar' + + 'b<$text fontFamily="complex">az' + ); + + expect( editor.getData() ).to.equal( + '

foo

' + + '

foo

' + + '

bar

' + + '

baz

' + ); + } ); + } ); } ); From 5296f54506aa8cee727fee2851d2212062cc11d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 18 Dec 2017 14:05:50 +0100 Subject: [PATCH 023/101] Added: Introduce FontFamily plugin. --- src/fontfamily.js | 32 ++++++++++++++++++++++++++++++++ tests/fontfamily.js | 17 +++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/fontfamily.js create mode 100644 tests/fontfamily.js diff --git a/src/fontfamily.js b/src/fontfamily.js new file mode 100644 index 0000000..efa4dc7 --- /dev/null +++ b/src/fontfamily.js @@ -0,0 +1,32 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontfamily + */ + +import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; +import FontFamilyEditing from './fontfamily/fontfamilyediting'; + +/** + * The Font Size plugin. + * + * @extends module:core/plugin~Plugin + */ +export default class FontFamily extends Plugin { + /** + * @inheritDoc + */ + static get requires() { + return [ FontFamilyEditing ]; + } + + /** + * @inheritDoc + */ + static get pluginName() { + return 'FontFamily'; + } +} diff --git a/tests/fontfamily.js b/tests/fontfamily.js new file mode 100644 index 0000000..68dfa46 --- /dev/null +++ b/tests/fontfamily.js @@ -0,0 +1,17 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import FontFamily from './../src/fontfamily'; +import FontFamilyEditing from './../src/fontfamily/fontfamilyediting'; + +describe( 'FontFamily', () => { + it( 'requires FontFamilyEditing', () => { + expect( FontFamily.requires ).to.deep.equal( [ FontFamilyEditing ] ); + } ); + + it( 'defines plugin name', () => { + expect( FontFamily.pluginName ).to.equal( 'FontFamily' ); + } ); +} ); From efdb954de7e2eb83991856585f27cd802361fce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 18 Dec 2017 14:12:02 +0100 Subject: [PATCH 024/101] Tests: Added Font Family feature manual tests. --- tests/manual/font-family.html | 41 +++++++++++++++++++++++++++++++++++ tests/manual/font-family.js | 24 ++++++++++++++++++++ tests/manual/font-family.md | 7 ++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/manual/font-family.html create mode 100644 tests/manual/font-family.js create mode 100644 tests/manual/font-family.md diff --git a/tests/manual/font-family.html b/tests/manual/font-family.html new file mode 100644 index 0000000..cf49af0 --- /dev/null +++ b/tests/manual/font-family.html @@ -0,0 +1,41 @@ +
+

Font Size feature sample.

+ +

+ Brownie sugar plum jelly beans. Brownie pudding liquorice jelly-o cotton candy sesame snaps powder. Chocolate cake pastry dragée dragée carrot cake. Caramels powder bear claw macaroon tiramisu. Soufflé gummies cheesecake apple pie. Candy jelly-o marzipan cake lollipop candy candy sweet. Pudding marshmallow dragée pastry cotton candy pudding caramels lemon drops. Sugar plum candy toffee donut brownie macaroon soufflé oat cake. +

+ +

+ Icing powder ice cream tootsie roll cake wafer icing sweet roll. Dessert tiramisu tart. Macaroon marzipan dessert cheesecake. Pudding chupa chups liquorice sesame snaps cookie ice cream croissant. Liquorice pie cake pudding. Carrot cake gummi bears chocolate bar. Pie gummies lemon drops tiramisu sugar plum powder sesame snaps halvah lollipop. +

+ +

+ Powder donut sesame snaps jelly cake. Gummi bears sweet icing cotton candy oat cake dragée bonbon cotton candy. Caramels tootsie roll soufflé candy canes. Chocolate bar jelly-o apple pie. Pastry gingerbread chocolate powder pie cupcake sesame snaps. Chocolate cake topping donut chocolate cake dragée. Lemon drops powder jelly-o cake chupa chups lollipop soufflé. +

+ +

+ Halvah wafer ice cream dragée tart topping ice cream gummi bears. Donut donut lollipop donut gummies tiramisu. Cupcake cotton candy donut cake soufflé bear claw oat cake gingerbread. Sweet gummies bonbon jujubes gummies topping. Toffee macaroon caramels. Gummi bears ice cream gummi bears pudding ice cream lemon drops sweet roll icing jelly beans. Muffin cotton candy cheesecake. Chocolate bar fruitcake icing brownie croissant donut jelly chocolate bar jelly-o. Macaroon wafer sweet roll soufflé sweet roll apple pie. +

+ +

+ Chocolate bar pie cake chupa chups topping bear claw icing. Macaroon tootsie roll chocolate chupa chups chocolate bar candy canes bonbon jelly beans. Tootsie roll danish topping chocolate bar. Fruitcake cheesecake jelly-o. Chocolate cake muffin candy canes marzipan. Sesame snaps lollipop sweet bear claw donut dessert apple pie. Chocolate bar cookie cake dessert powder biscuit dragée. +

+ +

+ Gingerbread cupcake candy powder. Chocolate cake chocolate bar marzipan lollipop. Tootsie roll chocolate cake marzipan dragée danish gummi bears gummies jelly beans. Chocolate bar candy toffee tart marshmallow cupcake. Oat cake gingerbread cheesecake toffee. Muffin chupa chups pastry. Muffin lemon drops danish dragée cake pastry pie powder. Lollipop bear claw cake halvah marshmallow liquorice. +

+ +
    +
  • It's a list item with tiny text
  • +
  • It's a list item with small text
  • +
  • It's a list item with big text
  • +
  • It's a list item with huge text
  • +
+ +
+ CKEditor logo +
+ Sample image with changed font caption. +
+
+
diff --git a/tests/manual/font-family.js b/tests/manual/font-family.js new file mode 100644 index 0000000..26c879c --- /dev/null +++ b/tests/manual/font-family.js @@ -0,0 +1,24 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals console, window, document */ + +import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'; +import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset'; +import FontFamily from '../../src/fontfamily'; + +ClassicEditor + .create( document.querySelector( '#editor' ), { + plugins: [ ArticlePluginSet, FontFamily ], + toolbar: [ + 'headings', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' + ] + } ) + .then( editor => { + window.editor = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); diff --git a/tests/manual/font-family.md b/tests/manual/font-family.md new file mode 100644 index 0000000..3659f30 --- /dev/null +++ b/tests/manual/font-family.md @@ -0,0 +1,7 @@ +### Loading + +The data should be loaded with paragraphs, each with different font. + +### Testing + +TODO From 8e9ecd11c95125e59aa868f1e3f7971453779c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 19 Dec 2017 07:56:35 +0100 Subject: [PATCH 025/101] Other: Make FontFamily work with various font-family style markup. --- src/fontfamily/fontfamilyediting.js | 39 +++++++++--- tests/fontfamily/fontfamilyediting.js | 86 ++++++++++++++++++++++++--- tests/manual/font-family.html | 32 +++++----- 3 files changed, 127 insertions(+), 30 deletions(-) diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index c8429cb..1560c1d 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -29,7 +29,13 @@ export default class FontFamilyEditing extends Plugin { editor.config.define( 'fontFamily', { items: [ 'Arial, Helvetica, sans-serif', - 'Courier New, Courier, monospace' + 'Courier New, Courier, monospace', + 'Georgia, serif', + 'Lucida Sans Unicode, Lucida Grande, sans-serif', + 'Tahoma, Geneva, sans-serif', + 'Times New Roman, Times, serif', + 'Trebuchet MS, Helvetica, sans-serif', + 'Verdana, Geneva, sans-serif' ] } ); @@ -102,29 +108,48 @@ function getItemDefinition( item ) { // Creates a predefined preset for pixel size. function generateFontPreset( font ) { // Remove quotes from font names - const fontNames = font.replace( /"/g, '' ).split( ',' ); + const fontNames = font.replace( /"|'/g, '' ).split( ',' ); - const cssFontNames = fontNames.map( cleanFontName ).join( ', ' ); + const cssFontNames = fontNames.map( normalizeFontName ); const firstFontName = fontNames[ 0 ]; + const quotesMatch = '("|\'|&qout;|\\W){0,2}'; + + const regexString = `${ quotesMatch }${ fontNames.map( n => n.trim() ).join( `${ quotesMatch },${ quotesMatch }` ) }${ quotesMatch }`; + + const fontFamilyRegexp = new RegExp( regexString ); + return { label: firstFontName, model: firstFontName, view: { name: 'span', styles: { - 'font-family': cssFontNames + 'font-family': cssFontNames.join( ', ' ) } - } + }, + acceptsAlso: [ + { + name: 'span', + styles: { + 'font-family': fontFamilyRegexp + } + } + ] }; } -function cleanFontName( fontName ) { +/** + * + * @param fontName + * @returns {string|*} + */ +function normalizeFontName( fontName ) { fontName = fontName.trim(); if ( fontName.indexOf( ' ' ) > 0 ) { - fontName = `"${ fontName }"`; + fontName = `'${ fontName }'`; } return fontName; diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 069b8fd..55b3d98 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -39,7 +39,13 @@ describe( 'FontFamilyEditing', () => { it( 'should be set', () => { expect( editor.config.get( 'fontFamily.items' ) ).to.deep.equal( [ 'Arial, Helvetica, sans-serif', - 'Courier New, Courier, monospace' + 'Courier New, Courier, monospace', + 'Georgia, serif', + 'Lucida Sans Unicode, Lucida Grande, sans-serif', + 'Tahoma, Geneva, sans-serif', + 'Times New Roman, Times, serif', + 'Trebuchet MS, Helvetica, sans-serif', + 'Verdana, Geneva, sans-serif' ] ); } ); } ); @@ -92,7 +98,8 @@ describe( 'FontFamilyEditing', () => { label: 'Comic Sans', model: 'comic', view: { - name: 'span', styles: { + name: 'span', + styles: { 'font-family': 'Comic Sans' } } @@ -110,7 +117,7 @@ describe( 'FontFamilyEditing', () => { items: [ 'Arial', '"Comic Sans MS", sans-serif', - 'Lucida Console, Courier New, Courier, monospace' + 'Lucida Console, \'Courier New\', Courier, monospace' ] } } ) @@ -128,7 +135,15 @@ describe( 'FontFamilyEditing', () => { styles: { 'font-family': 'Arial' } - } + }, + acceptsAlso: [ + { + name: 'span', + styles: { + 'font-family': new RegExp( '("|\'|&qout;|\\W){0,2}Arial("|\'|&qout;|\\W){0,2}' ) + } + } + ] }, { label: 'Comic Sans MS', @@ -136,9 +151,20 @@ describe( 'FontFamilyEditing', () => { view: { name: 'span', styles: { - 'font-family': '"Comic Sans MS", sans-serif' + 'font-family': '\'Comic Sans MS\', sans-serif' } - } + }, + acceptsAlso: [ + { + name: 'span', + styles: { + 'font-family': new RegExp( + '("|\'|&qout;|\\W){0,2}Comic Sans MS("|\'|&qout;|\\W){0,2},' + + '("|\'|&qout;|\\W){0,2}sans-serif("|\'|&qout;|\\W){0,2}' + ) + } + } + ] }, { label: 'Lucida Console', @@ -146,9 +172,22 @@ describe( 'FontFamilyEditing', () => { view: { name: 'span', styles: { - 'font-family': '"Lucida Console", "Courier New", Courier, monospace' + 'font-family': '\'Lucida Console\', \'Courier New\', Courier, monospace' } - } + }, + acceptsAlso: [ + { + name: 'span', + styles: { + 'font-family': new RegExp( + '("|\'|&qout;|\\W){0,2}Lucida Console("|\'|&qout;|\\W){0,2},' + + '("|\'|&qout;|\\W){0,2}Courier New("|\'|&qout;|\\W){0,2},' + + '("|\'|&qout;|\\W){0,2}Courier("|\'|&qout;|\\W){0,2},' + + '("|\'|&qout;|\\W){0,2}monospace("|\'|&qout;|\\W){0,2}' + ) + } + } + ] } ] ); } ); @@ -164,6 +203,7 @@ describe( 'FontFamilyEditing', () => { fontFamily: { items: [ 'Arial', + 'Lucida Sans Unicode, Lucida Grande, sans-serif', { label: 'My font', model: 'my', @@ -188,12 +228,19 @@ describe( 'FontFamilyEditing', () => { expect( editor.getData() ).to.equal( '

foo

' ); } ); - it( 'should convert fontFamily attribute to configured preset', () => { + it( 'should convert fontFamily attribute to configured simple preset', () => { setModelData( doc, 'f<$text fontFamily="Arial">oo' ); expect( editor.getData() ).to.equal( '

foo

' ); } ); + it( 'should convert fontFamily attribute to configured complex preset', () => { + setModelData( doc, 'f<$text fontFamily="Lucida Sans Unicode">oo' ); + + expect( editor.getData() ) + .to.equal( '

foo

' ); + } ); + it( 'should convert fontFamily attribute from user defined settings', () => { setModelData( doc, 'f<$text fontFamily="my">oo' ); @@ -208,6 +255,7 @@ describe( 'FontFamilyEditing', () => { plugins: [ FontFamilyEditing, Paragraph ], fontFamily: { items: [ + 'Lucida Sans Unicode, Lucida Grande, sans-serif', { label: 'My other setting', model: 'my-other', @@ -290,5 +338,25 @@ describe( 'FontFamilyEditing', () => { '

baz

' ); } ); + + it( 'should convert from various inline style definitions', () => { + editor.setData( + '

foo

' + + '

foo

' + + '

foo

' + ); + + expect( getModelData( doc ) ).to.equal( + '[]f<$text fontFamily="Lucida Sans Unicode">oo' + + 'f<$text fontFamily="Lucida Sans Unicode">oo' + + 'f<$text fontFamily="Lucida Sans Unicode">oo' + ); + + expect( editor.getData() ).to.equal( + '

foo

' + + '

foo

' + + '

foo

' + ); + } ); } ); } ); diff --git a/tests/manual/font-family.html b/tests/manual/font-family.html index cf49af0..e35a8fd 100644 --- a/tests/manual/font-family.html +++ b/tests/manual/font-family.html @@ -1,41 +1,45 @@
-

Font Size feature sample.

+

Font Family feature sample.

+

Arial, Helvetica, sans-serif:

- Brownie sugar plum jelly beans. Brownie pudding liquorice jelly-o cotton candy sesame snaps powder. Chocolate cake pastry dragée dragée carrot cake. Caramels powder bear claw macaroon tiramisu. Soufflé gummies cheesecake apple pie. Candy jelly-o marzipan cake lollipop candy candy sweet. Pudding marshmallow dragée pastry cotton candy pudding caramels lemon drops. Sugar plum candy toffee donut brownie macaroon soufflé oat cake. + Topping cheesecake cotton candy toffee cookie cookie lemon drops cotton candy. Carrot cake dessert jelly beans powder cake cupcake tiramisu pastry gummi bears.

+

Courier New, Courier, monospace:

- Icing powder ice cream tootsie roll cake wafer icing sweet roll. Dessert tiramisu tart. Macaroon marzipan dessert cheesecake. Pudding chupa chups liquorice sesame snaps cookie ice cream croissant. Liquorice pie cake pudding. Carrot cake gummi bears chocolate bar. Pie gummies lemon drops tiramisu sugar plum powder sesame snaps halvah lollipop. + Jujubes sweet wafer. Pastry cotton candy sweet muffin dessert cookie. Chocolate cake candy canes dragée wafer donut bear claw.

+

Georgia, serif:

- Powder donut sesame snaps jelly cake. Gummi bears sweet icing cotton candy oat cake dragée bonbon cotton candy. Caramels tootsie roll soufflé candy canes. Chocolate bar jelly-o apple pie. Pastry gingerbread chocolate powder pie cupcake sesame snaps. Chocolate cake topping donut chocolate cake dragée. Lemon drops powder jelly-o cake chupa chups lollipop soufflé. + Chocolate bar candy fruitcake fruitcake. Lollipop gingerbread pudding sweet roll biscuit halvah marzipan croissant soufflé.

+

Lucida Sans Unicode, Lucida Grande, sans-serif:

- Halvah wafer ice cream dragée tart topping ice cream gummi bears. Donut donut lollipop donut gummies tiramisu. Cupcake cotton candy donut cake soufflé bear claw oat cake gingerbread. Sweet gummies bonbon jujubes gummies topping. Toffee macaroon caramels. Gummi bears ice cream gummi bears pudding ice cream lemon drops sweet roll icing jelly beans. Muffin cotton candy cheesecake. Chocolate bar fruitcake icing brownie croissant donut jelly chocolate bar jelly-o. Macaroon wafer sweet roll soufflé sweet roll apple pie. + Chocolate muffin apple pie toffee caramels chupa chups bear claw cotton candy. Lollipop dragée fruitcake fruitcake apple pie chocolate bar candy.

+

Tahoma, Geneva, sans-serif:

- Chocolate bar pie cake chupa chups topping bear claw icing. Macaroon tootsie roll chocolate chupa chups chocolate bar candy canes bonbon jelly beans. Tootsie roll danish topping chocolate bar. Fruitcake cheesecake jelly-o. Chocolate cake muffin candy canes marzipan. Sesame snaps lollipop sweet bear claw donut dessert apple pie. Chocolate bar cookie cake dessert powder biscuit dragée. + Brownie biscuit donut gummies pie cheesecake. Dessert ice cream lemon drops candy soufflé cookie lemon drops. Chupa chups powder muffin sugar plum gummi bears fruitcake.

+

Trebuchet MS, Helvetica, sans-serif:

- Gingerbread cupcake candy powder. Chocolate cake chocolate bar marzipan lollipop. Tootsie roll chocolate cake marzipan dragée danish gummi bears gummies jelly beans. Chocolate bar candy toffee tart marshmallow cupcake. Oat cake gingerbread cheesecake toffee. Muffin chupa chups pastry. Muffin lemon drops danish dragée cake pastry pie powder. Lollipop bear claw cake halvah marshmallow liquorice. + Jujubes candy lollipop. Tootsie roll cookie chocolate gummies cupcake sweet fruitcake oat cake danish.

-
    -
  • It's a list item with tiny text
  • -
  • It's a list item with small text
  • -
  • It's a list item with big text
  • -
  • It's a list item with huge text
  • -
+

Verdana, Geneva, sans-serif:

+

+ Danish brownie powder pie danish. Topping dragée oat cake caramels. Chocolate bar bonbon pastry apple pie icing chocolate danish carrot cake gummies. +

CKEditor logo
- Sample image with changed font caption. + Sample image with changed font caption.
From f59b1de5f021568443e804f05dc3990f48a61ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 20 Dec 2017 10:54:07 +0100 Subject: [PATCH 026/101] Other: Align FontFamilyEditing code to changes in ckeditor5-engine. --- src/fontfamily/fontfamilyediting.js | 6 +++--- tests/fontfamily/fontfamilyediting.js | 30 +++++++++++++-------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 1560c1d..190d487 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -11,7 +11,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import { modelAttributeToViewAttributeElement, viewToModelAttribute -} from '@ckeditor/ckeditor5-engine/src/conversion/configurationdefinedconverters'; +} from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters'; /** * The Font Family Editing feature. @@ -125,14 +125,14 @@ function generateFontPreset( font ) { model: firstFontName, view: { name: 'span', - styles: { + style: { 'font-family': cssFontNames.join( ', ' ) } }, acceptsAlso: [ { name: 'span', - styles: { + style: { 'font-family': fontFamilyRegexp } } diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 55b3d98..88675c5 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -80,7 +80,7 @@ describe( 'FontFamilyEditing', () => { model: 'comic', view: { name: 'span', - styles: { + style: { 'font-family': 'Comic Sans' } } @@ -99,7 +99,7 @@ describe( 'FontFamilyEditing', () => { model: 'comic', view: { name: 'span', - styles: { + style: { 'font-family': 'Comic Sans' } } @@ -132,14 +132,14 @@ describe( 'FontFamilyEditing', () => { model: 'Arial', view: { name: 'span', - styles: { + style: { 'font-family': 'Arial' } }, acceptsAlso: [ { name: 'span', - styles: { + style: { 'font-family': new RegExp( '("|\'|&qout;|\\W){0,2}Arial("|\'|&qout;|\\W){0,2}' ) } } @@ -150,14 +150,14 @@ describe( 'FontFamilyEditing', () => { model: 'Comic Sans MS', view: { name: 'span', - styles: { + style: { 'font-family': '\'Comic Sans MS\', sans-serif' } }, acceptsAlso: [ { name: 'span', - styles: { + style: { 'font-family': new RegExp( '("|\'|&qout;|\\W){0,2}Comic Sans MS("|\'|&qout;|\\W){0,2},' + '("|\'|&qout;|\\W){0,2}sans-serif("|\'|&qout;|\\W){0,2}' @@ -171,14 +171,14 @@ describe( 'FontFamilyEditing', () => { model: 'Lucida Console', view: { name: 'span', - styles: { + style: { 'font-family': '\'Lucida Console\', \'Courier New\', Courier, monospace' } }, acceptsAlso: [ { name: 'span', - styles: { + style: { 'font-family': new RegExp( '("|\'|&qout;|\\W){0,2}Lucida Console("|\'|&qout;|\\W){0,2},' + '("|\'|&qout;|\\W){0,2}Courier New("|\'|&qout;|\\W){0,2},' + @@ -209,7 +209,7 @@ describe( 'FontFamilyEditing', () => { model: 'my', view: { name: 'mark', - classes: 'my-style' + class: 'my-style' } } ] @@ -261,7 +261,7 @@ describe( 'FontFamilyEditing', () => { model: 'my-other', view: { name: 'span', - styles: { 'font-family': 'Other' } + style: { 'font-family': 'Other' } } }, { @@ -269,8 +269,8 @@ describe( 'FontFamilyEditing', () => { model: 'my', view: { name: 'mark', - styles: { 'font-family': 'Verdana' }, - classes: 'my-style' + style: { 'font-family': 'Verdana' }, + class: 'my-style' } }, { @@ -278,11 +278,11 @@ describe( 'FontFamilyEditing', () => { model: 'complex', view: { name: 'span', - classes: [ 'text-complex' ] + class: [ 'text-complex' ] }, acceptsAlso: [ - { name: 'span', styles: { 'font-family': 'Arial' } }, - { name: 'span', styles: { 'font-family': 'Arial,sans-serif' } }, + { name: 'span', style: { 'font-family': 'Arial' } }, + { name: 'span', style: { 'font-family': 'Arial,sans-serif' } }, { name: 'span', attributes: { 'data-font': 'Arial' } } ] } From b030a9ae3663f29012d56909a2e1b7dbdb3e1e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 20 Dec 2017 17:28:30 +0100 Subject: [PATCH 027/101] Added: Stub FontSizeUI plugin. --- src/fontsize.js | 3 +- src/fontsize/fontsizeui.js | 141 +++++++++++++++++++++++++++++++++++++ tests/fontsize.js | 5 +- tests/manual/font-size.js | 2 +- 4 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 src/fontsize/fontsizeui.js diff --git a/src/fontsize.js b/src/fontsize.js index ae9a377..94db1dc 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -9,6 +9,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import FontSizeEditing from './fontsize/fontsizeediting'; +import FontSizeUI from './fontsize/fontsizeui'; /** * The Font Size plugin. @@ -20,7 +21,7 @@ export default class FontSize extends Plugin { * @inheritDoc */ static get requires() { - return [ FontSizeEditing ]; + return [ FontSizeEditing, FontSizeUI ]; } /** diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js new file mode 100644 index 0000000..e5b7621 --- /dev/null +++ b/src/fontsize/fontsizeui.js @@ -0,0 +1,141 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module heading/heading + */ + +import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; +import Model from '@ckeditor/ckeditor5-ui/src/model'; +import Collection from '@ckeditor/ckeditor5-utils/src/collection'; +import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createlistdropdown'; + +import FontSizeEditing from './fontsizeediting'; + +import fontSizeIcon from '@ckeditor/ckeditor5-core/theme/icons/low-vision.svg'; + +/** + * @extends module:core/plugin~Plugin + */ +export default class FontSizeUI extends Plugin { + /** + * @inheritDoc + */ + init() { + const editor = this.editor; + const dropdownItems = new Collection(); + + const options = this._getLocalizedOptions(); + const commands = []; + const t = editor.t; + + const dropdownTooltip = t( 'Font Size' ); + + for ( const option of options ) { + const commandName = 'font-size:' + option.model; + + const command = editor.commands.get( commandName ); + const itemModel = new Model( { + commandName, + label: option.title, + class: option.class + } ); + + itemModel.bind( 'isActive' ).to( command, 'value' ); + + // Add the option to the collection. + dropdownItems.add( itemModel ); + + commands.push( command ); + } + + // Create dropdown model. + const dropdownModel = new Model( { + icon: fontSizeIcon, + withText: false, + items: dropdownItems, + tooltip: dropdownTooltip + } ); + + dropdownModel.bind( 'isEnabled' ).to( + // Bind to #isEnabled of each command... + ...getCommandsBindingTargets( commands, 'isEnabled' ), + // ...and set it true if any command #isEnabled is true. + ( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled ) + ); + + // Register UI component. + editor.ui.componentFactory.add( 'fontSize', locale => { + const dropdown = createListDropdown( dropdownModel, locale ); + + // TODO check if needed + dropdown.extendTemplate( { + attributes: { + class: [ + 'ck-font-size-dropdown' + ] + } + } ); + + // Execute command when an item from the dropdown is selected. + this.listenTo( dropdown, 'execute', evt => { + editor.execute( evt.source.commandName ); + editor.editing.view.focus(); + } ); + + return dropdown; + } ); + } + + /** + * Returns heading options as defined in `config.heading.options` but processed to consider + * editor localization, i.e. to display {@link module:heading/heading~HeadingOption} + * in the correct language. + * + * Note: The reason behind this method is that there's no way to use {@link module:utils/locale~Locale#t} + * when the user config is defined because the editor does not exist yet. + * + * @private + * @returns {Array.}. + */ + _getLocalizedOptions() { + const editor = this.editor; + const t = editor.t; + const localizedTitles = { + Tiny: t( 'Tiny' ), + Small: t( 'Small' ), + Big: t( 'Big' ), + Huge: t( 'Huge' ) + }; + + // TODO this is not nice :/ + const items = editor.plugins.get( FontSizeEditing ).configuredItems; + + return items.map( option => { + const title = localizedTitles[ option.title ]; + + if ( title && title != option.title ) { + // Clone the option to avoid altering the original `config.heading.options`. + option = Object.assign( {}, option, { title } ); + } + + return option; + } ); + } +} + +// Returns an array of binding components for +// {@link module:utils/observablemixin~Observable#bind} from a set of iterable +// commands. +// +// TODO: it's duplicated in various places - maybe it's worth exporting to utils? +// +// @private +// @param {Iterable.} commands +// @param {String} attribute +// @returns {Array.} +function getCommandsBindingTargets( commands, attribute ) { + return Array.prototype.concat( ...commands.map( c => [ c, attribute ] ) ); +} diff --git a/tests/fontsize.js b/tests/fontsize.js index 0e2bdeb..d7f49a7 100644 --- a/tests/fontsize.js +++ b/tests/fontsize.js @@ -5,10 +5,11 @@ import FontSize from './../src/fontsize'; import FontSizeEditing from './../src/fontsize/fontsizeediting'; +import FontSizeUI from './../src/fontsize/fontsizeui'; describe( 'FontSize', () => { - it( 'requires FontSizeEditing', () => { - expect( FontSize.requires ).to.deep.equal( [ FontSizeEditing ] ); + it( 'requires FontSizeEditing & FontSizeUI', () => { + expect( FontSize.requires ).to.deep.equal( [ FontSizeEditing, FontSizeUI ] ); } ); it( 'defines plugin name', () => { diff --git a/tests/manual/font-size.js b/tests/manual/font-size.js index 0ea5767..18e2a47 100644 --- a/tests/manual/font-size.js +++ b/tests/manual/font-size.js @@ -13,7 +13,7 @@ ClassicEditor .create( document.querySelector( '#editor' ), { plugins: [ ArticlePluginSet, FontSize ], toolbar: [ - 'headings', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' + 'headings', 'fontSize', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ] } ) .then( editor => { From cad324c6e3e257b19900dc534006ff3ff24f462f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 21 Dec 2017 12:45:26 +0100 Subject: [PATCH 028/101] Changed: Pass all `ConverterDefinition`s to modelAttributeToViewAttributeElement as an array. --- src/fontsize/fontsizecommand.js | 5 +++-- src/fontsize/fontsizeediting.js | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index 86cc950..40ce3ff 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -37,10 +37,11 @@ export default class FontSizeCommand extends Command { * @inheritDoc */ refresh() { - const doc = this.editor.model.document; + const model = this.editor.model; + const doc = model.document; this.value = doc.selection.getAttribute( 'fontSize' ) === this.fontSize; - this.isEnabled = this.editor.model.schema.checkAttributeInSelection( doc.selection, 'fontSize' ); + this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'fontSize' ); } /** diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index bc37ec7..8dc5376 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -51,12 +51,12 @@ export default class FontSizeEditing extends Plugin { // Covert view to model. viewToModelAttribute( 'fontSize', item, [ data.viewToModel ] ); - // Covert model to view. - modelAttributeToViewAttributeElement( 'fontSize', item, [ data.modelToView, editing.modelToView ] ); - // Add command. editor.commands.add( 'font-size:' + item.model, new FontSizeCommand( editor, item.model ) ); } + + // Covert model to view. + modelAttributeToViewAttributeElement( 'fontSize', this.configuredItems, [ data.modelToView, editing.modelToView ] ); } get configuredItems() { @@ -88,7 +88,7 @@ export default class FontSizeEditing extends Plugin { init() { const editor = this.editor; - // Allow highlight attribute on all elements + // Allow fontSize attribute on all elements editor.model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. editor.model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ); From 1543d0e131f6a251e68d16a1c67e270bb88fe13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 21 Dec 2017 15:25:22 +0100 Subject: [PATCH 029/101] Changed: Use one command for font features. --- src/fontfamily/fontfamilyediting.js | 13 ++++++---- src/fontsize/fontsizecommand.js | 35 ++++++++++++-------------- src/fontsize/fontsizeediting.js | 20 +++++++-------- src/fontsize/fontsizeui.js | 38 ++++++----------------------- 4 files changed, 40 insertions(+), 66 deletions(-) diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 190d487..14e6c77 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -13,6 +13,8 @@ import { viewToModelAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters'; +import FontFamilyCommand from './fontfamilycommand'; + /** * The Font Family Editing feature. * @@ -43,15 +45,16 @@ export default class FontFamilyEditing extends Plugin { const data = editor.data; const editing = editor.editing; + // Add converters from view to model. for ( const item of this.configuredItems ) { - // Covert view to model. viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] ); + } - // Covert model to view. - modelAttributeToViewAttributeElement( 'fontFamily', item, [ data.modelToView, editing.modelToView ] ); + // Covert from model to view. + modelAttributeToViewAttributeElement( 'fontFamily', this.configuredItems, [ data.modelToView, editing.modelToView ] ); - // Add command. - } + // Add FontSize command. + editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) ); } get configuredItems() { diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index 40ce3ff..a9e6183 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -16,13 +16,12 @@ import Command from '@ckeditor/ckeditor5-core/src/command'; * @extends module:core/command~Command */ export default class FontSizeCommand extends Command { - constructor( editor, fontSize ) { - super( editor ); - - /** - * Name of font size config. - */ - this.fontSize = fontSize; + /** + * @inheritDoc + */ + refresh() { + const model = this.editor.model; + const doc = model.document; /** * A flag indicating whether the command is active, which means that the selection has fontSize attribute set. @@ -31,16 +30,7 @@ export default class FontSizeCommand extends Command { * @readonly * @member {Boolean} module:font/fontsizecommand~FontSizeCommand#value */ - } - - /** - * @inheritDoc - */ - refresh() { - const model = this.editor.model; - const doc = model.document; - - this.value = doc.selection.getAttribute( 'fontSize' ) === this.fontSize; + this.value = doc.selection.getAttribute( 'fontSize' ); this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'fontSize' ); } @@ -49,8 +39,9 @@ export default class FontSizeCommand extends Command { * * @protected * @param {Object} [options] Options for the executed command. + * @param {String} [options.fontSize] FontSize value to apply. */ - execute() { + execute( options ) { const model = this.editor.model; const document = model.document; const selection = document.selection; @@ -60,11 +51,17 @@ export default class FontSizeCommand extends Command { return; } + const value = options.fontSize; + model.change( writer => { const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontSize' ); for ( const range of ranges ) { - writer.setAttribute( 'fontSize', this.fontSize, range ); + if ( value !== 'normal' ) { + writer.setAttribute( 'fontSize', value, range ); + } else { + writer.removeAttribute( 'fontSize', range ); + } } } ); } diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 8dc5376..33c686d 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -42,21 +42,19 @@ export default class FontSizeEditing extends Plugin { const data = editor.data; const editing = editor.editing; - for ( const item of this.configuredItems ) { - if ( item.model === 'normal' ) { - editor.commands.add( 'font-size:normal', new FontSizeCommand( editor ) ); - continue; - } + // Define view to model conversion. + const items = this.configuredItems.filter( item => item.model !== 'normal' ); + for ( const item of items ) { // Covert view to model. viewToModelAttribute( 'fontSize', item, [ data.viewToModel ] ); - - // Add command. - editor.commands.add( 'font-size:' + item.model, new FontSizeCommand( editor, item.model ) ); } - // Covert model to view. - modelAttributeToViewAttributeElement( 'fontSize', this.configuredItems, [ data.modelToView, editing.modelToView ] ); + // Define model to view conversion. + modelAttributeToViewAttributeElement( 'fontSize', items, [ data.modelToView, editing.modelToView ] ); + + // Add FontSize command. + editor.commands.add( 'fontSize', new FontSizeCommand( editor ) ); } get configuredItems() { @@ -88,7 +86,7 @@ export default class FontSizeEditing extends Plugin { init() { const editor = this.editor; - // Allow fontSize attribute on all elements + // Allow fontSize attribute on all elements. editor.model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. editor.model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ); diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index e5b7621..720ec52 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -28,27 +28,22 @@ export default class FontSizeUI extends Plugin { const dropdownItems = new Collection(); const options = this._getLocalizedOptions(); - const commands = []; const t = editor.t; + const command = editor.commands.get( 'fontSize' ); + const dropdownTooltip = t( 'Font Size' ); for ( const option of options ) { - const commandName = 'font-size:' + option.model; - - const command = editor.commands.get( commandName ); const itemModel = new Model( { - commandName, + commandName: 'fontSize', + commandParam: option.model, label: option.title, class: option.class } ); - itemModel.bind( 'isActive' ).to( command, 'value' ); - // Add the option to the collection. dropdownItems.add( itemModel ); - - commands.push( command ); } // Create dropdown model. @@ -59,12 +54,7 @@ export default class FontSizeUI extends Plugin { tooltip: dropdownTooltip } ); - dropdownModel.bind( 'isEnabled' ).to( - // Bind to #isEnabled of each command... - ...getCommandsBindingTargets( commands, 'isEnabled' ), - // ...and set it true if any command #isEnabled is true. - ( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled ) - ); + dropdownModel.bind( 'isEnabled' ).to( command, 'isEnabled' ); // Register UI component. editor.ui.componentFactory.add( 'fontSize', locale => { @@ -81,7 +71,7 @@ export default class FontSizeUI extends Plugin { // Execute command when an item from the dropdown is selected. this.listenTo( dropdown, 'execute', evt => { - editor.execute( evt.source.commandName ); + editor.execute( evt.source.commandName, { fontSize: evt.source.commandParam } ); editor.editing.view.focus(); } ); @@ -110,7 +100,7 @@ export default class FontSizeUI extends Plugin { Huge: t( 'Huge' ) }; - // TODO this is not nice :/ + // TODO this is not nice :/ in terms of feature split. const items = editor.plugins.get( FontSizeEditing ).configuredItems; return items.map( option => { @@ -125,17 +115,3 @@ export default class FontSizeUI extends Plugin { } ); } } - -// Returns an array of binding components for -// {@link module:utils/observablemixin~Observable#bind} from a set of iterable -// commands. -// -// TODO: it's duplicated in various places - maybe it's worth exporting to utils? -// -// @private -// @param {Iterable.} commands -// @param {String} attribute -// @returns {Array.} -function getCommandsBindingTargets( commands, attribute ) { - return Array.prototype.concat( ...commands.map( c => [ c, attribute ] ) ); -} From dff41a306830d2c855f82138623e1e0e2cc2bf53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 21 Dec 2017 15:27:50 +0100 Subject: [PATCH 030/101] Changed: Refactor FontFamilyCommand to match FontSizeCommand. --- src/fontfamily/fontfamilycommand.js | 39 +++++++++++++---------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index 8e5a11e..fb269d5 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -13,16 +13,16 @@ import Command from '@ckeditor/ckeditor5-core/src/command'; * The font family command. It is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} * to apply font family. * + * TODO: those commands are duplicated here and there - maybe make them one? + * * @extends module:core/command~Command */ export default class FontFamilyCommand extends Command { - constructor( editor, fontFamily ) { - super( editor ); - - /** - * Name of font family config. - */ - this.fontFamily = fontFamily; + /** + * @inheritDoc + */ + refresh() { + const doc = this.editor.model.document; /** * A flag indicating whether the command is active, which means that the selection has fontFamily attribute set. @@ -31,15 +31,7 @@ export default class FontFamilyCommand extends Command { * @readonly * @member {Boolean} module:font/fontfamilycommand~FontFamilyCommand#value */ - } - - /** - * @inheritDoc - */ - refresh() { - const doc = this.editor.model.document; - - this.value = doc.selection.getAttribute( 'fontFamily' ) === this.fontFamily; + this.value = doc.selection.getAttribute( 'fontFamily' ); this.isEnabled = this.editor.model.schema.checkAttributeInSelection( doc.selection, 'fontFamily' ); } @@ -48,24 +40,29 @@ export default class FontFamilyCommand extends Command { * * @protected * @param {Object} [options] Options for the executed command. - * @param {module:engine/model/batch~Batch} [options.batch] A batch to collect all the change steps. - * A new batch will be created if this option is not set. + * @param {String} [options.fontSize] FontSize value to apply. */ - execute() { + execute( options ) { const model = this.editor.model; const document = model.document; const selection = document.selection; - // Do not apply fontFamily on collapsed selection. + // Do not apply fontSize on collapsed selection. if ( selection.isCollapsed ) { return; } + const value = options.fontSize; + model.change( writer => { const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontFamily' ); for ( const range of ranges ) { - writer.setAttribute( 'fontFamily', this.fontFamily, range ); + if ( value !== 'normal' ) { + writer.setAttribute( 'fontFamily', value, range ); + } else { + writer.removeAttribute( 'fontFamily', range ); + } } } ); } From d5bdd79f65a19ed16af0a412b3a0447c1650312a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 21 Dec 2017 15:34:39 +0100 Subject: [PATCH 031/101] Added: Bootstrap FontFamilyUI. --- src/fontfamily.js | 3 +- src/fontfamily/fontfamilyediting.js | 4 +- src/fontfamily/fontfamilyui.js | 117 ++++++++++++++++++++++++++++ tests/manual/font-family.js | 2 +- 4 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 src/fontfamily/fontfamilyui.js diff --git a/src/fontfamily.js b/src/fontfamily.js index efa4dc7..087445a 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -9,6 +9,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import FontFamilyEditing from './fontfamily/fontfamilyediting'; +import FontFamilyUI from './fontfamily/fontfamilyui'; /** * The Font Size plugin. @@ -20,7 +21,7 @@ export default class FontFamily extends Plugin { * @inheritDoc */ static get requires() { - return [ FontFamilyEditing ]; + return [ FontFamilyEditing, FontFamilyUI ]; } /** diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 14e6c77..5171892 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -124,7 +124,7 @@ function generateFontPreset( font ) { const fontFamilyRegexp = new RegExp( regexString ); return { - label: firstFontName, + title: firstFontName, model: firstFontName, view: { name: 'span', @@ -163,7 +163,7 @@ function normalizeFontName( fontName ) { * * @typedef {Object} module:font/fontfamily/fontfamilyediting~FontFamilyOption * - * @property {String} label TODO me + * @property {String} title TODO me * @property {String} model TODO me * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. */ diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js new file mode 100644 index 0000000..11113f3 --- /dev/null +++ b/src/fontfamily/fontfamilyui.js @@ -0,0 +1,117 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module heading/heading + */ + +import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; +import Model from '@ckeditor/ckeditor5-ui/src/model'; +import Collection from '@ckeditor/ckeditor5-utils/src/collection'; +import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createlistdropdown'; + +import FontFamilyEditing from './fontfamilyediting'; + +import fontFamilyIcon from '@ckeditor/ckeditor5-core/theme/icons/low-vision.svg'; + +/** + * @extends module:core/plugin~Plugin + */ +export default class FontFamilyUI extends Plugin { + /** + * @inheritDoc + */ + init() { + const editor = this.editor; + const dropdownItems = new Collection(); + + const options = this._getLocalizedOptions(); + const t = editor.t; + + const command = editor.commands.get( 'fontFamily' ); + + const dropdownTooltip = t( 'Font Family' ); + + for ( const option of options ) { + const itemModel = new Model( { + commandName: 'fontFamily', + commandParam: option.model, + label: option.title, + style: 'font-family:' + option.view.style[ 'font-family' ] + } ); + + // Add the option to the collection. + dropdownItems.add( itemModel ); + } + + // Create dropdown model. + const dropdownModel = new Model( { + icon: fontFamilyIcon, + withText: false, + items: dropdownItems, + tooltip: dropdownTooltip + } ); + + dropdownModel.bind( 'isEnabled' ).to( command, 'isEnabled' ); + + // Register UI component. + editor.ui.componentFactory.add( 'fontFamily', locale => { + const dropdown = createListDropdown( dropdownModel, locale ); + + // TODO check if needed + dropdown.extendTemplate( { + attributes: { + class: [ + 'ck-font-family-dropdown' + ] + } + } ); + + // Execute command when an item from the dropdown is selected. + this.listenTo( dropdown, 'execute', evt => { + editor.execute( evt.source.commandName, { fontFamily: evt.source.commandParam } ); + editor.editing.view.focus(); + } ); + + return dropdown; + } ); + } + + /** + * Returns heading options as defined in `config.heading.options` but processed to consider + * editor localization, i.e. to display {@link module:heading/heading~HeadingOption} + * in the correct language. + * + * Note: The reason behind this method is that there's no way to use {@link module:utils/locale~Locale#t} + * when the user config is defined because the editor does not exist yet. + * + * @private + * @returns {Array.}. + */ + _getLocalizedOptions() { + const editor = this.editor; + const t = editor.t; + const localizedTitles = { + Tiny: t( 'Tiny' ), + Small: t( 'Small' ), + Big: t( 'Big' ), + Huge: t( 'Huge' ) + }; + + // TODO this is not nice :/ in terms of feature split. + const items = editor.plugins.get( FontFamilyEditing ).configuredItems; + + return items.map( option => { + const title = localizedTitles[ option.title ]; + + if ( title && title != option.title ) { + // Clone the option to avoid altering the original `config.heading.options`. + option = Object.assign( {}, option, { title } ); + } + + return option; + } ); + } +} diff --git a/tests/manual/font-family.js b/tests/manual/font-family.js index 26c879c..5d76622 100644 --- a/tests/manual/font-family.js +++ b/tests/manual/font-family.js @@ -13,7 +13,7 @@ ClassicEditor .create( document.querySelector( '#editor' ), { plugins: [ ArticlePluginSet, FontFamily ], toolbar: [ - 'headings', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' + 'headings', 'fontFamily', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ] } ) .then( editor => { From f624e3b349878389f025810f8ef613d4cc84cf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 21 Dec 2017 15:41:45 +0100 Subject: [PATCH 032/101] Added: Add 'default' option to FontFamily options. --- src/fontfamily/fontfamilycommand.js | 8 ++++---- src/fontfamily/fontfamilyediting.js | 14 ++++++++++++-- src/fontfamily/fontfamilyui.js | 6 +++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index fb269d5..878c816 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -40,25 +40,25 @@ export default class FontFamilyCommand extends Command { * * @protected * @param {Object} [options] Options for the executed command. - * @param {String} [options.fontSize] FontSize value to apply. + * @param {String} [options.fontFamily] FontSize value to apply. */ execute( options ) { const model = this.editor.model; const document = model.document; const selection = document.selection; - // Do not apply fontSize on collapsed selection. + // Do not apply fontFamily on collapsed selection. if ( selection.isCollapsed ) { return; } - const value = options.fontSize; + const value = options.fontFamily; model.change( writer => { const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontFamily' ); for ( const range of ranges ) { - if ( value !== 'normal' ) { + if ( value !== 'default' ) { writer.setAttribute( 'fontFamily', value, range ); } else { writer.removeAttribute( 'fontFamily', range ); diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 5171892..819a060 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -30,6 +30,7 @@ export default class FontFamilyEditing extends Plugin { // Define default configuration using named presets editor.config.define( 'fontFamily', { items: [ + 'default', 'Arial, Helvetica, sans-serif', 'Courier New, Courier, monospace', 'Georgia, serif', @@ -46,12 +47,14 @@ export default class FontFamilyEditing extends Plugin { const editing = editor.editing; // Add converters from view to model. - for ( const item of this.configuredItems ) { + const items = this.configuredItems.filter( item => item.model !== 'default' ); + + for ( const item of items ) { viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] ); } // Covert from model to view. - modelAttributeToViewAttributeElement( 'fontFamily', this.configuredItems, [ data.modelToView, editing.modelToView ] ); + modelAttributeToViewAttributeElement( 'fontFamily', items, [ data.modelToView, editing.modelToView ] ); // Add FontSize command. editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) ); @@ -100,6 +103,13 @@ function getItemDefinition( item ) { return item; } + if ( item === 'default' ) { + return { + title: 'Default', // TODO localize us + model: 'default' + }; + } + // Ignore falsy values if ( typeof item !== 'string' ) { return; diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 11113f3..c7dcd56 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -39,9 +39,13 @@ export default class FontFamilyUI extends Plugin { commandName: 'fontFamily', commandParam: option.model, label: option.title, - style: 'font-family:' + option.view.style[ 'font-family' ] } ); + // Try to set style + if ( option.view && option.view.style ) { + itemModel.set( 'style', 'font-family:' + option.view.style[ 'font-family' ] ); + } + // Add the option to the collection. dropdownItems.add( itemModel ); } From d38ddc51dba66553bd1659c9cc3cb54568b858c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 21 Dec 2017 16:21:44 +0100 Subject: [PATCH 033/101] Tests: Fix current tests. --- src/fontfamily/fontfamilycommand.js | 2 +- src/fontsize/fontsizecommand.js | 2 +- tests/fontfamily.js | 5 +- tests/fontfamily/fontfamilycommand.js | 48 +++++++++++++------ tests/fontfamily/fontfamilyediting.js | 19 ++++---- tests/fontsize/fontsizecommand.js | 68 +++++++++++++++++---------- 6 files changed, 93 insertions(+), 51 deletions(-) diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index 878c816..a2c7d0d 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -42,7 +42,7 @@ export default class FontFamilyCommand extends Command { * @param {Object} [options] Options for the executed command. * @param {String} [options.fontFamily] FontSize value to apply. */ - execute( options ) { + execute( options = {} ) { const model = this.editor.model; const document = model.document; const selection = document.selection; diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index a9e6183..0dff959 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -41,7 +41,7 @@ export default class FontSizeCommand extends Command { * @param {Object} [options] Options for the executed command. * @param {String} [options.fontSize] FontSize value to apply. */ - execute( options ) { + execute( options = {} ) { const model = this.editor.model; const document = model.document; const selection = document.selection; diff --git a/tests/fontfamily.js b/tests/fontfamily.js index 68dfa46..7d89f49 100644 --- a/tests/fontfamily.js +++ b/tests/fontfamily.js @@ -5,10 +5,11 @@ import FontFamily from './../src/fontfamily'; import FontFamilyEditing from './../src/fontfamily/fontfamilyediting'; +import FontFamilyUI from '../src/fontfamily/fontfamilyui'; describe( 'FontFamily', () => { - it( 'requires FontFamilyEditing', () => { - expect( FontFamily.requires ).to.deep.equal( [ FontFamilyEditing ] ); + it( 'requires FontFamilyEditing and FontFamilyUI', () => { + expect( FontFamily.requires ).to.deep.equal( [ FontFamilyEditing, FontFamilyUI ] ); } ); it( 'defines plugin name', () => { diff --git a/tests/fontfamily/fontfamilycommand.js b/tests/fontfamily/fontfamilycommand.js index 104c20b..4795269 100644 --- a/tests/fontfamily/fontfamilycommand.js +++ b/tests/fontfamily/fontfamilycommand.js @@ -18,7 +18,7 @@ describe( 'FontFamilyCommand', () => { editor = newEditor; model = editor.model; - command = new FontFamilyCommand( editor, 'arial' ); + command = new FontFamilyCommand( editor ); editor.commands.add( 'fontFamily', command ); model.schema.registerItem( 'paragraph', '$block' ); @@ -36,16 +36,16 @@ describe( 'FontFamilyCommand', () => { } ); describe( 'value', () => { - it( 'is set to true when selection is in text with fontFamily attribute', () => { + it( 'is set to fontFamily value when selection is in text with fontFamily attribute', () => { setData( model, '<$text fontFamily="arial">fo[]o' ); - expect( command ).to.have.property( 'value', true ); + expect( command ).to.have.property( 'value', 'arial' ); } ); it( 'is undefined when selection is not in text with fontFamily attribute', () => { setData( model, 'fo[]o' ); - expect( command ).to.have.property( 'value', false ); + expect( command ).to.have.property( 'value', undefined ); } ); } ); @@ -61,11 +61,11 @@ describe( 'FontFamilyCommand', () => { it( 'should add fontFamily attribute on selected text', () => { setData( model, 'a[bc<$text fontFamily="arial">fo]obarxyz' ); - expect( command.value ).to.be.false; + expect( command.value ).to.be.undefined; - command.execute(); + command.execute( { fontFamily: 'arial' } ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'arial' ); expect( getData( model ) ).to.equal( 'a[<$text fontFamily="arial">bcfo]obarxyz' ); } ); @@ -78,9 +78,9 @@ describe( 'FontFamilyCommand', () => { 'barbar]bar' ); - command.execute(); + command.execute( { fontFamily: 'arial' } ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'arial' ); expect( getData( model ) ).to.equal( 'abcabc[<$text fontFamily="arial">abc' + @@ -97,9 +97,9 @@ describe( 'FontFamilyCommand', () => { '<$text fontFamily="text-small">bar]barbar' ); - command.execute(); + command.execute( { fontFamily: 'arial' } ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'arial' ); expect( getData( model ) ).to.equal( 'abc[<$text fontFamily="arial">abcabc' + @@ -111,13 +111,33 @@ describe( 'FontFamilyCommand', () => { it( 'should do nothing on collapsed range', () => { setData( model, 'abc<$text fontFamily="arial">foo[]barxyz' ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'arial' ); - command.execute(); + command.execute( { fontFamily: 'arial' } ); expect( getData( model ) ).to.equal( 'abc<$text fontFamily="arial">foo[]barxyz' ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'arial' ); + } ); + + it( 'should remove fontFamily attribute on selected nodes when passing undefined fontFamily param', () => { + setData( + model, + 'abcabc[<$text fontFamily="arial">abc' + + '<$text fontFamily="arial">foofoofoo' + + '<$text fontFamily="arial">barbar]bar' + ); + expect( command.value ).to.equal( 'arial' ); + + command.execute(); + + expect( command.value ).to.be.undefined; + + expect( getData( model ) ).to.equal( + 'abcabc[abc' + + 'foofoofoo' + + 'barbar]bar' + ); } ); } ); } ); diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 88675c5..d338824 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -38,6 +38,7 @@ describe( 'FontFamilyEditing', () => { describe( 'default value', () => { it( 'should be set', () => { expect( editor.config.get( 'fontFamily.items' ) ).to.deep.equal( [ + 'default', 'Arial, Helvetica, sans-serif', 'Courier New, Courier, monospace', 'Georgia, serif', @@ -76,7 +77,7 @@ describe( 'FontFamilyEditing', () => { fontFamily: { items: [ { - label: 'Comic Sans', + title: 'Comic Sans', model: 'comic', view: { name: 'span', @@ -95,7 +96,7 @@ describe( 'FontFamilyEditing', () => { expect( plugin.configuredItems ).to.deep.equal( [ { - label: 'Comic Sans', + title: 'Comic Sans', model: 'comic', view: { name: 'span', @@ -128,7 +129,7 @@ describe( 'FontFamilyEditing', () => { expect( plugin.configuredItems ).to.deep.equal( [ { - label: 'Arial', + title: 'Arial', model: 'Arial', view: { name: 'span', @@ -146,7 +147,7 @@ describe( 'FontFamilyEditing', () => { ] }, { - label: 'Comic Sans MS', + title: 'Comic Sans MS', model: 'Comic Sans MS', view: { name: 'span', @@ -167,7 +168,7 @@ describe( 'FontFamilyEditing', () => { ] }, { - label: 'Lucida Console', + title: 'Lucida Console', model: 'Lucida Console', view: { name: 'span', @@ -205,7 +206,7 @@ describe( 'FontFamilyEditing', () => { 'Arial', 'Lucida Sans Unicode, Lucida Grande, sans-serif', { - label: 'My font', + title: 'My font', model: 'my', view: { name: 'mark', @@ -257,7 +258,7 @@ describe( 'FontFamilyEditing', () => { items: [ 'Lucida Sans Unicode, Lucida Grande, sans-serif', { - label: 'My other setting', + title: 'My other setting', model: 'my-other', view: { name: 'span', @@ -265,7 +266,7 @@ describe( 'FontFamilyEditing', () => { } }, { - label: 'My setting', + title: 'My setting', model: 'my', view: { name: 'mark', @@ -274,7 +275,7 @@ describe( 'FontFamilyEditing', () => { } }, { - label: 'Hybrid', + title: 'Hybrid', model: 'complex', view: { name: 'span', diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js index 084a684..dd9caa0 100644 --- a/tests/fontsize/fontsizecommand.js +++ b/tests/fontsize/fontsizecommand.js @@ -18,7 +18,7 @@ describe( 'FontSizeCommand', () => { editor = newEditor; model = editor.model; - command = new FontSizeCommand( editor, 'text-huge' ); + command = new FontSizeCommand( editor ); editor.commands.add( 'fontSize', command ); model.schema.registerItem( 'paragraph', '$block' ); @@ -36,16 +36,16 @@ describe( 'FontSizeCommand', () => { } ); describe( 'value', () => { - it( 'is set to true when selection is in text with fontSize attribute', () => { - setData( model, '<$text fontSize="text-huge">fo[]o' ); + it( 'is set to fontSize value when selection is in text with fontSize attribute', () => { + setData( model, '<$text fontSize="huge">fo[]o' ); - expect( command ).to.have.property( 'value', true ); + expect( command ).to.have.property( 'value', 'huge' ); } ); it( 'is undefined when selection is not in text with fontSize attribute', () => { setData( model, 'fo[]o' ); - expect( command ).to.have.property( 'value', false ); + expect( command ).to.have.property( 'value', undefined ); } ); } ); @@ -59,15 +59,15 @@ describe( 'FontSizeCommand', () => { describe( 'execute()', () => { it( 'should add fontSize attribute on selected text', () => { - setData( model, 'a[bc<$text fontSize="text-huge">fo]obarxyz' ); + setData( model, 'a[bc<$text fontSize="huge">fo]obarxyz' ); - expect( command.value ).to.be.false; + expect( command.value ).to.be.undefined; - command.execute(); + command.execute( { fontSize: 'huge' } ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'huge' ); - expect( getData( model ) ).to.equal( 'a[<$text fontSize="text-huge">bcfo]obarxyz' ); + expect( getData( model ) ).to.equal( 'a[<$text fontSize="huge">bcfo]obarxyz' ); } ); it( 'should add fontSize attribute on selected nodes (multiple nodes)', () => { @@ -78,14 +78,14 @@ describe( 'FontSizeCommand', () => { 'barbar]bar' ); - command.execute(); + command.execute( { fontSize: 'huge' } ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'huge' ); expect( getData( model ) ).to.equal( - 'abcabc[<$text fontSize="text-huge">abc' + - '<$text fontSize="text-huge">foofoofoo' + - '<$text fontSize="text-huge">barbar]bar' + 'abcabc[<$text fontSize="huge">abc' + + '<$text fontSize="huge">foofoofoo' + + '<$text fontSize="huge">barbar]bar' ); } ); @@ -97,27 +97,47 @@ describe( 'FontSizeCommand', () => { '<$text fontSize="text-small">bar]barbar' ); - command.execute(); + command.execute( { fontSize: 'huge' } ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'huge' ); expect( getData( model ) ).to.equal( - 'abc[<$text fontSize="text-huge">abcabc' + - '<$text fontSize="text-huge">foofoofoo' + - '<$text fontSize="text-huge">bar]<$text fontSize="text-small">barbar' + 'abc[<$text fontSize="huge">abcabc' + + '<$text fontSize="huge">foofoofoo' + + '<$text fontSize="huge">bar]<$text fontSize="text-small">barbar' ); } ); it( 'should do nothing on collapsed range', () => { - setData( model, 'abc<$text fontSize="text-huge">foo[]barxyz' ); + setData( model, 'abc<$text fontSize="huge">foo[]barxyz' ); - expect( command.value ).to.be.true; + expect( command.value ).to.equal( 'huge' ); command.execute(); - expect( getData( model ) ).to.equal( 'abc<$text fontSize="text-huge">foo[]barxyz' ); + expect( getData( model ) ).to.equal( 'abc<$text fontSize="huge">foo[]barxyz' ); + + expect( command.value ).to.equal( 'huge' ); + } ); + + it( 'should remove fontSize attribute on selected nodes when passing undefined fontSize param', () => { + setData( + model, + 'abcabc[<$text fontSize="huge">abc' + + '<$text fontSize="huge">foofoofoo' + + '<$text fontSize="huge">barbar]bar' + ); + expect( command.value ).to.equal( 'huge' ); - expect( command.value ).to.be.true; + command.execute(); + + expect( command.value ).to.be.undefined; + + expect( getData( model ) ).to.equal( + 'abcabc[abc' + + 'foofoofoo' + + 'barbar]bar' + ); } ); } ); } ); From e6f687781d44bc25da27622d943b66e58be1c3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 21 Dec 2017 16:24:54 +0100 Subject: [PATCH 034/101] Tests: Fix commands coverage. --- src/fontfamily/fontfamilycommand.js | 2 +- src/fontsize/fontsizecommand.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index a2c7d0d..6a370b5 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -58,7 +58,7 @@ export default class FontFamilyCommand extends Command { const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontFamily' ); for ( const range of ranges ) { - if ( value !== 'default' ) { + if ( value && value !== 'default' ) { writer.setAttribute( 'fontFamily', value, range ); } else { writer.removeAttribute( 'fontFamily', range ); diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index 0dff959..84886ce 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -57,7 +57,7 @@ export default class FontSizeCommand extends Command { const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontSize' ); for ( const range of ranges ) { - if ( value !== 'normal' ) { + if ( value && value !== 'normal' ) { writer.setAttribute( 'fontSize', value, range ); } else { writer.removeAttribute( 'fontSize', range ); From 3c5bcf6f9d886f70ee94adc727eff320ff71cde5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 10:30:03 +0100 Subject: [PATCH 035/101] Tests: Add FontFamilyUI tests. --- tests/fontfamily/fontfamilyui.js | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 tests/fontfamily/fontfamilyui.js diff --git a/tests/fontfamily/fontfamilyui.js b/tests/fontfamily/fontfamilyui.js new file mode 100644 index 0000000..e53cb2c --- /dev/null +++ b/tests/fontfamily/fontfamilyui.js @@ -0,0 +1,85 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* global document */ + +import FontFamilyEditing from '../../src/fontfamily/fontfamilyediting'; +import FontFamilyUI from '../../src/fontfamily/fontfamilyui'; + +import fontFamilyIcon from '@ckeditor/ckeditor5-core/theme/icons/low-vision.svg'; + +import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; +import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; + +testUtils.createSinonSandbox(); + +describe( 'FontFamilyUI', () => { + let editor, command, element; + + beforeEach( () => { + element = document.createElement( 'div' ); + document.body.appendChild( element ); + + return ClassicTestEditor + .create( element, { + plugins: [ FontFamilyEditing, FontFamilyUI ] + } ) + .then( newEditor => { + editor = newEditor; + } ); + } ); + + afterEach( () => { + element.remove(); + + return editor.destroy(); + } ); + + describe( 'fontFamily Dropdown', () => { + let dropdown; + + beforeEach( () => { + command = editor.commands.get( 'fontFamily' ); + dropdown = editor.ui.componentFactory.create( 'fontFamily' ); + } ); + + it( 'button has the base properties', () => { + const button = dropdown.buttonView; + + expect( button ).to.have.property( 'tooltip', 'Font Family' ); + expect( button ).to.have.property( 'icon', fontFamilyIcon ); + expect( button ).to.have.property( 'withText', false ); + } ); + + it( 'should add custom CSS class to dropdown', () => { + const dropdown = editor.ui.componentFactory.create( 'fontFamily' ); + + dropdown.render(); + + expect( dropdown.element.classList.contains( 'ck-font-family-dropdown' ) ).to.be.true; + } ); + + it( 'should focus view after command execution', () => { + const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' ); + const dropdown = editor.ui.componentFactory.create( 'fontFamily' ); + + dropdown.commandName = 'fontFamily'; + dropdown.fire( 'execute' ); + + sinon.assert.calledOnce( focusSpy ); + } ); + + describe( 'model to command binding', () => { + it( 'isEnabled', () => { + command.isEnabled = false; + + expect( dropdown.buttonView.isEnabled ).to.be.false; + + command.isEnabled = true; + expect( dropdown.buttonView.isEnabled ).to.be.true; + } ); + } ); + } ); +} ); From e34994c9176b41f3dc7ab2612ae7e585b92562b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 10:46:56 +0100 Subject: [PATCH 036/101] Tests: Add FontSizeUI tests. --- src/fontsize/fontsizeui.js | 4 +- tests/fontsize/fontsizeui.js | 85 ++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 tests/fontsize/fontsizeui.js diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 720ec52..9b09076 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -32,8 +32,6 @@ export default class FontSizeUI extends Plugin { const command = editor.commands.get( 'fontSize' ); - const dropdownTooltip = t( 'Font Size' ); - for ( const option of options ) { const itemModel = new Model( { commandName: 'fontSize', @@ -51,7 +49,7 @@ export default class FontSizeUI extends Plugin { icon: fontSizeIcon, withText: false, items: dropdownItems, - tooltip: dropdownTooltip + tooltip: t( 'Font Size' ) } ); dropdownModel.bind( 'isEnabled' ).to( command, 'isEnabled' ); diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js new file mode 100644 index 0000000..e055b22 --- /dev/null +++ b/tests/fontsize/fontsizeui.js @@ -0,0 +1,85 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* global document */ + +import FontSizeEditing from '../../src/fontsize/fontsizeediting'; +import FontSizeUI from '../../src/fontsize/fontsizeui'; + +import fontSizeIcon from '@ckeditor/ckeditor5-core/theme/icons/low-vision.svg'; + +import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; +import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; + +testUtils.createSinonSandbox(); + +describe( 'FontSizeUI', () => { + let editor, command, element; + + beforeEach( () => { + element = document.createElement( 'div' ); + document.body.appendChild( element ); + + return ClassicTestEditor + .create( element, { + plugins: [ FontSizeEditing, FontSizeUI ] + } ) + .then( newEditor => { + editor = newEditor; + } ); + } ); + + afterEach( () => { + element.remove(); + + return editor.destroy(); + } ); + + describe( 'fontSize Dropdown', () => { + let dropdown; + + beforeEach( () => { + command = editor.commands.get( 'fontSize' ); + dropdown = editor.ui.componentFactory.create( 'fontSize' ); + } ); + + it( 'button has the base properties', () => { + const button = dropdown.buttonView; + + expect( button ).to.have.property( 'tooltip', 'Font Size' ); + expect( button ).to.have.property( 'icon', fontSizeIcon ); + expect( button ).to.have.property( 'withText', false ); + } ); + + it( 'should add custom CSS class to dropdown', () => { + const dropdown = editor.ui.componentFactory.create( 'fontSize' ); + + dropdown.render(); + + expect( dropdown.element.classList.contains( 'ck-font-size-dropdown' ) ).to.be.true; + } ); + + it( 'should focus view after command execution', () => { + const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' ); + const dropdown = editor.ui.componentFactory.create( 'fontSize' ); + + dropdown.commandName = 'fontSize'; + dropdown.fire( 'execute' ); + + sinon.assert.calledOnce( focusSpy ); + } ); + + describe( 'model to command binding', () => { + it( 'isEnabled', () => { + command.isEnabled = false; + + expect( dropdown.buttonView.isEnabled ).to.be.false; + + command.isEnabled = true; + expect( dropdown.buttonView.isEnabled ).to.be.true; + } ); + } ); + } ); +} ); From 19432edf3908175bd264258f439974b6e2467647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 12:35:47 +0100 Subject: [PATCH 037/101] Added: Font should require FontFamily & FontSize plugins. --- src/font.js | 5 +++-- tests/font.js | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/font.js b/src/font.js index aa884d2..aa459e2 100644 --- a/src/font.js +++ b/src/font.js @@ -9,12 +9,13 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; +import FontFamily from './fontfamily'; import FontSize from './fontsize'; /** * The Font plugin. * - * It requires {@link module:font/fontsize~FontSize} plugin. + * It requires {@link module:font/fontsize~FontSize} and {@link module:font/fontfamily~FontFamily} plugins. * * @extends module:core/plugin~Plugin */ @@ -23,7 +24,7 @@ export default class Font extends Plugin { * @inheritDoc */ static get requires() { - return [ FontSize ]; + return [ FontFamily, FontSize ]; } /** diff --git a/tests/font.js b/tests/font.js index c2eeef5..910fc79 100644 --- a/tests/font.js +++ b/tests/font.js @@ -4,11 +4,12 @@ */ import Font from './../src/font'; +import FontFamily from './../src/fontfamily'; import FontSize from './../src/fontsize'; describe( 'Font', () => { it( 'requires FontSize', () => { - expect( Font.requires ).to.deep.equal( [ FontSize ] ); + expect( Font.requires ).to.deep.equal( [ FontFamily, FontSize ] ); } ); it( 'defines plugin name', () => { From db1f2608b5c793a06452f4cbfd80ebfadcbdeb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 12:46:48 +0100 Subject: [PATCH 038/101] Docs: Update FontFamily & FontSize plugin docs. --- src/fontfamily.js | 4 +++- src/fontsize.js | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fontfamily.js b/src/fontfamily.js index 087445a..d6a77d7 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -12,7 +12,9 @@ import FontFamilyEditing from './fontfamily/fontfamilyediting'; import FontFamilyUI from './fontfamily/fontfamilyui'; /** - * The Font Size plugin. + * The Font Family plugin. + * + * It requires {@link module:font/fontfamilyediting~FontFamilyEditing} and {@link module:font/fontfamilyui~FontFamilyUI} plugins. * * @extends module:core/plugin~Plugin */ diff --git a/src/fontsize.js b/src/fontsize.js index 94db1dc..cac1804 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -14,6 +14,8 @@ import FontSizeUI from './fontsize/fontsizeui'; /** * The Font Size plugin. * + * It requires {@link module:font/fontsizeediting~FontSizeEditing} and {@link module:font/fontsizeui~FontSizeUI} plugins. + * * @extends module:core/plugin~Plugin */ export default class FontSize extends Plugin { From c536ba465c52d1a24ec5c85965d54305e669f01f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 13:01:34 +0100 Subject: [PATCH 039/101] Changed: Extract FontCommand. --- src/fontcommand.js | 81 +++++++++++++++++++++++++++ src/fontfamily/fontfamilycommand.js | 55 ++---------------- src/fontsize/fontsizecommand.js | 56 +++--------------- tests/fontfamily/fontfamilycommand.js | 8 +-- tests/fontsize/fontsizecommand.js | 6 +- 5 files changed, 101 insertions(+), 105 deletions(-) create mode 100644 src/fontcommand.js diff --git a/src/fontcommand.js b/src/fontcommand.js new file mode 100644 index 0000000..15b886b --- /dev/null +++ b/src/fontcommand.js @@ -0,0 +1,81 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontfamilycommand + */ + +import Command from '@ckeditor/ckeditor5-core/src/command'; + +/** + * The font family command. It is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} + * to apply font family. + * + * TODO: those commands are duplicated here and there - maybe make them one? + * + * @extends module:core/command~Command + */ +export default class FontCommand extends Command { + /** + * TODO: docs me + * @param editor + * @param attribute + */ + constructor( editor, attribute ) { + super( editor ); + + this.attribute = attribute; + } + + /** + * @inheritDoc + */ + refresh() { + const model = this.editor.model; + const doc = model.document; + + /** + * A flag indicating whether the command is active, which means that the selection has fontFamily attribute set. + * + * @observable + * @readonly + * @member {Boolean} module:font/fontfamilycommand~FontFamilyCommand#value + */ + this.value = doc.selection.getAttribute( this.attribute ); + this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, this.attribute ); + } + + /** + * Executes the command. + * + * @protected + * @param {Object} [options] Options for the executed command. + * @param {String} [options.value] a value to apply. + */ + execute( options = {} ) { + const model = this.editor.model; + const document = model.document; + const selection = document.selection; + + // Do not apply value on collapsed selection. + if ( selection.isCollapsed ) { + return; + } + + const value = options.value; + + model.change( writer => { + const ranges = model.schema.getValidRanges( selection.getRanges(), this.attribute ); + + for ( const range of ranges ) { + if ( value && value !== 'default' ) { + writer.setAttribute( this.attribute, value, range ); + } else { + writer.removeAttribute( this.attribute, range ); + } + } + } ); + } +} diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index 6a370b5..61b1379 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -7,63 +7,20 @@ * @module font/fontfamilycommand */ -import Command from '@ckeditor/ckeditor5-core/src/command'; +import FontCommand from '../fontcommand'; /** * The font family command. It is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} * to apply font family. * - * TODO: those commands are duplicated here and there - maybe make them one? - * * @extends module:core/command~Command */ -export default class FontFamilyCommand extends Command { - /** - * @inheritDoc - */ - refresh() { - const doc = this.editor.model.document; - - /** - * A flag indicating whether the command is active, which means that the selection has fontFamily attribute set. - * - * @observable - * @readonly - * @member {Boolean} module:font/fontfamilycommand~FontFamilyCommand#value - */ - this.value = doc.selection.getAttribute( 'fontFamily' ); - this.isEnabled = this.editor.model.schema.checkAttributeInSelection( doc.selection, 'fontFamily' ); - } - +export default class FontFamilyCommand extends FontCommand { /** - * Executes the command. - * - * @protected - * @param {Object} [options] Options for the executed command. - * @param {String} [options.fontFamily] FontSize value to apply. + * TODO: docs me + * @param editor */ - execute( options = {} ) { - const model = this.editor.model; - const document = model.document; - const selection = document.selection; - - // Do not apply fontFamily on collapsed selection. - if ( selection.isCollapsed ) { - return; - } - - const value = options.fontFamily; - - model.change( writer => { - const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontFamily' ); - - for ( const range of ranges ) { - if ( value && value !== 'default' ) { - writer.setAttribute( 'fontFamily', value, range ); - } else { - writer.removeAttribute( 'fontFamily', range ); - } - } - } ); + constructor( editor ) { + super( editor, 'fontFamily' ); } } diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index 84886ce..e96f370 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -7,62 +7,20 @@ * @module font/fontsizecommand */ -import Command from '@ckeditor/ckeditor5-core/src/command'; +import FontCommand from '../fontcommand'; /** - * The font size command. It is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing FontSizeEditing feature} + * The font size command. It is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} * to apply font size. * * @extends module:core/command~Command */ -export default class FontSizeCommand extends Command { +export default class FontSizeCommand extends FontCommand { /** - * @inheritDoc + * TODO: docs me + * @param editor */ - refresh() { - const model = this.editor.model; - const doc = model.document; - - /** - * A flag indicating whether the command is active, which means that the selection has fontSize attribute set. - * - * @observable - * @readonly - * @member {Boolean} module:font/fontsizecommand~FontSizeCommand#value - */ - this.value = doc.selection.getAttribute( 'fontSize' ); - this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'fontSize' ); - } - - /** - * Executes the command. - * - * @protected - * @param {Object} [options] Options for the executed command. - * @param {String} [options.fontSize] FontSize value to apply. - */ - execute( options = {} ) { - const model = this.editor.model; - const document = model.document; - const selection = document.selection; - - // Do not apply fontSize on collapsed selection. - if ( selection.isCollapsed ) { - return; - } - - const value = options.fontSize; - - model.change( writer => { - const ranges = model.schema.getValidRanges( selection.getRanges(), 'fontSize' ); - - for ( const range of ranges ) { - if ( value && value !== 'normal' ) { - writer.setAttribute( 'fontSize', value, range ); - } else { - writer.removeAttribute( 'fontSize', range ); - } - } - } ); + constructor( editor ) { + super( editor, 'fontSize' ); } } diff --git a/tests/fontfamily/fontfamilycommand.js b/tests/fontfamily/fontfamilycommand.js index 4795269..f025e4b 100644 --- a/tests/fontfamily/fontfamilycommand.js +++ b/tests/fontfamily/fontfamilycommand.js @@ -63,7 +63,7 @@ describe( 'FontFamilyCommand', () => { expect( command.value ).to.be.undefined; - command.execute( { fontFamily: 'arial' } ); + command.execute( { value: 'arial' } ); expect( command.value ).to.equal( 'arial' ); @@ -78,7 +78,7 @@ describe( 'FontFamilyCommand', () => { 'barbar]bar' ); - command.execute( { fontFamily: 'arial' } ); + command.execute( { value: 'arial' } ); expect( command.value ).to.equal( 'arial' ); @@ -97,7 +97,7 @@ describe( 'FontFamilyCommand', () => { '<$text fontFamily="text-small">bar]barbar' ); - command.execute( { fontFamily: 'arial' } ); + command.execute( { value: 'arial' } ); expect( command.value ).to.equal( 'arial' ); @@ -113,7 +113,7 @@ describe( 'FontFamilyCommand', () => { expect( command.value ).to.equal( 'arial' ); - command.execute( { fontFamily: 'arial' } ); + command.execute( { value: 'arial' } ); expect( getData( model ) ).to.equal( 'abc<$text fontFamily="arial">foo[]barxyz' ); diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js index dd9caa0..0f39520 100644 --- a/tests/fontsize/fontsizecommand.js +++ b/tests/fontsize/fontsizecommand.js @@ -63,7 +63,7 @@ describe( 'FontSizeCommand', () => { expect( command.value ).to.be.undefined; - command.execute( { fontSize: 'huge' } ); + command.execute( { value: 'huge' } ); expect( command.value ).to.equal( 'huge' ); @@ -78,7 +78,7 @@ describe( 'FontSizeCommand', () => { 'barbar]bar' ); - command.execute( { fontSize: 'huge' } ); + command.execute( { value: 'huge' } ); expect( command.value ).to.equal( 'huge' ); @@ -97,7 +97,7 @@ describe( 'FontSizeCommand', () => { '<$text fontSize="text-small">bar]barbar' ); - command.execute( { fontSize: 'huge' } ); + command.execute( { value: 'huge' } ); expect( command.value ).to.equal( 'huge' ); From 2bc0718aba815d9bf65e88428834f4e62e695c66 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Fri, 22 Dec 2017 13:47:47 +0100 Subject: [PATCH 040/101] Added font size and family icons. --- theme/icons/font-family.svg | 1 + theme/icons/font-size.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 theme/icons/font-family.svg create mode 100644 theme/icons/font-size.svg diff --git a/theme/icons/font-family.svg b/theme/icons/font-family.svg new file mode 100644 index 0000000..f8252ad --- /dev/null +++ b/theme/icons/font-family.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/theme/icons/font-size.svg b/theme/icons/font-size.svg new file mode 100644 index 0000000..5f34091 --- /dev/null +++ b/theme/icons/font-size.svg @@ -0,0 +1 @@ + \ No newline at end of file From 581a24299f66d0c09030da518b4c63a25973e3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 14:34:51 +0100 Subject: [PATCH 041/101] Changed: Updated icons in font toolbar buttons. --- src/fontfamily/fontfamilyui.js | 2 +- src/fontsize/fontsizeui.js | 2 +- tests/fontfamily/fontfamilyui.js | 2 +- tests/fontsize/fontsizeui.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index c7dcd56..49233f2 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -14,7 +14,7 @@ import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createl import FontFamilyEditing from './fontfamilyediting'; -import fontFamilyIcon from '@ckeditor/ckeditor5-core/theme/icons/low-vision.svg'; +import fontFamilyIcon from '../../theme/icons/font-family.svg'; /** * @extends module:core/plugin~Plugin diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 9b09076..eae6140 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -14,7 +14,7 @@ import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createl import FontSizeEditing from './fontsizeediting'; -import fontSizeIcon from '@ckeditor/ckeditor5-core/theme/icons/low-vision.svg'; +import fontSizeIcon from '../../theme/icons/font-size.svg'; /** * @extends module:core/plugin~Plugin diff --git a/tests/fontfamily/fontfamilyui.js b/tests/fontfamily/fontfamilyui.js index e53cb2c..d25cf03 100644 --- a/tests/fontfamily/fontfamilyui.js +++ b/tests/fontfamily/fontfamilyui.js @@ -8,7 +8,7 @@ import FontFamilyEditing from '../../src/fontfamily/fontfamilyediting'; import FontFamilyUI from '../../src/fontfamily/fontfamilyui'; -import fontFamilyIcon from '@ckeditor/ckeditor5-core/theme/icons/low-vision.svg'; +import fontFamilyIcon from '../../theme/icons/font-family.svg'; import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js index e055b22..601625f 100644 --- a/tests/fontsize/fontsizeui.js +++ b/tests/fontsize/fontsizeui.js @@ -8,7 +8,7 @@ import FontSizeEditing from '../../src/fontsize/fontsizeediting'; import FontSizeUI from '../../src/fontsize/fontsizeui'; -import fontSizeIcon from '@ckeditor/ckeditor5-core/theme/icons/low-vision.svg'; +import fontSizeIcon from '../../theme/icons/font-size.svg'; import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; From 1d77a894e609ed3cdd422658319196c0b5a8c0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 14:44:46 +0100 Subject: [PATCH 042/101] Changed: Use undefined as command value to pass to unset font attribute. --- src/fontcommand.js | 2 +- src/fontfamily/fontfamilyediting.js | 4 ++-- src/fontfamily/fontfamilyui.js | 2 +- src/fontsize/fontsizeediting.js | 4 ++-- src/fontsize/fontsizeui.js | 2 +- tests/fontfamily/fontfamilyediting.js | 5 +++++ tests/fontsize/fontsizeediting.js | 6 +++--- 7 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/fontcommand.js b/src/fontcommand.js index 15b886b..be9ae7f 100644 --- a/src/fontcommand.js +++ b/src/fontcommand.js @@ -70,7 +70,7 @@ export default class FontCommand extends Command { const ranges = model.schema.getValidRanges( selection.getRanges(), this.attribute ); for ( const range of ranges ) { - if ( value && value !== 'default' ) { + if ( value ) { writer.setAttribute( this.attribute, value, range ); } else { writer.removeAttribute( this.attribute, range ); diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 819a060..85982ff 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -47,7 +47,7 @@ export default class FontFamilyEditing extends Plugin { const editing = editor.editing; // Add converters from view to model. - const items = this.configuredItems.filter( item => item.model !== 'default' ); + const items = this.configuredItems.filter( item => item.model ); for ( const item of items ) { viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] ); @@ -106,7 +106,7 @@ function getItemDefinition( item ) { if ( item === 'default' ) { return { title: 'Default', // TODO localize us - model: 'default' + model: undefined }; } diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 49233f2..6f29d0c 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -75,7 +75,7 @@ export default class FontFamilyUI extends Plugin { // Execute command when an item from the dropdown is selected. this.listenTo( dropdown, 'execute', evt => { - editor.execute( evt.source.commandName, { fontFamily: evt.source.commandParam } ); + editor.execute( evt.source.commandName, { value: evt.source.commandParam } ); editor.editing.view.focus(); } ); diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 33c686d..37c7b22 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -43,7 +43,7 @@ export default class FontSizeEditing extends Plugin { const editing = editor.editing; // Define view to model conversion. - const items = this.configuredItems.filter( item => item.model !== 'normal' ); + const items = this.configuredItems.filter( item => item.model ); for ( const item of items ) { // Covert view to model. @@ -142,7 +142,7 @@ function getItemDefinition( item ) { if ( item === 'normal' ) { return { - model: 'normal', + model: undefined, title: 'Normal' }; } diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index eae6140..c2327d8 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -69,7 +69,7 @@ export default class FontSizeUI extends Plugin { // Execute command when an item from the dropdown is selected. this.listenTo( dropdown, 'execute', evt => { - editor.execute( evt.source.commandName, { fontSize: evt.source.commandParam } ); + editor.execute( evt.source.commandName, { value: evt.source.commandParam } ); editor.editing.view.focus(); } ); diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index d338824..2444bb1 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -76,6 +76,7 @@ describe( 'FontFamilyEditing', () => { plugins: [ FontFamilyEditing ], fontFamily: { items: [ + 'default', { title: 'Comic Sans', model: 'comic', @@ -95,6 +96,10 @@ describe( 'FontFamilyEditing', () => { const plugin = editor.plugins.get( FontFamilyEditing ); expect( plugin.configuredItems ).to.deep.equal( [ + { + model: undefined, + title: 'Default' + }, { title: 'Comic Sans', model: 'comic', diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index b09125b..ed03d02 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -56,7 +56,7 @@ describe( 'FontSizeEditing', () => { const plugin = editor.plugins.get( FontSizeEditing ); - expect( plugin.configuredItems ).to.deep.equal( [ { title: 'Normal', model: 'normal' } ] ); + expect( plugin.configuredItems ).to.deep.equal( [ { title: 'Normal', model: undefined } ] ); } ); } ); @@ -100,7 +100,7 @@ describe( 'FontSizeEditing', () => { expect( plugin.configuredItems ).to.deep.equal( [ { title: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, { title: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, - { title: 'Normal', model: 'normal' }, + { title: 'Normal', model: undefined }, { title: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, { title: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } ] ); @@ -125,7 +125,7 @@ describe( 'FontSizeEditing', () => { expect( plugin.configuredItems ).to.deep.equal( [ { title: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, { title: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, - { title: 'Normal', model: 'normal' }, + { title: 'Normal', model: undefined }, { title: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, { title: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } ] ); From af0d4634bfdd5548f7bca44a4bbdd8abcfebfef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 15:18:50 +0100 Subject: [PATCH 043/101] Docs: Update Font commands documentation. --- src/fontcommand.js | 39 +++++++++++++++++------------ src/fontfamily/fontfamilycommand.js | 7 +++--- src/fontfamily/fontfamilyui.js | 2 +- src/fontsize/fontsizecommand.js | 7 +++--- src/fontsize/fontsizeui.js | 2 +- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/fontcommand.js b/src/fontcommand.js index be9ae7f..6419b12 100644 --- a/src/fontcommand.js +++ b/src/fontcommand.js @@ -4,28 +4,40 @@ */ /** - * @module font/fontfamilycommand + * @module font/fontcommand */ import Command from '@ckeditor/ckeditor5-core/src/command'; /** - * The font family command. It is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} - * to apply font family. - * - * TODO: those commands are duplicated here and there - maybe make them one? + * The base font command. * * @extends module:core/command~Command */ export default class FontCommand extends Command { /** - * TODO: docs me - * @param editor - * @param attribute + * Creates a new `FontCommand` instance. + * + * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used. + * @param {String} attribute Name of an model attribute on which this command operates. */ constructor( editor, attribute ) { super( editor ); + /** + * If is set it means that selection has `attribute` set. + * + * @observable + * @readonly + * @member {Boolean} module:font/fontcommand~FontCommand#value + */ + + /** + * A model attribute on which this command operates. + * + * @readonly + * @member {Boolean} module:font/fontcommand~FontCommand#attribute + */ this.attribute = attribute; } @@ -36,23 +48,18 @@ export default class FontCommand extends Command { const model = this.editor.model; const doc = model.document; - /** - * A flag indicating whether the command is active, which means that the selection has fontFamily attribute set. - * - * @observable - * @readonly - * @member {Boolean} module:font/fontfamilycommand~FontFamilyCommand#value - */ this.value = doc.selection.getAttribute( this.attribute ); this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, this.attribute ); } /** - * Executes the command. + * Executes the command. Applies the the attribute `value` to a selection. If no value is passed it removes attribute from selection. * * @protected * @param {Object} [options] Options for the executed command. * @param {String} [options.value] a value to apply. + * + * @fires execute */ execute( options = {} ) { const model = this.editor.model; diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index 61b1379..8dcfd20 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -4,7 +4,7 @@ */ /** - * @module font/fontfamilycommand + * @module font/fontfamily/fontfamilycommand */ import FontCommand from '../fontcommand'; @@ -17,8 +17,9 @@ import FontCommand from '../fontcommand'; */ export default class FontFamilyCommand extends FontCommand { /** - * TODO: docs me - * @param editor + * Creates a new `FontFamilyCommand` instance. + * + * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used. */ constructor( editor ) { super( editor, 'fontFamily' ); diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 6f29d0c..904a28c 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -4,7 +4,7 @@ */ /** - * @module heading/heading + * @module font/fontfamily/fontfamilyui */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index e96f370..ad132e1 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -4,7 +4,7 @@ */ /** - * @module font/fontsizecommand + * @module font/fontsize/fontsizecommand */ import FontCommand from '../fontcommand'; @@ -17,8 +17,9 @@ import FontCommand from '../fontcommand'; */ export default class FontSizeCommand extends FontCommand { /** - * TODO: docs me - * @param editor + * Creates a new `FontSizeCommand` instance. + * + * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used. */ constructor( editor ) { super( editor, 'fontSize' ); diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index c2327d8..4f5cd45 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -4,7 +4,7 @@ */ /** - * @module heading/heading + * @module font/fontsize/fontsizeui */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; From d3a2e0d63d8e45927987d00c2a9e47925313927f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 15:19:11 +0100 Subject: [PATCH 044/101] Tests: Refactor Font commands tests. --- tests/fontcommand.js | 143 ++++++++++++++++++++++++++ tests/fontfamily/fontfamilycommand.js | 122 ++-------------------- tests/fontsize/fontsizecommand.js | 122 ++-------------------- 3 files changed, 157 insertions(+), 230 deletions(-) create mode 100644 tests/fontcommand.js diff --git a/tests/fontcommand.js b/tests/fontcommand.js new file mode 100644 index 0000000..705c7bd --- /dev/null +++ b/tests/fontcommand.js @@ -0,0 +1,143 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import FontCommand from '../src/fontcommand'; + +import Command from '@ckeditor/ckeditor5-core/src/command'; +import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; +import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; + +describe( 'FontCommand', () => { + let editor, model, command; + + beforeEach( () => { + return ModelTestEditor.create() + .then( newEditor => { + editor = newEditor; + model = editor.model; + + command = new FontCommand( editor, 'font' ); + editor.commands.add( 'font', command ); + + model.schema.registerItem( 'paragraph', '$block' ); + model.schema.allow( { name: '$inline', attributes: 'font', inside: '$block' } ); + } ); + } ); + + afterEach( () => { + editor.destroy(); + } ); + + it( 'is a command', () => { + expect( FontCommand.prototype ).to.be.instanceOf( Command ); + expect( command ).to.be.instanceOf( Command ); + } ); + + describe( 'value', () => { + it( 'is set to font value when selection is in text with font attribute', () => { + setData( model, '<$text font="foo">fo[]o' ); + + expect( command ).to.have.property( 'value', 'foo' ); + } ); + + it( 'is undefined when selection is not in text with font attribute', () => { + setData( model, 'fo[]o' ); + + expect( command ).to.have.property( 'value', undefined ); + } ); + } ); + + describe( 'isEnabled', () => { + it( 'is true when selection is on text which can have font added', () => { + setData( model, 'fo[]o' ); + + expect( command ).to.have.property( 'isEnabled', true ); + } ); + } ); + + describe( 'execute()', () => { + it( 'should add font attribute on selected text', () => { + setData( model, 'a[bc<$text font="foo">fo]obarxyz' ); + + expect( command.value ).to.be.undefined; + + command.execute( { value: 'foo' } ); + + expect( command.value ).to.equal( 'foo' ); + + expect( getData( model ) ).to.equal( 'a[<$text font="foo">bcfo]obarxyz' ); + } ); + + it( 'should add font attribute on selected nodes (multiple nodes)', () => { + setData( + model, + 'abcabc[abc' + + 'foofoofoo' + + 'barbar]bar' + ); + + command.execute( { value: 'foo' } ); + + expect( command.value ).to.equal( 'foo' ); + + expect( getData( model ) ).to.equal( + 'abcabc[<$text font="foo">abc' + + '<$text font="foo">foofoofoo' + + '<$text font="foo">barbar]bar' + ); + } ); + + it( 'should change font attribute on selected nodes', () => { + setData( + model, + 'abc[abc<$text font="text-small">abc' + + '<$text font="text-small">foofoofoo' + + '<$text font="text-small">bar]barbar' + ); + + command.execute( { value: 'foo' } ); + + expect( command.value ).to.equal( 'foo' ); + + expect( getData( model ) ).to.equal( + 'abc[<$text font="foo">abcabc' + + '<$text font="foo">foofoofoo' + + '<$text font="foo">bar]<$text font="text-small">barbar' + ); + } ); + + it( 'should do nothing on collapsed range', () => { + setData( model, 'abc<$text font="foo">foo[]barxyz' ); + + expect( command.value ).to.equal( 'foo' ); + + command.execute( { value: 'foo' } ); + + expect( getData( model ) ).to.equal( 'abc<$text font="foo">foo[]barxyz' ); + + expect( command.value ).to.equal( 'foo' ); + } ); + + it( 'should remove font attribute on selected nodes when passing undefined font param', () => { + setData( + model, + 'abcabc[<$text font="foo">abc' + + '<$text font="foo">foofoofoo' + + '<$text font="foo">barbar]bar' + ); + expect( command.value ).to.equal( 'foo' ); + + command.execute(); + + expect( command.value ).to.be.undefined; + + expect( getData( model ) ).to.equal( + 'abcabc[abc' + + 'foofoofoo' + + 'barbar]bar' + ); + } ); + } ); +} ); diff --git a/tests/fontfamily/fontfamilycommand.js b/tests/fontfamily/fontfamilycommand.js index f025e4b..2b15eb5 100644 --- a/tests/fontfamily/fontfamilycommand.js +++ b/tests/fontfamily/fontfamilycommand.js @@ -4,25 +4,19 @@ */ import FontFamilyCommand from '../../src/fontfamily/fontfamilycommand'; +import FontCommand from '../../src/fontcommand'; -import Command from '@ckeditor/ckeditor5-core/src/command'; import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; -import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; describe( 'FontFamilyCommand', () => { - let editor, model, command; + let editor, command; beforeEach( () => { return ModelTestEditor.create() .then( newEditor => { editor = newEditor; - model = editor.model; command = new FontFamilyCommand( editor ); - editor.commands.add( 'fontFamily', command ); - - model.schema.registerItem( 'paragraph', '$block' ); - model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$block' } ); } ); } ); @@ -30,114 +24,12 @@ describe( 'FontFamilyCommand', () => { editor.destroy(); } ); - it( 'is a command', () => { - expect( FontFamilyCommand.prototype ).to.be.instanceOf( Command ); - expect( command ).to.be.instanceOf( Command ); - } ); - - describe( 'value', () => { - it( 'is set to fontFamily value when selection is in text with fontFamily attribute', () => { - setData( model, '<$text fontFamily="arial">fo[]o' ); - - expect( command ).to.have.property( 'value', 'arial' ); - } ); - - it( 'is undefined when selection is not in text with fontFamily attribute', () => { - setData( model, 'fo[]o' ); - - expect( command ).to.have.property( 'value', undefined ); - } ); + it( 'is a FontCommand', () => { + expect( FontFamilyCommand.prototype ).to.be.instanceOf( FontCommand ); + expect( command ).to.be.instanceOf( FontCommand ); } ); - describe( 'isEnabled', () => { - it( 'is true when selection is on text which can have fontFamily added', () => { - setData( model, 'fo[]o' ); - - expect( command ).to.have.property( 'isEnabled', true ); - } ); - } ); - - describe( 'execute()', () => { - it( 'should add fontFamily attribute on selected text', () => { - setData( model, 'a[bc<$text fontFamily="arial">fo]obarxyz' ); - - expect( command.value ).to.be.undefined; - - command.execute( { value: 'arial' } ); - - expect( command.value ).to.equal( 'arial' ); - - expect( getData( model ) ).to.equal( 'a[<$text fontFamily="arial">bcfo]obarxyz' ); - } ); - - it( 'should add fontFamily attribute on selected nodes (multiple nodes)', () => { - setData( - model, - 'abcabc[abc' + - 'foofoofoo' + - 'barbar]bar' - ); - - command.execute( { value: 'arial' } ); - - expect( command.value ).to.equal( 'arial' ); - - expect( getData( model ) ).to.equal( - 'abcabc[<$text fontFamily="arial">abc' + - '<$text fontFamily="arial">foofoofoo' + - '<$text fontFamily="arial">barbar]bar' - ); - } ); - - it( 'should change fontFamily attribute on selected nodes', () => { - setData( - model, - 'abc[abc<$text fontFamily="text-small">abc' + - '<$text fontFamily="text-small">foofoofoo' + - '<$text fontFamily="text-small">bar]barbar' - ); - - command.execute( { value: 'arial' } ); - - expect( command.value ).to.equal( 'arial' ); - - expect( getData( model ) ).to.equal( - 'abc[<$text fontFamily="arial">abcabc' + - '<$text fontFamily="arial">foofoofoo' + - '<$text fontFamily="arial">bar]<$text fontFamily="text-small">barbar' - ); - } ); - - it( 'should do nothing on collapsed range', () => { - setData( model, 'abc<$text fontFamily="arial">foo[]barxyz' ); - - expect( command.value ).to.equal( 'arial' ); - - command.execute( { value: 'arial' } ); - - expect( getData( model ) ).to.equal( 'abc<$text fontFamily="arial">foo[]barxyz' ); - - expect( command.value ).to.equal( 'arial' ); - } ); - - it( 'should remove fontFamily attribute on selected nodes when passing undefined fontFamily param', () => { - setData( - model, - 'abcabc[<$text fontFamily="arial">abc' + - '<$text fontFamily="arial">foofoofoo' + - '<$text fontFamily="arial">barbar]bar' - ); - expect( command.value ).to.equal( 'arial' ); - - command.execute(); - - expect( command.value ).to.be.undefined; - - expect( getData( model ) ).to.equal( - 'abcabc[abc' + - 'foofoofoo' + - 'barbar]bar' - ); - } ); + it( 'opeartes on fontFamily attribute', () => { + expect( command ).to.have.property( 'attribute', 'fontFamily' ); } ); } ); diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js index 0f39520..456bc97 100644 --- a/tests/fontsize/fontsizecommand.js +++ b/tests/fontsize/fontsizecommand.js @@ -4,25 +4,19 @@ */ import FontSizeCommand from '../../src/fontsize/fontsizecommand'; +import FontCommand from '../../src/fontcommand'; -import Command from '@ckeditor/ckeditor5-core/src/command'; import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; -import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; describe( 'FontSizeCommand', () => { - let editor, model, command; + let editor, command; beforeEach( () => { return ModelTestEditor.create() .then( newEditor => { editor = newEditor; - model = editor.model; command = new FontSizeCommand( editor ); - editor.commands.add( 'fontSize', command ); - - model.schema.registerItem( 'paragraph', '$block' ); - model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); } ); } ); @@ -30,114 +24,12 @@ describe( 'FontSizeCommand', () => { editor.destroy(); } ); - it( 'is a command', () => { - expect( FontSizeCommand.prototype ).to.be.instanceOf( Command ); - expect( command ).to.be.instanceOf( Command ); - } ); - - describe( 'value', () => { - it( 'is set to fontSize value when selection is in text with fontSize attribute', () => { - setData( model, '<$text fontSize="huge">fo[]o' ); - - expect( command ).to.have.property( 'value', 'huge' ); - } ); - - it( 'is undefined when selection is not in text with fontSize attribute', () => { - setData( model, 'fo[]o' ); - - expect( command ).to.have.property( 'value', undefined ); - } ); + it( 'is a FontCommand', () => { + expect( FontSizeCommand.prototype ).to.be.instanceOf( FontCommand ); + expect( command ).to.be.instanceOf( FontCommand ); } ); - describe( 'isEnabled', () => { - it( 'is true when selection is on text which can have fontSize added', () => { - setData( model, 'fo[]o' ); - - expect( command ).to.have.property( 'isEnabled', true ); - } ); - } ); - - describe( 'execute()', () => { - it( 'should add fontSize attribute on selected text', () => { - setData( model, 'a[bc<$text fontSize="huge">fo]obarxyz' ); - - expect( command.value ).to.be.undefined; - - command.execute( { value: 'huge' } ); - - expect( command.value ).to.equal( 'huge' ); - - expect( getData( model ) ).to.equal( 'a[<$text fontSize="huge">bcfo]obarxyz' ); - } ); - - it( 'should add fontSize attribute on selected nodes (multiple nodes)', () => { - setData( - model, - 'abcabc[abc' + - 'foofoofoo' + - 'barbar]bar' - ); - - command.execute( { value: 'huge' } ); - - expect( command.value ).to.equal( 'huge' ); - - expect( getData( model ) ).to.equal( - 'abcabc[<$text fontSize="huge">abc' + - '<$text fontSize="huge">foofoofoo' + - '<$text fontSize="huge">barbar]bar' - ); - } ); - - it( 'should change fontSize attribute on selected nodes', () => { - setData( - model, - 'abc[abc<$text fontSize="text-small">abc' + - '<$text fontSize="text-small">foofoofoo' + - '<$text fontSize="text-small">bar]barbar' - ); - - command.execute( { value: 'huge' } ); - - expect( command.value ).to.equal( 'huge' ); - - expect( getData( model ) ).to.equal( - 'abc[<$text fontSize="huge">abcabc' + - '<$text fontSize="huge">foofoofoo' + - '<$text fontSize="huge">bar]<$text fontSize="text-small">barbar' - ); - } ); - - it( 'should do nothing on collapsed range', () => { - setData( model, 'abc<$text fontSize="huge">foo[]barxyz' ); - - expect( command.value ).to.equal( 'huge' ); - - command.execute(); - - expect( getData( model ) ).to.equal( 'abc<$text fontSize="huge">foo[]barxyz' ); - - expect( command.value ).to.equal( 'huge' ); - } ); - - it( 'should remove fontSize attribute on selected nodes when passing undefined fontSize param', () => { - setData( - model, - 'abcabc[<$text fontSize="huge">abc' + - '<$text fontSize="huge">foofoofoo' + - '<$text fontSize="huge">barbar]bar' - ); - expect( command.value ).to.equal( 'huge' ); - - command.execute(); - - expect( command.value ).to.be.undefined; - - expect( getData( model ) ).to.equal( - 'abcabc[abc' + - 'foofoofoo' + - 'barbar]bar' - ); - } ); + it( 'opeartes on fontSize attribute', () => { + expect( command ).to.have.property( 'attribute', 'fontSize' ); } ); } ); From c3ef7864d35f358d7abf0df9c2c1fdc1112906c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 22 Dec 2017 16:30:20 +0100 Subject: [PATCH 045/101] Docs: Update FontFamily documentation. --- src/fontfamily/fontfamilyediting.js | 167 +++++++++++++++++--------- src/fontfamily/fontfamilyui.js | 4 +- tests/fontfamily/fontfamilyediting.js | 20 +-- 3 files changed, 119 insertions(+), 72 deletions(-) diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 85982ff..615cc9c 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -27,9 +27,8 @@ export default class FontFamilyEditing extends Plugin { constructor( editor ) { super( editor ); - // Define default configuration using named presets editor.config.define( 'fontFamily', { - items: [ + options: [ 'default', 'Arial, Helvetica, sans-serif', 'Courier New, Courier, monospace', @@ -41,46 +40,43 @@ export default class FontFamilyEditing extends Plugin { 'Verdana, Geneva, sans-serif' ] } ); - - // Get configuration - const data = editor.data; - const editing = editor.editing; - - // Add converters from view to model. - const items = this.configuredItems.filter( item => item.model ); - - for ( const item of items ) { - viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] ); - } - - // Covert from model to view. - modelAttributeToViewAttributeElement( 'fontFamily', items, [ data.modelToView, editing.modelToView ] ); - - // Add FontSize command. - editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) ); } - get configuredItems() { + /** + * Returns {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options} array with options normalized in the + * {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} format, translated + * `title` for each style. + * + * @readonly + * @type {Array.} + */ + get configuredOptions() { // Cache value - if ( this._cachedItems ) { - return this._cachedItems; + if ( this._cachedOptions ) { + return this._cachedOptions; } - const items = []; const editor = this.editor; - const config = editor.config; + const t = editor.t; - const configuredItems = config.get( 'fontFamily.items' ); + const options = []; + const configuredOptions = editor.config.get( 'fontFamily.options' ); - for ( const item of configuredItems ) { + for ( const item of configuredOptions ) { const itemDefinition = getItemDefinition( item ); + // Set only valid definitions. if ( itemDefinition ) { - items.push( itemDefinition ); + // Localize the "Default" title if set. + if ( itemDefinition.title === 'Default' ) { + itemDefinition.title = t( 'Default' ); + } + + options.push( itemDefinition ); } } - return ( this._cachedItems = items ); + return ( this._cachedOptions = options ); } /** @@ -88,29 +84,45 @@ export default class FontFamilyEditing extends Plugin { */ init() { const editor = this.editor; + const data = editor.data; + const editing = editor.editing; // Allow highlight attribute on all elements editor.model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$block' } ); // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. editor.model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$clipboardHolder' } ); + + // Get configured font family options without "default" option. + const fontFamilyOptions = this.configuredOptions.filter( item => item.model ); + + // Define view to model conversion. + for ( const item of fontFamilyOptions ) { + viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] ); + } + + // Define model to view conversion. + modelAttributeToViewAttributeElement( 'fontFamily', fontFamilyOptions, [ data.modelToView, editing.modelToView ] ); + + editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) ); } } // Returns item definition from preset function getItemDefinition( item ) { - // Probably it is full item definition so return it + // Probably it is full item definition so return it. if ( typeof item === 'object' ) { return item; } + // Handle 'default' string as a special case. It will be used to remove the fontFamily attribute. if ( item === 'default' ) { return { - title: 'Default', // TODO localize us + title: 'Default', model: undefined }; } - // Ignore falsy values + // Ignore values that we cannot parse to a definition. if ( typeof item !== 'string' ) { return; } @@ -118,21 +130,33 @@ function getItemDefinition( item ) { return generateFontPreset( item ); } -// Creates a predefined preset for pixel size. -function generateFontPreset( font ) { - // Remove quotes from font names - const fontNames = font.replace( /"|'/g, '' ).split( ',' ); - - const cssFontNames = fontNames.map( normalizeFontName ); +// Creates a predefined preset for pixel size. It deconstructs font-family like string into full configuration option. +// A font definition is passed as coma delimited set of font family names. Font names might be quoted. +// +// @param {String} A font definition form configuration. +function generateFontPreset( fontDefinition ) { + // Remove quotes from font names - will be normalized later. + const fontNames = fontDefinition.replace( /"|'/g, '' ).split( ',' ); + // The first matched font name will be used as dropdown list item title and as model value const firstFontName = fontNames[ 0 ]; - const quotesMatch = '("|\'|&qout;|\\W){0,2}'; + const cssFontNames = fontNames.map( normalizeFontName ); + // TODO: Maybe we can come with something better here? + // TODO: Also document this behavior in engine as it uses matcher~Pattern not ViewElementDefinition. + // TODO: Maybe a better solution will be a callback here? (also needs documentation) + // This will match any quote type with whitespace. + const quotesMatch = '("|\'|&qout;|\\W){0,2}'; + // Full regex will catch any style of quotation used in view. + // Example: + // from string: "Font Foo Foo, Font Bar" + // it will create a regex that will match any quotation mix: + // - "Font Foo Foo", Font Bar + // - 'Font Foo Foo', "Font Bar" + // - ... etc. const regexString = `${ quotesMatch }${ fontNames.map( n => n.trim() ).join( `${ quotesMatch },${ quotesMatch }` ) }${ quotesMatch }`; - const fontFamilyRegexp = new RegExp( regexString ); - return { title: firstFontName, model: firstFontName, @@ -146,21 +170,21 @@ function generateFontPreset( font ) { { name: 'span', style: { - 'font-family': fontFamilyRegexp + 'font-family': new RegExp( regexString ) } } ] }; } -/** - * - * @param fontName - * @returns {string|*} - */ +// Normalizes font name for the view style attribute. +// +// @param {String} fontName +// @returns {String} function normalizeFontName( fontName ) { fontName = fontName.trim(); + // Compound font names should be quoted. if ( fontName.indexOf( ' ' ) > 0 ) { fontName = `'${ fontName }'`; } @@ -169,17 +193,22 @@ function normalizeFontName( fontName ) { } /** - * Font family option descriptor. + * Font family option. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. * * @typedef {Object} module:font/fontfamily/fontfamilyediting~FontFamilyOption * - * @property {String} title TODO me - * @property {String} model TODO me - * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. + * @property {String} model The `fontFamily` attribute value in the model. + * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view The view representation for that option. + * @property {String} title The user-readable title of the option. + * @property {String} [uiStyle] The style which will be added to the dropdown item representing this option. + * Defaults to `view.style[ 'font-family' ]`. + * @property {Array.} acceptAlso An array with all matched elements that + * view to model conversion should also accept. */ /** - * The configuration of the heading feature. Introduced by the {@link module:font/fontfamily/fontfamilyediting~FontSizeEditing} feature. + * The configuration of the font family feature. + * Introduced by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature. * * Read more in {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig}. * @@ -188,14 +217,14 @@ function normalizeFontName( fontName ) { /** * The configuration of the font family feature. - * The option is used by the {@link module:font/fontfamily/fontfamilyediting~FontSizeEditing} feature. + * The option is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature. * - * ClassicEditor - * .create( { + * ClassicEditor + * .create( { * fontFamily: ... // Font family feature config. * } ) - * .then( ... ) - * .catch( ... ); + * .then( ... ) + * .catch( ... ); * * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. * @@ -206,9 +235,27 @@ function normalizeFontName( fontName ) { * Available font family options. Defined either as array of strings. * * The default value is - * TODO code - * which configures * - * @member {Array.} - * module:font/fontfamily/fontfamilyediting~FontFamilyConfig#items + * const fontFamilyConfig = { + * options: [ + * 'default', + * 'Arial, Helvetica, sans-serif', + * 'Courier New, Courier, monospace', + * 'Georgia, serif', + * 'Lucida Sans Unicode, Lucida Grande, sans-serif', + * 'Tahoma, Geneva, sans-serif', + * 'Times New Roman, Times, serif', + * 'Trebuchet MS, Helvetica, sans-serif', + * 'Verdana, Geneva, sans-serif' + * ] + * }; + * + * which configures 8 font family options and a default option that will remove font family to text default setting (defaulting to content + * CSS styles). + * + * TODO: what 'default' does. + * TODO: how those string are translated to configuration + * + * @member {Array.} + * module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options */ diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 904a28c..9c39077 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -105,9 +105,9 @@ export default class FontFamilyUI extends Plugin { }; // TODO this is not nice :/ in terms of feature split. - const items = editor.plugins.get( FontFamilyEditing ).configuredItems; + const options = editor.plugins.get( FontFamilyEditing ).configuredOptions; - return items.map( option => { + return options.map( option => { const title = localizedTitles[ option.title ]; if ( title && title != option.title ) { diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 2444bb1..f648b6d 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -37,7 +37,7 @@ describe( 'FontFamilyEditing', () => { describe( 'config', () => { describe( 'default value', () => { it( 'should be set', () => { - expect( editor.config.get( 'fontFamily.items' ) ).to.deep.equal( [ + expect( editor.config.get( 'fontFamily.options' ) ).to.deep.equal( [ 'default', 'Arial, Helvetica, sans-serif', 'Courier New, Courier, monospace', @@ -52,13 +52,13 @@ describe( 'FontFamilyEditing', () => { } ); } ); - describe( 'configuredItems', () => { + describe( 'configuredOptions', () => { it( 'should discard unparsable values', () => { return VirtualTestEditor .create( { plugins: [ FontFamilyEditing ], fontFamily: { - items: [ () => {}, 0, true ] + options: [ () => {}, 0, true ] } } ) .then( newEditor => { @@ -66,7 +66,7 @@ describe( 'FontFamilyEditing', () => { const plugin = editor.plugins.get( FontFamilyEditing ); - expect( plugin.configuredItems ).to.deep.equal( [] ); + expect( plugin.configuredOptions ).to.deep.equal( [] ); } ); } ); @@ -75,7 +75,7 @@ describe( 'FontFamilyEditing', () => { .create( { plugins: [ FontFamilyEditing ], fontFamily: { - items: [ + options: [ 'default', { title: 'Comic Sans', @@ -95,7 +95,7 @@ describe( 'FontFamilyEditing', () => { const plugin = editor.plugins.get( FontFamilyEditing ); - expect( plugin.configuredItems ).to.deep.equal( [ + expect( plugin.configuredOptions ).to.deep.equal( [ { model: undefined, title: 'Default' @@ -120,7 +120,7 @@ describe( 'FontFamilyEditing', () => { .create( { plugins: [ FontFamilyEditing ], fontFamily: { - items: [ + options: [ 'Arial', '"Comic Sans MS", sans-serif', 'Lucida Console, \'Courier New\', Courier, monospace' @@ -132,7 +132,7 @@ describe( 'FontFamilyEditing', () => { const plugin = editor.plugins.get( FontFamilyEditing ); - expect( plugin.configuredItems ).to.deep.equal( [ + expect( plugin.configuredOptions ).to.deep.equal( [ { title: 'Arial', model: 'Arial', @@ -207,7 +207,7 @@ describe( 'FontFamilyEditing', () => { .create( { plugins: [ FontFamilyEditing, Paragraph ], fontFamily: { - items: [ + options: [ 'Arial', 'Lucida Sans Unicode, Lucida Grande, sans-serif', { @@ -260,7 +260,7 @@ describe( 'FontFamilyEditing', () => { .create( { plugins: [ FontFamilyEditing, Paragraph ], fontFamily: { - items: [ + options: [ 'Lucida Sans Unicode, Lucida Grande, sans-serif', { title: 'My other setting', From c1e94a9ff969f27c66468557fc3c20abe2669703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 8 Jan 2018 16:00:50 +0100 Subject: [PATCH 046/101] Other: Update schema use to match the new API. --- src/fontfamily/fontfamilyediting.js | 6 ++---- src/fontsize/fontsizeediting.js | 6 ++---- tests/fontcommand.js | 4 ++-- tests/fontfamily/fontfamilyediting.js | 6 ++++-- tests/fontsize/fontsizeediting.js | 6 ++++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 615cc9c..9f4b546 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -87,10 +87,8 @@ export default class FontFamilyEditing extends Plugin { const data = editor.data; const editing = editor.editing; - // Allow highlight attribute on all elements - editor.model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$block' } ); - // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. - editor.model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$clipboardHolder' } ); + // Allow fontFamily attribute on text nodes. + editor.model.schema.extend( '$text', { allowAttributes: 'fontFamily' } ); // Get configured font family options without "default" option. const fontFamilyOptions = this.configuredOptions.filter( item => item.model ); diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 37c7b22..8266df0 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -86,10 +86,8 @@ export default class FontSizeEditing extends Plugin { init() { const editor = this.editor; - // Allow fontSize attribute on all elements. - editor.model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$block' } ); - // Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. - editor.model.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ); + // Allow fontSize attribute on text nodes. + editor.model.schema.extend( '$text', { allowAttributes: 'fontSize' } ); } } diff --git a/tests/fontcommand.js b/tests/fontcommand.js index 705c7bd..4c5338c 100644 --- a/tests/fontcommand.js +++ b/tests/fontcommand.js @@ -21,8 +21,8 @@ describe( 'FontCommand', () => { command = new FontCommand( editor, 'font' ); editor.commands.add( 'font', command ); - model.schema.registerItem( 'paragraph', '$block' ); - model.schema.allow( { name: '$inline', attributes: 'font', inside: '$block' } ); + model.schema.register( 'paragraph', { inheritAllFrom: '$block' } ); + model.schema.extend( '$text', { allowAttributes: 'font' } ); } ); } ); diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index f648b6d..2ab6e49 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -30,8 +30,10 @@ describe( 'FontFamilyEditing', () => { } ); it( 'should set proper schema rules', () => { - expect( editor.model.schema.check( { name: '$inline', attributes: 'fontFamily', inside: '$block' } ) ).to.be.true; - expect( editor.model.schema.check( { name: '$inline', attributes: 'fontFamily', inside: '$clipboardHolder' } ) ).to.be.true; + expect( editor.model.schema.checkAttribute( [ '$block', '$text' ], 'fontFamily' ) ).to.be.true; + expect( editor.model.schema.checkAttribute( [ '$clipboardHolder', '$text' ], 'fontFamily' ) ).to.be.true; + + expect( editor.model.schema.checkAttribute( [ '$block' ], 'fontFamily' ) ).to.be.false; } ); describe( 'config', () => { diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index ed03d02..9eff28b 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -30,8 +30,10 @@ describe( 'FontSizeEditing', () => { } ); it( 'should set proper schema rules', () => { - expect( editor.model.schema.check( { name: '$inline', attributes: 'fontSize', inside: '$block' } ) ).to.be.true; - expect( editor.model.schema.check( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } ) ).to.be.true; + expect( editor.model.schema.checkAttribute( [ '$block', '$text' ], 'fontSize' ) ).to.be.true; + expect( editor.model.schema.checkAttribute( [ '$clipboardHolder', '$text' ], 'fontSize' ) ).to.be.true; + + expect( editor.model.schema.checkAttribute( [ '$block' ], 'fontSize' ) ).to.be.false; } ); describe( 'config', () => { From beb0b8cd3fa81b3f1ad4bb9db87f68ec828d3582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 9 Jan 2018 14:52:12 +0100 Subject: [PATCH 047/101] Other: Extract common UI and Editing parts as utils. --- src/fontfamily/fontfamilyediting.js | 131 +--------------------- src/fontfamily/fontfamilyui.js | 24 ++--- src/fontfamily/utils.js | 114 ++++++++++++++++++++ src/fontsize/fontsizeediting.js | 117 ++------------------ src/fontsize/fontsizeui.js | 9 +- src/fontsize/utils.js | 116 ++++++++++++++++++++ tests/fontfamily/fontfamilyediting.js | 149 -------------------------- tests/fontfamily/utils.js | 118 ++++++++++++++++++++ tests/fontsize/fontsizeediting.js | 98 +---------------- tests/fontsize/utils.js | 52 +++++++++ 10 files changed, 430 insertions(+), 498 deletions(-) create mode 100644 src/fontfamily/utils.js create mode 100644 src/fontsize/utils.js create mode 100644 tests/fontfamily/utils.js create mode 100644 tests/fontsize/utils.js diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 9f4b546..9642c28 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -14,6 +14,7 @@ import { } from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters'; import FontFamilyCommand from './fontfamilycommand'; +import { normalizeOptions } from './utils'; /** * The Font Family Editing feature. @@ -42,43 +43,6 @@ export default class FontFamilyEditing extends Plugin { } ); } - /** - * Returns {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options} array with options normalized in the - * {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} format, translated - * `title` for each style. - * - * @readonly - * @type {Array.} - */ - get configuredOptions() { - // Cache value - if ( this._cachedOptions ) { - return this._cachedOptions; - } - - const editor = this.editor; - const t = editor.t; - - const options = []; - const configuredOptions = editor.config.get( 'fontFamily.options' ); - - for ( const item of configuredOptions ) { - const itemDefinition = getItemDefinition( item ); - - // Set only valid definitions. - if ( itemDefinition ) { - // Localize the "Default" title if set. - if ( itemDefinition.title === 'Default' ) { - itemDefinition.title = t( 'Default' ); - } - - options.push( itemDefinition ); - } - } - - return ( this._cachedOptions = options ); - } - /** * @inheritDoc */ @@ -91,105 +55,20 @@ export default class FontFamilyEditing extends Plugin { editor.model.schema.extend( '$text', { allowAttributes: 'fontFamily' } ); // Get configured font family options without "default" option. - const fontFamilyOptions = this.configuredOptions.filter( item => item.model ); + const options = normalizeOptions( editor.config.get( 'fontFamily.options' ) ).filter( item => item.model ); // Define view to model conversion. - for ( const item of fontFamilyOptions ) { - viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] ); + for ( const option of options ) { + viewToModelAttribute( 'fontFamily', option, [ data.viewToModel ] ); } // Define model to view conversion. - modelAttributeToViewAttributeElement( 'fontFamily', fontFamilyOptions, [ data.modelToView, editing.modelToView ] ); + modelAttributeToViewAttributeElement( 'fontFamily', options, [ data.modelToView, editing.modelToView ] ); editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) ); } } -// Returns item definition from preset -function getItemDefinition( item ) { - // Probably it is full item definition so return it. - if ( typeof item === 'object' ) { - return item; - } - - // Handle 'default' string as a special case. It will be used to remove the fontFamily attribute. - if ( item === 'default' ) { - return { - title: 'Default', - model: undefined - }; - } - - // Ignore values that we cannot parse to a definition. - if ( typeof item !== 'string' ) { - return; - } - - return generateFontPreset( item ); -} - -// Creates a predefined preset for pixel size. It deconstructs font-family like string into full configuration option. -// A font definition is passed as coma delimited set of font family names. Font names might be quoted. -// -// @param {String} A font definition form configuration. -function generateFontPreset( fontDefinition ) { - // Remove quotes from font names - will be normalized later. - const fontNames = fontDefinition.replace( /"|'/g, '' ).split( ',' ); - - // The first matched font name will be used as dropdown list item title and as model value - const firstFontName = fontNames[ 0 ]; - - const cssFontNames = fontNames.map( normalizeFontName ); - - // TODO: Maybe we can come with something better here? - // TODO: Also document this behavior in engine as it uses matcher~Pattern not ViewElementDefinition. - // TODO: Maybe a better solution will be a callback here? (also needs documentation) - // This will match any quote type with whitespace. - const quotesMatch = '("|\'|&qout;|\\W){0,2}'; - // Full regex will catch any style of quotation used in view. - // Example: - // from string: "Font Foo Foo, Font Bar" - // it will create a regex that will match any quotation mix: - // - "Font Foo Foo", Font Bar - // - 'Font Foo Foo', "Font Bar" - // - ... etc. - const regexString = `${ quotesMatch }${ fontNames.map( n => n.trim() ).join( `${ quotesMatch },${ quotesMatch }` ) }${ quotesMatch }`; - - return { - title: firstFontName, - model: firstFontName, - view: { - name: 'span', - style: { - 'font-family': cssFontNames.join( ', ' ) - } - }, - acceptsAlso: [ - { - name: 'span', - style: { - 'font-family': new RegExp( regexString ) - } - } - ] - }; -} - -// Normalizes font name for the view style attribute. -// -// @param {String} fontName -// @returns {String} -function normalizeFontName( fontName ) { - fontName = fontName.trim(); - - // Compound font names should be quoted. - if ( fontName.indexOf( ' ' ) > 0 ) { - fontName = `'${ fontName }'`; - } - - return fontName; -} - /** * Font family option. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. * diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 9c39077..1ca11cd 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -12,9 +12,8 @@ import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createlistdropdown'; -import FontFamilyEditing from './fontfamilyediting'; - import fontFamilyIcon from '../../theme/icons/font-family.svg'; +import { normalizeOptions } from './utils'; /** * @extends module:core/plugin~Plugin @@ -25,15 +24,15 @@ export default class FontFamilyUI extends Plugin { */ init() { const editor = this.editor; + const t = editor.t; + const dropdownItems = new Collection(); const options = this._getLocalizedOptions(); - const t = editor.t; const command = editor.commands.get( 'fontFamily' ); - const dropdownTooltip = t( 'Font Family' ); - + // Create dropdown items. for ( const option of options ) { const itemModel = new Model( { commandName: 'fontFamily', @@ -41,12 +40,11 @@ export default class FontFamilyUI extends Plugin { label: option.title, } ); - // Try to set style + // Try to set a dropdown list item style. if ( option.view && option.view.style ) { itemModel.set( 'style', 'font-family:' + option.view.style[ 'font-family' ] ); } - // Add the option to the collection. dropdownItems.add( itemModel ); } @@ -55,7 +53,7 @@ export default class FontFamilyUI extends Plugin { icon: fontFamilyIcon, withText: false, items: dropdownItems, - tooltip: dropdownTooltip + tooltip: t( 'Font Family' ) } ); dropdownModel.bind( 'isEnabled' ).to( command, 'isEnabled' ); @@ -64,7 +62,6 @@ export default class FontFamilyUI extends Plugin { editor.ui.componentFactory.add( 'fontFamily', locale => { const dropdown = createListDropdown( dropdownModel, locale ); - // TODO check if needed dropdown.extendTemplate( { attributes: { class: [ @@ -84,7 +81,7 @@ export default class FontFamilyUI extends Plugin { } /** - * Returns heading options as defined in `config.heading.options` but processed to consider + * Returns TODO options as defined in `config.heading.options` but processed to consider * editor localization, i.e. to display {@link module:heading/heading~HeadingOption} * in the correct language. * @@ -97,21 +94,22 @@ export default class FontFamilyUI extends Plugin { _getLocalizedOptions() { const editor = this.editor; const t = editor.t; + const localizedTitles = { + Default: t( 'Default' ), Tiny: t( 'Tiny' ), Small: t( 'Small' ), Big: t( 'Big' ), Huge: t( 'Huge' ) }; - // TODO this is not nice :/ in terms of feature split. - const options = editor.plugins.get( FontFamilyEditing ).configuredOptions; + const options = normalizeOptions( editor.config.get( 'fontFamily.options' ) ); return options.map( option => { const title = localizedTitles[ option.title ]; if ( title && title != option.title ) { - // Clone the option to avoid altering the original `config.heading.options`. + // Clone the option to avoid altering the original `config.fontFamily.options`. option = Object.assign( {}, option, { title } ); } diff --git a/src/fontfamily/utils.js b/src/fontfamily/utils.js new file mode 100644 index 0000000..94627de --- /dev/null +++ b/src/fontfamily/utils.js @@ -0,0 +1,114 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontfamily/fontfamilyediting + */ + +/** + * Returns {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options} array with options normalized in the + * {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} format, translated + * `title` for each style. + * + * @returns {Array.} + */ +export function normalizeOptions( configuredOptions ) { + const options = []; + for ( const item of configuredOptions ) { + const itemDefinition = getItemDefinition( item ); + + // Set only valid definitions. + if ( itemDefinition ) { + options.push( itemDefinition ); + } + } + + return options; +} + +// Returns item definition from preset +function getItemDefinition( item ) { + // Probably it is full item definition so return it. + if ( typeof item === 'object' ) { + return item; + } + + // Handle 'default' string as a special case. It will be used to remove the fontFamily attribute. + if ( item === 'default' ) { + return { + title: 'Default', + model: undefined + }; + } + + // Ignore values that we cannot parse to a definition. + if ( typeof item !== 'string' ) { + return; + } + + return generateFontPreset( item ); +} + +// Creates a predefined preset for pixel size. It deconstructs font-family like string into full configuration option. +// A font definition is passed as coma delimited set of font family names. Font names might be quoted. +// +// @param {String} A font definition form configuration. +function generateFontPreset( fontDefinition ) { + // Remove quotes from font names - will be normalized later. + const fontNames = fontDefinition.replace( /"|'/g, '' ).split( ',' ); + + // The first matched font name will be used as dropdown list item title and as model value + const firstFontName = fontNames[ 0 ]; + + const cssFontNames = fontNames.map( normalizeFontName ); + + // TODO: Maybe we can come with something better here? + // TODO: Also document this behavior in engine as it uses matcher~Pattern not ViewElementDefinition. + // TODO: Maybe a better solution will be a callback here? (also needs documentation) + // This will match any quote type with whitespace. + const quotesMatch = '("|\'|&qout;|\\W){0,2}'; + // Full regex will catch any style of quotation used in view. + // Example: + // from string: "Font Foo Foo, Font Bar" + // it will create a regex that will match any quotation mix: + // - "Font Foo Foo", Font Bar + // - 'Font Foo Foo', "Font Bar" + // - ... etc. + const regexString = `${ quotesMatch }${ fontNames.map( n => n.trim() ).join( `${ quotesMatch },${ quotesMatch }` ) }${ quotesMatch }`; + + return { + title: firstFontName, + model: firstFontName, + view: { + name: 'span', + style: { + 'font-family': cssFontNames.join( ', ' ) + } + }, + acceptsAlso: [ + { + name: 'span', + style: { + 'font-family': new RegExp( regexString ) + } + } + ] + }; +} + +// Normalizes font name for the view style attribute. +// +// @param {String} fontName +// @returns {String} +function normalizeFontName( fontName ) { + fontName = fontName.trim(); + + // Compound font names should be quoted. + if ( fontName.indexOf( ' ' ) > 0 ) { + fontName = `'${ fontName }'`; + } + + return fontName; +} diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 8266df0..05c5f7e 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -14,6 +14,7 @@ import { } from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters'; import FontSizeCommand from './fontsizecommand'; +import { normalizeOptions } from './utils'; /** * The Font Size Editing feature. @@ -29,7 +30,7 @@ export default class FontSizeEditing extends Plugin { // Define default configuration using named presets editor.config.define( 'fontSize', { - items: [ + options: [ 'tiny', 'small', 'normal', @@ -43,43 +44,20 @@ export default class FontSizeEditing extends Plugin { const editing = editor.editing; // Define view to model conversion. - const items = this.configuredItems.filter( item => item.model ); + const options = normalizeOptions( this.editor.config.get( 'fontSize.options' ) ).filter( item => item.model ); - for ( const item of items ) { + for ( const option of options ) { // Covert view to model. - viewToModelAttribute( 'fontSize', item, [ data.viewToModel ] ); + viewToModelAttribute( 'fontSize', option, [ data.viewToModel ] ); } // Define model to view conversion. - modelAttributeToViewAttributeElement( 'fontSize', items, [ data.modelToView, editing.modelToView ] ); + modelAttributeToViewAttributeElement( 'fontSize', options, [ data.modelToView, editing.modelToView ] ); // Add FontSize command. editor.commands.add( 'fontSize', new FontSizeCommand( editor ) ); } - get configuredItems() { - // Cache value - if ( this._cachedItems ) { - return this._cachedItems; - } - - const items = []; - const editor = this.editor; - const config = editor.config; - - const configuredItems = config.get( 'fontSize.items' ); - - for ( const item of configuredItems ) { - const itemDefinition = getItemDefinition( item ); - - if ( itemDefinition ) { - items.push( itemDefinition ); - } - } - - return ( this._cachedItems = items ); - } - /** * @inheritDoc */ @@ -91,87 +69,6 @@ export default class FontSizeEditing extends Plugin { } } -const namedPresets = { - tiny: { - title: 'Tiny', - model: 'text-tiny', - view: { - name: 'span', - class: 'text-tiny' - } - }, - small: { - title: 'Small', - model: 'text-small', - view: { - name: 'span', - class: 'text-small' - } - }, - big: { - title: 'Big', - model: 'text-big', - view: { - name: 'span', - class: 'text-big' - } - }, - huge: { - title: 'Huge', - model: 'text-huge', - view: { - name: 'span', - class: 'text-huge' - } - } -}; - -// Returns item definition from preset -function getItemDefinition( item ) { - // Named preset exist so return it - if ( namedPresets[ item ] ) { - return namedPresets[ item ]; - } - - // Probably it is full item definition so return it - if ( typeof item === 'object' ) { - return item; - } - - if ( item === 'normal' ) { - return { - model: undefined, - title: 'Normal' - }; - } - - // At this stage we probably have numerical value to generate a preset so parse it's value. - const sizePreset = parseInt( item ); // TODO: Should we parse floats? 🤔 - - // Discard any faulty values. - if ( isNaN( sizePreset ) ) { - return; - } - - return generatePixelPreset( sizePreset ); -} - -// Creates a predefined preset for pixel size. -function generatePixelPreset( size ) { - const sizeName = String( size ); - - return { - title: sizeName, - model: sizeName, - view: { - name: 'span', - style: { - 'font-size': `${ size }px` - } - } - }; -} - /** * Font size option descriptor. * @@ -214,5 +111,5 @@ function generatePixelPreset( size ) { * which configures * * @member {Array.} - * module:font/fontsize/fontsizeediting~FontSizeConfig#items + * module:font/fontsize/fontsizeediting~FontSizeConfig#options */ diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 4f5cd45..5bf3f44 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -12,8 +12,7 @@ import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createlistdropdown'; -import FontSizeEditing from './fontsizeediting'; - +import { normalizeOptions } from '../fontsize/utils'; import fontSizeIcon from '../../theme/icons/font-size.svg'; /** @@ -91,6 +90,7 @@ export default class FontSizeUI extends Plugin { _getLocalizedOptions() { const editor = this.editor; const t = editor.t; + const localizedTitles = { Tiny: t( 'Tiny' ), Small: t( 'Small' ), @@ -98,10 +98,9 @@ export default class FontSizeUI extends Plugin { Huge: t( 'Huge' ) }; - // TODO this is not nice :/ in terms of feature split. - const items = editor.plugins.get( FontSizeEditing ).configuredItems; + const options = normalizeOptions( editor.config.get( 'fontSize.options' ) ); - return items.map( option => { + return options.map( option => { const title = localizedTitles[ option.title ]; if ( title && title != option.title ) { diff --git a/src/fontsize/utils.js b/src/fontsize/utils.js new file mode 100644 index 0000000..7063809 --- /dev/null +++ b/src/fontsize/utils.js @@ -0,0 +1,116 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module font/fontsize/utils + */ + +/** + * Returns {@link module:font/fontsize/fontsizeediting~FontSizeConfig#options} array with options normalized in the + * {@link module:font/fontsize/fontsizeediting~FontSizeOption} format, translated + * `title` for each style. + * + * @returns {Array.} + */ +export function normalizeOptions( configuredOptions ) { + const options = []; + + for ( const option of configuredOptions ) { + const definition = getOptionDefinition( option ); + + if ( definition ) { + options.push( definition ); + } + } + return options; +} + +const namedPresets = { + tiny: { + title: 'Tiny', + model: 'text-tiny', + view: { + name: 'span', + class: 'text-tiny' + } + }, + small: { + title: 'Small', + model: 'text-small', + view: { + name: 'span', + class: 'text-small' + } + }, + big: { + title: 'Big', + model: 'text-big', + view: { + name: 'span', + class: 'text-big' + } + }, + huge: { + title: 'Huge', + model: 'text-huge', + view: { + name: 'span', + class: 'text-huge' + } + } +}; + +// Returns item definition from preset. Returns undefined for unparsable item. If object is passed then this method will return it without +// alternating. +// +// @param {String|Number|Object} item +// @returns {undefinde|module:font/fontsize/fontsizeediting~FontSizeOption} +function getOptionDefinition( item ) { + // Named preset exist so return it + if ( namedPresets[ item ] ) { + return namedPresets[ item ]; + } + + // Probably it is full item definition so return it + if ( typeof item === 'object' ) { + return item; + } + + if ( item === 'normal' ) { + return { + model: undefined, + title: 'Normal' + }; + } + + // At this stage we probably have numerical value to generate a preset so parse it's value. + const sizePreset = parseInt( item ); // TODO: Should we parse floats? 🤔 + + // Discard any faulty values. + if ( isNaN( sizePreset ) ) { + return; + } + + return generatePixelPreset( sizePreset ); +} + +// Creates a predefined preset for pixel size. +// +// @param {Number} size Font size in pixels. +// @returns {module:font/fontsize/fontsizeediting~FontSizeOption} +function generatePixelPreset( size ) { + const sizeName = String( size ); + + return { + title: sizeName, + model: sizeName, + view: { + name: 'span', + style: { + 'font-size': `${ size }px` + } + } + }; +} diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 2ab6e49..65efefa 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -54,155 +54,6 @@ describe( 'FontFamilyEditing', () => { } ); } ); - describe( 'configuredOptions', () => { - it( 'should discard unparsable values', () => { - return VirtualTestEditor - .create( { - plugins: [ FontFamilyEditing ], - fontFamily: { - options: [ () => {}, 0, true ] - } - } ) - .then( newEditor => { - editor = newEditor; - - const plugin = editor.plugins.get( FontFamilyEditing ); - - expect( plugin.configuredOptions ).to.deep.equal( [] ); - } ); - } ); - - it( 'should pass through object definition', () => { - return VirtualTestEditor - .create( { - plugins: [ FontFamilyEditing ], - fontFamily: { - options: [ - 'default', - { - title: 'Comic Sans', - model: 'comic', - view: { - name: 'span', - style: { - 'font-family': 'Comic Sans' - } - } - } - ] - } - } ) - .then( newEditor => { - editor = newEditor; - - const plugin = editor.plugins.get( FontFamilyEditing ); - - expect( plugin.configuredOptions ).to.deep.equal( [ - { - model: undefined, - title: 'Default' - }, - { - title: 'Comic Sans', - model: 'comic', - view: { - name: 'span', - style: { - 'font-family': 'Comic Sans' - } - } - } - ] ); - } ); - } ); - - describe( 'shorthand presets', () => { - it( 'should return full preset from string presets', () => { - return VirtualTestEditor - .create( { - plugins: [ FontFamilyEditing ], - fontFamily: { - options: [ - 'Arial', - '"Comic Sans MS", sans-serif', - 'Lucida Console, \'Courier New\', Courier, monospace' - ] - } - } ) - .then( newEditor => { - editor = newEditor; - - const plugin = editor.plugins.get( FontFamilyEditing ); - - expect( plugin.configuredOptions ).to.deep.equal( [ - { - title: 'Arial', - model: 'Arial', - view: { - name: 'span', - style: { - 'font-family': 'Arial' - } - }, - acceptsAlso: [ - { - name: 'span', - style: { - 'font-family': new RegExp( '("|\'|&qout;|\\W){0,2}Arial("|\'|&qout;|\\W){0,2}' ) - } - } - ] - }, - { - title: 'Comic Sans MS', - model: 'Comic Sans MS', - view: { - name: 'span', - style: { - 'font-family': '\'Comic Sans MS\', sans-serif' - } - }, - acceptsAlso: [ - { - name: 'span', - style: { - 'font-family': new RegExp( - '("|\'|&qout;|\\W){0,2}Comic Sans MS("|\'|&qout;|\\W){0,2},' + - '("|\'|&qout;|\\W){0,2}sans-serif("|\'|&qout;|\\W){0,2}' - ) - } - } - ] - }, - { - title: 'Lucida Console', - model: 'Lucida Console', - view: { - name: 'span', - style: { - 'font-family': '\'Lucida Console\', \'Courier New\', Courier, monospace' - } - }, - acceptsAlso: [ - { - name: 'span', - style: { - 'font-family': new RegExp( - '("|\'|&qout;|\\W){0,2}Lucida Console("|\'|&qout;|\\W){0,2},' + - '("|\'|&qout;|\\W){0,2}Courier New("|\'|&qout;|\\W){0,2},' + - '("|\'|&qout;|\\W){0,2}Courier("|\'|&qout;|\\W){0,2},' + - '("|\'|&qout;|\\W){0,2}monospace("|\'|&qout;|\\W){0,2}' - ) - } - } - ] - } - ] ); - } ); - } ); - } ); - } ); - describe( 'editing pipeline conversion', () => { beforeEach( () => { return VirtualTestEditor diff --git a/tests/fontfamily/utils.js b/tests/fontfamily/utils.js new file mode 100644 index 0000000..aa6c6ff --- /dev/null +++ b/tests/fontfamily/utils.js @@ -0,0 +1,118 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { normalizeOptions } from '../../src/fontfamily/utils'; + +describe( 'FontFamily utils', () => { + describe( 'normalizeOptions', () => { + it( 'should discard unparsable values', () => { + expect( normalizeOptions( [ () => {}, 0, true ] ) ).to.deep.equal( [] ); + } ); + + it( 'should pass through object definition', () => { + expect( normalizeOptions( [ + 'default', + { + title: 'Comic Sans', + model: 'comic', + view: { + name: 'span', + style: { + 'font-family': 'Comic Sans' + } + } + } + ] ) ).to.deep.equal( [ + { + model: undefined, + title: 'Default' + }, + { + title: 'Comic Sans', + model: 'comic', + view: { + name: 'span', + style: { + 'font-family': 'Comic Sans' + } + } + } + ] ); + } ); + + describe( 'shorthand presets', () => { + it( 'should return full preset from string presets', () => { + expect( normalizeOptions( ( [ + 'Arial', + '"Comic Sans MS", sans-serif', + 'Lucida Console, \'Courier New\', Courier, monospace' + ] ) ) ).to.deep.equal( [ + { + title: 'Arial', + model: 'Arial', + view: { + name: 'span', + style: { + 'font-family': 'Arial' + } + }, + acceptsAlso: [ + { + name: 'span', + style: { + 'font-family': new RegExp( '("|\'|&qout;|\\W){0,2}Arial("|\'|&qout;|\\W){0,2}' ) + } + } + ] + }, + { + title: 'Comic Sans MS', + model: 'Comic Sans MS', + view: { + name: 'span', + style: { + 'font-family': '\'Comic Sans MS\', sans-serif' + } + }, + acceptsAlso: [ + { + name: 'span', + style: { + 'font-family': new RegExp( + '("|\'|&qout;|\\W){0,2}Comic Sans MS("|\'|&qout;|\\W){0,2},' + + '("|\'|&qout;|\\W){0,2}sans-serif("|\'|&qout;|\\W){0,2}' + ) + } + } + ] + }, + { + title: 'Lucida Console', + model: 'Lucida Console', + view: { + name: 'span', + style: { + 'font-family': '\'Lucida Console\', \'Courier New\', Courier, monospace' + } + }, + acceptsAlso: [ + { + name: 'span', + style: { + 'font-family': new RegExp( + '("|\'|&qout;|\\W){0,2}Lucida Console("|\'|&qout;|\\W){0,2},' + + '("|\'|&qout;|\\W){0,2}Courier New("|\'|&qout;|\\W){0,2},' + + '("|\'|&qout;|\\W){0,2}Courier("|\'|&qout;|\\W){0,2},' + + '("|\'|&qout;|\\W){0,2}monospace("|\'|&qout;|\\W){0,2}' + ) + } + } + ] + } + ] ); + } ); + } ); + } ); +} ); diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index 9eff28b..dffe1fd 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -39,99 +39,7 @@ describe( 'FontSizeEditing', () => { describe( 'config', () => { describe( 'default value', () => { it( 'should be set', () => { - expect( editor.config.get( 'fontSize.items' ) ).to.deep.equal( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); - } ); - } ); - } ); - - describe( 'configuredItems', () => { - it( 'should discard unsupported values', () => { - return VirtualTestEditor - .create( { - plugins: [ FontSizeEditing ], - fontSize: { - items: [ () => {}, 'normal', 'unknown' ] - } - } ) - .then( newEditor => { - editor = newEditor; - - const plugin = editor.plugins.get( FontSizeEditing ); - - expect( plugin.configuredItems ).to.deep.equal( [ { title: 'Normal', model: undefined } ] ); - } ); - } ); - - it( 'should pass through object definition', () => { - return VirtualTestEditor - .create( { - plugins: [ FontSizeEditing ], - fontSize: { - items: [ { title: 'My Size', model: 'my-size', view: { name: 'span', style: 'font-size: 12em;' } } ] - } - } ) - .then( newEditor => { - editor = newEditor; - - const plugin = editor.plugins.get( FontSizeEditing ); - - expect( plugin.configuredItems ).to.deep.equal( [ - { - title: 'My Size', - model: 'my-size', - view: { name: 'span', style: 'font-size: 12em;' } - } - ] ); - } ); - } ); - - describe( 'named presets', () => { - it( 'should return defined presets', () => { - return VirtualTestEditor - .create( { - plugins: [ FontSizeEditing ], - fontSize: { - items: [ 'tiny', 'small', 'normal', 'big', 'huge' ] - } - } ) - .then( newEditor => { - editor = newEditor; - - const plugin = editor.plugins.get( FontSizeEditing ); - - expect( plugin.configuredItems ).to.deep.equal( [ - { title: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, - { title: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, - { title: 'Normal', model: undefined }, - { title: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, - { title: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } - ] ); - } ); - } ); - } ); - - describe( 'numeric presets', () => { - it( 'should return generated presets', () => { - return VirtualTestEditor - .create( { - plugins: [ FontSizeEditing ], - fontSize: { - items: [ '10', 12, 'normal', '14.1', 18.3 ] - } - } ) - .then( newEditor => { - editor = newEditor; - - const plugin = editor.plugins.get( FontSizeEditing ); - - expect( plugin.configuredItems ).to.deep.equal( [ - { title: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, - { title: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, - { title: 'Normal', model: undefined }, - { title: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, - { title: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } - ] ); - } ); + expect( editor.config.get( 'fontSize.options' ) ).to.deep.equal( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); } ); } ); } ); @@ -142,7 +50,7 @@ describe( 'FontSizeEditing', () => { .create( { plugins: [ FontSizeEditing, Paragraph ], fontSize: { - items: [ + options: [ 'tiny', 'normal', 18, @@ -196,7 +104,7 @@ describe( 'FontSizeEditing', () => { .create( { plugins: [ FontSizeEditing, Paragraph ], fontSize: { - items: [ + options: [ 'tiny', 'normal', 18, diff --git a/tests/fontsize/utils.js b/tests/fontsize/utils.js new file mode 100644 index 0000000..28119ca --- /dev/null +++ b/tests/fontsize/utils.js @@ -0,0 +1,52 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import { normalizeOptions } from '../../src/fontsize/utils'; + +describe( 'FontSizeEditing', () => { + describe( 'configuredItems', () => { + it( 'should discard unsupported values', () => { + expect( normalizeOptions( [ () => {}, 'normal', 'unknown' ] ) ).to.deep.equal( [ { title: 'Normal', model: undefined } ] ); + } ); + + it( 'should pass through object definition', () => { + expect( normalizeOptions( [ { + title: 'My Size', + model: 'my-size', + view: { name: 'span', style: 'font-size: 12em;' } + } ] ) ).to.deep.equal( [ + { + title: 'My Size', + model: 'my-size', + view: { name: 'span', style: 'font-size: 12em;' } + } + ] ); + } ); + + describe( 'named presets', () => { + it( 'should return defined presets', () => { + expect( normalizeOptions( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ) ).to.deep.equal( [ + { title: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, + { title: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, + { title: 'Normal', model: undefined }, + { title: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, + { title: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } + ] ); + } ); + } ); + + describe( 'numeric presets', () => { + it( 'should return generated presets', () => { + expect( normalizeOptions( [ '10', 12, 'normal', '14.1', 18.3 ] ) ).to.deep.equal( [ + { title: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, + { title: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, + { title: 'Normal', model: undefined }, + { title: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, + { title: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } + ] ); + } ); + } ); + } ); +} ); From e71c8e2971cd22a01a1b7c1efe77cdca7508ab7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 9 Jan 2018 14:53:11 +0100 Subject: [PATCH 048/101] Other: Update copyright year. --- .eslintrc.js | 2 +- LICENSE.md | 2 +- src/font.js | 2 +- src/fontcommand.js | 2 +- src/fontfamily.js | 2 +- src/fontfamily/fontfamilycommand.js | 2 +- src/fontfamily/fontfamilyediting.js | 2 +- src/fontfamily/fontfamilyui.js | 2 +- src/fontfamily/utils.js | 2 +- src/fontsize.js | 2 +- src/fontsize/fontsizecommand.js | 2 +- src/fontsize/fontsizeediting.js | 2 +- src/fontsize/fontsizeui.js | 2 +- src/fontsize/utils.js | 2 +- tests/font.js | 2 +- tests/fontcommand.js | 2 +- tests/fontfamily.js | 2 +- tests/fontfamily/fontfamilycommand.js | 2 +- tests/fontfamily/fontfamilyediting.js | 2 +- tests/fontfamily/fontfamilyui.js | 2 +- tests/fontfamily/utils.js | 2 +- tests/fontsize.js | 2 +- tests/fontsize/fontsizecommand.js | 2 +- tests/fontsize/fontsizeediting.js | 2 +- tests/fontsize/fontsizeui.js | 2 +- tests/fontsize/utils.js | 2 +- tests/manual/font-family.js | 2 +- tests/manual/font-size.js | 2 +- 28 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b5a22b0..88dfc81 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/LICENSE.md b/LICENSE.md index 0cfa151..f8483c4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,7 +2,7 @@ Software License Agreement ========================== **CKEditor 5 Font Feature** – https://github.com/ckeditor/ckeditor5-font
-Copyright (c) 2003-2017, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved. +Copyright (c) 2003-2018, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved. Licensed under the terms of any of the following licenses at your choice: diff --git a/src/font.js b/src/font.js index aa459e2..892c0ea 100644 --- a/src/font.js +++ b/src/font.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontcommand.js b/src/fontcommand.js index 6419b12..0a9353f 100644 --- a/src/fontcommand.js +++ b/src/fontcommand.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontfamily.js b/src/fontfamily.js index d6a77d7..ed6a150 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index 8dcfd20..e06ee35 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 9642c28..dea0954 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 1ca11cd..cf940e9 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontfamily/utils.js b/src/fontfamily/utils.js index 94627de..8574dde 100644 --- a/src/fontfamily/utils.js +++ b/src/fontfamily/utils.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontsize.js b/src/fontsize.js index cac1804..c53f37a 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index ad132e1..1f35b3a 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 05c5f7e..eb56a63 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 5bf3f44..ab5d111 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/src/fontsize/utils.js b/src/fontsize/utils.js index 7063809..cc45417 100644 --- a/src/fontsize/utils.js +++ b/src/fontsize/utils.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/font.js b/tests/font.js index 910fc79..07e02ed 100644 --- a/tests/font.js +++ b/tests/font.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontcommand.js b/tests/fontcommand.js index 4c5338c..9481be1 100644 --- a/tests/fontcommand.js +++ b/tests/fontcommand.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontfamily.js b/tests/fontfamily.js index 7d89f49..b8b37d5 100644 --- a/tests/fontfamily.js +++ b/tests/fontfamily.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontfamily/fontfamilycommand.js b/tests/fontfamily/fontfamilycommand.js index 2b15eb5..fbe63be 100644 --- a/tests/fontfamily/fontfamilycommand.js +++ b/tests/fontfamily/fontfamilycommand.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 65efefa..441e0c4 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontfamily/fontfamilyui.js b/tests/fontfamily/fontfamilyui.js index d25cf03..69ad962 100644 --- a/tests/fontfamily/fontfamilyui.js +++ b/tests/fontfamily/fontfamilyui.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontfamily/utils.js b/tests/fontfamily/utils.js index aa6c6ff..9157fec 100644 --- a/tests/fontfamily/utils.js +++ b/tests/fontfamily/utils.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontsize.js b/tests/fontsize.js index d7f49a7..f7f42d1 100644 --- a/tests/fontsize.js +++ b/tests/fontsize.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js index 456bc97..5d9e9fe 100644 --- a/tests/fontsize/fontsizecommand.js +++ b/tests/fontsize/fontsizecommand.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index dffe1fd..8886cb6 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js index 601625f..2591720 100644 --- a/tests/fontsize/fontsizeui.js +++ b/tests/fontsize/fontsizeui.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/fontsize/utils.js b/tests/fontsize/utils.js index 28119ca..26bf5fe 100644 --- a/tests/fontsize/utils.js +++ b/tests/fontsize/utils.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/manual/font-family.js b/tests/manual/font-family.js index 5d76622..9f34df8 100644 --- a/tests/manual/font-family.js +++ b/tests/manual/font-family.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ diff --git a/tests/manual/font-size.js b/tests/manual/font-size.js index 18e2a47..8fe9c91 100644 --- a/tests/manual/font-size.js +++ b/tests/manual/font-size.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ From c77be7eb2d85820b9515bef63d15b8eae21f3f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 9 Jan 2018 17:33:28 +0100 Subject: [PATCH 049/101] Docs: Review Font documentation. --- src/fontcommand.js | 4 +- src/fontfamily/fontfamilycommand.js | 4 +- src/fontfamily/fontfamilyediting.js | 30 ++++++++++----- src/fontfamily/fontfamilyui.js | 6 +-- src/fontfamily/utils.js | 52 ++++++++++++------------- src/fontsize/fontsizecommand.js | 4 +- src/fontsize/fontsizeediting.js | 60 ++++++++++++++++++++++------- src/fontsize/fontsizeui.js | 9 ++--- src/fontsize/utils.js | 46 +++++++++++----------- tests/fontfamily/utils.js | 4 +- tests/fontsize/utils.js | 8 ++-- 11 files changed, 135 insertions(+), 92 deletions(-) diff --git a/src/fontcommand.js b/src/fontcommand.js index 0a9353f..d6053cc 100644 --- a/src/fontcommand.js +++ b/src/fontcommand.js @@ -16,9 +16,9 @@ import Command from '@ckeditor/ckeditor5-core/src/command'; */ export default class FontCommand extends Command { /** - * Creates a new `FontCommand` instance. + * Creates an instance of the command. * - * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used. + * @param {module:core/editor/editor~Editor} editor Editor instance. * @param {String} attribute Name of an model attribute on which this command operates. */ constructor( editor, attribute ) { diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index e06ee35..cc2e0f4 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -17,9 +17,9 @@ import FontCommand from '../fontcommand'; */ export default class FontFamilyCommand extends FontCommand { /** - * Creates a new `FontFamilyCommand` instance. + * Creates an instance of the command. * - * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used. + * @param {module:core/editor/editor~Editor} editor Editor instance. */ constructor( editor ) { super( editor, 'fontFamily' ); diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index dea0954..3546497 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -28,6 +28,7 @@ export default class FontFamilyEditing extends Plugin { constructor( editor ) { super( editor ); + // Define default configuration using font families shortcuts. editor.config.define( 'fontFamily', { options: [ 'default', @@ -70,17 +71,17 @@ export default class FontFamilyEditing extends Plugin { } /** - * Font family option. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. + * Font family option descriptor. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. * * @typedef {Object} module:font/fontfamily/fontfamilyediting~FontFamilyOption * - * @property {String} model The `fontFamily` attribute value in the model. - * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view The view representation for that option. * @property {String} title The user-readable title of the option. + * @property {String} model Attribute's unique value in the model. + * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. + * @property {Array.} [acceptAlso] An array with all matched elements that + * view to model conversion should also accept. * @property {String} [uiStyle] The style which will be added to the dropdown item representing this option. * Defaults to `view.style[ 'font-family' ]`. - * @property {Array.} acceptAlso An array with all matched elements that - * view to model conversion should also accept. */ /** @@ -127,11 +128,22 @@ export default class FontFamilyEditing extends Plugin { * ] * }; * - * which configures 8 font family options and a default option that will remove font family to text default setting (defaulting to content - * CSS styles). + * which configures 8 font family options. Each option consist one or more font-family names separated with coma. The first font name is + * used as dropdown item description in UI. The family names that consist spaces should not have quotes (as opposed to CSS standard). + * Appropriate quotes will be added in the view. For example, for the "Lucida Sans Unicode" the editor will render: + * + * ... + * + * The "default" option is used to remove fontFamily from selection. In such case the text will + * be represented in view using default content CSS font-family. + + * To use defined font families from {@link module:core/commandcollection~CommandCollection} use `fontFamily` command and pass desired + * font family as a value. + * For example, the below code will apply `fontFamily` attribute with `tiny` value to the current selection: + * + * editor.execute( 'fontFamily', { value: 'tiny' } ); * - * TODO: what 'default' does. - * TODO: how those string are translated to configuration + * Executing `fontFamily` command without value will remove `fontFamily` attribute from the current selection. * * @member {Array.} * module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index cf940e9..074adc5 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -81,15 +81,15 @@ export default class FontFamilyUI extends Plugin { } /** - * Returns TODO options as defined in `config.heading.options` but processed to consider - * editor localization, i.e. to display {@link module:heading/heading~HeadingOption} + * Returns options as defined in `config.fontFamily.options` but processed to consider + * editor localization, i.e. to display {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} * in the correct language. * * Note: The reason behind this method is that there's no way to use {@link module:utils/locale~Locale#t} * when the user config is defined because the editor does not exist yet. * * @private - * @returns {Array.}. + * @returns {Array.}. */ _getLocalizedOptions() { const editor = this.editor; diff --git a/src/fontfamily/utils.js b/src/fontfamily/utils.js index 8574dde..f886ff8 100644 --- a/src/fontfamily/utils.js +++ b/src/fontfamily/utils.js @@ -4,39 +4,37 @@ */ /** - * @module font/fontfamily/fontfamilyediting + * @module font/fontfamily/utils */ /** * Returns {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options} array with options normalized in the - * {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} format, translated - * `title` for each style. + * {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} format. * + * @param {Array.} configuredOptions An array of options taken from configuration. * @returns {Array.} */ export function normalizeOptions( configuredOptions ) { - const options = []; - for ( const item of configuredOptions ) { - const itemDefinition = getItemDefinition( item ); - - // Set only valid definitions. - if ( itemDefinition ) { - options.push( itemDefinition ); - } - } - - return options; + // Convert options to objects. + return configuredOptions + .map( getOptionDefinition ) + // Filter out undefined values that `getOptionDefinition` might return. + .filter( option => !!option ); } -// Returns item definition from preset -function getItemDefinition( item ) { - // Probably it is full item definition so return it. - if ( typeof item === 'object' ) { - return item; +// Returns an option definition either created from string shortcut. +// If object is passed then this method will return it without alternating it. Returns undefined for item than cannot be parsed. +// +// @param {String|Object} option +// @returns {undefined|module:font/fontfamily/fontfamilyediting~FontFamilyOption} +function getOptionDefinition( option ) { + // Treat any object as full item definition provided by user in configuration. + if ( typeof option === 'object' ) { + return option; } // Handle 'default' string as a special case. It will be used to remove the fontFamily attribute. - if ( item === 'default' ) { + if ( option === 'default' ) { return { title: 'Default', model: undefined @@ -44,11 +42,12 @@ function getItemDefinition( item ) { } // Ignore values that we cannot parse to a definition. - if ( typeof item !== 'string' ) { + if ( typeof option !== 'string' ) { return; } - return generateFontPreset( item ); + // Return font family definition from font string. + return generateFontPreset( option ); } // Creates a predefined preset for pixel size. It deconstructs font-family like string into full configuration option. @@ -56,13 +55,14 @@ function getItemDefinition( item ) { // // @param {String} A font definition form configuration. function generateFontPreset( fontDefinition ) { - // Remove quotes from font names - will be normalized later. + // Remove quotes from font names. They will be normalized later. const fontNames = fontDefinition.replace( /"|'/g, '' ).split( ',' ); // The first matched font name will be used as dropdown list item title and as model value const firstFontName = fontNames[ 0 ]; - const cssFontNames = fontNames.map( normalizeFontName ); + // CSS-compatible font names. + const cssFontNames = fontNames.map( normalizeFontNameForCSS ); // TODO: Maybe we can come with something better here? // TODO: Also document this behavior in engine as it uses matcher~Pattern not ViewElementDefinition. @@ -98,11 +98,11 @@ function generateFontPreset( fontDefinition ) { }; } -// Normalizes font name for the view style attribute. +// Normalizes font name for the style attribute. It adds braces (') if font name contains spaces. // // @param {String} fontName // @returns {String} -function normalizeFontName( fontName ) { +function normalizeFontNameForCSS( fontName ) { fontName = fontName.trim(); // Compound font names should be quoted. diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index 1f35b3a..31c87a8 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -17,9 +17,9 @@ import FontCommand from '../fontcommand'; */ export default class FontSizeCommand extends FontCommand { /** - * Creates a new `FontSizeCommand` instance. + * Creates an instance of the command. * - * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used. + * @param {module:core/editor/editor~Editor} editor Editor instance. */ constructor( editor ) { super( editor, 'fontSize' ); diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index eb56a63..1317633 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -28,7 +28,7 @@ export default class FontSizeEditing extends Plugin { constructor( editor ) { super( editor ); - // Define default configuration using named presets + // Define default configuration using named presets. editor.config.define( 'fontSize', { options: [ 'tiny', @@ -70,17 +70,20 @@ export default class FontSizeEditing extends Plugin { } /** - * Font size option descriptor. + * Font size option descriptor. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. * * @typedef {Object} module:font/fontsize/fontsizeediting~FontSizeOption * - * @property {String} title TODO me - * @property {String} model TODO me + * @property {String} title The user-readable title of the option. + * @property {String} model Attribute's unique value in the model. * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. + * @property {Array.} [acceptAlso] An array with all matched elements that + * view to model conversion should also accept. */ /** - * The configuration of the heading feature. Introduced by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. + * The configuration of the font size feature. + * Introduced by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. * * Read more in {@link module:font/fontsize/fontsizeediting~FontSizeConfig}. * @@ -91,12 +94,12 @@ export default class FontSizeEditing extends Plugin { * The configuration of the font size feature. * The option is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. * - * ClassicEditor - * .create( { + * ClassicEditor + * .create( { * fontSize: ... // Font size feature config. * } ) - * .then( ... ) - * .catch( ... ); + * .then( ... ) + * .catch( ... ); * * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. * @@ -104,11 +107,42 @@ export default class FontSizeEditing extends Plugin { */ /** - * Available font size options. Defined either as array of strings. + * Available font size options. Defined either using predefined presets, numeric pixel values + * or {@link module:font/fontsize/fontsizeediting~FontSizeOption}. + * + * The default value is: + * + * const fontSizeConfig = { + * options: [ + * 'tiny', + * 'small', + * 'normal', + * 'big', + * 'huge' + * ] + * }; + * + * It defines 4 sizes: "tiny", "small", "big" and "huge". Those values will be rendered as `span` elements in view. The "normal" defines + * text without a `fontSize` attribute set. + * + * Each rendered span in the view will have class attribute set corresponding to size name. + * For instance for "small" size the view will render: + * + * ... + * + * As an alternative the font size might be defined using numeric values (either as Number or as String): + * + * const fontSizeConfig = { + * options: [ 9, 10, 11, 12, 13, 14, 15 ] + * }; + * + * To use defined font sizes from {@link module:core/commandcollection~CommandCollection} use `fontSize` command and pass desired + * font size as a value. + * For example, the below code will apply `fontSize` attribute with `tiny` value to the current selection: + * + * editor.execute( 'fontSize', { value: 'tiny' } ); * - * The default value is - * TODO code - * which configures + * Executing `fontSize` command without value will remove `fontSize` attribute from the current selection. * * @member {Array.} * module:font/fontsize/fontsizeediting~FontSizeConfig#options diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index ab5d111..af8dd5a 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -57,7 +57,6 @@ export default class FontSizeUI extends Plugin { editor.ui.componentFactory.add( 'fontSize', locale => { const dropdown = createListDropdown( dropdownModel, locale ); - // TODO check if needed dropdown.extendTemplate( { attributes: { class: [ @@ -77,15 +76,15 @@ export default class FontSizeUI extends Plugin { } /** - * Returns heading options as defined in `config.heading.options` but processed to consider - * editor localization, i.e. to display {@link module:heading/heading~HeadingOption} + * Returns options as defined in `config.fontSize.options` but processed to consider + * editor localization, i.e. to display {@link module:font/fontsize/fontsizeediting~FontSizeOption} * in the correct language. * * Note: The reason behind this method is that there's no way to use {@link module:utils/locale~Locale#t} * when the user config is defined because the editor does not exist yet. * * @private - * @returns {Array.}. + * @returns {Array.}. */ _getLocalizedOptions() { const editor = this.editor; @@ -104,7 +103,7 @@ export default class FontSizeUI extends Plugin { const title = localizedTitles[ option.title ]; if ( title && title != option.title ) { - // Clone the option to avoid altering the original `config.heading.options`. + // Clone the option to avoid altering the original `config.fontSize.options`. option = Object.assign( {}, option, { title } ); } diff --git a/src/fontsize/utils.js b/src/fontsize/utils.js index cc45417..a041112 100644 --- a/src/fontsize/utils.js +++ b/src/fontsize/utils.js @@ -9,24 +9,20 @@ /** * Returns {@link module:font/fontsize/fontsizeediting~FontSizeConfig#options} array with options normalized in the - * {@link module:font/fontsize/fontsizeediting~FontSizeOption} format, translated - * `title` for each style. + * {@link module:font/fontsize/fontsizeediting~FontSizeOption} format, translated. * + * @param {Array.} configuredOptions An array of options taken from configuration. * @returns {Array.} */ export function normalizeOptions( configuredOptions ) { - const options = []; - - for ( const option of configuredOptions ) { - const definition = getOptionDefinition( option ); - - if ( definition ) { - options.push( definition ); - } - } - return options; + // Convert options to objects. + return configuredOptions + .map( getOptionDefinition ) + // Filter out undefined values that `getOptionDefinition` might return. + .filter( option => !!option ); } +// Default named presets map. const namedPresets = { tiny: { title: 'Tiny', @@ -62,23 +58,24 @@ const namedPresets = { } }; -// Returns item definition from preset. Returns undefined for unparsable item. If object is passed then this method will return it without -// alternating. +// Returns an option definition either from preset or creates one from number shortcut. +// If object is passed then this method will return it without alternating it. Returns undefined for item than cannot be parsed. // // @param {String|Number|Object} item -// @returns {undefinde|module:font/fontsize/fontsizeediting~FontSizeOption} -function getOptionDefinition( item ) { - // Named preset exist so return it - if ( namedPresets[ item ] ) { - return namedPresets[ item ]; +// @returns {undefined|module:font/fontsize/fontsizeediting~FontSizeOption} +function getOptionDefinition( option ) { + // Treat any object as full item definition provided by user in configuration. + if ( typeof option === 'object' ) { + return option; } - // Probably it is full item definition so return it - if ( typeof item === 'object' ) { - return item; + // Item is a named preset. + if ( namedPresets[ option ] ) { + return namedPresets[ option ]; } - if ( item === 'normal' ) { + // 'Normal' font size. It will be used to remove the fontSize attribute. + if ( option === 'normal' ) { return { model: undefined, title: 'Normal' @@ -86,13 +83,14 @@ function getOptionDefinition( item ) { } // At this stage we probably have numerical value to generate a preset so parse it's value. - const sizePreset = parseInt( item ); // TODO: Should we parse floats? 🤔 + const sizePreset = parseFloat( option ); // Discard any faulty values. if ( isNaN( sizePreset ) ) { return; } + // Return font size definition from size value. return generatePixelPreset( sizePreset ); } diff --git a/tests/fontfamily/utils.js b/tests/fontfamily/utils.js index 9157fec..0d032d7 100644 --- a/tests/fontfamily/utils.js +++ b/tests/fontfamily/utils.js @@ -6,8 +6,8 @@ import { normalizeOptions } from '../../src/fontfamily/utils'; describe( 'FontFamily utils', () => { - describe( 'normalizeOptions', () => { - it( 'should discard unparsable values', () => { + describe( 'normalizeOptions()', () => { + it( 'should discard unsupported values', () => { expect( normalizeOptions( [ () => {}, 0, true ] ) ).to.deep.equal( [] ); } ); diff --git a/tests/fontsize/utils.js b/tests/fontsize/utils.js index 26bf5fe..371f7b6 100644 --- a/tests/fontsize/utils.js +++ b/tests/fontsize/utils.js @@ -5,8 +5,8 @@ import { normalizeOptions } from '../../src/fontsize/utils'; -describe( 'FontSizeEditing', () => { - describe( 'configuredItems', () => { +describe( 'FontSizeEditing Utils', () => { + describe( 'normalizeOptions()', () => { it( 'should discard unsupported values', () => { expect( normalizeOptions( [ () => {}, 'normal', 'unknown' ] ) ).to.deep.equal( [ { title: 'Normal', model: undefined } ] ); } ); @@ -43,8 +43,8 @@ describe( 'FontSizeEditing', () => { { title: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, { title: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, { title: 'Normal', model: undefined }, - { title: '14', model: '14', view: { name: 'span', style: { 'font-size': '14px' } } }, - { title: '18', model: '18', view: { name: 'span', style: { 'font-size': '18px' } } } + { title: '14.1', model: '14.1', view: { name: 'span', style: { 'font-size': '14.1px' } } }, + { title: '18.3', model: '18.3', view: { name: 'span', style: { 'font-size': '18.3px' } } } ] ); } ); } ); From dab591de20b79f16203ab19098e5568125893506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 10 Jan 2018 13:45:23 +0100 Subject: [PATCH 050/101] Other: Fix localization support. --- lang/contexts.json | 10 ++++ lang/translations/pl.po | 38 ++++++++++++++ src/fontfamily/fontfamilyui.js | 16 ++---- src/fontsize/fontsizeui.js | 3 +- tests/fontfamily/fontfamilyui.js | 62 +++++++++++++++++++++++ tests/fontsize/fontsizeui.js | 85 ++++++++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 14 deletions(-) create mode 100644 lang/contexts.json create mode 100644 lang/translations/pl.po diff --git a/lang/contexts.json b/lang/contexts.json new file mode 100644 index 0000000..5d6f2a0 --- /dev/null +++ b/lang/contexts.json @@ -0,0 +1,10 @@ +{ + "Font Size": "Tooltip for the font size drop-down.", + "Normal": "Drop-down option label for the normal font size.", + "Tiny": "Drop-down option label for the 'tiny' font size preset.", + "Small": "Drop-down option label for the 'small' font size preset.", + "Big": "Drop-down option label for the 'big' font size preset.", + "Huge": "Drop-down option label for the 'huge' font size preset.", + "Font Family": "Tooltip for the font family drop-down.", + "Default": "Drop-down option label for the default font family." +} diff --git a/lang/translations/pl.po b/lang/translations/pl.po new file mode 100644 index 0000000..2f444fa --- /dev/null +++ b/lang/translations/pl.po @@ -0,0 +1,38 @@ +# Copyright (c) Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +msgid "" +msgstr "" +"Language: pl\n" +"Language-Team: Polish (https://www.transifex.com/ckeditor/teams/11143/pl/)\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" + +msgctxt "Tooltip for the font size drop-down." +msgid "Font Size" +msgstr "Rozmiar czcionki" + +msgctxt "Drop-down option label for the normal font size." +msgid "Normal" +msgstr "Normalny" + +msgctxt "Drop-down option label for the 'tiny' font size preset." +msgid "Tiny" +msgstr "Tyci" + +msgctxt "Drop-down option label for the 'small' font size preset." +msgid "Small" +msgstr "Mały" + +msgctxt "Drop-down option label for the 'big' font size preset." +msgid "Big" +msgstr "Duży" + +msgctxt "Drop-down option label for the 'huge' font size preset." +msgid "Huge" +msgstr "Ogromny" + +msgctxt "Tooltip for the font family drop-down." +msgid "Font Family" +msgstr "Czcionka" + +msgctxt "Drop-down option label for the default font family." +msgid "Default" +msgstr "Domyślna" diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 074adc5..162c054 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -95,22 +95,12 @@ export default class FontFamilyUI extends Plugin { const editor = this.editor; const t = editor.t; - const localizedTitles = { - Default: t( 'Default' ), - Tiny: t( 'Tiny' ), - Small: t( 'Small' ), - Big: t( 'Big' ), - Huge: t( 'Huge' ) - }; - const options = normalizeOptions( editor.config.get( 'fontFamily.options' ) ); return options.map( option => { - const title = localizedTitles[ option.title ]; - - if ( title && title != option.title ) { - // Clone the option to avoid altering the original `config.fontFamily.options`. - option = Object.assign( {}, option, { title } ); + // The only title to localize is "Default" others are font names. + if ( option.title === 'Default' ) { + option.title = t( 'Default' ); } return option; diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index af8dd5a..0c53ce5 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -91,6 +91,7 @@ export default class FontSizeUI extends Plugin { const t = editor.t; const localizedTitles = { + Normal: t( 'Normal' ), Tiny: t( 'Tiny' ), Small: t( 'Small' ), Big: t( 'Big' ), @@ -103,7 +104,7 @@ export default class FontSizeUI extends Plugin { const title = localizedTitles[ option.title ]; if ( title && title != option.title ) { - // Clone the option to avoid altering the original `config.fontSize.options`. + // Clone the option to avoid altering the original `namedPresets` from `./utils.js`. option = Object.assign( {}, option, { title } ); } diff --git a/tests/fontfamily/fontfamilyui.js b/tests/fontfamily/fontfamilyui.js index 69ad962..ee0ddab 100644 --- a/tests/fontfamily/fontfamilyui.js +++ b/tests/fontfamily/fontfamilyui.js @@ -12,12 +12,29 @@ import fontFamilyIcon from '../../theme/icons/font-family.svg'; import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; +import { add as addTranslations, _clear as clearTranslations } from '@ckeditor/ckeditor5-utils/src/translation-service'; testUtils.createSinonSandbox(); describe( 'FontFamilyUI', () => { let editor, command, element; + before( () => { + addTranslations( 'en', { + 'Font Family': 'Font Family', + 'Default': 'Default' + } ); + + addTranslations( 'pl', { + 'Font Family': 'Czcionka', + 'Default': 'Domyślna' + } ); + } ); + + after( () => { + clearTranslations(); + } ); + beforeEach( () => { element = document.createElement( 'div' ); document.body.appendChild( element ); @@ -81,5 +98,50 @@ describe( 'FontFamilyUI', () => { expect( dropdown.buttonView.isEnabled ).to.be.true; } ); } ); + + describe( 'localization', () => { + beforeEach( () => { + return localizedEditor( [ 'default', 'Arial' ] ); + } ); + + it( 'works for the #buttonView', () => { + const buttonView = dropdown.buttonView; + + expect( buttonView.tooltip ).to.equal( 'Czcionka' ); + } ); + + it( 'works for the listView#items in the panel', () => { + const listView = dropdown.listView; + + expect( listView.items.map( item => item.label ) ).to.deep.equal( [ + 'Domyślna', + 'Arial' + ] ); + } ); + + function localizedEditor( options ) { + const editorElement = document.createElement( 'div' ); + document.body.appendChild( editorElement ); + + return ClassicTestEditor + .create( editorElement, { + plugins: [ FontFamilyEditing, FontFamilyUI ], + toolbar: [ 'fontFamily' ], + language: 'pl', + fontFamily: { + options + } + } ) + .then( newEditor => { + editor = newEditor; + dropdown = editor.ui.componentFactory.create( 'fontFamily' ); + command = editor.commands.get( 'fontFamily' ); + + editorElement.remove(); + + return editor.destroy(); + } ); + } + } ); } ); } ); diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js index 2591720..cf4106d 100644 --- a/tests/fontsize/fontsizeui.js +++ b/tests/fontsize/fontsizeui.js @@ -12,12 +12,38 @@ import fontSizeIcon from '../../theme/icons/font-size.svg'; import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; +import { _clear as clearTranslations, add as addTranslations } from '@ckeditor/ckeditor5-utils/src/translation-service'; +import { normalizeOptions } from '../../src/fontsize/utils'; testUtils.createSinonSandbox(); describe( 'FontSizeUI', () => { let editor, command, element; + before( () => { + addTranslations( 'en', { + 'Font Size': 'Font Size', + 'Normal': 'Normal', + 'Tiny': 'Tiny', + 'Small': 'Small', + 'Big': 'Big', + 'Huge': 'Huge' + } ); + + addTranslations( 'pl', { + 'Font Size': 'Rozmiar czcionki', + 'Normal': 'Normalny', + 'Tiny': 'Tyci', + 'Small': 'Mały', + 'Big': 'Duży', + 'Huge': 'Ogromny' + } ); + } ); + + after( () => { + clearTranslations(); + } ); + beforeEach( () => { element = document.createElement( 'div' ); document.body.appendChild( element ); @@ -81,5 +107,64 @@ describe( 'FontSizeUI', () => { expect( dropdown.buttonView.isEnabled ).to.be.true; } ); } ); + + describe( 'localization', () => { + beforeEach( () => { + return localizedEditor( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); + } ); + + it( 'does not alter normalizeOptions() internals', () => { + const options = normalizeOptions( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); + expect( options ).to.deep.equal( [ + { title: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, + { title: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, + { title: 'Normal', model: undefined }, + { title: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, + { title: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } + ] ); + } ); + + it( 'works for the #buttonView', () => { + const buttonView = dropdown.buttonView; + + expect( buttonView.tooltip ).to.equal( 'Rozmiar czcionki' ); + } ); + + it( 'works for the listView#items in the panel', () => { + const listView = dropdown.listView; + + expect( listView.items.map( item => item.label ) ).to.deep.equal( [ + 'Tyci', + 'Mały', + 'Normalny', + 'Duży', + 'Ogromny' + ] ); + } ); + + function localizedEditor( options ) { + const editorElement = document.createElement( 'div' ); + document.body.appendChild( editorElement ); + + return ClassicTestEditor + .create( editorElement, { + plugins: [ FontSizeEditing, FontSizeUI ], + toolbar: [ 'fontSize' ], + language: 'pl', + fontSize: { + options + } + } ) + .then( newEditor => { + editor = newEditor; + dropdown = editor.ui.componentFactory.create( 'fontSize' ); + command = editor.commands.get( 'fontSize' ); + + editorElement.remove(); + + return editor.destroy(); + } ); + } + } ); } ); } ); From f138202c73e6ce952f4f5f958433b567ab235a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 10 Jan 2018 13:57:48 +0100 Subject: [PATCH 051/101] Docs: Remove `uiStyle` description from `FontFamilyOption`. --- src/fontfamily/fontfamilyediting.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 3546497..3858ae0 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -80,8 +80,6 @@ export default class FontFamilyEditing extends Plugin { * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. * @property {Array.} [acceptAlso] An array with all matched elements that * view to model conversion should also accept. - * @property {String} [uiStyle] The style which will be added to the dropdown item representing this option. - * Defaults to `view.style[ 'font-family' ]`. */ /** From 2b023134fc4780baa20eeb17f9db771430909d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 10 Jan 2018 14:00:46 +0100 Subject: [PATCH 052/101] Tests: Update manual tests description. --- tests/manual/font-family.md | 7 ++++++- tests/manual/font-size.md | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/manual/font-family.md b/tests/manual/font-family.md index 3659f30..bdd7ecb 100644 --- a/tests/manual/font-family.md +++ b/tests/manual/font-family.md @@ -1,7 +1,12 @@ ### Loading The data should be loaded with paragraphs, each with different font. +Also the image caption should have "changed font" string with different font. ### Testing -TODO +Try to: +- Change font size by selecting many paragraphs. +- Change font size by selecting some text. +- Change to default font size by selecting many paragraphs. +- Change to default font size by selecting some text. diff --git a/tests/manual/font-size.md b/tests/manual/font-size.md index 33d6147..34c2488 100644 --- a/tests/manual/font-size.md +++ b/tests/manual/font-size.md @@ -8,4 +8,8 @@ The data should be loaded with: ### Testing -TODO +Try to: +- Change font by selecting many paragraphs. +- Change font by selecting some text. +- Change to default font by selecting many paragraphs. +- Change to default font by selecting some text. From 40030a0842a5fee35502361ce5293725644451fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 12 Jan 2018 10:41:25 +0100 Subject: [PATCH 053/101] Tests: Fix FontFamilyEditing and FontSizeEditing tests. --- tests/fontfamily/fontfamilyediting.js | 2 +- tests/fontsize/fontsizeediting.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 441e0c4..28333a3 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -8,7 +8,7 @@ import FontFamilyEditing from './../../src/fontfamily/fontfamilyediting'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; -import { getData as getModelData, setData as setModelData } from '../../../ckeditor5-engine/src/dev-utils/model'; +import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; describe( 'FontFamilyEditing', () => { let editor, doc; diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index 8886cb6..ca7303d 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -8,7 +8,7 @@ import FontSizeEditing from './../../src/fontsize/fontsizeediting'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; -import { getData as getModelData, setData as setModelData } from '../../../ckeditor5-engine/src/dev-utils/model'; +import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; describe( 'FontSizeEditing', () => { let editor, doc; From cab014dcb19ded2b21e9993cc096930ae68de3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Fri, 12 Jan 2018 14:23:58 +0100 Subject: [PATCH 054/101] Translations need to be defined on Transifex. --- lang/translations/pl.po | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 lang/translations/pl.po diff --git a/lang/translations/pl.po b/lang/translations/pl.po deleted file mode 100644 index 2f444fa..0000000 --- a/lang/translations/pl.po +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. -msgid "" -msgstr "" -"Language: pl\n" -"Language-Team: Polish (https://www.transifex.com/ckeditor/teams/11143/pl/)\n" -"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" - -msgctxt "Tooltip for the font size drop-down." -msgid "Font Size" -msgstr "Rozmiar czcionki" - -msgctxt "Drop-down option label for the normal font size." -msgid "Normal" -msgstr "Normalny" - -msgctxt "Drop-down option label for the 'tiny' font size preset." -msgid "Tiny" -msgstr "Tyci" - -msgctxt "Drop-down option label for the 'small' font size preset." -msgid "Small" -msgstr "Mały" - -msgctxt "Drop-down option label for the 'big' font size preset." -msgid "Big" -msgstr "Duży" - -msgctxt "Drop-down option label for the 'huge' font size preset." -msgid "Huge" -msgstr "Ogromny" - -msgctxt "Tooltip for the font family drop-down." -msgid "Font Family" -msgstr "Czcionka" - -msgctxt "Drop-down option label for the default font family." -msgid "Default" -msgstr "Domyślna" From 9b83506785251ba9f8e61f915075700af42a895b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Fri, 12 Jan 2018 15:50:51 +0100 Subject: [PATCH 055/101] Fixed API docs. --- src/fontfamily.js | 5 +++-- src/fontsize.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/fontfamily.js b/src/fontfamily.js index ed6a150..2ed4381 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -12,9 +12,10 @@ import FontFamilyEditing from './fontfamily/fontfamilyediting'; import FontFamilyUI from './fontfamily/fontfamilyui'; /** - * The Font Family plugin. + * The Font family plugin. * - * It requires {@link module:font/fontfamilyediting~FontFamilyEditing} and {@link module:font/fontfamilyui~FontFamilyUI} plugins. + * It requires {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} and + * {@link module:font/fontfamily/fontfamilyui~FontFamilyUI} plugins. * * @extends module:core/plugin~Plugin */ diff --git a/src/fontsize.js b/src/fontsize.js index c53f37a..0449c8c 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -12,9 +12,10 @@ import FontSizeEditing from './fontsize/fontsizeediting'; import FontSizeUI from './fontsize/fontsizeui'; /** - * The Font Size plugin. + * The Font size plugin. * - * It requires {@link module:font/fontsizeediting~FontSizeEditing} and {@link module:font/fontsizeui~FontSizeUI} plugins. + * It requires {@link module:font/fontsize/fontsizeediting~FontSizeEditing} and + * {@link module:font/fontsize/fontsizeui~FontSizeUI} plugins. * * @extends module:core/plugin~Plugin */ From ff7dfc1944d662ebe37d125dd0a3aee3533188f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 16 Jan 2018 10:29:45 +0100 Subject: [PATCH 056/101] Changed: Make `FontCommand` work also on collapsed selection. --- src/fontcommand.js | 37 +++--- tests/fontcommand.js | 164 +++++++++++++++++++++++--- tests/fontfamily/fontfamilycommand.js | 4 +- tests/fontsize/fontsizecommand.js | 4 +- 4 files changed, 174 insertions(+), 35 deletions(-) diff --git a/src/fontcommand.js b/src/fontcommand.js index d6053cc..43619b4 100644 --- a/src/fontcommand.js +++ b/src/fontcommand.js @@ -19,13 +19,13 @@ export default class FontCommand extends Command { * Creates an instance of the command. * * @param {module:core/editor/editor~Editor} editor Editor instance. - * @param {String} attribute Name of an model attribute on which this command operates. + * @param {String} attributeKey Name of an model attribute on which this command operates. */ - constructor( editor, attribute ) { + constructor( editor, attributeKey ) { super( editor ); /** - * If is set it means that selection has `attribute` set. + * If is set it means that selection has `attributeKey` set to that value. * * @observable * @readonly @@ -36,9 +36,9 @@ export default class FontCommand extends Command { * A model attribute on which this command operates. * * @readonly - * @member {Boolean} module:font/fontcommand~FontCommand#attribute + * @member {Boolean} module:font/fontcommand~FontCommand#attributeKey */ - this.attribute = attribute; + this.attributeKey = attributeKey; } /** @@ -48,8 +48,8 @@ export default class FontCommand extends Command { const model = this.editor.model; const doc = model.document; - this.value = doc.selection.getAttribute( this.attribute ); - this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, this.attribute ); + this.value = doc.selection.getAttribute( this.attributeKey ); + this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, this.attributeKey ); } /** @@ -66,21 +66,24 @@ export default class FontCommand extends Command { const document = model.document; const selection = document.selection; - // Do not apply value on collapsed selection. - if ( selection.isCollapsed ) { - return; - } - const value = options.value; model.change( writer => { - const ranges = model.schema.getValidRanges( selection.getRanges(), this.attribute ); - - for ( const range of ranges ) { + if ( selection.isCollapsed ) { if ( value ) { - writer.setAttribute( this.attribute, value, range ); + selection.setAttribute( this.attributeKey, value ); } else { - writer.removeAttribute( this.attribute, range ); + selection.removeAttribute( this.attributeKey ); + } + } else { + const ranges = model.schema.getValidRanges( selection.getRanges(), this.attributeKey ); + + for ( const range of ranges ) { + if ( value ) { + writer.setAttribute( this.attributeKey, value, range ); + } else { + writer.removeAttribute( this.attributeKey, range ); + } } } } ); diff --git a/tests/fontcommand.js b/tests/fontcommand.js index 9481be1..1a99a97 100644 --- a/tests/fontcommand.js +++ b/tests/fontcommand.js @@ -8,20 +8,29 @@ import FontCommand from '../src/fontcommand'; import Command from '@ckeditor/ckeditor5-core/src/command'; import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; +import Range from '../../ckeditor5-engine/src/model/range'; +import Position from '../../ckeditor5-engine/src/model/position'; describe( 'FontCommand', () => { - let editor, model, command; + let editor, model, doc, root, command; beforeEach( () => { return ModelTestEditor.create() .then( newEditor => { editor = newEditor; model = editor.model; + doc = model.document; + root = doc.getRoot(); command = new FontCommand( editor, 'font' ); editor.commands.add( 'font', command ); model.schema.register( 'paragraph', { inheritAllFrom: '$block' } ); + model.schema.register( 'img', { + allowWhere: [ '$block', '$text' ], + isObject: true + } ); + model.schema.extend( '$text', { allowAttributes: 'font' } ); } ); } ); @@ -58,6 +67,16 @@ describe( 'FontCommand', () => { } ); describe( 'execute()', () => { + it( 'should do nothing if the command is disabled', () => { + setData( model, 'fo[ob]ar' ); + + command.isEnabled = false; + + command.execute( { value: 'foo' } ); + + expect( getData( model ) ).to.equal( 'fo[ob]ar' ); + } ); + it( 'should add font attribute on selected text', () => { setData( model, 'a[bc<$text font="foo">fo]obarxyz' ); @@ -108,19 +127,7 @@ describe( 'FontCommand', () => { ); } ); - it( 'should do nothing on collapsed range', () => { - setData( model, 'abc<$text font="foo">foo[]barxyz' ); - - expect( command.value ).to.equal( 'foo' ); - - command.execute( { value: 'foo' } ); - - expect( getData( model ) ).to.equal( 'abc<$text font="foo">foo[]barxyz' ); - - expect( command.value ).to.equal( 'foo' ); - } ); - - it( 'should remove font attribute on selected nodes when passing undefined font param', () => { + it( 'should remove font attribute on selected nodes when passing undefined value', () => { setData( model, 'abcabc[<$text font="foo">abc' + @@ -139,5 +146,134 @@ describe( 'FontCommand', () => { 'barbar]bar' ); } ); + + it( 'should change selection attribute if selection is collapsed in non-empty parent', () => { + setData( model, 'a[]bc<$text font="foo">foobarxyz' ); + + expect( command.value ).to.be.undefined; + + command.execute( { value: 'foo' } ); + + expect( command.value ).to.equal( 'foo' ); + expect( doc.selection.hasAttribute( 'font' ) ).to.be.true; + + command.execute(); + + expect( command.value ).to.be.undefined; + expect( doc.selection.hasAttribute( 'font' ) ).to.be.false; + } ); + + it( 'should not store attribute change on selection if selection is collapsed in non-empty parent', () => { + setData( model, 'a[]bc<$text font="foo">foobarxyz' ); + + command.execute( { value: 'foo' } ); + + // It should not save that bold was executed at position ( root, [ 0, 1 ] ). + + model.change( () => { + // Simulate clicking right arrow key by changing selection ranges. + doc.selection.setRanges( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] ); + + // Get back to previous selection. + doc.selection.setRanges( [ new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 1 ] ) ) ] ); + } ); + + expect( command.value ).to.be.undefined; + } ); + + it( 'should change selection attribute and store it if selection is collapsed in empty parent', () => { + setData( model, 'abc<$text font="foo">foobarxyz[]' ); + + expect( command.value ).to.be.undefined; + + command.execute( { value: 'foo' } ); + + expect( command.value ).to.equal( 'foo' ); + expect( doc.selection.hasAttribute( 'font' ) ).to.be.true; + + // Attribute should be stored. + // Simulate clicking somewhere else in the editor. + model.change( () => { + doc.selection.setRanges( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] ); + } ); + + expect( command.value ).to.be.undefined; + + // Go back to where attribute was stored. + model.change( () => { + doc.selection.setRanges( [ new Range( new Position( root, [ 1, 0 ] ), new Position( root, [ 1, 0 ] ) ) ] ); + } ); + + // Attribute should be restored. + expect( command.value ).to.equal( 'foo' ); + + command.execute(); + + expect( command.value ).to.be.undefined; + expect( doc.selection.hasAttribute( 'font' ) ).to.be.false; + } ); + + it( 'should not apply attribute change where it would invalid schema', () => { + model.schema.register( 'image', { inheritAllFrom: '$block' } ); + setData( model, 'ab[c<$text font="foo">foobarxy]z' ); + + expect( command.isEnabled ).to.be.true; + + command.execute( { value: 'foo' } ); + + expect( getData( model ) ).to.equal( + 'ab[<$text font="foo">c<$text font="foo">foobarxy]z' + ); + } ); + + it( 'should use parent batch for storing undo steps', () => { + setData( model, 'a[bc<$text font="foo">fo]obarxyz' ); + + model.change( writer => { + expect( writer.batch.deltas.length ).to.equal( 0 ); + command.execute( { value: 'foo' } ); + expect( writer.batch.deltas.length ).to.equal( 1 ); + } ); + + expect( getData( model ) ).to.equal( 'a[<$text font="foo">bcfo]obarxyz' ); + } ); + + describe( 'should cause firing model change event', () => { + let spy; + + beforeEach( () => { + spy = sinon.spy(); + } ); + + it( 'collapsed selection in non-empty parent', () => { + setData( model, 'x[]y' ); + + model.document.on( 'change', spy ); + + command.execute( { value: 'foo' } ); + + expect( spy.called ).to.be.true; + } ); + + it( 'non-collapsed selection', () => { + setData( model, '[xy]' ); + + model.document.on( 'change', spy ); + + command.execute( { value: 'foo' } ); + + expect( spy.called ).to.be.true; + } ); + + it( 'in empty parent', () => { + setData( model, '[]' ); + + model.document.on( 'change', spy ); + + command.execute( { value: 'foo' } ); + + expect( spy.called ).to.be.true; + } ); + } ); } ); } ); diff --git a/tests/fontfamily/fontfamilycommand.js b/tests/fontfamily/fontfamilycommand.js index fbe63be..f5974f3 100644 --- a/tests/fontfamily/fontfamilycommand.js +++ b/tests/fontfamily/fontfamilycommand.js @@ -29,7 +29,7 @@ describe( 'FontFamilyCommand', () => { expect( command ).to.be.instanceOf( FontCommand ); } ); - it( 'opeartes on fontFamily attribute', () => { - expect( command ).to.have.property( 'attribute', 'fontFamily' ); + it( 'operates on fontFamily attribute', () => { + expect( command ).to.have.property( 'attributeKey', 'fontFamily' ); } ); } ); diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js index 5d9e9fe..6fa67a4 100644 --- a/tests/fontsize/fontsizecommand.js +++ b/tests/fontsize/fontsizecommand.js @@ -29,7 +29,7 @@ describe( 'FontSizeCommand', () => { expect( command ).to.be.instanceOf( FontCommand ); } ); - it( 'opeartes on fontSize attribute', () => { - expect( command ).to.have.property( 'attribute', 'fontSize' ); + it( 'operates on fontSize attribute', () => { + expect( command ).to.have.property( 'attributeKey', 'fontSize' ); } ); } ); From 8bff4c37afb7ea6a4de7bdbda8d3061a437fa007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 16 Jan 2018 10:47:48 +0100 Subject: [PATCH 057/101] Other: Update .travis.yml file as in other repos. --- .travis.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 29678a9..cd72612 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,24 @@ sudo: required dist: trusty addons: + firefox: "latest" apt: sources: - - google-chrome + - google-chrome packages: - - google-chrome-stable + - google-chrome-stable language: node_js node_js: - - "6" +- '6' cache: - - node_modules +- node_modules before_install: - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start +- export DISPLAY=:99.0 +- sh -e /etc/init.d/xvfb start install: - - npm install @ckeditor/ckeditor5-dev-tests - - ckeditor5-dev-tests-install-dependencies +- npm install @ckeditor/ckeditor5-dev-tests +- ckeditor5-dev-tests-install-dependencies script: - - ckeditor5-dev-tests-travis +- ckeditor5-dev-tests-travis after_success: - - codeclimate-test-reporter < coverage/lcov.info - - ckeditor5-dev-tests-save-revision +- ckeditor5-dev-tests-save-revision From c157feea16c55f71232f81e92b5d7e43dae34d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Tue, 16 Jan 2018 10:54:42 +0100 Subject: [PATCH 058/101] Updated dependencies. --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index abc66bd..b0cffef 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,10 @@ }, "devDependencies": { "@ckeditor/ckeditor5-paragraph": "^1.0.0-alpha.2", - "eslint": "^4.8.0", - "eslint-config-ckeditor5": "^1.0.6", + "eslint": "^4.15.0", + "eslint-config-ckeditor5": "^1.0.7", "husky": "^0.14.3", - "lint-staged": "^4.2.3" + "lint-staged": "^6.0.0" }, "engines": { "node": ">=6.0.0", From 202c67c99d152ba548414afefb1bb2ba8dede90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 16 Jan 2018 11:26:03 +0100 Subject: [PATCH 059/101] Added: Mark font family/font size in the dropdown's panel items. --- src/fontfamily/fontfamilyui.js | 4 +++- src/fontsize/fontsizeui.js | 2 ++ src/fontsize/utils.js | 8 ++++---- tests/fontfamily/fontfamilyui.js | 16 ++++++++++++++++ tests/fontsize/fontsizeediting.js | 4 ++-- tests/fontsize/fontsizeui.js | 25 ++++++++++++++++++------- tests/fontsize/utils.js | 8 ++++---- 7 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 162c054..d9507c3 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -37,9 +37,11 @@ export default class FontFamilyUI extends Plugin { const itemModel = new Model( { commandName: 'fontFamily', commandParam: option.model, - label: option.title, + label: option.title } ); + itemModel.bind( 'isActive' ).to( command, 'value', value => value === option.model ); + // Try to set a dropdown list item style. if ( option.view && option.view.style ) { itemModel.set( 'style', 'font-family:' + option.view.style[ 'font-family' ] ); diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 0c53ce5..c189311 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -39,6 +39,8 @@ export default class FontSizeUI extends Plugin { class: option.class } ); + itemModel.bind( 'isActive' ).to( command, 'value', value => value === option.model ); + // Add the option to the collection. dropdownItems.add( itemModel ); } diff --git a/src/fontsize/utils.js b/src/fontsize/utils.js index a041112..18572ce 100644 --- a/src/fontsize/utils.js +++ b/src/fontsize/utils.js @@ -26,7 +26,7 @@ export function normalizeOptions( configuredOptions ) { const namedPresets = { tiny: { title: 'Tiny', - model: 'text-tiny', + model: 'tiny', view: { name: 'span', class: 'text-tiny' @@ -34,7 +34,7 @@ const namedPresets = { }, small: { title: 'Small', - model: 'text-small', + model: 'small', view: { name: 'span', class: 'text-small' @@ -42,7 +42,7 @@ const namedPresets = { }, big: { title: 'Big', - model: 'text-big', + model: 'big', view: { name: 'span', class: 'text-big' @@ -50,7 +50,7 @@ const namedPresets = { }, huge: { title: 'Huge', - model: 'text-huge', + model: 'huge', view: { name: 'span', class: 'text-huge' diff --git a/tests/fontfamily/fontfamilyui.js b/tests/fontfamily/fontfamilyui.js index ee0ddab..020516a 100644 --- a/tests/fontfamily/fontfamilyui.js +++ b/tests/fontfamily/fontfamilyui.js @@ -88,6 +88,22 @@ describe( 'FontFamilyUI', () => { sinon.assert.calledOnce( focusSpy ); } ); + it( 'should activate current option in dropdown', () => { + const listView = dropdown.listView; + + command.value = undefined; + + // The first item is 'default' font family. + expect( listView.items.map( item => item.isActive ) ) + .to.deep.equal( [ true, false, false, false, false, false, false, false, false ] ); + + command.value = 'Arial'; + + // The second item is 'Arial' font family. + expect( listView.items.map( item => item.isActive ) ) + .to.deep.equal( [ false, true, false, false, false, false, false, false, false ] ); + } ); + describe( 'model to command binding', () => { it( 'isEnabled', () => { command.isEnabled = false; diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index ca7303d..05b686d 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -80,7 +80,7 @@ describe( 'FontSizeEditing', () => { } ); it( 'should convert fontSize attribute to predefined named preset', () => { - setModelData( doc, 'f<$text fontSize="text-tiny">oo' ); + setModelData( doc, 'f<$text fontSize="tiny">oo' ); expect( editor.getData() ).to.equal( '

foo

' ); } ); @@ -152,7 +152,7 @@ describe( 'FontSizeEditing', () => { editor.setData( data ); - expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="text-tiny">oo' ); + expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="tiny">oo' ); expect( editor.getData() ).to.equal( '

foo

' ); } ); diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js index cf4106d..bd43478 100644 --- a/tests/fontsize/fontsizeui.js +++ b/tests/fontsize/fontsizeui.js @@ -80,8 +80,6 @@ describe( 'FontSizeUI', () => { } ); it( 'should add custom CSS class to dropdown', () => { - const dropdown = editor.ui.componentFactory.create( 'fontSize' ); - dropdown.render(); expect( dropdown.element.classList.contains( 'ck-font-size-dropdown' ) ).to.be.true; @@ -89,7 +87,6 @@ describe( 'FontSizeUI', () => { it( 'should focus view after command execution', () => { const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' ); - const dropdown = editor.ui.componentFactory.create( 'fontSize' ); dropdown.commandName = 'fontSize'; dropdown.fire( 'execute' ); @@ -97,6 +94,20 @@ describe( 'FontSizeUI', () => { sinon.assert.calledOnce( focusSpy ); } ); + it( 'should activate current option in dropdown', () => { + const listView = dropdown.listView; + + command.value = undefined; + + // The third item is 'normal' font size. + expect( listView.items.map( item => item.isActive ) ).to.deep.equal( [ false, false, true, false, false ] ); + + command.value = 'tiny'; + + // The first item is 'tiny' font size. + expect( listView.items.map( item => item.isActive ) ).to.deep.equal( [ true, false, false, false, false ] ); + } ); + describe( 'model to command binding', () => { it( 'isEnabled', () => { command.isEnabled = false; @@ -116,11 +127,11 @@ describe( 'FontSizeUI', () => { it( 'does not alter normalizeOptions() internals', () => { const options = normalizeOptions( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); expect( options ).to.deep.equal( [ - { title: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, - { title: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, + { title: 'Tiny', model: 'tiny', view: { name: 'span', class: 'text-tiny' } }, + { title: 'Small', model: 'small', view: { name: 'span', class: 'text-small' } }, { title: 'Normal', model: undefined }, - { title: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, - { title: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } + { title: 'Big', model: 'big', view: { name: 'span', class: 'text-big' } }, + { title: 'Huge', model: 'huge', view: { name: 'span', class: 'text-huge' } } ] ); } ); diff --git a/tests/fontsize/utils.js b/tests/fontsize/utils.js index 371f7b6..749011a 100644 --- a/tests/fontsize/utils.js +++ b/tests/fontsize/utils.js @@ -28,11 +28,11 @@ describe( 'FontSizeEditing Utils', () => { describe( 'named presets', () => { it( 'should return defined presets', () => { expect( normalizeOptions( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ) ).to.deep.equal( [ - { title: 'Tiny', model: 'text-tiny', view: { name: 'span', class: 'text-tiny' } }, - { title: 'Small', model: 'text-small', view: { name: 'span', class: 'text-small' } }, + { title: 'Tiny', model: 'tiny', view: { name: 'span', class: 'text-tiny' } }, + { title: 'Small', model: 'small', view: { name: 'span', class: 'text-small' } }, { title: 'Normal', model: undefined }, - { title: 'Big', model: 'text-big', view: { name: 'span', class: 'text-big' } }, - { title: 'Huge', model: 'text-huge', view: { name: 'span', class: 'text-huge' } } + { title: 'Big', model: 'big', view: { name: 'span', class: 'text-big' } }, + { title: 'Huge', model: 'huge', view: { name: 'span', class: 'text-huge' } } ] ); } ); } ); From 1d71287cf07d2f39030e6593bf318d9c709f3e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 17 Jan 2018 11:29:51 +0100 Subject: [PATCH 060/101] Changed: Accept only single quotes for multi-word font family definition. --- src/fontfamily/fontfamilyediting.js | 4 +-- src/fontfamily/fontfamilyui.js | 2 +- src/fontfamily/utils.js | 28 +++----------------- src/fontsize/fontsizeediting.js | 2 +- tests/fontfamily/fontfamilyediting.js | 20 -------------- tests/fontfamily/utils.js | 38 +++------------------------ 6 files changed, 10 insertions(+), 84 deletions(-) diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 3858ae0..04b86d9 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -78,7 +78,7 @@ export default class FontFamilyEditing extends Plugin { * @property {String} title The user-readable title of the option. * @property {String} model Attribute's unique value in the model. * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. - * @property {Array.} [acceptAlso] An array with all matched elements that + * @property {Array.} [acceptsAlso] An array with all matched elements that * view to model conversion should also accept. */ @@ -130,7 +130,7 @@ export default class FontFamilyEditing extends Plugin { * used as dropdown item description in UI. The family names that consist spaces should not have quotes (as opposed to CSS standard). * Appropriate quotes will be added in the view. For example, for the "Lucida Sans Unicode" the editor will render: * - * ... + * ... * * The "default" option is used to remove fontFamily from selection. In such case the text will * be represented in view using default content CSS font-family. diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index d9507c3..b48fc41 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -44,7 +44,7 @@ export default class FontFamilyUI extends Plugin { // Try to set a dropdown list item style. if ( option.view && option.view.style ) { - itemModel.set( 'style', 'font-family:' + option.view.style[ 'font-family' ] ); + itemModel.set( 'style', `font-family: ${ option.view.style[ 'font-family' ] }` ); } dropdownItems.add( itemModel ); diff --git a/src/fontfamily/utils.js b/src/fontfamily/utils.js index f886ff8..d11b596 100644 --- a/src/fontfamily/utils.js +++ b/src/fontfamily/utils.js @@ -62,21 +62,7 @@ function generateFontPreset( fontDefinition ) { const firstFontName = fontNames[ 0 ]; // CSS-compatible font names. - const cssFontNames = fontNames.map( normalizeFontNameForCSS ); - - // TODO: Maybe we can come with something better here? - // TODO: Also document this behavior in engine as it uses matcher~Pattern not ViewElementDefinition. - // TODO: Maybe a better solution will be a callback here? (also needs documentation) - // This will match any quote type with whitespace. - const quotesMatch = '("|\'|&qout;|\\W){0,2}'; - // Full regex will catch any style of quotation used in view. - // Example: - // from string: "Font Foo Foo, Font Bar" - // it will create a regex that will match any quotation mix: - // - "Font Foo Foo", Font Bar - // - 'Font Foo Foo', "Font Bar" - // - ... etc. - const regexString = `${ quotesMatch }${ fontNames.map( n => n.trim() ).join( `${ quotesMatch },${ quotesMatch }` ) }${ quotesMatch }`; + const cssFontNames = fontNames.map( normalizeFontNameForCSS ).join( ', ' ); return { title: firstFontName, @@ -84,17 +70,9 @@ function generateFontPreset( fontDefinition ) { view: { name: 'span', style: { - 'font-family': cssFontNames.join( ', ' ) - } - }, - acceptsAlso: [ - { - name: 'span', - style: { - 'font-family': new RegExp( regexString ) - } + 'font-family': cssFontNames } - ] + } }; } diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index 1317633..cbca127 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -77,7 +77,7 @@ export default class FontSizeEditing extends Plugin { * @property {String} title The user-readable title of the option. * @property {String} model Attribute's unique value in the model. * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. - * @property {Array.} [acceptAlso] An array with all matched elements that + * @property {Array.} [acceptsAlso] An array with all matched elements that * view to model conversion should also accept. */ diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index 28333a3..dd03501 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -197,25 +197,5 @@ describe( 'FontFamilyEditing', () => { '

baz

' ); } ); - - it( 'should convert from various inline style definitions', () => { - editor.setData( - '

foo

' + - '

foo

' + - '

foo

' - ); - - expect( getModelData( doc ) ).to.equal( - '[]f<$text fontFamily="Lucida Sans Unicode">oo' + - 'f<$text fontFamily="Lucida Sans Unicode">oo' + - 'f<$text fontFamily="Lucida Sans Unicode">oo' - ); - - expect( editor.getData() ).to.equal( - '

foo

' + - '

foo

' + - '

foo

' - ); - } ); } ); } ); diff --git a/tests/fontfamily/utils.js b/tests/fontfamily/utils.js index 0d032d7..ffb4947 100644 --- a/tests/fontfamily/utils.js +++ b/tests/fontfamily/utils.js @@ -57,15 +57,7 @@ describe( 'FontFamily utils', () => { style: { 'font-family': 'Arial' } - }, - acceptsAlso: [ - { - name: 'span', - style: { - 'font-family': new RegExp( '("|\'|&qout;|\\W){0,2}Arial("|\'|&qout;|\\W){0,2}' ) - } - } - ] + } }, { title: 'Comic Sans MS', @@ -75,18 +67,7 @@ describe( 'FontFamily utils', () => { style: { 'font-family': '\'Comic Sans MS\', sans-serif' } - }, - acceptsAlso: [ - { - name: 'span', - style: { - 'font-family': new RegExp( - '("|\'|&qout;|\\W){0,2}Comic Sans MS("|\'|&qout;|\\W){0,2},' + - '("|\'|&qout;|\\W){0,2}sans-serif("|\'|&qout;|\\W){0,2}' - ) - } - } - ] + } }, { title: 'Lucida Console', @@ -96,20 +77,7 @@ describe( 'FontFamily utils', () => { style: { 'font-family': '\'Lucida Console\', \'Courier New\', Courier, monospace' } - }, - acceptsAlso: [ - { - name: 'span', - style: { - 'font-family': new RegExp( - '("|\'|&qout;|\\W){0,2}Lucida Console("|\'|&qout;|\\W){0,2},' + - '("|\'|&qout;|\\W){0,2}Courier New("|\'|&qout;|\\W){0,2},' + - '("|\'|&qout;|\\W){0,2}Courier("|\'|&qout;|\\W){0,2},' + - '("|\'|&qout;|\\W){0,2}monospace("|\'|&qout;|\\W){0,2}' - ) - } - } - ] + } } ] ); } ); From d92c785132ad296cfb297a4e068ec7b8a7be76d8 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 22 Jan 2018 17:27:45 +0100 Subject: [PATCH 061/101] Updated font size and font family icons. --- theme/icons/font-family.svg | 2 +- theme/icons/font-size.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/icons/font-family.svg b/theme/icons/font-family.svg index f8252ad..3150ce5 100644 --- a/theme/icons/font-family.svg +++ b/theme/icons/font-family.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/theme/icons/font-size.svg b/theme/icons/font-size.svg index 5f34091..3575530 100644 --- a/theme/icons/font-size.svg +++ b/theme/icons/font-size.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 0f2620dec175ce5794da8cb209aa166d18316346 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 23 Jan 2018 11:48:01 +0100 Subject: [PATCH 062/101] Updated font size and family icons. --- theme/icons/font-family.svg | 2 +- theme/icons/font-size.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/icons/font-family.svg b/theme/icons/font-family.svg index 3150ce5..330647f 100644 --- a/theme/icons/font-family.svg +++ b/theme/icons/font-family.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/theme/icons/font-size.svg b/theme/icons/font-size.svg index 3575530..bbae46a 100644 --- a/theme/icons/font-size.svg +++ b/theme/icons/font-size.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From ae89fb56524d879158dcae40f6bc448b24f82fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 25 Jan 2018 14:24:17 +0100 Subject: [PATCH 063/101] Docs: Move FontSizeConfig options documentation to FontSize. --- src/fontsize.js | 78 ++++++++++++++++++++++++++++++++ src/fontsize/fontsizeediting.js | 79 --------------------------------- src/fontsize/fontsizeui.js | 4 +- src/fontsize/utils.js | 10 ++--- 4 files changed, 85 insertions(+), 86 deletions(-) diff --git a/src/fontsize.js b/src/fontsize.js index 0449c8c..99c1882 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -34,3 +34,81 @@ export default class FontSize extends Plugin { return 'FontSize'; } } + +/** + * Font size option descriptor. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. + * + * @typedef {Object} module:font/fontsize~FontSizeOption + * + * @property {String} title The user-readable title of the option. + * @property {String} model Attribute's unique value in the model. + * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. + * @property {Array.} [acceptsAlso] An array with all matched elements that + * view to model conversion should also accept. + */ + +/** + * The configuration of the font size feature. + * Introduced by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. + * + * Read more in {@link module:font/fontsize~FontSizeConfig}. + * + * @member {module:font/fontsize~FontSizeConfig} module:core/editor/editorconfig~EditorConfig#fontSize + */ + +/** + * The configuration of the font size feature. + * The option is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. + * + * ClassicEditor + * .create( { + * fontSize: ... // Font size feature config. + * } ) + * .then( ... ) + * .catch( ... ); + * + * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. + * + * @interface module:font/fontsize~FontSizeConfig + */ + +/** + * Available font size options. Defined either using predefined presets, numeric pixel values + * or {@link module:font/fontsize~FontSizeOption}. + * + * The default value is: + * + * const fontSizeConfig = { + * options: [ + * 'tiny', + * 'small', + * 'normal', + * 'big', + * 'huge' + * ] + * }; + * + * It defines 4 sizes: "tiny", "small", "big" and "huge". Those values will be rendered as `span` elements in view. The "normal" defines + * text without a `fontSize` attribute set. + * + * Each rendered span in the view will have class attribute set corresponding to size name. + * For instance for "small" size the view will render: + * + * ... + * + * As an alternative the font size might be defined using numeric values (either as Number or as String): + * + * const fontSizeConfig = { + * options: [ 9, 10, 11, 12, 13, 14, 15 ] + * }; + * + * To use defined font sizes from {@link module:core/commandcollection~CommandCollection} use `fontSize` command and pass desired + * font size as a value. + * For example, the below code will apply `fontSize` attribute with `tiny` value to the current selection: + * + * editor.execute( 'fontSize', { value: 'tiny' } ); + * + * Executing `fontSize` command without value will remove `fontSize` attribute from the current selection. + * + * @member {Array.} module:font/fontsize~FontSizeConfig#options + */ diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index cbca127..ad0319f 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -68,82 +68,3 @@ export default class FontSizeEditing extends Plugin { editor.model.schema.extend( '$text', { allowAttributes: 'fontSize' } ); } } - -/** - * Font size option descriptor. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. - * - * @typedef {Object} module:font/fontsize/fontsizeediting~FontSizeOption - * - * @property {String} title The user-readable title of the option. - * @property {String} model Attribute's unique value in the model. - * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. - * @property {Array.} [acceptsAlso] An array with all matched elements that - * view to model conversion should also accept. - */ - -/** - * The configuration of the font size feature. - * Introduced by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. - * - * Read more in {@link module:font/fontsize/fontsizeediting~FontSizeConfig}. - * - * @member {module:font/fontsize/fontsizeediting~FontSizeConfig} module:core/editor/editorconfig~EditorConfig#fontSize - */ - -/** - * The configuration of the font size feature. - * The option is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature. - * - * ClassicEditor - * .create( { - * fontSize: ... // Font size feature config. - * } ) - * .then( ... ) - * .catch( ... ); - * - * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. - * - * @interface module:font/fontsize/fontsizeediting~FontSizeConfig - */ - -/** - * Available font size options. Defined either using predefined presets, numeric pixel values - * or {@link module:font/fontsize/fontsizeediting~FontSizeOption}. - * - * The default value is: - * - * const fontSizeConfig = { - * options: [ - * 'tiny', - * 'small', - * 'normal', - * 'big', - * 'huge' - * ] - * }; - * - * It defines 4 sizes: "tiny", "small", "big" and "huge". Those values will be rendered as `span` elements in view. The "normal" defines - * text without a `fontSize` attribute set. - * - * Each rendered span in the view will have class attribute set corresponding to size name. - * For instance for "small" size the view will render: - * - * ... - * - * As an alternative the font size might be defined using numeric values (either as Number or as String): - * - * const fontSizeConfig = { - * options: [ 9, 10, 11, 12, 13, 14, 15 ] - * }; - * - * To use defined font sizes from {@link module:core/commandcollection~CommandCollection} use `fontSize` command and pass desired - * font size as a value. - * For example, the below code will apply `fontSize` attribute with `tiny` value to the current selection: - * - * editor.execute( 'fontSize', { value: 'tiny' } ); - * - * Executing `fontSize` command without value will remove `fontSize` attribute from the current selection. - * - * @member {Array.} - * module:font/fontsize/fontsizeediting~FontSizeConfig#options - */ diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index c189311..92667a2 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -79,14 +79,14 @@ export default class FontSizeUI extends Plugin { /** * Returns options as defined in `config.fontSize.options` but processed to consider - * editor localization, i.e. to display {@link module:font/fontsize/fontsizeediting~FontSizeOption} + * editor localization, i.e. to display {@link module:font/fontsize~FontSizeOption} * in the correct language. * * Note: The reason behind this method is that there's no way to use {@link module:utils/locale~Locale#t} * when the user config is defined because the editor does not exist yet. * * @private - * @returns {Array.}. + * @returns {Array.}. */ _getLocalizedOptions() { const editor = this.editor; diff --git a/src/fontsize/utils.js b/src/fontsize/utils.js index 18572ce..3401e29 100644 --- a/src/fontsize/utils.js +++ b/src/fontsize/utils.js @@ -8,11 +8,11 @@ */ /** - * Returns {@link module:font/fontsize/fontsizeediting~FontSizeConfig#options} array with options normalized in the - * {@link module:font/fontsize/fontsizeediting~FontSizeOption} format, translated. + * Returns {@link module:font/fontsize~FontSizeConfig#options} array with options normalized in the + * {@link module:font/fontsize~FontSizeOption} format, translated. * * @param {Array.} configuredOptions An array of options taken from configuration. - * @returns {Array.} + * @returns {Array.} */ export function normalizeOptions( configuredOptions ) { // Convert options to objects. @@ -62,7 +62,7 @@ const namedPresets = { // If object is passed then this method will return it without alternating it. Returns undefined for item than cannot be parsed. // // @param {String|Number|Object} item -// @returns {undefined|module:font/fontsize/fontsizeediting~FontSizeOption} +// @returns {undefined|module:font/fontsize~FontSizeOption} function getOptionDefinition( option ) { // Treat any object as full item definition provided by user in configuration. if ( typeof option === 'object' ) { @@ -97,7 +97,7 @@ function getOptionDefinition( option ) { // Creates a predefined preset for pixel size. // // @param {Number} size Font size in pixels. -// @returns {module:font/fontsize/fontsizeediting~FontSizeOption} +// @returns {module:font/fontsize~FontSizeOption} function generatePixelPreset( size ) { const sizeName = String( size ); From 1534f1e6264c90f77576a411650ef7759693dccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 25 Jan 2018 14:24:46 +0100 Subject: [PATCH 064/101] Docs: Move FontFamilyConfig options documentation to FontFamily. --- src/fontfamily.js | 76 ++++++++++++++++++++++++++++ src/fontfamily/fontfamilyediting.js | 77 ----------------------------- src/fontfamily/fontfamilyui.js | 4 +- src/fontfamily/utils.js | 8 +-- 4 files changed, 82 insertions(+), 83 deletions(-) diff --git a/src/fontfamily.js b/src/fontfamily.js index 2ed4381..578c281 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -34,3 +34,79 @@ export default class FontFamily extends Plugin { return 'FontFamily'; } } + +/** + * Font family option descriptor. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. + * + * @typedef {Object} module:font/fontfamily~FontFamilyOption + * + * @property {String} title The user-readable title of the option. + * @property {String} model Attribute's unique value in the model. + * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. + * @property {Array.} [acceptsAlso] An array with all matched elements that + * view to model conversion should also accept. + */ + +/** + * The configuration of the font family feature. + * Introduced by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature. + * + * Read more in {@link module:font/fontfamily~FontFamilyConfig}. + * + * @member {module:font/fontfamily~FontFamilyConfig} module:core/editor/editorconfig~EditorConfig#fontFamily + */ + +/** + * The configuration of the font family feature. + * The option is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature. + * + * ClassicEditor + * .create( { + * fontFamily: ... // Font family feature config. + * } ) + * .then( ... ) + * .catch( ... ); + * + * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. + * + * @interface module:font/fontfamily~FontFamilyConfig + */ + +/** + * Available font family options. Defined either as array of strings. + * + * The default value is + * + * const fontFamilyConfig = { + * options: [ + * 'default', + * 'Arial, Helvetica, sans-serif', + * 'Courier New, Courier, monospace', + * 'Georgia, serif', + * 'Lucida Sans Unicode, Lucida Grande, sans-serif', + * 'Tahoma, Geneva, sans-serif', + * 'Times New Roman, Times, serif', + * 'Trebuchet MS, Helvetica, sans-serif', + * 'Verdana, Geneva, sans-serif' + * ] + * }; + * + * which configures 8 font family options. Each option consist one or more font-family names separated with coma. The first font name is + * used as dropdown item description in UI. The family names that consist spaces should not have quotes (as opposed to CSS standard). + * Appropriate quotes will be added in the view. For example, for the "Lucida Sans Unicode" the editor will render: + * + * ... + * + * The "default" option is used to remove fontFamily from selection. In such case the text will + * be represented in view using default content CSS font-family. + + * To use defined font families from {@link module:core/commandcollection~CommandCollection} use `fontFamily` command and pass desired + * font family as a value. + * For example, the below code will apply `fontFamily` attribute with `tiny` value to the current selection: + * + * editor.execute( 'fontFamily', { value: 'tiny' } ); + * + * Executing `fontFamily` command without value will remove `fontFamily` attribute from the current selection. + * + * @member {Array.} module:font/fontfamily~FontFamilyConfig#options + */ diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 04b86d9..1f80297 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -69,80 +69,3 @@ export default class FontFamilyEditing extends Plugin { editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) ); } } - -/** - * Font family option descriptor. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. - * - * @typedef {Object} module:font/fontfamily/fontfamilyediting~FontFamilyOption - * - * @property {String} title The user-readable title of the option. - * @property {String} model Attribute's unique value in the model. - * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. - * @property {Array.} [acceptsAlso] An array with all matched elements that - * view to model conversion should also accept. - */ - -/** - * The configuration of the font family feature. - * Introduced by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature. - * - * Read more in {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig}. - * - * @member {module:font/fontfamily/fontfamilyediting~FontFamilyConfig} module:core/editor/editorconfig~EditorConfig#fontFamily - */ - -/** - * The configuration of the font family feature. - * The option is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature. - * - * ClassicEditor - * .create( { - * fontFamily: ... // Font family feature config. - * } ) - * .then( ... ) - * .catch( ... ); - * - * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. - * - * @interface module:font/fontfamily/fontfamilyediting~FontFamilyConfig - */ - -/** - * Available font family options. Defined either as array of strings. - * - * The default value is - * - * const fontFamilyConfig = { - * options: [ - * 'default', - * 'Arial, Helvetica, sans-serif', - * 'Courier New, Courier, monospace', - * 'Georgia, serif', - * 'Lucida Sans Unicode, Lucida Grande, sans-serif', - * 'Tahoma, Geneva, sans-serif', - * 'Times New Roman, Times, serif', - * 'Trebuchet MS, Helvetica, sans-serif', - * 'Verdana, Geneva, sans-serif' - * ] - * }; - * - * which configures 8 font family options. Each option consist one or more font-family names separated with coma. The first font name is - * used as dropdown item description in UI. The family names that consist spaces should not have quotes (as opposed to CSS standard). - * Appropriate quotes will be added in the view. For example, for the "Lucida Sans Unicode" the editor will render: - * - * ... - * - * The "default" option is used to remove fontFamily from selection. In such case the text will - * be represented in view using default content CSS font-family. - - * To use defined font families from {@link module:core/commandcollection~CommandCollection} use `fontFamily` command and pass desired - * font family as a value. - * For example, the below code will apply `fontFamily` attribute with `tiny` value to the current selection: - * - * editor.execute( 'fontFamily', { value: 'tiny' } ); - * - * Executing `fontFamily` command without value will remove `fontFamily` attribute from the current selection. - * - * @member {Array.} - * module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options - */ diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index b48fc41..76cf6cb 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -84,14 +84,14 @@ export default class FontFamilyUI extends Plugin { /** * Returns options as defined in `config.fontFamily.options` but processed to consider - * editor localization, i.e. to display {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} + * editor localization, i.e. to display {@link module:font/fontfamily~FontFamilyOption} * in the correct language. * * Note: The reason behind this method is that there's no way to use {@link module:utils/locale~Locale#t} * when the user config is defined because the editor does not exist yet. * * @private - * @returns {Array.}. + * @returns {Array.}. */ _getLocalizedOptions() { const editor = this.editor; diff --git a/src/fontfamily/utils.js b/src/fontfamily/utils.js index d11b596..324c7b9 100644 --- a/src/fontfamily/utils.js +++ b/src/fontfamily/utils.js @@ -8,11 +8,11 @@ */ /** - * Returns {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options} array with options normalized in the - * {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} format. + * Returns {@link module:font/fontfamily~FontFamilyConfig#options} array with options normalized in the + * {@link module:font/fontfamily~FontFamilyOption} format. * * @param {Array.} configuredOptions An array of options taken from configuration. - * @returns {Array.} + * @returns {Array.} */ export function normalizeOptions( configuredOptions ) { // Convert options to objects. @@ -26,7 +26,7 @@ export function normalizeOptions( configuredOptions ) { // If object is passed then this method will return it without alternating it. Returns undefined for item than cannot be parsed. // // @param {String|Object} option -// @returns {undefined|module:font/fontfamily/fontfamilyediting~FontFamilyOption} +// @returns {undefined|module:font/fontfamily~FontFamilyOption} function getOptionDefinition( option ) { // Treat any object as full item definition provided by user in configuration. if ( typeof option === 'object' ) { From 5a98b1f084d7fdd3e2587fbd438fb561e101060f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 25 Jan 2018 16:45:25 +0100 Subject: [PATCH 065/101] Docs: Add Font Family feature guide. --- .../features/build-font-family-source.html | 0 .../features/build-font-family-source.js | 14 +++ .../features/custom-font-family-options.html | 13 ++ .../features/custom-font-family-options.js | 28 +++++ docs/_snippets/features/font-family.html | 31 +++++ docs/_snippets/features/font-family.js | 22 ++++ docs/features/font-family.md | 115 ++++++++++++++++++ 7 files changed, 223 insertions(+) create mode 100644 docs/_snippets/features/build-font-family-source.html create mode 100644 docs/_snippets/features/build-font-family-source.js create mode 100644 docs/_snippets/features/custom-font-family-options.html create mode 100644 docs/_snippets/features/custom-font-family-options.js create mode 100644 docs/_snippets/features/font-family.html create mode 100644 docs/_snippets/features/font-family.js create mode 100644 docs/features/font-family.md diff --git a/docs/_snippets/features/build-font-family-source.html b/docs/_snippets/features/build-font-family-source.html new file mode 100644 index 0000000..e69de29 diff --git a/docs/_snippets/features/build-font-family-source.js b/docs/_snippets/features/build-font-family-source.js new file mode 100644 index 0000000..38a9817 --- /dev/null +++ b/docs/_snippets/features/build-font-family-source.js @@ -0,0 +1,14 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals window */ + +import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor'; + +import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily'; + +ClassicEditor.build.plugins.push( FontFamily ); + +window.ClassicEditor = ClassicEditor; diff --git a/docs/_snippets/features/custom-font-family-options.html b/docs/_snippets/features/custom-font-family-options.html new file mode 100644 index 0000000..250aaa7 --- /dev/null +++ b/docs/_snippets/features/custom-font-family-options.html @@ -0,0 +1,13 @@ + + +
+

Font Family feature sample.

+ +

+ This text has "Ubuntu, Arial, Helvetica, sans-serif" font family set. +

+ +

+ This text has "Ubuntu Mono, Courier New, Courier, monospace" font family set. +

+
diff --git a/docs/_snippets/features/custom-font-family-options.js b/docs/_snippets/features/custom-font-family-options.js new file mode 100644 index 0000000..6dc45be --- /dev/null +++ b/docs/_snippets/features/custom-font-family-options.js @@ -0,0 +1,28 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals ClassicEditor, console, window, document */ +ClassicEditor + .create( document.querySelector( '#snippet-custom-font-family-options' ), { + toolbar: { + items: [ + 'headings', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' + ], + viewportTopOffset: 60 + }, + fontFamily: { + options: [ + 'default', + 'Ubuntu, Arial, sans-serif', + 'Ubuntu Mono, Courier New, Courier, monospace' + ] + } + } ) + .then( editor => { + window.editor = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); diff --git a/docs/_snippets/features/font-family.html b/docs/_snippets/features/font-family.html new file mode 100644 index 0000000..ef5d31b --- /dev/null +++ b/docs/_snippets/features/font-family.html @@ -0,0 +1,31 @@ +
+

Font Family feature sample.

+ +

+ This text has "Arial, Helvetica, sans-serif" family set. +

+ +

+ This text has "Courier New, Courier, monospace" family set. +

+ +

+ This text has "Georgia, serif" family set. +

+ +

+ This text has "Lucida Sans Unicode, Lucida Grande, sans-serif" font family set. +

+ +

+ This text has "Tahoma, Geneva, sans-serif" font family set. +

+ +

+ This text has "Trebuchet MS, Helvetica, sans-serif" font family set. +

+ +

+ This text has "Verdana, Geneva, sans-serif" font family set. +

+
diff --git a/docs/_snippets/features/font-family.js b/docs/_snippets/features/font-family.js new file mode 100644 index 0000000..fd92257 --- /dev/null +++ b/docs/_snippets/features/font-family.js @@ -0,0 +1,22 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals ClassicEditor, console, window, document */ + +ClassicEditor + .create( document.querySelector( '#snippet-font-family' ), { + toolbar: { + items: [ + 'headings', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' + ], + viewportTopOffset: 60 + } + } ) + .then( editor => { + window.editor = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); diff --git a/docs/features/font-family.md b/docs/features/font-family.md new file mode 100644 index 0000000..a281573 --- /dev/null +++ b/docs/features/font-family.md @@ -0,0 +1,115 @@ +--- +title: Font family +category: features +--- + +{@snippet features/build-font-family-source} + +The {@link module:font/fontfamily~FontFamily} feature enables support for setting font family. + +## Demo + +{@snippet features/font-family} + +## Configuring font family options + +It is, of course, possible to configure which font family options the editor should support. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamiliy.options`} configuration option to do so. +Use special keyword `'default'` to use document's default font family as defined in CSS. + +For example, the following editor will support only two font families besides "default" one: + +```js +ClassicEditor + .create( document.querySelector( '#editor' ), { + fontFamily: { + options: [ + 'default', + 'Ubuntu, Arial, sans-serif', + 'Ubuntu Mono, Courier New, Courier, monospace' + ] + }, + toolbar: [ + 'headings', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' + ] + } ) + .then( ... ) + .catch( ... ); +``` + +{@snippet features/custom-font-family-options} + +## Installation + +To add this feature to your editor install the [`@ckeditor/ckeditor5-font`](https://www.npmjs.com/package/@ckeditor/ckeditor5-font) package: + +``` +npm install --save @ckeditor/ckeditor5-font +``` + +And add it to your plugin list and toolbar configuration: + +```js +import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily'; + +ClassicEditor + .create( document.querySelector( '#editor' ), { + plugins: [ FontFamily, ... ], + toolbar: [ 'fontFamily', ... ] + } ) + .then( ... ) + .catch( ... ); +``` + + + Read more about {@link builds/guides/development/installing-plugins installing plugins}. + + +## Common API + +The {@link module:font/fontfamily~FontFamily} plugin registers: + +* Dropdown: `'fontFamily'`. +* Command: `'fontFamily'`. + + The number of options and their names are based on the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option). + + You can change font family of the current selection by executing command with proper value: + + ```js + editor.execute( 'fontFamily', { value: 'Arial' } ); + ``` + + The Value passed to `family` corresponds to the first font name in configuration string. For default configuration: + ```js + fontFamily.options = [ + 'default', + 'Arial, Helvetica, sans-serif', + 'Courier New, Courier, monospace', + 'Georgia, serif', + 'Lucida Sans Unicode, Lucida Grande, sans-serif', + 'Tahoma, Geneva, sans-serif', + 'Times New Roman, Times, serif', + 'Trebuchet MS, Helvetica, sans-serif', + 'Verdana, Geneva, sans-serif' + ] + ``` + + the `fontFamily` command will accept strings below as value: + * `'Arial'` + * `'Courier New'` + * `'Georgia'` + * `'Lucida Sans Unicode'` + * `'Tahoma'` + * `'Times New Roman'` + * `'Trebuchet MS'` + * `'Verdana'` + + passing empty value will remove any `fontFamily` set: + + ```js + editor.execute( 'fontFamily' ); + ``` + +## Contribute + +The source code of the feature is available on GitHub in https://github.com/ckeditor/ckeditor5-font. From aea1a9ad7df05f0b57c88640eb46559cb3c7035f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 25 Jan 2018 18:26:30 +0100 Subject: [PATCH 066/101] Docs: Add Font Size feature guide. --- .../features/build-font-size-source.html | 17 ++ .../features/build-font-size-source.js | 14 ++ .../custom-font-size-named-options.html | 9 ++ .../custom-font-size-named-options.js | 28 ++++ .../custom-font-size-numeric-options.html | 11 ++ .../custom-font-size-numeric-options.js | 32 ++++ docs/_snippets/features/font-size.html | 11 ++ docs/_snippets/features/font-size.js | 22 +++ docs/features/font-family.md | 2 +- docs/features/font-size.md | 146 ++++++++++++++++++ 10 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 docs/_snippets/features/build-font-size-source.html create mode 100644 docs/_snippets/features/build-font-size-source.js create mode 100644 docs/_snippets/features/custom-font-size-named-options.html create mode 100644 docs/_snippets/features/custom-font-size-named-options.js create mode 100644 docs/_snippets/features/custom-font-size-numeric-options.html create mode 100644 docs/_snippets/features/custom-font-size-numeric-options.js create mode 100644 docs/_snippets/features/font-size.html create mode 100644 docs/_snippets/features/font-size.js create mode 100644 docs/features/font-size.md diff --git a/docs/_snippets/features/build-font-size-source.html b/docs/_snippets/features/build-font-size-source.html new file mode 100644 index 0000000..c4a4bca --- /dev/null +++ b/docs/_snippets/features/build-font-size-source.html @@ -0,0 +1,17 @@ + diff --git a/docs/_snippets/features/build-font-size-source.js b/docs/_snippets/features/build-font-size-source.js new file mode 100644 index 0000000..474fcfd --- /dev/null +++ b/docs/_snippets/features/build-font-size-source.js @@ -0,0 +1,14 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals window */ + +import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor'; + +import FontSize from '@ckeditor/ckeditor5-font/src/fontsize'; + +ClassicEditor.build.plugins.push( FontSize ); + +window.ClassicEditor = ClassicEditor; diff --git a/docs/_snippets/features/custom-font-size-named-options.html b/docs/_snippets/features/custom-font-size-named-options.html new file mode 100644 index 0000000..7120e1b --- /dev/null +++ b/docs/_snippets/features/custom-font-size-named-options.html @@ -0,0 +1,9 @@ +
+

Font Size named options sample.

+ +

+ This is a mixed text with different sizes of text: + small and + big +

+
diff --git a/docs/_snippets/features/custom-font-size-named-options.js b/docs/_snippets/features/custom-font-size-named-options.js new file mode 100644 index 0000000..79d655b --- /dev/null +++ b/docs/_snippets/features/custom-font-size-named-options.js @@ -0,0 +1,28 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals ClassicEditor, console, window, document */ +ClassicEditor + .create( document.querySelector( '#snippet-custom-font-size-named-options' ), { + toolbar: { + items: [ + 'headings', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + ], + viewportTopOffset: 60 + }, + fontSize: { + options: [ + 'small', + 'normal', + 'big' + ] + } + } ) + .then( editor => { + window.editor = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); diff --git a/docs/_snippets/features/custom-font-size-numeric-options.html b/docs/_snippets/features/custom-font-size-numeric-options.html new file mode 100644 index 0000000..0bf72e9 --- /dev/null +++ b/docs/_snippets/features/custom-font-size-numeric-options.html @@ -0,0 +1,11 @@ +
+

Font Size feature numeric options sample.

+ +

9px

+

11px

+

13px

+

Normal

+

17px

+

19px

+

21px

+
diff --git a/docs/_snippets/features/custom-font-size-numeric-options.js b/docs/_snippets/features/custom-font-size-numeric-options.js new file mode 100644 index 0000000..814c2e1 --- /dev/null +++ b/docs/_snippets/features/custom-font-size-numeric-options.js @@ -0,0 +1,32 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals ClassicEditor, console, window, document */ +ClassicEditor + .create( document.querySelector( '#snippet-custom-font-size-numeric-options' ), { + toolbar: { + items: [ + 'headings', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + ], + viewportTopOffset: 60 + }, + fontSize: { + options: [ + 9, + 11, + 13, + 'normal', + 17, + 19, + 21 + ] + } + } ) + .then( editor => { + window.editor = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); diff --git a/docs/_snippets/features/font-size.html b/docs/_snippets/features/font-size.html new file mode 100644 index 0000000..6a3c077 --- /dev/null +++ b/docs/_snippets/features/font-size.html @@ -0,0 +1,11 @@ +
+

Font Size feature sample.

+ +

+ This is a mixed text with different sizes of text: + tiny, + small, + big and + huge. +

+
diff --git a/docs/_snippets/features/font-size.js b/docs/_snippets/features/font-size.js new file mode 100644 index 0000000..9131008 --- /dev/null +++ b/docs/_snippets/features/font-size.js @@ -0,0 +1,22 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals ClassicEditor, console, window, document */ + +ClassicEditor + .create( document.querySelector( '#snippet-font-size' ), { + toolbar: { + items: [ + 'headings', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + ], + viewportTopOffset: 60 + } + } ) + .then( editor => { + window.editor = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); diff --git a/docs/features/font-family.md b/docs/features/font-family.md index a281573..c96fc98 100644 --- a/docs/features/font-family.md +++ b/docs/features/font-family.md @@ -104,7 +104,7 @@ The {@link module:font/fontfamily~FontFamily} plugin registers: * `'Trebuchet MS'` * `'Verdana'` - passing empty value will remove any `fontFamily` set: + passing an empty value will remove any `fontFamily` set: ```js editor.execute( 'fontFamily' ); diff --git a/docs/features/font-size.md b/docs/features/font-size.md new file mode 100644 index 0000000..07501af --- /dev/null +++ b/docs/features/font-size.md @@ -0,0 +1,146 @@ +--- +title: Font size +category: features +--- + +{@snippet features/build-font-size-source} + +The {@link module:font/fontsize~FontSize} feature enables support for setting font size. + +## Demo + +{@snippet features/font-size} + +## Configuring font size options + +It is, of course, possible to configure which font size options the editor should support. Use the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option to do so. +Use special keyword `'normal'` to use document's normal font size as defined in CSS. + +The font size feature supports two ways of defining configuration using either predefined named presets or numeric values. + +### Configuration using predefined named presets + +The font size feature defines 4 named presets: +- `'tiny'` +- `'small'` +- `'big'` +- `'huge'` + +Each size will be represented in the view as `` element with `text-*` class set. For example for `'tiny'` preset the editor will output: + +```html +... +``` + +Below is the editor that will support only two font sizes: + +```js +ClassicEditor + .create( document.querySelector( '#editor' ), { + fontSize: { + options: [ + 'tiny', + 'normal', + 'big' + ] + }, + toolbar: [ + 'headings', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + ] + } ) + .then( ... ) + .catch( ... ); +``` + +{@snippet features/custom-font-size-named-options} + +### Configuration using numeric presets + +As an alternative the font feature supports numeric values as options. + +Each size will be represented in the view as `` element with `font-size` style set as `px` value. +For example for `14` the editor will output: + +```html +... +``` + +Below is the editor that will support numeric font sizes (it is assumed that `'normal'` size is defined by CSS as `15px`: + +```js +ClassicEditor + .create( document.querySelector( '#editor' ), { + fontSize: { + options: [ + 9, + 11, + 13, + 'normal', + 17, + 19, + 21 + ] + }, + toolbar: [ + 'headings', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + ] + } ) + .then( ... ) + .catch( ... ); +``` + +{@snippet features/custom-font-size-numeric-options} + +## Installation + +To add this feature to your editor install the [`@ckeditor/ckeditor5-font`](https://www.npmjs.com/package/@ckeditor/ckeditor5-font) package: + +``` +npm install --save @ckeditor/ckeditor5-font +``` + +And add it to your plugin list and toolbar configuration: + +```js +import FontSize from '@ckeditor/ckeditor5-font/src/fontsize'; + +ClassicEditor + .create( document.querySelector( '#editor' ), { + plugins: [ FontSize, ... ], + toolbar: [ 'fontSize', ... ] + } ) + .then( ... ) + .catch( ... ); +``` + + + Read more about {@link builds/guides/development/installing-plugins installing plugins}. + + +## Common API + +The {@link module:font/fontsize~FontSize} plugin registers: + +* Dropdown: `'fontSize'`. +* Command: `'fontSize'`. + + The number of options and their names are based on the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option). + + You can change font size of the current selection by executing command with proper value: + + ```js + // For numeric values: + editor.execute( 'fontSize', { value: 10 } ); + + // For named presets: + editor.execute( 'fontSize', { value: 'small' } ); + ``` + passing an empty value will remove any `fontSize` set: + + ```js + editor.execute( 'fontSize' ); + ``` + +## Contribute + +The source code of the feature is available on GitHub in https://github.com/ckeditor/ckeditor5-font. From b35152b11e532fe06a891b811dff234c389a821f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 25 Jan 2018 18:37:44 +0100 Subject: [PATCH 067/101] Docs: Add font api page. --- docs/api/font.md | 31 +++++++++++++++++++++++++++++++ src/font.js | 2 ++ 2 files changed, 33 insertions(+) create mode 100644 docs/api/font.md diff --git a/docs/api/font.md b/docs/api/font.md new file mode 100644 index 0000000..e2bfae3 --- /dev/null +++ b/docs/api/font.md @@ -0,0 +1,31 @@ +--- +category: api-reference +--- + +# CKEditor 5 font feature + +[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-font.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-font) + +This package implements the font family and font size features for CKEditor 5. + +## Documentation + +See the {@link features/font-family Font family feature} and {@link features/font-size Font size feature} guides +with corresponding {@link module:font/fontfamily~FontFamily} and {@link module:font/fontsize~FontSize} plugin documentation. + +## Installation + +```bash +npm install --save @ckeditor/ckeditor5-font +``` + +## Contribute + +The source code of this package is available on GitHub in https://github.com/ckeditor/ckeditor5-font. + +## External links + +* [`@ckeditor/ckeditor5-font` on npm](https://www.npmjs.com/package/@ckeditor/ckeditor5-font) +* [`ckeditor/ckeditor5-font` on GitHub](https://github.com/ckeditor/ckeditor5-font) +* [Issue tracker](https://github.com/ckeditor/ckeditor5-font/issues) +* [Changelog](https://github.com/ckeditor/ckeditor5-font/blob/master/CHANGELOG.md) diff --git a/src/font.js b/src/font.js index 892c0ea..39ab650 100644 --- a/src/font.js +++ b/src/font.js @@ -17,6 +17,8 @@ import FontSize from './fontsize'; * * It requires {@link module:font/fontsize~FontSize} and {@link module:font/fontfamily~FontFamily} plugins. * + * Read more about the feature in the {@glink api/font font package} page. + * * @extends module:core/plugin~Plugin */ export default class Font extends Plugin { From 3b9cdfcad23e9b464601761c7fb64acd9b466ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 26 Jan 2018 10:45:43 +0100 Subject: [PATCH 068/101] Docs: Typo fix. --- docs/features/font-family.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/font-family.md b/docs/features/font-family.md index c96fc98..7bc853a 100644 --- a/docs/features/font-family.md +++ b/docs/features/font-family.md @@ -13,7 +13,7 @@ The {@link module:font/fontfamily~FontFamily} feature enables support for settin ## Configuring font family options -It is, of course, possible to configure which font family options the editor should support. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamiliy.options`} configuration option to do so. +It is, of course, possible to configure which font family options the editor should support. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option to do so. Use special keyword `'default'` to use document's default font family as defined in CSS. For example, the following editor will support only two font families besides "default" one: From f793fe66969b0e60b779556a5bee689db0c18a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 29 Jan 2018 15:20:36 +0100 Subject: [PATCH 069/101] Aligned code to the changes in DocumentSelection API. --- src/fontcommand.js | 4 ++-- tests/fontcommand.js | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/fontcommand.js b/src/fontcommand.js index 43619b4..c2ada6e 100644 --- a/src/fontcommand.js +++ b/src/fontcommand.js @@ -71,9 +71,9 @@ export default class FontCommand extends Command { model.change( writer => { if ( selection.isCollapsed ) { if ( value ) { - selection.setAttribute( this.attributeKey, value ); + writer.setSelectionAttribute( this.attributeKey, value ); } else { - selection.removeAttribute( this.attributeKey ); + writer.removeSelectionAttribute( this.attributeKey ); } } else { const ranges = model.schema.getValidRanges( selection.getRanges(), this.attributeKey ); diff --git a/tests/fontcommand.js b/tests/fontcommand.js index 1a99a97..48454ad 100644 --- a/tests/fontcommand.js +++ b/tests/fontcommand.js @@ -170,12 +170,12 @@ describe( 'FontCommand', () => { // It should not save that bold was executed at position ( root, [ 0, 1 ] ). - model.change( () => { + model.change( writer => { // Simulate clicking right arrow key by changing selection ranges. - doc.selection.setRanges( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] ); + writer.setSelection( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] ); // Get back to previous selection. - doc.selection.setRanges( [ new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 1 ] ) ) ] ); + writer.setSelection( [ new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 1 ] ) ) ] ); } ); expect( command.value ).to.be.undefined; @@ -193,15 +193,15 @@ describe( 'FontCommand', () => { // Attribute should be stored. // Simulate clicking somewhere else in the editor. - model.change( () => { - doc.selection.setRanges( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] ); + model.change( writer => { + writer.setSelection( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] ); } ); expect( command.value ).to.be.undefined; // Go back to where attribute was stored. - model.change( () => { - doc.selection.setRanges( [ new Range( new Position( root, [ 1, 0 ] ), new Position( root, [ 1, 0 ] ) ) ] ); + model.change( writer => { + writer.setSelection( [ new Range( new Position( root, [ 1, 0 ] ), new Position( root, [ 1, 0 ] ) ) ] ); } ); // Attribute should be restored. From 26503b3ee61665dfa7a0197dc9cf3d57445fde16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 29 Jan 2018 17:28:19 +0100 Subject: [PATCH 070/101] Make font-size option configuration reflect dropdown items UI. --- src/fontsize/fontsizeui.js | 13 +++- tests/fontsize/fontsizeui.js | 76 +++++++++++++++++++ tests/manual/font-size-numeric.html | 11 +++ tests/manual/font-size-numeric.js | 25 ++++++ tests/manual/font-size-numeric.md | 12 +++ ...{font-size.html => font-size-presets.html} | 0 .../{font-size.js => font-size-presets.js} | 0 .../{font-size.md => font-size-presets.md} | 0 theme/fontsize.css | 10 +++ 9 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 tests/manual/font-size-numeric.html create mode 100644 tests/manual/font-size-numeric.js create mode 100644 tests/manual/font-size-numeric.md rename tests/manual/{font-size.html => font-size-presets.html} (100%) rename tests/manual/{font-size.js => font-size-presets.js} (100%) rename tests/manual/{font-size.md => font-size-presets.md} (100%) create mode 100644 theme/fontsize.css diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 92667a2..ceeb7ca 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -13,8 +13,11 @@ import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createlistdropdown'; import { normalizeOptions } from '../fontsize/utils'; + import fontSizeIcon from '../../theme/icons/font-size.svg'; +import '../../theme/fontsize.css'; + /** * @extends module:core/plugin~Plugin */ @@ -36,9 +39,17 @@ export default class FontSizeUI extends Plugin { commandName: 'fontSize', commandParam: option.model, label: option.title, - class: option.class + class: 'ck-fontsize-option' } ); + if ( option.view && option.view.style ) { + itemModel.set( 'style', `font-size:${ option.view.style[ 'font-size' ] }` ); + } + + if ( option.view && option.view.class ) { + itemModel.set( 'class', `${ itemModel.class } ${ option.view.class }` ); + } + itemModel.bind( 'isActive' ).to( command, 'value', value => value === option.model ); // Add the option to the collection. diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js index bd43478..2b0b121 100644 --- a/tests/fontsize/fontsizeui.js +++ b/tests/fontsize/fontsizeui.js @@ -119,6 +119,82 @@ describe( 'FontSizeUI', () => { } ); } ); + describe( 'config', () => { + describe( 'using presets', () => { + beforeEach( () => { + element = document.createElement( 'div' ); + document.body.appendChild( element ); + + return ClassicTestEditor + .create( element, { + plugins: [ FontSizeEditing, FontSizeUI ], + fontSize: { + options: [ 'tiny', 'small', 'normal', 'big', 'huge' ] + } + } ) + .then( newEditor => { + editor = newEditor; + dropdown = editor.ui.componentFactory.create( 'fontSize' ); + } ); + } ); + + it( 'adds css class to listView#items in the panel', () => { + const listView = dropdown.listView; + + expect( listView.items.map( item => item.class ) ).to.deep.equal( [ + 'ck-fontsize-option text-tiny', + 'ck-fontsize-option text-small', + 'ck-fontsize-option', + 'ck-fontsize-option text-big', + 'ck-fontsize-option text-huge' + ] ); + } ); + } ); + + describe( 'using numeric values', () => { + beforeEach( () => { + element = document.createElement( 'div' ); + document.body.appendChild( element ); + + return ClassicTestEditor + .create( element, { + plugins: [ FontSizeEditing, FontSizeUI ], + fontSize: { + options: [ 10, 12, 'normal', 16, 18 ] + } + } ) + .then( newEditor => { + editor = newEditor; + dropdown = editor.ui.componentFactory.create( 'fontSize' ); + } ); + } ); + + it( 'adds css class to listView#items in the panel', () => { + const listView = dropdown.listView; + + expect( listView.items.map( item => item.class ) ).to.deep.equal( [ + 'ck-fontsize-option', + 'ck-fontsize-option', + 'ck-fontsize-option', + 'ck-fontsize-option', + 'ck-fontsize-option' + ] ); + } ); + + it( 'adds font-size style to listView#items in the panel', () => { + const listView = dropdown.listView; + + expect( listView.items.map( item => item.style ) ).to.deep.equal( [ + 'font-size:10px', + 'font-size:12px', + undefined, + 'font-size:16px', + 'font-size:18px' + ] ); + } ); + } ); + } ); + describe( 'localization', () => { beforeEach( () => { return localizedEditor( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); diff --git a/tests/manual/font-size-numeric.html b/tests/manual/font-size-numeric.html new file mode 100644 index 0000000..88567b5 --- /dev/null +++ b/tests/manual/font-size-numeric.html @@ -0,0 +1,11 @@ +
+

Font Size feature sample.

+ +

Some text with font-size set to: 10px.

+

Some text with font-size set to: 12px.

+

Some text with font-size set to: 14px.

+

Some text with normal size (font-size not set to).

+

Some text with font-size set to: 18px.

+

Some text with font-size set to: 20px.

+

Some text with font-size set to: 22px.

+
diff --git a/tests/manual/font-size-numeric.js b/tests/manual/font-size-numeric.js new file mode 100644 index 0000000..3ca3777 --- /dev/null +++ b/tests/manual/font-size-numeric.js @@ -0,0 +1,25 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* globals console, window, document */ + +import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'; +import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset'; +import FontSize from '../../src/fontsize'; + +ClassicEditor + .create( document.querySelector( '#editor' ), { + plugins: [ ArticlePluginSet, FontSize ], + toolbar: [ + 'headings', 'fontSize', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' + ], + fontSize: { options: [ 10, 12, 14, 'normal', 18, 20, 22 ] } + } ) + .then( editor => { + window.editor = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); diff --git a/tests/manual/font-size-numeric.md b/tests/manual/font-size-numeric.md new file mode 100644 index 0000000..19f1f8f --- /dev/null +++ b/tests/manual/font-size-numeric.md @@ -0,0 +1,12 @@ +### Loading + +The data should be loaded with: +- 7 paragraphs with font sizes (10, 12, 14, normal, 18, 20, 22), + +### Testing + +Try to: +- Change font by selecting many paragraphs. +- Change font by selecting some text. +- Change to default font by selecting many paragraphs. +- Change to default font by selecting some text. diff --git a/tests/manual/font-size.html b/tests/manual/font-size-presets.html similarity index 100% rename from tests/manual/font-size.html rename to tests/manual/font-size-presets.html diff --git a/tests/manual/font-size.js b/tests/manual/font-size-presets.js similarity index 100% rename from tests/manual/font-size.js rename to tests/manual/font-size-presets.js diff --git a/tests/manual/font-size.md b/tests/manual/font-size-presets.md similarity index 100% rename from tests/manual/font-size.md rename to tests/manual/font-size-presets.md diff --git a/theme/fontsize.css b/theme/fontsize.css new file mode 100644 index 0000000..66c26cc --- /dev/null +++ b/theme/fontsize.css @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/* + * Note: This file should contain the wireframe styles only. But since there are no such styles, + * it acts as a message to the builder telling that it should look for the corresponding styles + * **in the theme** when compiling the editor. + */ From f30b467754c7f3755ecd73835c361952b15a42f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 29 Jan 2018 18:38:35 +0100 Subject: [PATCH 071/101] Code style: The dots are required at the end of the comment. --- src/fontfamily/utils.js | 2 +- src/fontsize/fontsizeediting.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fontfamily/utils.js b/src/fontfamily/utils.js index 324c7b9..6f947aa 100644 --- a/src/fontfamily/utils.js +++ b/src/fontfamily/utils.js @@ -58,7 +58,7 @@ function generateFontPreset( fontDefinition ) { // Remove quotes from font names. They will be normalized later. const fontNames = fontDefinition.replace( /"|'/g, '' ).split( ',' ); - // The first matched font name will be used as dropdown list item title and as model value + // The first matched font name will be used as dropdown list item title and as model value. const firstFontName = fontNames[ 0 ]; // CSS-compatible font names. diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index ad0319f..d6def2c 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -39,7 +39,6 @@ export default class FontSizeEditing extends Plugin { ] } ); - // Get configuration const data = editor.data; const editing = editor.editing; From 32d283b274f79220d7ee9e8541634e4bfd4aab4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 22 Jan 2018 16:50:19 +0100 Subject: [PATCH 072/101] Changed: update dropdowns to match the new API. --- src/fontfamily/fontfamilyui.js | 13 ++++++++----- src/fontsize/fontsizeui.js | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 76cf6cb..005ad1d 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -10,10 +10,10 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; -import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createlistdropdown'; import fontFamilyIcon from '../../theme/icons/font-family.svg'; import { normalizeOptions } from './utils'; +import { addDefaultBehavior, addListViewToDropdown, createSingleButtonDropdown } from '../../../ckeditor5-ui/src/dropdown/utils'; /** * @extends module:core/plugin~Plugin @@ -62,9 +62,12 @@ export default class FontFamilyUI extends Plugin { // Register UI component. editor.ui.componentFactory.add( 'fontFamily', locale => { - const dropdown = createListDropdown( dropdownModel, locale ); + const dropdownView = createSingleButtonDropdown( dropdownModel, locale ); - dropdown.extendTemplate( { + addListViewToDropdown( dropdownView, dropdownModel, locale ); + addDefaultBehavior( dropdownView ); + + dropdownView.extendTemplate( { attributes: { class: [ 'ck-font-family-dropdown' @@ -73,12 +76,12 @@ export default class FontFamilyUI extends Plugin { } ); // Execute command when an item from the dropdown is selected. - this.listenTo( dropdown, 'execute', evt => { + this.listenTo( dropdownView, 'execute', evt => { editor.execute( evt.source.commandName, { value: evt.source.commandParam } ); editor.editing.view.focus(); } ); - return dropdown; + return dropdownView; } ); } diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index ceeb7ca..c739c2a 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -10,11 +10,11 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; -import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createlistdropdown'; import { normalizeOptions } from '../fontsize/utils'; import fontSizeIcon from '../../theme/icons/font-size.svg'; +import { addDefaultBehavior, addListViewToDropdown, createSingleButtonDropdown } from '../../../ckeditor5-ui/src/dropdown/utils'; import '../../theme/fontsize.css'; @@ -68,9 +68,12 @@ export default class FontSizeUI extends Plugin { // Register UI component. editor.ui.componentFactory.add( 'fontSize', locale => { - const dropdown = createListDropdown( dropdownModel, locale ); + const dropdownView = createSingleButtonDropdown( dropdownModel, locale ); - dropdown.extendTemplate( { + addListViewToDropdown( dropdownView, dropdownModel, locale ); + addDefaultBehavior( dropdownView ); + + dropdownView.extendTemplate( { attributes: { class: [ 'ck-font-size-dropdown' @@ -79,12 +82,12 @@ export default class FontSizeUI extends Plugin { } ); // Execute command when an item from the dropdown is selected. - this.listenTo( dropdown, 'execute', evt => { + this.listenTo( dropdownView, 'execute', evt => { editor.execute( evt.source.commandName, { value: evt.source.commandParam } ); editor.editing.view.focus(); } ); - return dropdown; + return dropdownView; } ); } From bc8c3364d63f1f70408664d4680e700fdd365f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 23 Jan 2018 14:07:24 +0100 Subject: [PATCH 073/101] Changed: Removed `addDefaultBehavior()` from dropdown utils. --- src/fontfamily/fontfamilyui.js | 12 ++++++++++-- src/fontsize/fontsizeui.js | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 005ad1d..c8980d5 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -10,10 +10,16 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; +import { + addListViewToDropdown, + closeDropdownOnBlur, + closeDropdownOnExecute, + createSingleButtonDropdown, + focusDropdownContentsOnArrows +} from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import fontFamilyIcon from '../../theme/icons/font-family.svg'; import { normalizeOptions } from './utils'; -import { addDefaultBehavior, addListViewToDropdown, createSingleButtonDropdown } from '../../../ckeditor5-ui/src/dropdown/utils'; /** * @extends module:core/plugin~Plugin @@ -65,7 +71,9 @@ export default class FontFamilyUI extends Plugin { const dropdownView = createSingleButtonDropdown( dropdownModel, locale ); addListViewToDropdown( dropdownView, dropdownModel, locale ); - addDefaultBehavior( dropdownView ); + closeDropdownOnBlur( dropdownView ); + closeDropdownOnExecute( dropdownView ); + focusDropdownContentsOnArrows( dropdownView ); dropdownView.extendTemplate( { attributes: { diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index c739c2a..c11bdb6 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -10,11 +10,17 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; +import { + addListViewToDropdown, + closeDropdownOnBlur, + closeDropdownOnExecute, + createSingleButtonDropdown, + focusDropdownContentsOnArrows +} from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import { normalizeOptions } from '../fontsize/utils'; import fontSizeIcon from '../../theme/icons/font-size.svg'; -import { addDefaultBehavior, addListViewToDropdown, createSingleButtonDropdown } from '../../../ckeditor5-ui/src/dropdown/utils'; import '../../theme/fontsize.css'; @@ -71,7 +77,9 @@ export default class FontSizeUI extends Plugin { const dropdownView = createSingleButtonDropdown( dropdownModel, locale ); addListViewToDropdown( dropdownView, dropdownModel, locale ); - addDefaultBehavior( dropdownView ); + closeDropdownOnBlur( dropdownView ); + closeDropdownOnExecute( dropdownView ); + focusDropdownContentsOnArrows( dropdownView ); dropdownView.extendTemplate( { attributes: { From c79974d9d34e3046f3a0f9a49427d84431a40c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 23 Jan 2018 14:18:16 +0100 Subject: [PATCH 074/101] Changed: Removed `createSingleButtonDropdown()` from dropdown utils. --- src/fontfamily/fontfamilyui.js | 6 ++++-- src/fontsize/fontsizeui.js | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index c8980d5..860c372 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -14,7 +14,8 @@ import { addListViewToDropdown, closeDropdownOnBlur, closeDropdownOnExecute, - createSingleButtonDropdown, + createButtonForDropdown, + createDropdownView, focusDropdownContentsOnArrows } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; @@ -68,7 +69,8 @@ export default class FontFamilyUI extends Plugin { // Register UI component. editor.ui.componentFactory.add( 'fontFamily', locale => { - const dropdownView = createSingleButtonDropdown( dropdownModel, locale ); + const buttonView = createButtonForDropdown( dropdownModel, locale ); + const dropdownView = createDropdownView( dropdownModel, buttonView, locale ); addListViewToDropdown( dropdownView, dropdownModel, locale ); closeDropdownOnBlur( dropdownView ); diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index c11bdb6..16c46ef 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -14,7 +14,8 @@ import { addListViewToDropdown, closeDropdownOnBlur, closeDropdownOnExecute, - createSingleButtonDropdown, + createButtonForDropdown, + createDropdownView, focusDropdownContentsOnArrows } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; @@ -74,7 +75,8 @@ export default class FontSizeUI extends Plugin { // Register UI component. editor.ui.componentFactory.add( 'fontSize', locale => { - const dropdownView = createSingleButtonDropdown( dropdownModel, locale ); + const buttonView = createButtonForDropdown( dropdownModel, locale ); + const dropdownView = createDropdownView( dropdownModel, buttonView, locale ); addListViewToDropdown( dropdownView, dropdownModel, locale ); closeDropdownOnBlur( dropdownView ); From 22a6deb3ae58e8337f16e761eb3f684aefb4c15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 24 Jan 2018 14:31:52 +0100 Subject: [PATCH 075/101] Changed: update dropdowns to match the new API. --- src/fontfamily/fontfamilyui.js | 15 +++++++-------- src/fontsize/fontsizeui.js | 15 +++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 860c372..95cebd6 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -10,14 +10,13 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; -import { - addListViewToDropdown, - closeDropdownOnBlur, - closeDropdownOnExecute, - createButtonForDropdown, - createDropdownView, - focusDropdownContentsOnArrows -} from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; + +import addListViewToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addlistviewtodropdown'; +import closeDropdownOnBlur from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonblur'; +import closeDropdownOnExecute from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonexecute'; +import createButtonForDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createbuttonfordropdown'; +import createDropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createdropdownview'; +import focusDropdownContentsOnArrows from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/focusdropdowncontentsonarrows'; import fontFamilyIcon from '../../theme/icons/font-family.svg'; import { normalizeOptions } from './utils'; diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 16c46ef..3f7435c 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -10,14 +10,13 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; -import { - addListViewToDropdown, - closeDropdownOnBlur, - closeDropdownOnExecute, - createButtonForDropdown, - createDropdownView, - focusDropdownContentsOnArrows -} from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; + +import addListViewToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addlistviewtodropdown'; +import closeDropdownOnBlur from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonblur'; +import closeDropdownOnExecute from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonexecute'; +import createButtonForDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createbuttonfordropdown'; +import createDropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createdropdownview'; +import focusDropdownContentsOnArrows from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/focusdropdowncontentsonarrows'; import { normalizeOptions } from '../fontsize/utils'; From 057ea7c684df223f95db6d639bb669a565cb99b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Sun, 28 Jan 2018 12:59:03 +0100 Subject: [PATCH 076/101] Changed: Change how dropdowns with `ButtonView` and `SplitButtonView` are created. --- src/fontfamily/fontfamilyui.js | 6 ++---- src/fontsize/fontsizeui.js | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 95cebd6..1b7aa9f 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -14,9 +14,8 @@ import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import addListViewToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addlistviewtodropdown'; import closeDropdownOnBlur from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonblur'; import closeDropdownOnExecute from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonexecute'; -import createButtonForDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createbuttonfordropdown'; -import createDropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createdropdownview'; import focusDropdownContentsOnArrows from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/focusdropdowncontentsonarrows'; +import { createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import fontFamilyIcon from '../../theme/icons/font-family.svg'; import { normalizeOptions } from './utils'; @@ -68,8 +67,7 @@ export default class FontFamilyUI extends Plugin { // Register UI component. editor.ui.componentFactory.add( 'fontFamily', locale => { - const buttonView = createButtonForDropdown( dropdownModel, locale ); - const dropdownView = createDropdownView( dropdownModel, buttonView, locale ); + const dropdownView = createDropdown( dropdownModel, locale ); addListViewToDropdown( dropdownView, dropdownModel, locale ); closeDropdownOnBlur( dropdownView ); diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 3f7435c..621b02d 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -14,9 +14,8 @@ import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import addListViewToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addlistviewtodropdown'; import closeDropdownOnBlur from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonblur'; import closeDropdownOnExecute from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonexecute'; -import createButtonForDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createbuttonfordropdown'; -import createDropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createdropdownview'; import focusDropdownContentsOnArrows from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/focusdropdowncontentsonarrows'; +import { createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import { normalizeOptions } from '../fontsize/utils'; @@ -74,8 +73,7 @@ export default class FontSizeUI extends Plugin { // Register UI component. editor.ui.componentFactory.add( 'fontSize', locale => { - const buttonView = createButtonForDropdown( dropdownModel, locale ); - const dropdownView = createDropdownView( dropdownModel, buttonView, locale ); + const dropdownView = createDropdown( dropdownModel, locale ); addListViewToDropdown( dropdownView, dropdownModel, locale ); closeDropdownOnBlur( dropdownView ); From dc8a2befd0f812d5c80ab08820781bb9a4095afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Sun, 28 Jan 2018 13:30:55 +0100 Subject: [PATCH 077/101] Changed: Remove dafult dropdown behavior helper methods from API. --- src/fontfamily/fontfamilyui.js | 6 ------ src/fontsize/fontsizeui.js | 6 ------ 2 files changed, 12 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 1b7aa9f..a06b620 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -12,9 +12,6 @@ import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import addListViewToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addlistviewtodropdown'; -import closeDropdownOnBlur from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonblur'; -import closeDropdownOnExecute from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonexecute'; -import focusDropdownContentsOnArrows from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/focusdropdowncontentsonarrows'; import { createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import fontFamilyIcon from '../../theme/icons/font-family.svg'; @@ -70,9 +67,6 @@ export default class FontFamilyUI extends Plugin { const dropdownView = createDropdown( dropdownModel, locale ); addListViewToDropdown( dropdownView, dropdownModel, locale ); - closeDropdownOnBlur( dropdownView ); - closeDropdownOnExecute( dropdownView ); - focusDropdownContentsOnArrows( dropdownView ); dropdownView.extendTemplate( { attributes: { diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 621b02d..da78e13 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -12,9 +12,6 @@ import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import addListViewToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addlistviewtodropdown'; -import closeDropdownOnBlur from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonblur'; -import closeDropdownOnExecute from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonexecute'; -import focusDropdownContentsOnArrows from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/focusdropdowncontentsonarrows'; import { createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import { normalizeOptions } from '../fontsize/utils'; @@ -76,9 +73,6 @@ export default class FontSizeUI extends Plugin { const dropdownView = createDropdown( dropdownModel, locale ); addListViewToDropdown( dropdownView, dropdownModel, locale ); - closeDropdownOnBlur( dropdownView ); - closeDropdownOnExecute( dropdownView ); - focusDropdownContentsOnArrows( dropdownView ); dropdownView.extendTemplate( { attributes: { From 3051e8115b3e392826b248dfc973fb44201e74dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 29 Jan 2018 10:55:29 +0100 Subject: [PATCH 078/101] Changed: Move `addListViewToDropdown()` helper to ui/dropdown/utils. --- src/fontfamily/fontfamilyui.js | 9 ++++----- src/fontsize/fontsizeui.js | 6 ++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index a06b620..9a6959d 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -11,12 +11,12 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; -import addListViewToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addlistviewtodropdown'; -import { createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; +import { createDropdown, addListViewToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; -import fontFamilyIcon from '../../theme/icons/font-family.svg'; import { normalizeOptions } from './utils'; +import fontFamilyIcon from '../../theme/icons/font-family.svg'; + /** * @extends module:core/plugin~Plugin */ @@ -56,7 +56,6 @@ export default class FontFamilyUI extends Plugin { const dropdownModel = new Model( { icon: fontFamilyIcon, withText: false, - items: dropdownItems, tooltip: t( 'Font Family' ) } ); @@ -66,7 +65,7 @@ export default class FontFamilyUI extends Plugin { editor.ui.componentFactory.add( 'fontFamily', locale => { const dropdownView = createDropdown( dropdownModel, locale ); - addListViewToDropdown( dropdownView, dropdownModel, locale ); + addListViewToDropdown( dropdownView, dropdownItems, dropdownModel, locale ); dropdownView.extendTemplate( { attributes: { diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index da78e13..4e997bb 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -11,8 +11,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; -import addListViewToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addlistviewtodropdown'; -import { createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; +import { createDropdown, addListViewToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import { normalizeOptions } from '../fontsize/utils'; @@ -62,7 +61,6 @@ export default class FontSizeUI extends Plugin { const dropdownModel = new Model( { icon: fontSizeIcon, withText: false, - items: dropdownItems, tooltip: t( 'Font Size' ) } ); @@ -72,7 +70,7 @@ export default class FontSizeUI extends Plugin { editor.ui.componentFactory.add( 'fontSize', locale => { const dropdownView = createDropdown( dropdownModel, locale ); - addListViewToDropdown( dropdownView, dropdownModel, locale ); + addListViewToDropdown( dropdownView, dropdownItems, dropdownModel, locale ); dropdownView.extendTemplate( { attributes: { From 5a55304084313d9253665d59d89dda49f084b2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 29 Jan 2018 11:43:04 +0100 Subject: [PATCH 079/101] Changed: Bind dropdown behavior to `DropdownView` instead of `Model`. --- src/fontfamily/fontfamilyui.js | 20 +++++++++----------- src/fontsize/fontsizeui.js | 21 ++++++++++----------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 9a6959d..e1f6af1 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -52,20 +52,18 @@ export default class FontFamilyUI extends Plugin { dropdownItems.add( itemModel ); } - // Create dropdown model. - const dropdownModel = new Model( { - icon: fontFamilyIcon, - withText: false, - tooltip: t( 'Font Family' ) - } ); - - dropdownModel.bind( 'isEnabled' ).to( command, 'isEnabled' ); - // Register UI component. editor.ui.componentFactory.add( 'fontFamily', locale => { - const dropdownView = createDropdown( dropdownModel, locale ); + const dropdownView = createDropdown( locale ); + addListViewToDropdown( dropdownView, dropdownItems ); + + dropdownView.set( { + icon: fontFamilyIcon, + withText: false, + tooltip: t( 'Font Family' ) + } ); - addListViewToDropdown( dropdownView, dropdownItems, dropdownModel, locale ); + dropdownView.bind( 'isEnabled' ).to( command, 'isEnabled' ); dropdownView.extendTemplate( { attributes: { diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 4e997bb..2974155 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -57,20 +57,19 @@ export default class FontSizeUI extends Plugin { dropdownItems.add( itemModel ); } - // Create dropdown model. - const dropdownModel = new Model( { - icon: fontSizeIcon, - withText: false, - tooltip: t( 'Font Size' ) - } ); - - dropdownModel.bind( 'isEnabled' ).to( command, 'isEnabled' ); - // Register UI component. editor.ui.componentFactory.add( 'fontSize', locale => { - const dropdownView = createDropdown( dropdownModel, locale ); + const dropdownView = createDropdown( locale ); + addListViewToDropdown( dropdownView, dropdownItems ); + + // Create dropdown model. + dropdownView.set( { + icon: fontSizeIcon, + withText: false, + tooltip: t( 'Font Size' ) + } ); - addListViewToDropdown( dropdownView, dropdownItems, dropdownModel, locale ); + dropdownView.bind( 'isEnabled' ).to( command, 'isEnabled' ); dropdownView.extendTemplate( { attributes: { From 44a2dbeb43d3bfc339ccc09ba7df1a1431f8ba76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 1 Feb 2018 17:08:32 +0100 Subject: [PATCH 080/101] Review dropdown creation process in `FontSizeUI` and `FontFamilyUI`. --- src/fontfamily/fontfamilyui.js | 61 ++++++++++++++++------------- src/fontsize/fontsizeui.js | 67 ++++++++++++++++++-------------- tests/fontfamily/fontfamilyui.js | 6 +-- tests/fontsize/fontsizeui.js | 6 +-- 4 files changed, 77 insertions(+), 63 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index e1f6af1..e8ab72a 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -28,51 +28,29 @@ export default class FontFamilyUI extends Plugin { const editor = this.editor; const t = editor.t; - const dropdownItems = new Collection(); - const options = this._getLocalizedOptions(); const command = editor.commands.get( 'fontFamily' ); - // Create dropdown items. - for ( const option of options ) { - const itemModel = new Model( { - commandName: 'fontFamily', - commandParam: option.model, - label: option.title - } ); - - itemModel.bind( 'isActive' ).to( command, 'value', value => value === option.model ); - - // Try to set a dropdown list item style. - if ( option.view && option.view.style ) { - itemModel.set( 'style', `font-family: ${ option.view.style[ 'font-family' ] }` ); - } - - dropdownItems.add( itemModel ); - } - // Register UI component. editor.ui.componentFactory.add( 'fontFamily', locale => { const dropdownView = createDropdown( locale ); - addListViewToDropdown( dropdownView, dropdownItems ); + addListViewToDropdown( dropdownView, _prepareListOptions( options, command ) ); dropdownView.set( { + label: t( 'Font Family' ), icon: fontFamilyIcon, - withText: false, - tooltip: t( 'Font Family' ) + tooltip: true } ); - dropdownView.bind( 'isEnabled' ).to( command, 'isEnabled' ); - dropdownView.extendTemplate( { attributes: { - class: [ - 'ck-font-family-dropdown' - ] + class: 'ck-font-family-dropdown' } } ); + dropdownView.bind( 'isEnabled' ).to( command ); + // Execute command when an item from the dropdown is selected. this.listenTo( dropdownView, 'execute', evt => { editor.execute( evt.source.commandName, { value: evt.source.commandParam } ); @@ -110,3 +88,30 @@ export default class FontFamilyUI extends Plugin { } ); } } + +// Prepares FontFamily dropdown items. +// @private +// @param {Array.} options +// @param {module:font/fontsize/fontsizecommand~FontSizeCommand} command +function _prepareListOptions( options, command ) { + const dropdownItems = new Collection(); + + // Create dropdown items. + for ( const option of options ) { + const itemModel = new Model( { + commandName: 'fontFamily', + commandParam: option.model, + label: option.title + } ); + + itemModel.bind( 'isActive' ).to( command, 'value', value => value === option.model ); + + // Try to set a dropdown list item style. + if ( option.view && option.view.style ) { + itemModel.set( 'style', `font-family: ${ option.view.style[ 'font-family' ] }` ); + } + + dropdownItems.add( itemModel ); + } + return dropdownItems; +} diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 2974155..d3e8c46 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -28,49 +28,24 @@ export default class FontSizeUI extends Plugin { */ init() { const editor = this.editor; - const dropdownItems = new Collection(); + const t = editor.t; const options = this._getLocalizedOptions(); - const t = editor.t; const command = editor.commands.get( 'fontSize' ); - for ( const option of options ) { - const itemModel = new Model( { - commandName: 'fontSize', - commandParam: option.model, - label: option.title, - class: 'ck-fontsize-option' - } ); - - if ( option.view && option.view.style ) { - itemModel.set( 'style', `font-size:${ option.view.style[ 'font-size' ] }` ); - } - - if ( option.view && option.view.class ) { - itemModel.set( 'class', `${ itemModel.class } ${ option.view.class }` ); - } - - itemModel.bind( 'isActive' ).to( command, 'value', value => value === option.model ); - - // Add the option to the collection. - dropdownItems.add( itemModel ); - } - // Register UI component. editor.ui.componentFactory.add( 'fontSize', locale => { const dropdownView = createDropdown( locale ); - addListViewToDropdown( dropdownView, dropdownItems ); + addListViewToDropdown( dropdownView, _prepareListOptions( options, command ) ); // Create dropdown model. dropdownView.set( { + label: t( 'Font Size' ), icon: fontSizeIcon, - withText: false, - tooltip: t( 'Font Size' ) + tooltip: true } ); - dropdownView.bind( 'isEnabled' ).to( command, 'isEnabled' ); - dropdownView.extendTemplate( { attributes: { class: [ @@ -79,6 +54,8 @@ export default class FontSizeUI extends Plugin { } } ); + dropdownView.bind( 'isEnabled' ).to( command ); + // Execute command when an item from the dropdown is selected. this.listenTo( dropdownView, 'execute', evt => { editor.execute( evt.source.commandName, { value: evt.source.commandParam } ); @@ -126,3 +103,35 @@ export default class FontSizeUI extends Plugin { } ); } } + +// Prepares FontSize dropdown items. +// @private +// @param {Array.} options +// @param {module:font/fontsize/fontsizecommand~FontSizeCommand} command +function _prepareListOptions( options, command ) { + const dropdownItems = new Collection(); + + for ( const option of options ) { + const itemModel = new Model( { + commandName: 'fontSize', + commandParam: option.model, + label: option.title, + class: 'ck-fontsize-option' + } ); + + if ( option.view && option.view.style ) { + itemModel.set( 'style', `font-size:${ option.view.style[ 'font-size' ] }` ); + } + + if ( option.view && option.view.class ) { + itemModel.set( 'class', `${ itemModel.class } ${ option.view.class }` ); + } + + itemModel.bind( 'isActive' ).to( command, 'value', value => value === option.model ); + + // Add the option to the collection. + dropdownItems.add( itemModel ); + } + + return dropdownItems; +} diff --git a/tests/fontfamily/fontfamilyui.js b/tests/fontfamily/fontfamilyui.js index 020516a..37e27d2 100644 --- a/tests/fontfamily/fontfamilyui.js +++ b/tests/fontfamily/fontfamilyui.js @@ -65,9 +65,9 @@ describe( 'FontFamilyUI', () => { it( 'button has the base properties', () => { const button = dropdown.buttonView; - expect( button ).to.have.property( 'tooltip', 'Font Family' ); + expect( button ).to.have.property( 'label', 'Font Family' ); + expect( button ).to.have.property( 'tooltip', true ); expect( button ).to.have.property( 'icon', fontFamilyIcon ); - expect( button ).to.have.property( 'withText', false ); } ); it( 'should add custom CSS class to dropdown', () => { @@ -123,7 +123,7 @@ describe( 'FontFamilyUI', () => { it( 'works for the #buttonView', () => { const buttonView = dropdown.buttonView; - expect( buttonView.tooltip ).to.equal( 'Czcionka' ); + expect( buttonView.label ).to.equal( 'Czcionka' ); } ); it( 'works for the listView#items in the panel', () => { diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js index 2b0b121..9f3abfd 100644 --- a/tests/fontsize/fontsizeui.js +++ b/tests/fontsize/fontsizeui.js @@ -74,9 +74,9 @@ describe( 'FontSizeUI', () => { it( 'button has the base properties', () => { const button = dropdown.buttonView; - expect( button ).to.have.property( 'tooltip', 'Font Size' ); + expect( button ).to.have.property( 'label', 'Font Size' ); + expect( button ).to.have.property( 'tooltip', true ); expect( button ).to.have.property( 'icon', fontSizeIcon ); - expect( button ).to.have.property( 'withText', false ); } ); it( 'should add custom CSS class to dropdown', () => { @@ -214,7 +214,7 @@ describe( 'FontSizeUI', () => { it( 'works for the #buttonView', () => { const buttonView = dropdown.buttonView; - expect( buttonView.tooltip ).to.equal( 'Rozmiar czcionki' ); + expect( buttonView.label ).to.equal( 'Rozmiar czcionki' ); } ); it( 'works for the listView#items in the panel', () => { From d8f2ed4b002f584791dc0bc57ddf59ecced2b8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 2 Feb 2018 11:23:55 +0100 Subject: [PATCH 081/101] Docs: Brought dropdown/utils methods to a common naming scheme. --- src/fontfamily/fontfamilyui.js | 4 ++-- src/fontsize/fontsizeui.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index e8ab72a..4408a7c 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -11,7 +11,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; -import { createDropdown, addListViewToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; +import { createDropdown, addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import { normalizeOptions } from './utils'; @@ -35,7 +35,7 @@ export default class FontFamilyUI extends Plugin { // Register UI component. editor.ui.componentFactory.add( 'fontFamily', locale => { const dropdownView = createDropdown( locale ); - addListViewToDropdown( dropdownView, _prepareListOptions( options, command ) ); + addListToDropdown( dropdownView, _prepareListOptions( options, command ) ); dropdownView.set( { label: t( 'Font Family' ), diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index d3e8c46..bcae267 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -11,7 +11,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; -import { createDropdown, addListViewToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; +import { createDropdown, addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; import { normalizeOptions } from '../fontsize/utils'; @@ -37,7 +37,7 @@ export default class FontSizeUI extends Plugin { // Register UI component. editor.ui.componentFactory.add( 'fontSize', locale => { const dropdownView = createDropdown( locale ); - addListViewToDropdown( dropdownView, _prepareListOptions( options, command ) ); + addListToDropdown( dropdownView, _prepareListOptions( options, command ) ); // Create dropdown model. dropdownView.set( { From 6031d401ac402f5abe79b0c2e2cadaa935185ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 5 Feb 2018 18:49:39 +0100 Subject: [PATCH 082/101] Changed: Remove `dropdown.buttonView` bindings to `dropdown`. --- src/fontfamily/fontfamilyui.js | 2 +- src/fontsize/fontsizeui.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index 4408a7c..e4384d5 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -37,7 +37,7 @@ export default class FontFamilyUI extends Plugin { const dropdownView = createDropdown( locale ); addListToDropdown( dropdownView, _prepareListOptions( options, command ) ); - dropdownView.set( { + dropdownView.buttonView.set( { label: t( 'Font Family' ), icon: fontFamilyIcon, tooltip: true diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index bcae267..fb14148 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -40,7 +40,7 @@ export default class FontSizeUI extends Plugin { addListToDropdown( dropdownView, _prepareListOptions( options, command ) ); // Create dropdown model. - dropdownView.set( { + dropdownView.buttonView.set( { label: t( 'Font Size' ), icon: fontSizeIcon, tooltip: true From 8657667ebdb7ec4a6eb2fb1bd19a3471f84bdf1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 9 Feb 2018 10:51:58 +0100 Subject: [PATCH 083/101] Fix: Update imports to CKEditor5 plugins. --- tests/fontcommand.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fontcommand.js b/tests/fontcommand.js index 48454ad..3eff014 100644 --- a/tests/fontcommand.js +++ b/tests/fontcommand.js @@ -8,8 +8,8 @@ import FontCommand from '../src/fontcommand'; import Command from '@ckeditor/ckeditor5-core/src/command'; import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; -import Range from '../../ckeditor5-engine/src/model/range'; -import Position from '../../ckeditor5-engine/src/model/position'; +import Range from '@ckeditor/ckeditor5-engine/src/model/range'; +import Position from '@ckeditor/ckeditor5-engine/src/model/position'; describe( 'FontCommand', () => { let editor, model, doc, root, command; From 1e7c54d8b5b10df487222c13bb3e5019c21270b8 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 12:33:19 +0100 Subject: [PATCH 084/101] Updated the font features' conversion to the newest API. --- src/fontfamily.js | 2 +- src/fontfamily/fontfamilyediting.js | 24 ++++++++---------------- src/fontsize.js | 2 +- src/fontsize/fontsizeediting.js | 25 ++++++++----------------- tests/fontfamily/fontfamilyediting.js | 4 ++-- tests/fontsize/fontsizeediting.js | 4 ++-- 6 files changed, 22 insertions(+), 39 deletions(-) diff --git a/src/fontfamily.js b/src/fontfamily.js index 578c281..1479727 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -43,7 +43,7 @@ export default class FontFamily extends Plugin { * @property {String} title The user-readable title of the option. * @property {String} model Attribute's unique value in the model. * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. - * @property {Array.} [acceptsAlso] An array with all matched elements that + * @property {Array.} [upcastAlso] An array with all matched elements that * view to model conversion should also accept. */ diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 1f80297..03c997c 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -8,14 +8,13 @@ */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; -import { - modelAttributeToViewAttributeElement, - viewToModelAttribute -} from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters'; +import { attributeToElement } from '@ckeditor/ckeditor5-engine/src/conversion/two-way-converters'; import FontFamilyCommand from './fontfamilycommand'; import { normalizeOptions } from './utils'; +const FONT_FAMILY = 'fontFamily'; + /** * The Font Family Editing feature. * @@ -29,7 +28,7 @@ export default class FontFamilyEditing extends Plugin { super( editor ); // Define default configuration using font families shortcuts. - editor.config.define( 'fontFamily', { + editor.config.define( FONT_FAMILY, { options: [ 'default', 'Arial, Helvetica, sans-serif', @@ -49,23 +48,16 @@ export default class FontFamilyEditing extends Plugin { */ init() { const editor = this.editor; - const data = editor.data; - const editing = editor.editing; // Allow fontFamily attribute on text nodes. - editor.model.schema.extend( '$text', { allowAttributes: 'fontFamily' } ); + editor.model.schema.extend( '$text', { allowAttributes: FONT_FAMILY } ); // Get configured font family options without "default" option. const options = normalizeOptions( editor.config.get( 'fontFamily.options' ) ).filter( item => item.model ); - // Define view to model conversion. - for ( const option of options ) { - viewToModelAttribute( 'fontFamily', option, [ data.viewToModel ] ); - } - - // Define model to view conversion. - modelAttributeToViewAttributeElement( 'fontFamily', options, [ data.modelToView, editing.modelToView ] ); + // Set-up the two-way conversion. + attributeToElement( editor.conversion, FONT_FAMILY, options ); - editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) ); + editor.commands.add( FONT_FAMILY, new FontFamilyCommand( editor ) ); } } diff --git a/src/fontsize.js b/src/fontsize.js index 99c1882..47b358e 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -43,7 +43,7 @@ export default class FontSize extends Plugin { * @property {String} title The user-readable title of the option. * @property {String} model Attribute's unique value in the model. * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. - * @property {Array.} [acceptsAlso] An array with all matched elements that + * @property {Array.} [upcastAlso] An array with all matched elements that * view to model conversion should also accept. */ diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index d6def2c..cda548a 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -8,14 +8,13 @@ */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; -import { - modelAttributeToViewAttributeElement, - viewToModelAttribute -} from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters'; +import { attributeToElement } from '@ckeditor/ckeditor5-engine/src/conversion/two-way-converters'; import FontSizeCommand from './fontsizecommand'; import { normalizeOptions } from './utils'; +const FONT_SIZE = 'fontSize'; + /** * The Font Size Editing feature. * @@ -29,7 +28,7 @@ export default class FontSizeEditing extends Plugin { super( editor ); // Define default configuration using named presets. - editor.config.define( 'fontSize', { + editor.config.define( FONT_SIZE, { options: [ 'tiny', 'small', @@ -39,22 +38,14 @@ export default class FontSizeEditing extends Plugin { ] } ); - const data = editor.data; - const editing = editor.editing; - // Define view to model conversion. const options = normalizeOptions( this.editor.config.get( 'fontSize.options' ) ).filter( item => item.model ); - for ( const option of options ) { - // Covert view to model. - viewToModelAttribute( 'fontSize', option, [ data.viewToModel ] ); - } - - // Define model to view conversion. - modelAttributeToViewAttributeElement( 'fontSize', options, [ data.modelToView, editing.modelToView ] ); + // Set-up the two-way conversion. + attributeToElement( editor.conversion, FONT_SIZE, options ); // Add FontSize command. - editor.commands.add( 'fontSize', new FontSizeCommand( editor ) ); + editor.commands.add( FONT_SIZE, new FontSizeCommand( editor ) ); } /** @@ -64,6 +55,6 @@ export default class FontSizeEditing extends Plugin { const editor = this.editor; // Allow fontSize attribute on text nodes. - editor.model.schema.extend( '$text', { allowAttributes: 'fontSize' } ); + editor.model.schema.extend( '$text', { allowAttributes: FONT_SIZE } ); } } diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js index dd03501..7669fbc 100644 --- a/tests/fontfamily/fontfamilyediting.js +++ b/tests/fontfamily/fontfamilyediting.js @@ -139,10 +139,10 @@ describe( 'FontFamilyEditing', () => { name: 'span', class: [ 'text-complex' ] }, - acceptsAlso: [ + upcastAlso: [ { name: 'span', style: { 'font-family': 'Arial' } }, { name: 'span', style: { 'font-family': 'Arial,sans-serif' } }, - { name: 'span', attributes: { 'data-font': 'Arial' } } + { name: 'span', attribute: { 'data-font': 'Arial' } } ] } ] diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index 05b686d..d33e3f7 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -132,9 +132,9 @@ describe( 'FontSizeEditing', () => { name: 'span', class: [ 'text-complex' ] }, - acceptsAlso: [ + upcastAlso: [ { name: 'span', style: { 'font-size': '77em' } }, - { name: 'span', attributes: { 'data-size': '77em' } } + { name: 'span', attribute: { 'data-size': '77em' } } ] } ] From ba2379e888a6666cbd8265fb7c5e56d3c4fa05af Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 13:56:15 +0100 Subject: [PATCH 085/101] Fix: Font size's normalizeOptions should not change the type of the model property. --- src/fontsize/utils.js | 2 +- tests/fontsize/utils.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fontsize/utils.js b/src/fontsize/utils.js index 3401e29..c781f51 100644 --- a/src/fontsize/utils.js +++ b/src/fontsize/utils.js @@ -103,7 +103,7 @@ function generatePixelPreset( size ) { return { title: sizeName, - model: sizeName, + model: size, view: { name: 'span', style: { diff --git a/tests/fontsize/utils.js b/tests/fontsize/utils.js index 749011a..8e0ff57 100644 --- a/tests/fontsize/utils.js +++ b/tests/fontsize/utils.js @@ -40,11 +40,11 @@ describe( 'FontSizeEditing Utils', () => { describe( 'numeric presets', () => { it( 'should return generated presets', () => { expect( normalizeOptions( [ '10', 12, 'normal', '14.1', 18.3 ] ) ).to.deep.equal( [ - { title: '10', model: '10', view: { name: 'span', style: { 'font-size': '10px' } } }, - { title: '12', model: '12', view: { name: 'span', style: { 'font-size': '12px' } } }, + { title: '10', model: 10, view: { name: 'span', style: { 'font-size': '10px' } } }, + { title: '12', model: 12, view: { name: 'span', style: { 'font-size': '12px' } } }, { title: 'Normal', model: undefined }, - { title: '14.1', model: '14.1', view: { name: 'span', style: { 'font-size': '14.1px' } } }, - { title: '18.3', model: '18.3', view: { name: 'span', style: { 'font-size': '18.3px' } } } + { title: '14.1', model: 14.1, view: { name: 'span', style: { 'font-size': '14.1px' } } }, + { title: '18.3', model: 18.3, view: { name: 'span', style: { 'font-size': '18.3px' } } } ] ); } ); } ); From 5d506ec8660c086df65b14a8124c3f51aa3a800e Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 14:12:57 +0100 Subject: [PATCH 086/101] Tests: Added separators to toolbar configs in manual tests. --- tests/manual/font-family.js | 2 +- tests/manual/font-size-numeric.js | 2 +- tests/manual/font-size-presets.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/manual/font-family.js b/tests/manual/font-family.js index 9f34df8..396dd02 100644 --- a/tests/manual/font-family.js +++ b/tests/manual/font-family.js @@ -13,7 +13,7 @@ ClassicEditor .create( document.querySelector( '#editor' ), { plugins: [ ArticlePluginSet, FontFamily ], toolbar: [ - 'headings', 'fontFamily', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' + 'headings', '|', 'fontFamily', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ] } ) .then( editor => { diff --git a/tests/manual/font-size-numeric.js b/tests/manual/font-size-numeric.js index 3ca3777..76dcf07 100644 --- a/tests/manual/font-size-numeric.js +++ b/tests/manual/font-size-numeric.js @@ -13,7 +13,7 @@ ClassicEditor .create( document.querySelector( '#editor' ), { plugins: [ ArticlePluginSet, FontSize ], toolbar: [ - 'headings', 'fontSize', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' + 'headings', '|', 'fontSize', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ], fontSize: { options: [ 10, 12, 14, 'normal', 18, 20, 22 ] } } ) diff --git a/tests/manual/font-size-presets.js b/tests/manual/font-size-presets.js index 8fe9c91..8af7685 100644 --- a/tests/manual/font-size-presets.js +++ b/tests/manual/font-size-presets.js @@ -13,7 +13,7 @@ ClassicEditor .create( document.querySelector( '#editor' ), { plugins: [ ArticlePluginSet, FontSize ], toolbar: [ - 'headings', 'fontSize', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' + 'headings', '|', 'fontSize', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ] } ) .then( editor => { From 2f7b5ec079304a4c2dc77b9991a1d1dc12227a3a Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 16:04:29 +0100 Subject: [PATCH 087/101] Docs: Added separators to toolbar configurations. --- docs/_snippets/features/custom-font-family-options.js | 2 +- docs/_snippets/features/custom-font-size-named-options.js | 2 +- docs/_snippets/features/custom-font-size-numeric-options.js | 2 +- docs/_snippets/features/font-family.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_snippets/features/custom-font-family-options.js b/docs/_snippets/features/custom-font-family-options.js index 6dc45be..e86cdca 100644 --- a/docs/_snippets/features/custom-font-family-options.js +++ b/docs/_snippets/features/custom-font-family-options.js @@ -8,7 +8,7 @@ ClassicEditor .create( document.querySelector( '#snippet-custom-font-family-options' ), { toolbar: { items: [ - 'headings', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' + 'headings', '|', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' ], viewportTopOffset: 60 }, diff --git a/docs/_snippets/features/custom-font-size-named-options.js b/docs/_snippets/features/custom-font-size-named-options.js index 79d655b..862934c 100644 --- a/docs/_snippets/features/custom-font-size-named-options.js +++ b/docs/_snippets/features/custom-font-size-named-options.js @@ -8,7 +8,7 @@ ClassicEditor .create( document.querySelector( '#snippet-custom-font-size-named-options' ), { toolbar: { items: [ - 'headings', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + 'headings', '|', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' ], viewportTopOffset: 60 }, diff --git a/docs/_snippets/features/custom-font-size-numeric-options.js b/docs/_snippets/features/custom-font-size-numeric-options.js index 814c2e1..5d65c58 100644 --- a/docs/_snippets/features/custom-font-size-numeric-options.js +++ b/docs/_snippets/features/custom-font-size-numeric-options.js @@ -8,7 +8,7 @@ ClassicEditor .create( document.querySelector( '#snippet-custom-font-size-numeric-options' ), { toolbar: { items: [ - 'headings', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + 'headings', '|', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' ], viewportTopOffset: 60 }, diff --git a/docs/_snippets/features/font-family.js b/docs/_snippets/features/font-family.js index fd92257..d7f1c8e 100644 --- a/docs/_snippets/features/font-family.js +++ b/docs/_snippets/features/font-family.js @@ -9,7 +9,7 @@ ClassicEditor .create( document.querySelector( '#snippet-font-family' ), { toolbar: { items: [ - 'headings', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' + 'headings', '|', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' ], viewportTopOffset: 60 } From 1324a23dc5d82ef2b2ad5f7e0c5cada9c018fd03 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 16:23:54 +0100 Subject: [PATCH 088/101] Docs: Improved font size and family feature guides. --- docs/features/font-family.md | 30 +++++++++++++++--------------- docs/features/font-size.md | 36 ++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/features/font-family.md b/docs/features/font-family.md index 7bc853a..46829fd 100644 --- a/docs/features/font-family.md +++ b/docs/features/font-family.md @@ -13,10 +13,10 @@ The {@link module:font/fontfamily~FontFamily} feature enables support for settin ## Configuring font family options -It is, of course, possible to configure which font family options the editor should support. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option to do so. -Use special keyword `'default'` to use document's default font family as defined in CSS. +It is possible to configure which font family options are supported by the editor. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option to do so. +Use the special keyword `'default'` to use the default `font-family` defined in the web page styles. -For example, the following editor will support only two font families besides "default" one: +For example, the following editor supports only two font families besides the "default" one: ```js ClassicEditor @@ -46,7 +46,7 @@ To add this feature to your editor install the [`@ckeditor/ckeditor5-font`](http npm install --save @ckeditor/ckeditor5-font ``` -And add it to your plugin list and toolbar configuration: +And add it to your plugin list and the toolbar configuration: ```js import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily'; @@ -68,18 +68,18 @@ ClassicEditor The {@link module:font/fontfamily~FontFamily} plugin registers: -* Dropdown: `'fontFamily'`. -* Command: `'fontFamily'`. +* The `'fontFamily'` dropdown, +* The `'fontFamily'` command. - The number of options and their names are based on the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option). + The number of options and their names correspond to the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option). - You can change font family of the current selection by executing command with proper value: + You can change the font family of the current selection by executing the command with a desired value: ```js editor.execute( 'fontFamily', { value: 'Arial' } ); ``` - The Value passed to `family` corresponds to the first font name in configuration string. For default configuration: + The Value passed to `family` corresponds to the first font name in the configuration string. For the following default configuration: ```js fontFamily.options = [ 'default', @@ -93,8 +93,8 @@ The {@link module:font/fontfamily~FontFamily} plugin registers: 'Verdana, Geneva, sans-serif' ] ``` - - the `fontFamily` command will accept strings below as value: + + the `fontFamily` command will accept the corresponding strings as a value: * `'Arial'` * `'Courier New'` * `'Georgia'` @@ -102,10 +102,10 @@ The {@link module:font/fontfamily~FontFamily} plugin registers: * `'Tahoma'` * `'Times New Roman'` * `'Trebuchet MS'` - * `'Verdana'` - - passing an empty value will remove any `fontFamily` set: - + * `'Verdana'` + + passing an empty value will remove any `fontFamily` (`default`): + ```js editor.execute( 'fontFamily' ); ``` diff --git a/docs/features/font-size.md b/docs/features/font-size.md index 07501af..370aae9 100644 --- a/docs/features/font-size.md +++ b/docs/features/font-size.md @@ -13,12 +13,12 @@ The {@link module:font/fontsize~FontSize} feature enables support for setting fo ## Configuring font size options -It is, of course, possible to configure which font size options the editor should support. Use the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option to do so. -Use special keyword `'normal'` to use document's normal font size as defined in CSS. +It is possible to configure which font size options are supported by the editor. Use the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option to do so. +Use the special keyword `'normal'` to use the default font size defined in the web page styles. -The font size feature supports two ways of defining configuration using either predefined named presets or numeric values. +The font size feature supports two ways of defining configuration: using predefined (named) presets or simple numeric values. -### Configuration using predefined named presets +### Configuration using the predefined named presets The font size feature defines 4 named presets: - `'tiny'` @@ -26,13 +26,13 @@ The font size feature defines 4 named presets: - `'big'` - `'huge'` -Each size will be represented in the view as `` element with `text-*` class set. For example for `'tiny'` preset the editor will output: +Each size is represented in the view as a `` element with the `text-*` class. For example, the `'tiny'` preset looks as follows in the editor data: ```html ... ``` -Below is the editor that will support only two font sizes: +An example of the editor that supports only two font sizes: ```js ClassicEditor @@ -56,16 +56,16 @@ ClassicEditor ### Configuration using numeric presets -As an alternative the font feature supports numeric values as options. +As an alternative, the font feature supports numeric values. -Each size will be represented in the view as `` element with `font-size` style set as `px` value. -For example for `14` the editor will output: +Each size is represented in the view as a `` element with the `font-size` style set in `px`. +For example, `14` will be represented in the editor data as: ```html ... ``` -Below is the editor that will support numeric font sizes (it is assumed that `'normal'` size is defined by CSS as `15px`: +Here's an example of the editor that supports numeric font sizes (assuming that the `'normal'` size is defined by CSS as `15px`): ```js ClassicEditor @@ -99,7 +99,7 @@ To add this feature to your editor install the [`@ckeditor/ckeditor5-font`](http npm install --save @ckeditor/ckeditor5-font ``` -And add it to your plugin list and toolbar configuration: +And add it to your plugin list and the toolbar configuration: ```js import FontSize from '@ckeditor/ckeditor5-font/src/fontsize'; @@ -119,24 +119,24 @@ ClassicEditor ## Common API -The {@link module:font/fontsize~FontSize} plugin registers: +The {@link module:font/fontsize~FontSize} plugin registers the following components: -* Dropdown: `'fontSize'`. -* Command: `'fontSize'`. +* The `'fontSize'` dropdown, +* The `'fontSize'` command. - The number of options and their names are based on the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option). + The number of options and their names correspond to the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option). - You can change font size of the current selection by executing command with proper value: + You can change the font size of the current selection by executing the command with a desired value: ```js // For numeric values: editor.execute( 'fontSize', { value: 10 } ); - + // For named presets: editor.execute( 'fontSize', { value: 'small' } ); ``` passing an empty value will remove any `fontSize` set: - + ```js editor.execute( 'fontSize' ); ``` From 4a4f47dacc77b9b99762d93b9a756003e47a32cb Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 16:31:38 +0100 Subject: [PATCH 089/101] Docs: Minor improvements in docs. --- docs/features/font-family.md | 3 ++- docs/features/font-size.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/features/font-family.md b/docs/features/font-family.md index 46829fd..244f272 100644 --- a/docs/features/font-family.md +++ b/docs/features/font-family.md @@ -14,7 +14,8 @@ The {@link module:font/fontfamily~FontFamily} feature enables support for settin ## Configuring font family options It is possible to configure which font family options are supported by the editor. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option to do so. -Use the special keyword `'default'` to use the default `font-family` defined in the web page styles. + +Use the special keyword `'default'` to use the default `font-family` defined in the web page styles — it disables the font family feature. For example, the following editor supports only two font families besides the "default" one: diff --git a/docs/features/font-size.md b/docs/features/font-size.md index 370aae9..b79b633 100644 --- a/docs/features/font-size.md +++ b/docs/features/font-size.md @@ -14,7 +14,8 @@ The {@link module:font/fontsize~FontSize} feature enables support for setting fo ## Configuring font size options It is possible to configure which font size options are supported by the editor. Use the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option to do so. -Use the special keyword `'normal'` to use the default font size defined in the web page styles. + +Use the special keyword `'normal'` to use the default font size defined in the web page styles — it disables the font-size feature. The font size feature supports two ways of defining configuration: using predefined (named) presets or simple numeric values. From 2ccbd9caf06977c71cce02b943c3dbc8f0b819cb Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 16:34:26 +0100 Subject: [PATCH 090/101] Docs: Minor improvements in docs. --- docs/features/font-family.md | 2 +- docs/features/font-size.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/font-family.md b/docs/features/font-family.md index 244f272..330b900 100644 --- a/docs/features/font-family.md +++ b/docs/features/font-family.md @@ -72,7 +72,7 @@ The {@link module:font/fontfamily~FontFamily} plugin registers: * The `'fontFamily'` dropdown, * The `'fontFamily'` command. - The number of options and their names correspond to the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option). + The number of options and their names correspond to the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option. You can change the font family of the current selection by executing the command with a desired value: diff --git a/docs/features/font-size.md b/docs/features/font-size.md index b79b633..8cb9808 100644 --- a/docs/features/font-size.md +++ b/docs/features/font-size.md @@ -125,7 +125,7 @@ The {@link module:font/fontsize~FontSize} plugin registers the following compone * The `'fontSize'` dropdown, * The `'fontSize'` command. - The number of options and their names correspond to the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option). + The number of options and their names correspond to the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option. You can change the font size of the current selection by executing the command with a desired value: From 85d1fa77b3f79f3ada9d2933587b53f834947ac5 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 16:43:29 +0100 Subject: [PATCH 091/101] Docs: Improvements to font package docs. --- docs/features/font-family.md | 8 ++++---- docs/features/font-size.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/features/font-family.md b/docs/features/font-family.md index 330b900..74b3500 100644 --- a/docs/features/font-family.md +++ b/docs/features/font-family.md @@ -70,7 +70,7 @@ ClassicEditor The {@link module:font/fontfamily~FontFamily} plugin registers: * The `'fontFamily'` dropdown, -* The `'fontFamily'` command. +* The {@link module:font/fontfamily/fontfamilycommand~FontFamilyCommand `'fontFamily'`} command. The number of options and their names correspond to the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option. @@ -80,7 +80,7 @@ The {@link module:font/fontfamily~FontFamily} plugin registers: editor.execute( 'fontFamily', { value: 'Arial' } ); ``` - The Value passed to `family` corresponds to the first font name in the configuration string. For the following default configuration: + The `value` must correspond to the first font name in the configuration string. For the following default configuration: ```js fontFamily.options = [ 'default', @@ -95,7 +95,7 @@ The {@link module:font/fontfamily~FontFamily} plugin registers: ] ``` - the `fontFamily` command will accept the corresponding strings as a value: + the `fontFamily` command will accept the corresponding strings as values: * `'Arial'` * `'Courier New'` * `'Georgia'` @@ -105,7 +105,7 @@ The {@link module:font/fontfamily~FontFamily} plugin registers: * `'Trebuchet MS'` * `'Verdana'` - passing an empty value will remove any `fontFamily` (`default`): + Note that passing an empty value will remove the `fontFamily` from the selection (`default`): ```js editor.execute( 'fontFamily' ); diff --git a/docs/features/font-size.md b/docs/features/font-size.md index 8cb9808..b8cb9db 100644 --- a/docs/features/font-size.md +++ b/docs/features/font-size.md @@ -123,7 +123,7 @@ ClassicEditor The {@link module:font/fontsize~FontSize} plugin registers the following components: * The `'fontSize'` dropdown, -* The `'fontSize'` command. +* The {@link module:font/fontsize/fontsizecommand~FontSizeCommand `'fontSize'`} command. The number of options and their names correspond to the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option. From 069143403bac4a69fc62a55f568eab8ad0373db9 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 16:45:06 +0100 Subject: [PATCH 092/101] Docs: Improvements to font package docs. --- docs/_snippets/features/font-size.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_snippets/features/font-size.js b/docs/_snippets/features/font-size.js index 9131008..af17578 100644 --- a/docs/_snippets/features/font-size.js +++ b/docs/_snippets/features/font-size.js @@ -9,7 +9,7 @@ ClassicEditor .create( document.querySelector( '#snippet-font-size' ), { toolbar: { items: [ - 'headings', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + 'headings', '|', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' ], viewportTopOffset: 60 } From 4e063c7fd9b2c7dabebc445539c20f87bf8c2fd0 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 12 Feb 2018 16:58:23 +0100 Subject: [PATCH 093/101] Docs: Improvements to font package docs. --- src/fontfamily.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/fontfamily.js b/src/fontfamily.js index 1479727..e42d6c8 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -73,9 +73,9 @@ export default class FontFamily extends Plugin { */ /** - * Available font family options. Defined either as array of strings. + * Available font family options defined as an array of strings. * - * The default value is + * The default value is: * * const fontFamilyConfig = { * options: [ @@ -91,22 +91,23 @@ export default class FontFamily extends Plugin { * ] * }; * - * which configures 8 font family options. Each option consist one or more font-family names separated with coma. The first font name is - * used as dropdown item description in UI. The family names that consist spaces should not have quotes (as opposed to CSS standard). - * Appropriate quotes will be added in the view. For example, for the "Lucida Sans Unicode" the editor will render: + * which configures 8 font family options. Each option consist one or more coma–separated font-family names. The first font name is + * used as a dropdown item description in the UI. The family names that consist of spaces should not have quotes + * (as opposed to the CSS standard). + * + * The necessary quotes will be added automatically in the view. For example, for the "Lucida Sans Unicode" the editor will render: * * ... * - * The "default" option is used to remove fontFamily from selection. In such case the text will - * be represented in view using default content CSS font-family. - - * To use defined font families from {@link module:core/commandcollection~CommandCollection} use `fontFamily` command and pass desired - * font family as a value. - * For example, the below code will apply `fontFamily` attribute with `tiny` value to the current selection: + * The "default" option can be used to remove the `font–family` from selection. In such case the text will + * be represented in view using default `font-family` defined in the styles of the web page. + * + * The `fontFamily` command is also available in the {@link module:core/commandcollection~CommandCollection}. + * For example, the following code will apply the `fontFamily` attribute with the `tiny` value to the current selection: * * editor.execute( 'fontFamily', { value: 'tiny' } ); * - * Executing `fontFamily` command without value will remove `fontFamily` attribute from the current selection. + * Executing `fontFamily` command without any value will remove `fontFamily` attribute from the current selection. * * @member {Array.} module:font/fontfamily~FontFamilyConfig#options */ From 9f794644fe01bd824f8c29baa2cb6f0e6b4fcc36 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 13 Feb 2018 11:13:58 +0100 Subject: [PATCH 094/101] Docs: Improved font package docs. --- src/fontcommand.js | 5 +++-- src/fontfamily.js | 2 +- src/fontfamily/utils.js | 4 ++-- src/fontsize.js | 19 +++++++++---------- src/fontsize/utils.js | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/fontcommand.js b/src/fontcommand.js index c2ada6e..0e44e29 100644 --- a/src/fontcommand.js +++ b/src/fontcommand.js @@ -25,7 +25,7 @@ export default class FontCommand extends Command { super( editor ); /** - * If is set it means that selection has `attributeKey` set to that value. + * When set, it reflects the {@link #attributeKey} value of the selection. * * @observable * @readonly @@ -53,7 +53,8 @@ export default class FontCommand extends Command { } /** - * Executes the command. Applies the the attribute `value` to a selection. If no value is passed it removes attribute from selection. + * Executes the command. Applies the `value` of the {@link #attributeKey} to the selection. + * If no `value` is passed, it removes the attribute from the selection. * * @protected * @param {Object} [options] Options for the executed command. diff --git a/src/fontfamily.js b/src/fontfamily.js index e42d6c8..056e4ed 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -91,7 +91,7 @@ export default class FontFamily extends Plugin { * ] * }; * - * which configures 8 font family options. Each option consist one or more coma–separated font-family names. The first font name is + * which configures 8 font family options. Each option consist one or more comma–separated font-family names. The first font name is * used as a dropdown item description in the UI. The family names that consist of spaces should not have quotes * (as opposed to the CSS standard). * diff --git a/src/fontfamily/utils.js b/src/fontfamily/utils.js index 6f947aa..3acdf5a 100644 --- a/src/fontfamily/utils.js +++ b/src/fontfamily/utils.js @@ -8,8 +8,8 @@ */ /** - * Returns {@link module:font/fontfamily~FontFamilyConfig#options} array with options normalized in the - * {@link module:font/fontfamily~FontFamilyOption} format. + * Normalizes the {@link module:font/fontfamily~FontFamilyConfig#options config options} + * to the {@link module:font/fontfamily~FontFamilyOption} format. * * @param {Array.} configuredOptions An array of options taken from configuration. * @returns {Array.} diff --git a/src/fontsize.js b/src/fontsize.js index 47b358e..736b42b 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -73,8 +73,8 @@ export default class FontSize extends Plugin { */ /** - * Available font size options. Defined either using predefined presets, numeric pixel values - * or {@link module:font/fontsize~FontSizeOption}. + * Available font size options. Expressed as predefined presets, numerical "pixel" values + * or the {@link module:font/fontsize~FontSizeOption}. * * The default value is: * @@ -88,23 +88,22 @@ export default class FontSize extends Plugin { * ] * }; * - * It defines 4 sizes: "tiny", "small", "big" and "huge". Those values will be rendered as `span` elements in view. The "normal" defines - * text without a `fontSize` attribute set. + * It defines 4 sizes: **tiny**, **small**, **big**, and **huge**. These values will be rendered as `span` elements in view. + * The **normal** defines a text without the `fontSize` attribute. * - * Each rendered span in the view will have class attribute set corresponding to size name. - * For instance for "small" size the view will render: + * Each `span` has the `class` attribute set to the corresponding size name. For instance, this is what the **small** size looks + * like in the view: * * ... * - * As an alternative the font size might be defined using numeric values (either as Number or as String): + * As an alternative, the font size might be defined using the numerical values (either as a `Number` or as a `String`): * * const fontSizeConfig = { * options: [ 9, 10, 11, 12, 13, 14, 15 ] * }; * - * To use defined font sizes from {@link module:core/commandcollection~CommandCollection} use `fontSize` command and pass desired - * font size as a value. - * For example, the below code will apply `fontSize` attribute with `tiny` value to the current selection: + * Font size can be applied using the command API. To do that, use the `fontSize` command and pass the desired font size as a `value`. + * For example, the below code will apply the `fontSize` attribute with the **tiny** value to the current selection: * * editor.execute( 'fontSize', { value: 'tiny' } ); * diff --git a/src/fontsize/utils.js b/src/fontsize/utils.js index c781f51..699d1ed 100644 --- a/src/fontsize/utils.js +++ b/src/fontsize/utils.js @@ -8,8 +8,8 @@ */ /** - * Returns {@link module:font/fontsize~FontSizeConfig#options} array with options normalized in the - * {@link module:font/fontsize~FontSizeOption} format, translated. + * Normalizes and translates the {@link module:font/fontsize~FontSizeConfig#options config options} + * to the {@link module:font/fontsize~FontSizeOption} format. * * @param {Array.} configuredOptions An array of options taken from configuration. * @returns {Array.} From d128512c4a88ebda8727dd0eacd4fd73d198dfd4 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 13 Feb 2018 11:20:42 +0100 Subject: [PATCH 095/101] Docs: Improved font package docs. --- src/fontfamily.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/fontfamily.js b/src/fontfamily.js index 056e4ed..0d268ed 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -73,9 +73,7 @@ export default class FontFamily extends Plugin { */ /** - * Available font family options defined as an array of strings. - * - * The default value is: + * Available font family options defined as an array of strings. The default value is: * * const fontFamilyConfig = { * options: [ @@ -91,21 +89,21 @@ export default class FontFamily extends Plugin { * ] * }; * - * which configures 8 font family options. Each option consist one or more comma–separated font-family names. The first font name is - * used as a dropdown item description in the UI. The family names that consist of spaces should not have quotes - * (as opposed to the CSS standard). + * which configures 8 font family options. Each option consist of one or more comma–separated font-family names. The first font name is + * used as a dropdown item description in the UI. * - * The necessary quotes will be added automatically in the view. For example, for the "Lucida Sans Unicode" the editor will render: + * **Note:** The family names that consist of spaces should not have quotes (as opposed to the CSS standard). The necessary quotes + * will be added automatically in the view. For example, the `"Lucida Sans Unicode"` will render as follows: * * ... * - * The "default" option can be used to remove the `font–family` from selection. In such case the text will - * be represented in view using default `font-family` defined in the styles of the web page. + * The "default" option removes the `fontFamily` attribute from the selection. In such case, the text will + * be rendered in the view using the default font family defined in the styles of the web page. * - * The `fontFamily` command is also available in the {@link module:core/commandcollection~CommandCollection}. - * For example, the following code will apply the `fontFamily` attribute with the `tiny` value to the current selection: + * Font family can be applied using the command API. To do that, use the `fontFamily` command and pass the desired family as a `value`. + * For example, the following code will apply the `fontFamily` attribute with the `'Arial'` `value` to the current selection: * - * editor.execute( 'fontFamily', { value: 'tiny' } ); + * editor.execute( 'fontFamily', { value: 'Arial' } ); * * Executing `fontFamily` command without any value will remove `fontFamily` attribute from the current selection. * From 5cf9a1eb4bedd9f9362fe66ee17a0cc87b4c45f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 14 Feb 2018 16:39:42 +0100 Subject: [PATCH 096/101] Docs: Add more explanatory descriptions. --- docs/features/font-family.md | 2 +- docs/features/font-size.md | 26 +++++++++++++++++++++++++- src/font.js | 6 ++++-- src/fontcommand.js | 1 - src/fontfamily.js | 2 +- src/fontfamily/fontfamilycommand.js | 6 +++--- src/fontfamily/fontfamilyediting.js | 3 ++- src/fontfamily/fontfamilyui.js | 2 ++ src/fontsize.js | 2 +- src/fontsize/fontsizecommand.js | 6 +++--- src/fontsize/fontsizeediting.js | 3 ++- src/fontsize/fontsizeui.js | 2 ++ 12 files changed, 46 insertions(+), 15 deletions(-) diff --git a/docs/features/font-family.md b/docs/features/font-family.md index 74b3500..f7fec9e 100644 --- a/docs/features/font-family.md +++ b/docs/features/font-family.md @@ -5,7 +5,7 @@ category: features {@snippet features/build-font-family-source} -The {@link module:font/fontfamily~FontFamily} feature enables support for setting font family. +The {@link module:font/fontfamily~FontFamily} feature enables support for setting font family. This feature allows to control size of a text by inline `` elements with a `font-family` CSS set in style attribute. ## Demo diff --git a/docs/features/font-size.md b/docs/features/font-size.md index b8cb9db..a68f885 100644 --- a/docs/features/font-size.md +++ b/docs/features/font-size.md @@ -5,7 +5,7 @@ category: features {@snippet features/build-font-size-source} -The {@link module:font/fontsize~FontSize} feature enables support for setting font size. +The {@link module:font/fontsize~FontSize} feature enables support for setting font size. This feature allows to control size of a text by inline `` elements that have set either class or `font-size` CSS set in style attribute. ## Demo @@ -33,6 +33,30 @@ Each size is represented in the view as a `` element with the `text-*` cla ... ``` +The CSS definition for those CSS classes must be included: +- on the web page's CSS stylesheet that the editor is included and +- on the web page's CSS stylesheet that renders edited content. + +The examples defines below CSS: + +```css +.text-tiny { + font-size: 0.7em; +} + +.text-small { + font-size: 0.85em; +} + +.text-big { + font-size: 1.4em; +} + +.text-huge { + font-size: 1.8em; +} +``` + An example of the editor that supports only two font sizes: ```js diff --git a/src/font.js b/src/font.js index 39ab650..9680abe 100644 --- a/src/font.js +++ b/src/font.js @@ -13,9 +13,11 @@ import FontFamily from './fontfamily'; import FontSize from './fontsize'; /** - * The Font plugin. + * A plugin that includes additional font features. It represents set of features that manipulates visual representation of text. * - * It requires {@link module:font/fontsize~FontSize} and {@link module:font/fontfamily~FontFamily} plugins. + * This plugin enables: + * * {@link module:font/fontsize~FontSize} + * * {@link module:font/fontfamily~FontFamily} plugins. * * Read more about the feature in the {@glink api/font font package} page. * diff --git a/src/fontcommand.js b/src/fontcommand.js index 0e44e29..2f023f6 100644 --- a/src/fontcommand.js +++ b/src/fontcommand.js @@ -59,7 +59,6 @@ export default class FontCommand extends Command { * @protected * @param {Object} [options] Options for the executed command. * @param {String} [options.value] a value to apply. - * * @fires execute */ execute( options = {} ) { diff --git a/src/fontfamily.js b/src/fontfamily.js index 0d268ed..87f882e 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -12,7 +12,7 @@ import FontFamilyEditing from './fontfamily/fontfamilyediting'; import FontFamilyUI from './fontfamily/fontfamilyui'; /** - * The Font family plugin. + * The font family plugin. * * It requires {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} and * {@link module:font/fontfamily/fontfamilyui~FontFamilyUI} plugins. diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index cc2e0f4..af56f50 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -13,13 +13,13 @@ import FontCommand from '../fontcommand'; * The font family command. It is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} * to apply font family. * + * editor.execute( 'fontFamily', { value: 'Arial' } ); + * * @extends module:core/command~Command */ export default class FontFamilyCommand extends FontCommand { /** - * Creates an instance of the command. - * - * @param {module:core/editor/editor~Editor} editor Editor instance. + * @inheritDoc */ constructor( editor ) { super( editor, 'fontFamily' ); diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 03c997c..710f91d 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -16,7 +16,8 @@ import { normalizeOptions } from './utils'; const FONT_FAMILY = 'fontFamily'; /** - * The Font Family Editing feature. + * The font family editing feature. It introduces the `'fontFamily'` command and introduces `fontFamily` attribute in the model + * which renders to the view as a `` element with a `font-family` style attribute. * * @extends module:core/plugin~Plugin */ diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index e4384d5..a5ff53c 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -18,6 +18,8 @@ import { normalizeOptions } from './utils'; import fontFamilyIcon from '../../theme/icons/font-family.svg'; /** + * The font family UI plugin. It introduces the `'fontFamily'` drop-down. + * * @extends module:core/plugin~Plugin */ export default class FontFamilyUI extends Plugin { diff --git a/src/fontsize.js b/src/fontsize.js index 736b42b..fd784e1 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -12,7 +12,7 @@ import FontSizeEditing from './fontsize/fontsizeediting'; import FontSizeUI from './fontsize/fontsizeui'; /** - * The Font size plugin. + * The font size plugin. * * It requires {@link module:font/fontsize/fontsizeediting~FontSizeEditing} and * {@link module:font/fontsize/fontsizeui~FontSizeUI} plugins. diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index 31c87a8..5c28d51 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -13,13 +13,13 @@ import FontCommand from '../fontcommand'; * The font size command. It is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} * to apply font size. * + * editor.execute( 'fontSize', { value: 'small' } ); + * * @extends module:core/command~Command */ export default class FontSizeCommand extends FontCommand { /** - * Creates an instance of the command. - * - * @param {module:core/editor/editor~Editor} editor Editor instance. + * @inheritDoc */ constructor( editor ) { super( editor, 'fontSize' ); diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index cda548a..c9961d6 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -16,7 +16,8 @@ import { normalizeOptions } from './utils'; const FONT_SIZE = 'fontSize'; /** - * The Font Size Editing feature. + * The font size editing feature. It introduces the `'fontSize'` command and introduces `fontSize` attribute in the model which renders + * to the view depending on configuration as a `` element with either a `font-size` style attribute or a class attribute. * * @extends module:core/plugin~Plugin */ diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index fb14148..98a5db4 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -20,6 +20,8 @@ import fontSizeIcon from '../../theme/icons/font-size.svg'; import '../../theme/fontsize.css'; /** + * The font family UI plugin. It introduces the `'fontSize'` drop-down. + * * @extends module:core/plugin~Plugin */ export default class FontSizeUI extends Plugin { From fbad3d616c77d9f03f35e0e1190566b2288e3ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 14 Feb 2018 18:24:01 +0100 Subject: [PATCH 097/101] Docs: Merge font-family and font-size feature documentation into one page. --- .../features/build-font-family-source.html | 0 .../features/build-font-family-source.js | 14 --- ...ize-source.html => build-font-source.html} | 0 ...nt-size-source.js => build-font-source.js} | 4 +- docs/_snippets/features/font-family.html | 31 ----- docs/_snippets/features/font-family.js | 22 ---- docs/_snippets/features/font-size.html | 11 -- docs/_snippets/features/font.html | 20 +++ .../features/{font-size.js => font.js} | 4 +- docs/api/font.md | 2 +- docs/features/font-family.md | 116 ------------------ docs/features/{font-size.md => font.md} | 107 ++++++++++++++-- 12 files changed, 124 insertions(+), 207 deletions(-) delete mode 100644 docs/_snippets/features/build-font-family-source.html delete mode 100644 docs/_snippets/features/build-font-family-source.js rename docs/_snippets/features/{build-font-size-source.html => build-font-source.html} (100%) rename docs/_snippets/features/{build-font-size-source.js => build-font-source.js} (71%) delete mode 100644 docs/_snippets/features/font-family.html delete mode 100644 docs/_snippets/features/font-family.js delete mode 100644 docs/_snippets/features/font-size.html create mode 100644 docs/_snippets/features/font.html rename docs/_snippets/features/{font-size.js => font.js} (83%) delete mode 100644 docs/features/font-family.md rename docs/features/{font-size.md => font.md} (53%) diff --git a/docs/_snippets/features/build-font-family-source.html b/docs/_snippets/features/build-font-family-source.html deleted file mode 100644 index e69de29..0000000 diff --git a/docs/_snippets/features/build-font-family-source.js b/docs/_snippets/features/build-font-family-source.js deleted file mode 100644 index 38a9817..0000000 --- a/docs/_snippets/features/build-font-family-source.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/* globals window */ - -import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor'; - -import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily'; - -ClassicEditor.build.plugins.push( FontFamily ); - -window.ClassicEditor = ClassicEditor; diff --git a/docs/_snippets/features/build-font-size-source.html b/docs/_snippets/features/build-font-source.html similarity index 100% rename from docs/_snippets/features/build-font-size-source.html rename to docs/_snippets/features/build-font-source.html diff --git a/docs/_snippets/features/build-font-size-source.js b/docs/_snippets/features/build-font-source.js similarity index 71% rename from docs/_snippets/features/build-font-size-source.js rename to docs/_snippets/features/build-font-source.js index 474fcfd..dd313b1 100644 --- a/docs/_snippets/features/build-font-size-source.js +++ b/docs/_snippets/features/build-font-source.js @@ -7,8 +7,8 @@ import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor'; -import FontSize from '@ckeditor/ckeditor5-font/src/fontsize'; +import Font from '@ckeditor/ckeditor5-font/src/font'; -ClassicEditor.build.plugins.push( FontSize ); +ClassicEditor.build.plugins.push( Font ); window.ClassicEditor = ClassicEditor; diff --git a/docs/_snippets/features/font-family.html b/docs/_snippets/features/font-family.html deleted file mode 100644 index ef5d31b..0000000 --- a/docs/_snippets/features/font-family.html +++ /dev/null @@ -1,31 +0,0 @@ -
-

Font Family feature sample.

- -

- This text has "Arial, Helvetica, sans-serif" family set. -

- -

- This text has "Courier New, Courier, monospace" family set. -

- -

- This text has "Georgia, serif" family set. -

- -

- This text has "Lucida Sans Unicode, Lucida Grande, sans-serif" font family set. -

- -

- This text has "Tahoma, Geneva, sans-serif" font family set. -

- -

- This text has "Trebuchet MS, Helvetica, sans-serif" font family set. -

- -

- This text has "Verdana, Geneva, sans-serif" font family set. -

-
diff --git a/docs/_snippets/features/font-family.js b/docs/_snippets/features/font-family.js deleted file mode 100644 index d7f1c8e..0000000 --- a/docs/_snippets/features/font-family.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/* globals ClassicEditor, console, window, document */ - -ClassicEditor - .create( document.querySelector( '#snippet-font-family' ), { - toolbar: { - items: [ - 'headings', '|', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' - ], - viewportTopOffset: 60 - } - } ) - .then( editor => { - window.editor = editor; - } ) - .catch( err => { - console.error( err.stack ); - } ); diff --git a/docs/_snippets/features/font-size.html b/docs/_snippets/features/font-size.html deleted file mode 100644 index 6a3c077..0000000 --- a/docs/_snippets/features/font-size.html +++ /dev/null @@ -1,11 +0,0 @@ -
-

Font Size feature sample.

- -

- This is a mixed text with different sizes of text: - tiny, - small, - big and - huge. -

-
diff --git a/docs/_snippets/features/font.html b/docs/_snippets/features/font.html new file mode 100644 index 0000000..1b85e09 --- /dev/null +++ b/docs/_snippets/features/font.html @@ -0,0 +1,20 @@ +
+

Font feature sample.

+ +

+ This is a mixed text with different sizes of text: + tiny, + small, + big and + huge. +

+ +

+ This text has "Arial, Helvetica, sans-serif" family set. +

+ +

+ This text has "Courier New, Courier, monospace" family set. +

+ +
diff --git a/docs/_snippets/features/font-size.js b/docs/_snippets/features/font.js similarity index 83% rename from docs/_snippets/features/font-size.js rename to docs/_snippets/features/font.js index af17578..6abe47e 100644 --- a/docs/_snippets/features/font-size.js +++ b/docs/_snippets/features/font.js @@ -6,10 +6,10 @@ /* globals ClassicEditor, console, window, document */ ClassicEditor - .create( document.querySelector( '#snippet-font-size' ), { + .create( document.querySelector( '#snippet-font' ), { toolbar: { items: [ - 'headings', '|', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + 'headings', '|', 'bulletedList', 'numberedList', 'fontSize', 'fontFamily', 'undo', 'redo' ], viewportTopOffset: 60 } diff --git a/docs/api/font.md b/docs/api/font.md index e2bfae3..e035e9e 100644 --- a/docs/api/font.md +++ b/docs/api/font.md @@ -10,7 +10,7 @@ This package implements the font family and font size features for CKEditor 5. ## Documentation -See the {@link features/font-family Font family feature} and {@link features/font-size Font size feature} guides +See the {@link features/font Font feature} guide with corresponding {@link module:font/fontfamily~FontFamily} and {@link module:font/fontsize~FontSize} plugin documentation. ## Installation diff --git a/docs/features/font-family.md b/docs/features/font-family.md deleted file mode 100644 index f7fec9e..0000000 --- a/docs/features/font-family.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Font family -category: features ---- - -{@snippet features/build-font-family-source} - -The {@link module:font/fontfamily~FontFamily} feature enables support for setting font family. This feature allows to control size of a text by inline `` elements with a `font-family` CSS set in style attribute. - -## Demo - -{@snippet features/font-family} - -## Configuring font family options - -It is possible to configure which font family options are supported by the editor. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option to do so. - -Use the special keyword `'default'` to use the default `font-family` defined in the web page styles — it disables the font family feature. - -For example, the following editor supports only two font families besides the "default" one: - -```js -ClassicEditor - .create( document.querySelector( '#editor' ), { - fontFamily: { - options: [ - 'default', - 'Ubuntu, Arial, sans-serif', - 'Ubuntu Mono, Courier New, Courier, monospace' - ] - }, - toolbar: [ - 'headings', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' - ] - } ) - .then( ... ) - .catch( ... ); -``` - -{@snippet features/custom-font-family-options} - -## Installation - -To add this feature to your editor install the [`@ckeditor/ckeditor5-font`](https://www.npmjs.com/package/@ckeditor/ckeditor5-font) package: - -``` -npm install --save @ckeditor/ckeditor5-font -``` - -And add it to your plugin list and the toolbar configuration: - -```js -import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily'; - -ClassicEditor - .create( document.querySelector( '#editor' ), { - plugins: [ FontFamily, ... ], - toolbar: [ 'fontFamily', ... ] - } ) - .then( ... ) - .catch( ... ); -``` - - - Read more about {@link builds/guides/development/installing-plugins installing plugins}. - - -## Common API - -The {@link module:font/fontfamily~FontFamily} plugin registers: - -* The `'fontFamily'` dropdown, -* The {@link module:font/fontfamily/fontfamilycommand~FontFamilyCommand `'fontFamily'`} command. - - The number of options and their names correspond to the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option. - - You can change the font family of the current selection by executing the command with a desired value: - - ```js - editor.execute( 'fontFamily', { value: 'Arial' } ); - ``` - - The `value` must correspond to the first font name in the configuration string. For the following default configuration: - ```js - fontFamily.options = [ - 'default', - 'Arial, Helvetica, sans-serif', - 'Courier New, Courier, monospace', - 'Georgia, serif', - 'Lucida Sans Unicode, Lucida Grande, sans-serif', - 'Tahoma, Geneva, sans-serif', - 'Times New Roman, Times, serif', - 'Trebuchet MS, Helvetica, sans-serif', - 'Verdana, Geneva, sans-serif' - ] - ``` - - the `fontFamily` command will accept the corresponding strings as values: - * `'Arial'` - * `'Courier New'` - * `'Georgia'` - * `'Lucida Sans Unicode'` - * `'Tahoma'` - * `'Times New Roman'` - * `'Trebuchet MS'` - * `'Verdana'` - - Note that passing an empty value will remove the `fontFamily` from the selection (`default`): - - ```js - editor.execute( 'fontFamily' ); - ``` - -## Contribute - -The source code of the feature is available on GitHub in https://github.com/ckeditor/ckeditor5-font. diff --git a/docs/features/font-size.md b/docs/features/font.md similarity index 53% rename from docs/features/font-size.md rename to docs/features/font.md index a68f885..31e11ad 100644 --- a/docs/features/font-size.md +++ b/docs/features/font.md @@ -1,15 +1,49 @@ --- -title: Font size +title: Font category: features --- -{@snippet features/build-font-size-source} +{@snippet features/build-font-source} -The {@link module:font/fontsize~FontSize} feature enables support for setting font size. This feature allows to control size of a text by inline `` elements that have set either class or `font-size` CSS set in style attribute. +The {@link module:font/font~Font} feature enables support for: +* setting font family - which allows to control font face of a text by inline `` elements with a `font-family` CSS set in style attribute. +* setting font size - which allows to control size of a text by inline `` elements that have set either class or `font-size` CSS set in style attribute. + +The font features can be used independently by using: +* {@link module:font/fontfamily~FontFamily} to control font face of a text. +* {@link module:font/fontsize~FontSize} to control size of a text. ## Demo -{@snippet features/font-size} +{@snippet features/font} + +## Configuring font family options + +It is possible to configure which font family options are supported by the editor. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option to do so. + +Use the special keyword `'default'` to use the default `font-family` defined in the web page styles — it disables the font family feature. + +For example, the following editor supports only two font families besides the "default" one: + +```js +ClassicEditor + .create( document.querySelector( '#editor' ), { + fontFamily: { + options: [ + 'default', + 'Ubuntu, Arial, sans-serif', + 'Ubuntu Mono, Courier New, Courier, monospace' + ] + }, + toolbar: [ + 'headings', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' + ] + } ) + .then( ... ) + .catch( ... ); +``` + +{@snippet features/custom-font-family-options} ## Configuring font size options @@ -127,12 +161,26 @@ npm install --save @ckeditor/ckeditor5-font And add it to your plugin list and the toolbar configuration: ```js -import FontSize from '@ckeditor/ckeditor5-font/src/fontsize'; +import Font from '@ckeditor/ckeditor5-font/src/font'; ClassicEditor .create( document.querySelector( '#editor' ), { - plugins: [ FontSize, ... ], - toolbar: [ 'fontSize', ... ] + plugins: [ Font, ... ], + toolbar: [ 'fontSize', 'fontFamily', ... ] + } ) + .then( ... ) + .catch( ... ); +``` + +or add one of the font features to your plugin list and the toolbar configuration: + +```js +import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily'; + +ClassicEditor + .create( document.querySelector( '#editor' ), { + plugins: [ FontFamily, ... ], + toolbar: [ 'fontFamily', ... ] } ) .then( ... ) .catch( ... ); @@ -144,6 +192,50 @@ ClassicEditor ## Common API +The {@link module:font/fontfamily~FontFamily} plugin registers: + +* The `'fontFamily'` dropdown, +* The {@link module:font/fontfamily/fontfamilycommand~FontFamilyCommand `'fontFamily'`} command. + + The number of options and their names correspond to the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option. + + You can change the font family of the current selection by executing the command with a desired value: + + ```js + editor.execute( 'fontFamily', { value: 'Arial' } ); + ``` + + The `value` must correspond to the first font name in the configuration string. For the following default configuration: + ```js + fontFamily.options = [ + 'default', + 'Arial, Helvetica, sans-serif', + 'Courier New, Courier, monospace', + 'Georgia, serif', + 'Lucida Sans Unicode, Lucida Grande, sans-serif', + 'Tahoma, Geneva, sans-serif', + 'Times New Roman, Times, serif', + 'Trebuchet MS, Helvetica, sans-serif', + 'Verdana, Geneva, sans-serif' + ] + ``` + + the `fontFamily` command will accept the corresponding strings as values: + * `'Arial'` + * `'Courier New'` + * `'Georgia'` + * `'Lucida Sans Unicode'` + * `'Tahoma'` + * `'Times New Roman'` + * `'Trebuchet MS'` + * `'Verdana'` + + Note that passing an empty value will remove the `fontFamily` from the selection (`default`): + + ```js + editor.execute( 'fontFamily' ); + ``` + The {@link module:font/fontsize~FontSize} plugin registers the following components: * The `'fontSize'` dropdown, @@ -165,7 +257,6 @@ The {@link module:font/fontsize~FontSize} plugin registers the following compone ```js editor.execute( 'fontSize' ); ``` - ## Contribute The source code of the feature is available on GitHub in https://github.com/ckeditor/ckeditor5-font. From 5929641b56a19629102b0e860edbc74465870922 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Thu, 15 Feb 2018 12:21:03 +0100 Subject: [PATCH 098/101] Docs: Various fixes and improvements in the documentation. --- .../features/custom-font-family-options.js | 2 +- .../custom-font-size-named-options.js | 2 +- .../custom-font-size-numeric-options.js | 2 +- docs/_snippets/features/font.js | 2 +- docs/features/font.md | 34 ++++++++----------- src/font.js | 8 ++--- src/fontfamily.js | 10 +++--- src/fontfamily/fontfamilycommand.js | 6 ++-- src/fontfamily/fontfamilyediting.js | 8 +++-- src/fontsize.js | 10 +++--- src/fontsize/fontsizecommand.js | 6 ++-- src/fontsize/fontsizeediting.js | 11 ++++-- 12 files changed, 55 insertions(+), 46 deletions(-) diff --git a/docs/_snippets/features/custom-font-family-options.js b/docs/_snippets/features/custom-font-family-options.js index e86cdca..117859b 100644 --- a/docs/_snippets/features/custom-font-family-options.js +++ b/docs/_snippets/features/custom-font-family-options.js @@ -8,7 +8,7 @@ ClassicEditor .create( document.querySelector( '#snippet-custom-font-family-options' ), { toolbar: { items: [ - 'headings', '|', 'bulletedList', 'numberedList', 'fontFamily', 'undo', 'redo' + 'headings', '|', 'fontFamily', 'bulletedList', 'numberedList', 'undo', 'redo' ], viewportTopOffset: 60 }, diff --git a/docs/_snippets/features/custom-font-size-named-options.js b/docs/_snippets/features/custom-font-size-named-options.js index 862934c..7fc515a 100644 --- a/docs/_snippets/features/custom-font-size-named-options.js +++ b/docs/_snippets/features/custom-font-size-named-options.js @@ -8,7 +8,7 @@ ClassicEditor .create( document.querySelector( '#snippet-custom-font-size-named-options' ), { toolbar: { items: [ - 'headings', '|', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + 'headings', '|', 'fontSize', 'bulletedList', 'numberedList', 'undo', 'redo' ], viewportTopOffset: 60 }, diff --git a/docs/_snippets/features/custom-font-size-numeric-options.js b/docs/_snippets/features/custom-font-size-numeric-options.js index 5d65c58..6764cf5 100644 --- a/docs/_snippets/features/custom-font-size-numeric-options.js +++ b/docs/_snippets/features/custom-font-size-numeric-options.js @@ -8,7 +8,7 @@ ClassicEditor .create( document.querySelector( '#snippet-custom-font-size-numeric-options' ), { toolbar: { items: [ - 'headings', '|', 'bulletedList', 'numberedList', 'fontSize', 'undo', 'redo' + 'headings', '|', 'fontSize', 'bulletedList', 'numberedList', 'undo', 'redo' ], viewportTopOffset: 60 }, diff --git a/docs/_snippets/features/font.js b/docs/_snippets/features/font.js index 6abe47e..d90e39d 100644 --- a/docs/_snippets/features/font.js +++ b/docs/_snippets/features/font.js @@ -9,7 +9,7 @@ ClassicEditor .create( document.querySelector( '#snippet-font' ), { toolbar: { items: [ - 'headings', '|', 'bulletedList', 'numberedList', 'fontSize', 'fontFamily', 'undo', 'redo' + 'headings', '|', 'fontSize', 'fontFamily', '|', 'bulletedList', 'numberedList', 'undo', 'redo' ], viewportTopOffset: 60 } diff --git a/docs/features/font.md b/docs/features/font.md index 31e11ad..7470c31 100644 --- a/docs/features/font.md +++ b/docs/features/font.md @@ -5,23 +5,19 @@ category: features {@snippet features/build-font-source} -The {@link module:font/font~Font} feature enables support for: -* setting font family - which allows to control font face of a text by inline `` elements with a `font-family` CSS set in style attribute. -* setting font size - which allows to control size of a text by inline `` elements that have set either class or `font-size` CSS set in style attribute. - -The font features can be used independently by using: -* {@link module:font/fontfamily~FontFamily} to control font face of a text. -* {@link module:font/fontsize~FontSize} to control size of a text. +The {@link module:font/font~Font} plugin enables the following features in the editor: +* {@link module:font/fontfamily~FontFamily} – allows to change the font family by applying inline `` elements with a `font-family` in the `style` attribute, +* {@link module:font/fontsize~FontSize} – allows to control size by applying inline `` elements that either have a CSS class or a `font-size` in the `style` attribute. ## Demo {@snippet features/font} -## Configuring font family options +## Configuring the font family It is possible to configure which font family options are supported by the editor. Use the {@link module:font/fontfamily~FontFamilyConfig#options `fontFamily.options`} configuration option to do so. -Use the special keyword `'default'` to use the default `font-family` defined in the web page styles — it disables the font family feature. +Use the special `'default'` keyword to use the default `font-family` defined in the web page styles (removes any custom font family). For example, the following editor supports only two font families besides the "default" one: @@ -45,11 +41,11 @@ ClassicEditor {@snippet features/custom-font-family-options} -## Configuring font size options +## Configuring the font size It is possible to configure which font size options are supported by the editor. Use the {@link module:font/fontsize~FontSizeConfig#options `fontSize.options`} configuration option to do so. -Use the special keyword `'normal'` to use the default font size defined in the web page styles — it disables the font-size feature. +Use the special `'normal'` keyword to use the default font size defined in the web page styles (removes any custom size). The font size feature supports two ways of defining configuration: using predefined (named) presets or simple numeric values. @@ -67,11 +63,11 @@ Each size is represented in the view as a `` element with the `text-*` cla ... ``` -The CSS definition for those CSS classes must be included: -- on the web page's CSS stylesheet that the editor is included and -- on the web page's CSS stylesheet that renders edited content. +The CSS definition for the classes (presets) must be included: +- in the web page's styles where the editor runs (backend), +- in the web page's styles where the edited content is rendered (frontend) -The examples defines below CSS: +Here's an example of the font size CSS classes: ```css .text-tiny { @@ -113,18 +109,18 @@ ClassicEditor {@snippet features/custom-font-size-named-options} -### Configuration using numeric presets +### Configuration using numerical values -As an alternative, the font feature supports numeric values. +The font feature also supports numerical values. -Each size is represented in the view as a `` element with the `font-size` style set in `px`. +In this case, each size is represented in the view as a `` element with the `font-size` style set in `px`. For example, `14` will be represented in the editor data as: ```html ... ``` -Here's an example of the editor that supports numeric font sizes (assuming that the `'normal'` size is defined by CSS as `15px`): +Here's an example of the editor that supports numerical font sizes. Note that `'normal'` is controlled by the default styles of the web page: ```js ClassicEditor diff --git a/src/font.js b/src/font.js index 9680abe..4b89a37 100644 --- a/src/font.js +++ b/src/font.js @@ -13,11 +13,9 @@ import FontFamily from './fontfamily'; import FontSize from './fontsize'; /** - * A plugin that includes additional font features. It represents set of features that manipulates visual representation of text. - * - * This plugin enables: - * * {@link module:font/fontsize~FontSize} - * * {@link module:font/fontfamily~FontFamily} plugins. + * A plugin that enables (aggregates) a set of text styling features: + * * {@link module:font/fontsize~FontSize}, + * * {@link module:font/fontfamily~FontFamily}. * * Read more about the feature in the {@glink api/font font package} page. * diff --git a/src/fontfamily.js b/src/fontfamily.js index 87f882e..d87d05b 100644 --- a/src/fontfamily.js +++ b/src/fontfamily.js @@ -14,8 +14,8 @@ import FontFamilyUI from './fontfamily/fontfamilyui'; /** * The font family plugin. * - * It requires {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} and - * {@link module:font/fontfamily/fontfamilyui~FontFamilyUI} plugins. + * It enables {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} and + * {@link module:font/fontfamily/fontfamilyui~FontFamilyUI} features in the editor. * * @extends module:core/plugin~Plugin */ @@ -36,14 +36,14 @@ export default class FontFamily extends Plugin { } /** - * Font family option descriptor. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. + * Font family option descriptor. * * @typedef {Object} module:font/fontfamily~FontFamilyOption * * @property {String} title The user-readable title of the option. * @property {String} model Attribute's unique value in the model. - * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. - * @property {Array.} [upcastAlso] An array with all matched elements that + * @property {module:engine/view/elementdefinition~ElementDefinition} view View element configuration. + * @property {Array.} [upcastAlso] An array with all matched elements that * view to model conversion should also accept. */ diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js index af56f50..91a5aab 100644 --- a/src/fontfamily/fontfamilycommand.js +++ b/src/fontfamily/fontfamilycommand.js @@ -11,11 +11,13 @@ import FontCommand from '../fontcommand'; /** * The font family command. It is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} - * to apply font family. + * to apply the font family. * * editor.execute( 'fontFamily', { value: 'Arial' } ); * - * @extends module:core/command~Command + * **Note**: Executing the command without the value removes the attribute from the model. + * + * @extends module:font/fontcommand~FontCommand */ export default class FontFamilyCommand extends FontCommand { /** diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js index 710f91d..a936ff5 100644 --- a/src/fontfamily/fontfamilyediting.js +++ b/src/fontfamily/fontfamilyediting.js @@ -16,8 +16,12 @@ import { normalizeOptions } from './utils'; const FONT_FAMILY = 'fontFamily'; /** - * The font family editing feature. It introduces the `'fontFamily'` command and introduces `fontFamily` attribute in the model - * which renders to the view as a `` element with a `font-family` style attribute. + * The font family editing feature. + * + * It introduces the {@link module:font/fontfamily/fontfamilycommand~FontFamilyCommand command} and + * the `fontFamily` attribute in the {@link module:engine/model/model~Model model} which renders + * in the {@link module:engine/view/view view} as an inline span (``), + * depending on the {@link module:font/fontfamily~FontFamilyConfig configuration}. * * @extends module:core/plugin~Plugin */ diff --git a/src/fontsize.js b/src/fontsize.js index fd784e1..faa1245 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -14,8 +14,8 @@ import FontSizeUI from './fontsize/fontsizeui'; /** * The font size plugin. * - * It requires {@link module:font/fontsize/fontsizeediting~FontSizeEditing} and - * {@link module:font/fontsize/fontsizeui~FontSizeUI} plugins. + * It enables {@link module:font/fontsize/fontsizeediting~FontSizeEditing} and + * {@link module:font/fontsize/fontsizeui~FontSizeUI} features in the editor. * * @extends module:core/plugin~Plugin */ @@ -36,14 +36,14 @@ export default class FontSize extends Plugin { } /** - * Font size option descriptor. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}. + * Font size option descriptor. * * @typedef {Object} module:font/fontsize~FontSizeOption * * @property {String} title The user-readable title of the option. * @property {String} model Attribute's unique value in the model. - * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration. - * @property {Array.} [upcastAlso] An array with all matched elements that + * @property {module:engine/view/elementdefinition~ElementDefinition} view View element configuration. + * @property {Array.} [upcastAlso] An array with all matched elements that * view to model conversion should also accept. */ diff --git a/src/fontsize/fontsizecommand.js b/src/fontsize/fontsizecommand.js index 5c28d51..293a8d9 100644 --- a/src/fontsize/fontsizecommand.js +++ b/src/fontsize/fontsizecommand.js @@ -11,11 +11,13 @@ import FontCommand from '../fontcommand'; /** * The font size command. It is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} - * to apply font size. + * to apply the font size. * * editor.execute( 'fontSize', { value: 'small' } ); * - * @extends module:core/command~Command + * **Note**: Executing the command without the value removes the attribute from the model. + * + * @extends module:font/fontcommand~FontCommand */ export default class FontSizeCommand extends FontCommand { /** diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index c9961d6..c36451a 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -16,8 +16,15 @@ import { normalizeOptions } from './utils'; const FONT_SIZE = 'fontSize'; /** - * The font size editing feature. It introduces the `'fontSize'` command and introduces `fontSize` attribute in the model which renders - * to the view depending on configuration as a `` element with either a `font-size` style attribute or a class attribute. + * The font size editing feature. + * + * It introduces the {@link module:font/fontsize/fontsizecommand~FontSizeCommand command} and the `fontSize` + * attribute in the {@link module:engine/model/model~Model model} which renders in the {@link module:engine/view/view view} + * as a `` element with either: + * * a style attribute (`...`), + * * or a class attribute (`...`) + * + * depending on the {@link module:font/fontsize~FontSizeConfig configuration}. * * @extends module:core/plugin~Plugin */ From 22a19465e99b5272af4200dfb1617e6abbbd06a9 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Thu, 15 Feb 2018 12:28:46 +0100 Subject: [PATCH 099/101] Changed 'normal' font size to 'default' to keep the same naming convention as the font family feature. --- src/fontsize.js | 4 ++-- src/fontsize/fontsizeediting.js | 2 +- src/fontsize/fontsizeui.js | 2 +- src/fontsize/utils.js | 6 +++--- tests/fontsize/fontsizeediting.js | 6 +++--- tests/fontsize/fontsizeui.js | 18 +++++++++--------- tests/fontsize/utils.js | 10 +++++----- tests/manual/font-size-numeric.html | 2 +- tests/manual/font-size-numeric.js | 2 +- tests/manual/font-size-numeric.md | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/fontsize.js b/src/fontsize.js index faa1245..fc2dbe1 100644 --- a/src/fontsize.js +++ b/src/fontsize.js @@ -82,14 +82,14 @@ export default class FontSize extends Plugin { * options: [ * 'tiny', * 'small', - * 'normal', + * 'default', * 'big', * 'huge' * ] * }; * * It defines 4 sizes: **tiny**, **small**, **big**, and **huge**. These values will be rendered as `span` elements in view. - * The **normal** defines a text without the `fontSize` attribute. + * The **default** defines a text without the `fontSize` attribute. * * Each `span` has the `class` attribute set to the corresponding size name. For instance, this is what the **small** size looks * like in the view: diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js index c36451a..b2719f4 100644 --- a/src/fontsize/fontsizeediting.js +++ b/src/fontsize/fontsizeediting.js @@ -40,7 +40,7 @@ export default class FontSizeEditing extends Plugin { options: [ 'tiny', 'small', - 'normal', + 'default', 'big', 'huge' ] diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index 98a5db4..fd528b0 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -84,7 +84,7 @@ export default class FontSizeUI extends Plugin { const t = editor.t; const localizedTitles = { - Normal: t( 'Normal' ), + Default: t( 'Default' ), Tiny: t( 'Tiny' ), Small: t( 'Small' ), Big: t( 'Big' ), diff --git a/src/fontsize/utils.js b/src/fontsize/utils.js index 699d1ed..9472b6e 100644 --- a/src/fontsize/utils.js +++ b/src/fontsize/utils.js @@ -74,11 +74,11 @@ function getOptionDefinition( option ) { return namedPresets[ option ]; } - // 'Normal' font size. It will be used to remove the fontSize attribute. - if ( option === 'normal' ) { + // 'Default' font size. It will be used to remove the fontSize attribute. + if ( option === 'default' ) { return { model: undefined, - title: 'Normal' + title: 'Default' }; } diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js index d33e3f7..8107966 100644 --- a/tests/fontsize/fontsizeediting.js +++ b/tests/fontsize/fontsizeediting.js @@ -39,7 +39,7 @@ describe( 'FontSizeEditing', () => { describe( 'config', () => { describe( 'default value', () => { it( 'should be set', () => { - expect( editor.config.get( 'fontSize.options' ) ).to.deep.equal( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); + expect( editor.config.get( 'fontSize.options' ) ).to.deep.equal( [ 'tiny', 'small', 'default', 'big', 'huge' ] ); } ); } ); } ); @@ -52,7 +52,7 @@ describe( 'FontSizeEditing', () => { fontSize: { options: [ 'tiny', - 'normal', + 'default', 18, { title: 'My setting', @@ -106,7 +106,7 @@ describe( 'FontSizeEditing', () => { fontSize: { options: [ 'tiny', - 'normal', + 'default', 18, { title: 'My setting', diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js index 9f3abfd..d840f2c 100644 --- a/tests/fontsize/fontsizeui.js +++ b/tests/fontsize/fontsizeui.js @@ -23,7 +23,7 @@ describe( 'FontSizeUI', () => { before( () => { addTranslations( 'en', { 'Font Size': 'Font Size', - 'Normal': 'Normal', + 'Default': 'Default', 'Tiny': 'Tiny', 'Small': 'Small', 'Big': 'Big', @@ -32,7 +32,7 @@ describe( 'FontSizeUI', () => { addTranslations( 'pl', { 'Font Size': 'Rozmiar czcionki', - 'Normal': 'Normalny', + 'Default': 'Domyślny', 'Tiny': 'Tyci', 'Small': 'Mały', 'Big': 'Duży', @@ -99,7 +99,7 @@ describe( 'FontSizeUI', () => { command.value = undefined; - // The third item is 'normal' font size. + // The third item is 'default' font size. expect( listView.items.map( item => item.isActive ) ).to.deep.equal( [ false, false, true, false, false ] ); command.value = 'tiny'; @@ -129,7 +129,7 @@ describe( 'FontSizeUI', () => { .create( element, { plugins: [ FontSizeEditing, FontSizeUI ], fontSize: { - options: [ 'tiny', 'small', 'normal', 'big', 'huge' ] + options: [ 'tiny', 'small', 'default', 'big', 'huge' ] } } ) .then( newEditor => { @@ -160,7 +160,7 @@ describe( 'FontSizeUI', () => { .create( element, { plugins: [ FontSizeEditing, FontSizeUI ], fontSize: { - options: [ 10, 12, 'normal', 16, 18 ] + options: [ 10, 12, 'default', 16, 18 ] } } ) .then( newEditor => { @@ -197,15 +197,15 @@ describe( 'FontSizeUI', () => { describe( 'localization', () => { beforeEach( () => { - return localizedEditor( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); + return localizedEditor( [ 'tiny', 'small', 'default', 'big', 'huge' ] ); } ); it( 'does not alter normalizeOptions() internals', () => { - const options = normalizeOptions( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ); + const options = normalizeOptions( [ 'tiny', 'small', 'default', 'big', 'huge' ] ); expect( options ).to.deep.equal( [ { title: 'Tiny', model: 'tiny', view: { name: 'span', class: 'text-tiny' } }, { title: 'Small', model: 'small', view: { name: 'span', class: 'text-small' } }, - { title: 'Normal', model: undefined }, + { title: 'Default', model: undefined }, { title: 'Big', model: 'big', view: { name: 'span', class: 'text-big' } }, { title: 'Huge', model: 'huge', view: { name: 'span', class: 'text-huge' } } ] ); @@ -223,7 +223,7 @@ describe( 'FontSizeUI', () => { expect( listView.items.map( item => item.label ) ).to.deep.equal( [ 'Tyci', 'Mały', - 'Normalny', + 'Domyślny', 'Duży', 'Ogromny' ] ); diff --git a/tests/fontsize/utils.js b/tests/fontsize/utils.js index 8e0ff57..1a1e2e4 100644 --- a/tests/fontsize/utils.js +++ b/tests/fontsize/utils.js @@ -8,7 +8,7 @@ import { normalizeOptions } from '../../src/fontsize/utils'; describe( 'FontSizeEditing Utils', () => { describe( 'normalizeOptions()', () => { it( 'should discard unsupported values', () => { - expect( normalizeOptions( [ () => {}, 'normal', 'unknown' ] ) ).to.deep.equal( [ { title: 'Normal', model: undefined } ] ); + expect( normalizeOptions( [ () => {}, 'default', 'unknown' ] ) ).to.deep.equal( [ { title: 'Default', model: undefined } ] ); } ); it( 'should pass through object definition', () => { @@ -27,10 +27,10 @@ describe( 'FontSizeEditing Utils', () => { describe( 'named presets', () => { it( 'should return defined presets', () => { - expect( normalizeOptions( [ 'tiny', 'small', 'normal', 'big', 'huge' ] ) ).to.deep.equal( [ + expect( normalizeOptions( [ 'tiny', 'small', 'default', 'big', 'huge' ] ) ).to.deep.equal( [ { title: 'Tiny', model: 'tiny', view: { name: 'span', class: 'text-tiny' } }, { title: 'Small', model: 'small', view: { name: 'span', class: 'text-small' } }, - { title: 'Normal', model: undefined }, + { title: 'Default', model: undefined }, { title: 'Big', model: 'big', view: { name: 'span', class: 'text-big' } }, { title: 'Huge', model: 'huge', view: { name: 'span', class: 'text-huge' } } ] ); @@ -39,10 +39,10 @@ describe( 'FontSizeEditing Utils', () => { describe( 'numeric presets', () => { it( 'should return generated presets', () => { - expect( normalizeOptions( [ '10', 12, 'normal', '14.1', 18.3 ] ) ).to.deep.equal( [ + expect( normalizeOptions( [ '10', 12, 'default', '14.1', 18.3 ] ) ).to.deep.equal( [ { title: '10', model: 10, view: { name: 'span', style: { 'font-size': '10px' } } }, { title: '12', model: 12, view: { name: 'span', style: { 'font-size': '12px' } } }, - { title: 'Normal', model: undefined }, + { title: 'Default', model: undefined }, { title: '14.1', model: 14.1, view: { name: 'span', style: { 'font-size': '14.1px' } } }, { title: '18.3', model: 18.3, view: { name: 'span', style: { 'font-size': '18.3px' } } } ] ); diff --git a/tests/manual/font-size-numeric.html b/tests/manual/font-size-numeric.html index 88567b5..6ef262a 100644 --- a/tests/manual/font-size-numeric.html +++ b/tests/manual/font-size-numeric.html @@ -4,7 +4,7 @@

Font Size feature sample.

Some text with font-size set to: 10px.

Some text with font-size set to: 12px.

Some text with font-size set to: 14px.

-

Some text with normal size (font-size not set to).

+

Some text with the default size

Some text with font-size set to: 18px.

Some text with font-size set to: 20px.

Some text with font-size set to: 22px.

diff --git a/tests/manual/font-size-numeric.js b/tests/manual/font-size-numeric.js index 76dcf07..b6a4c72 100644 --- a/tests/manual/font-size-numeric.js +++ b/tests/manual/font-size-numeric.js @@ -15,7 +15,7 @@ ClassicEditor toolbar: [ 'headings', '|', 'fontSize', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ], - fontSize: { options: [ 10, 12, 14, 'normal', 18, 20, 22 ] } + fontSize: { options: [ 10, 12, 14, 'default', 18, 20, 22 ] } } ) .then( editor => { window.editor = editor; diff --git a/tests/manual/font-size-numeric.md b/tests/manual/font-size-numeric.md index 19f1f8f..bf60baf 100644 --- a/tests/manual/font-size-numeric.md +++ b/tests/manual/font-size-numeric.md @@ -1,7 +1,7 @@ ### Loading The data should be loaded with: -- 7 paragraphs with font sizes (10, 12, 14, normal, 18, 20, 22), +- 7 paragraphs with font sizes (10, 12, 14, default, 18, 20, 22), ### Testing From 4e5979ace415d01a4582cfff97ff25d4ef1d1a42 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Thu, 15 Feb 2018 12:54:56 +0100 Subject: [PATCH 100/101] Removed the fontsize style sheet. Moved customizations to Lark. --- src/fontfamily/fontfamilyui.js | 2 -- src/fontsize/fontsizeui.js | 4 ---- theme/fontsize.css | 10 ---------- 3 files changed, 16 deletions(-) delete mode 100644 theme/fontsize.css diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js index a5ff53c..abef119 100644 --- a/src/fontfamily/fontfamilyui.js +++ b/src/fontfamily/fontfamilyui.js @@ -12,9 +12,7 @@ import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import { createDropdown, addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; - import { normalizeOptions } from './utils'; - import fontFamilyIcon from '../../theme/icons/font-family.svg'; /** diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js index fd528b0..5af87dd 100644 --- a/src/fontsize/fontsizeui.js +++ b/src/fontsize/fontsizeui.js @@ -12,13 +12,9 @@ import Model from '@ckeditor/ckeditor5-ui/src/model'; import Collection from '@ckeditor/ckeditor5-utils/src/collection'; import { createDropdown, addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils'; - import { normalizeOptions } from '../fontsize/utils'; - import fontSizeIcon from '../../theme/icons/font-size.svg'; -import '../../theme/fontsize.css'; - /** * The font family UI plugin. It introduces the `'fontSize'` drop-down. * diff --git a/theme/fontsize.css b/theme/fontsize.css deleted file mode 100644 index 66c26cc..0000000 --- a/theme/fontsize.css +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/* - * Note: This file should contain the wireframe styles only. But since there are no such styles, - * it acts as a message to the builder telling that it should look for the corresponding styles - * **in the theme** when compiling the editor. - */ From 33743e894700db1942c3842da9cb6b02c16f450d Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Thu, 15 Feb 2018 13:07:15 +0100 Subject: [PATCH 101/101] Minor lexical improvements. --- docs/_snippets/features/custom-font-size-numeric-options.html | 2 +- docs/features/font.md | 2 +- tests/fontsize/fontsizeui.js | 2 +- tests/fontsize/utils.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_snippets/features/custom-font-size-numeric-options.html b/docs/_snippets/features/custom-font-size-numeric-options.html index 0bf72e9..6bd000e 100644 --- a/docs/_snippets/features/custom-font-size-numeric-options.html +++ b/docs/_snippets/features/custom-font-size-numeric-options.html @@ -1,5 +1,5 @@
-

Font Size feature numeric options sample.

+

Font Size feature numerical options sample.

9px

11px

diff --git a/docs/features/font.md b/docs/features/font.md index 7470c31..417239f 100644 --- a/docs/features/font.md +++ b/docs/features/font.md @@ -242,7 +242,7 @@ The {@link module:font/fontsize~FontSize} plugin registers the following compone You can change the font size of the current selection by executing the command with a desired value: ```js - // For numeric values: + // For numerical values: editor.execute( 'fontSize', { value: 10 } ); // For named presets: diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js index d840f2c..5787c04 100644 --- a/tests/fontsize/fontsizeui.js +++ b/tests/fontsize/fontsizeui.js @@ -151,7 +151,7 @@ describe( 'FontSizeUI', () => { } ); } ); - describe( 'using numeric values', () => { + describe( 'using numerical values', () => { beforeEach( () => { element = document.createElement( 'div' ); document.body.appendChild( element ); diff --git a/tests/fontsize/utils.js b/tests/fontsize/utils.js index 1a1e2e4..94069a5 100644 --- a/tests/fontsize/utils.js +++ b/tests/fontsize/utils.js @@ -37,7 +37,7 @@ describe( 'FontSizeEditing Utils', () => { } ); } ); - describe( 'numeric presets', () => { + describe( 'numerical presets', () => { it( 'should return generated presets', () => { expect( normalizeOptions( [ '10', 12, 'default', '14.1', 18.3 ] ) ).to.deep.equal( [ { title: '10', model: 10, view: { name: 'span', style: { 'font-size': '10px' } } },