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/.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
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/docs/_snippets/features/build-font-source.html b/docs/_snippets/features/build-font-source.html
new file mode 100644
index 0000000..c4a4bca
--- /dev/null
+++ b/docs/_snippets/features/build-font-source.html
@@ -0,0 +1,17 @@
+
diff --git a/docs/_snippets/features/build-font-source.js b/docs/_snippets/features/build-font-source.js
new file mode 100644
index 0000000..dd313b1
--- /dev/null
+++ b/docs/_snippets/features/build-font-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 Font from '@ckeditor/ckeditor5-font/src/font';
+
+ClassicEditor.build.plugins.push( Font );
+
+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..117859b
--- /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', '|', 'fontFamily', 'bulletedList', 'numberedList', '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/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..7fc515a
--- /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', '|', 'fontSize', 'bulletedList', 'numberedList', '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..6bd000e
--- /dev/null
+++ b/docs/_snippets/features/custom-font-size-numeric-options.html
@@ -0,0 +1,11 @@
+
+
Font Size feature numerical 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..6764cf5
--- /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', '|', 'fontSize', 'bulletedList', 'numberedList', '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.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.js b/docs/_snippets/features/font.js
new file mode 100644
index 0000000..d90e39d
--- /dev/null
+++ b/docs/_snippets/features/font.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' ), {
+ toolbar: {
+ items: [
+ 'headings', '|', 'fontSize', 'fontFamily', '|', 'bulletedList', 'numberedList', 'undo', 'redo'
+ ],
+ viewportTopOffset: 60
+ }
+ } )
+ .then( editor => {
+ window.editor = editor;
+ } )
+ .catch( err => {
+ console.error( err.stack );
+ } );
diff --git a/docs/api/font.md b/docs/api/font.md
new file mode 100644
index 0000000..e035e9e
--- /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 Font feature} guide
+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/docs/features/font.md b/docs/features/font.md
new file mode 100644
index 0000000..417239f
--- /dev/null
+++ b/docs/features/font.md
@@ -0,0 +1,258 @@
+---
+title: Font
+category: features
+---
+
+{@snippet features/build-font-source}
+
+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 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 `'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:
+
+```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 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 `'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.
+
+### Configuration using the predefined named presets
+
+The font size feature defines 4 named presets:
+- `'tiny'`
+- `'small'`
+- `'big'`
+- `'huge'`
+
+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
+...
+```
+
+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)
+
+Here's an example of the font size CSS classes:
+
+```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
+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 numerical values
+
+The font feature also supports numerical values.
+
+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 numerical font sizes. Note that `'normal'` is controlled by the default styles of the web page:
+
+```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 the toolbar configuration:
+
+```js
+import Font from '@ckeditor/ckeditor5-font/src/font';
+
+ClassicEditor
+ .create( document.querySelector( '#editor' ), {
+ 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( ... );
+```
+
+
+ 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' );
+ ```
+
+The {@link module:font/fontsize~FontSize} plugin registers the following components:
+
+* The `'fontSize'` dropdown,
+* 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.
+
+ You can change the font size of the current selection by executing the command with a desired value:
+
+ ```js
+ // For numerical 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.
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/package.json b/package.json
index 57566e1..b0cffef 100644
--- a/package.json
+++ b/package.json
@@ -12,10 +12,11 @@
"@ckeditor/ckeditor5-ui": "^1.0.0-alpha.2"
},
"devDependencies": {
- "eslint": "^4.8.0",
- "eslint-config-ckeditor5": "^1.0.6",
+ "@ckeditor/ckeditor5-paragraph": "^1.0.0-alpha.2",
+ "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",
diff --git a/src/font.js b/src/font.js
new file mode 100644
index 0000000..4b89a37
--- /dev/null
+++ b/src/font.js
@@ -0,0 +1,38 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/font
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+
+import FontFamily from './fontfamily';
+import FontSize from './fontsize';
+
+/**
+ * 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.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class Font extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ static get requires() {
+ return [ FontFamily, FontSize ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ static get pluginName() {
+ return 'Font';
+ }
+}
diff --git a/src/fontcommand.js b/src/fontcommand.js
new file mode 100644
index 0000000..2f023f6
--- /dev/null
+++ b/src/fontcommand.js
@@ -0,0 +1,91 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontcommand
+ */
+
+import Command from '@ckeditor/ckeditor5-core/src/command';
+
+/**
+ * The base font command.
+ *
+ * @extends module:core/command~Command
+ */
+export default class FontCommand extends Command {
+ /**
+ * Creates an instance of the command.
+ *
+ * @param {module:core/editor/editor~Editor} editor Editor instance.
+ * @param {String} attributeKey Name of an model attribute on which this command operates.
+ */
+ constructor( editor, attributeKey ) {
+ super( editor );
+
+ /**
+ * When set, it reflects the {@link #attributeKey} value of the selection.
+ *
+ * @observable
+ * @readonly
+ * @member {Boolean} module:font/fontcommand~FontCommand#value
+ */
+
+ /**
+ * A model attribute on which this command operates.
+ *
+ * @readonly
+ * @member {Boolean} module:font/fontcommand~FontCommand#attributeKey
+ */
+ this.attributeKey = attributeKey;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ refresh() {
+ const model = this.editor.model;
+ const doc = model.document;
+
+ this.value = doc.selection.getAttribute( this.attributeKey );
+ this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, this.attributeKey );
+ }
+
+ /**
+ * 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.
+ * @param {String} [options.value] a value to apply.
+ * @fires execute
+ */
+ execute( options = {} ) {
+ const model = this.editor.model;
+ const document = model.document;
+ const selection = document.selection;
+
+ const value = options.value;
+
+ model.change( writer => {
+ if ( selection.isCollapsed ) {
+ if ( value ) {
+ writer.setSelectionAttribute( this.attributeKey, value );
+ } else {
+ writer.removeSelectionAttribute( 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/src/fontfamily.js b/src/fontfamily.js
new file mode 100644
index 0000000..d87d05b
--- /dev/null
+++ b/src/fontfamily.js
@@ -0,0 +1,111 @@
+/**
+ * @license Copyright (c) 2003-2018, 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';
+import FontFamilyUI from './fontfamily/fontfamilyui';
+
+/**
+ * The font family plugin.
+ *
+ * It enables {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} and
+ * {@link module:font/fontfamily/fontfamilyui~FontFamilyUI} features in the editor.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class FontFamily extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ static get requires() {
+ return [ FontFamilyEditing, FontFamilyUI ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ static get pluginName() {
+ return 'FontFamily';
+ }
+}
+
+/**
+ * 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/elementdefinition~ElementDefinition} view View element configuration.
+ * @property {Array.} [upcastAlso] 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 as an 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 of one or more comma–separated font-family names. The first font name is
+ * used as a dropdown item description in the UI.
+ *
+ * **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 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.
+ *
+ * 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: 'Arial' } );
+ *
+ * Executing `fontFamily` command without any value will remove `fontFamily` attribute from the current selection.
+ *
+ * @member {Array.} module:font/fontfamily~FontFamilyConfig#options
+ */
diff --git a/src/fontfamily/fontfamilycommand.js b/src/fontfamily/fontfamilycommand.js
new file mode 100644
index 0000000..91a5aab
--- /dev/null
+++ b/src/fontfamily/fontfamilycommand.js
@@ -0,0 +1,29 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontfamily/fontfamilycommand
+ */
+
+import FontCommand from '../fontcommand';
+
+/**
+ * The font family command. It is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing}
+ * to apply the font family.
+ *
+ * editor.execute( 'fontFamily', { value: 'Arial' } );
+ *
+ * **Note**: Executing the command without the value removes the attribute from the model.
+ *
+ * @extends module:font/fontcommand~FontCommand
+ */
+export default class FontFamilyCommand extends FontCommand {
+ /**
+ * @inheritDoc
+ */
+ constructor( editor ) {
+ super( editor, 'fontFamily' );
+ }
+}
diff --git a/src/fontfamily/fontfamilyediting.js b/src/fontfamily/fontfamilyediting.js
new file mode 100644
index 0000000..a936ff5
--- /dev/null
+++ b/src/fontfamily/fontfamilyediting.js
@@ -0,0 +1,68 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontfamily/fontfamilyediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+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.
+ *
+ * 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
+ */
+export default class FontFamilyEditing extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ constructor( editor ) {
+ super( editor );
+
+ // Define default configuration using font families shortcuts.
+ editor.config.define( FONT_FAMILY, {
+ 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'
+ ]
+ } );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ init() {
+ const editor = this.editor;
+
+ // Allow fontFamily attribute on text nodes.
+ 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 );
+
+ // Set-up the two-way conversion.
+ attributeToElement( editor.conversion, FONT_FAMILY, options );
+
+ editor.commands.add( FONT_FAMILY, new FontFamilyCommand( editor ) );
+ }
+}
diff --git a/src/fontfamily/fontfamilyui.js b/src/fontfamily/fontfamilyui.js
new file mode 100644
index 0000000..abef119
--- /dev/null
+++ b/src/fontfamily/fontfamilyui.js
@@ -0,0 +1,117 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontfamily/fontfamilyui
+ */
+
+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, addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
+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 {
+ /**
+ * @inheritDoc
+ */
+ init() {
+ const editor = this.editor;
+ const t = editor.t;
+
+ const options = this._getLocalizedOptions();
+
+ const command = editor.commands.get( 'fontFamily' );
+
+ // Register UI component.
+ editor.ui.componentFactory.add( 'fontFamily', locale => {
+ const dropdownView = createDropdown( locale );
+ addListToDropdown( dropdownView, _prepareListOptions( options, command ) );
+
+ dropdownView.buttonView.set( {
+ label: t( 'Font Family' ),
+ icon: fontFamilyIcon,
+ tooltip: true
+ } );
+
+ dropdownView.extendTemplate( {
+ attributes: {
+ 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 } );
+ editor.editing.view.focus();
+ } );
+
+ return dropdownView;
+ } );
+ }
+
+ /**
+ * Returns options as defined in `config.fontFamily.options` but processed to consider
+ * 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.}.
+ */
+ _getLocalizedOptions() {
+ const editor = this.editor;
+ const t = editor.t;
+
+ const options = normalizeOptions( editor.config.get( 'fontFamily.options' ) );
+
+ return options.map( option => {
+ // The only title to localize is "Default" others are font names.
+ if ( option.title === 'Default' ) {
+ option.title = t( 'Default' );
+ }
+
+ return option;
+ } );
+ }
+}
+
+// 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/fontfamily/utils.js b/src/fontfamily/utils.js
new file mode 100644
index 0000000..3acdf5a
--- /dev/null
+++ b/src/fontfamily/utils.js
@@ -0,0 +1,92 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontfamily/utils
+ */
+
+/**
+ * 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.}
+ */
+export function normalizeOptions( configuredOptions ) {
+ // Convert options to objects.
+ return configuredOptions
+ .map( getOptionDefinition )
+ // Filter out undefined values that `getOptionDefinition` might return.
+ .filter( option => !!option );
+}
+
+// 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~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 ( option === 'default' ) {
+ return {
+ title: 'Default',
+ model: undefined
+ };
+ }
+
+ // Ignore values that we cannot parse to a definition.
+ if ( typeof option !== 'string' ) {
+ return;
+ }
+
+ // 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.
+// 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. 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 ];
+
+ // CSS-compatible font names.
+ const cssFontNames = fontNames.map( normalizeFontNameForCSS ).join( ', ' );
+
+ return {
+ title: firstFontName,
+ model: firstFontName,
+ view: {
+ name: 'span',
+ style: {
+ 'font-family': cssFontNames
+ }
+ }
+ };
+}
+
+// Normalizes font name for the style attribute. It adds braces (') if font name contains spaces.
+//
+// @param {String} fontName
+// @returns {String}
+function normalizeFontNameForCSS( fontName ) {
+ fontName = fontName.trim();
+
+ // Compound font names should be quoted.
+ if ( fontName.indexOf( ' ' ) > 0 ) {
+ fontName = `'${ fontName }'`;
+ }
+
+ return fontName;
+}
diff --git a/src/fontsize.js b/src/fontsize.js
new file mode 100644
index 0000000..fc2dbe1
--- /dev/null
+++ b/src/fontsize.js
@@ -0,0 +1,113 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontsize
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import FontSizeEditing from './fontsize/fontsizeediting';
+import FontSizeUI from './fontsize/fontsizeui';
+
+/**
+ * The font size plugin.
+ *
+ * It enables {@link module:font/fontsize/fontsizeediting~FontSizeEditing} and
+ * {@link module:font/fontsize/fontsizeui~FontSizeUI} features in the editor.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class FontSize extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ static get requires() {
+ return [ FontSizeEditing, FontSizeUI ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ static get pluginName() {
+ return 'FontSize';
+ }
+}
+
+/**
+ * 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/elementdefinition~ElementDefinition} view View element configuration.
+ * @property {Array.} [upcastAlso] 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. Expressed as predefined presets, numerical "pixel" values
+ * or the {@link module:font/fontsize~FontSizeOption}.
+ *
+ * The default value is:
+ *
+ * const fontSizeConfig = {
+ * options: [
+ * 'tiny',
+ * 'small',
+ * 'default',
+ * 'big',
+ * 'huge'
+ * ]
+ * };
+ *
+ * It defines 4 sizes: **tiny**, **small**, **big**, and **huge**. These values will be rendered as `span` elements in view.
+ * 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:
+ *
+ * ...
+ *
+ * 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 ]
+ * };
+ *
+ * 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' } );
+ *
+ * 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/fontsizecommand.js b/src/fontsize/fontsizecommand.js
new file mode 100644
index 0000000..293a8d9
--- /dev/null
+++ b/src/fontsize/fontsizecommand.js
@@ -0,0 +1,29 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontsize/fontsizecommand
+ */
+
+import FontCommand from '../fontcommand';
+
+/**
+ * The font size command. It is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing}
+ * to apply the font size.
+ *
+ * editor.execute( 'fontSize', { value: 'small' } );
+ *
+ * **Note**: Executing the command without the value removes the attribute from the model.
+ *
+ * @extends module:font/fontcommand~FontCommand
+ */
+export default class FontSizeCommand extends FontCommand {
+ /**
+ * @inheritDoc
+ */
+ constructor( editor ) {
+ super( editor, 'fontSize' );
+ }
+}
diff --git a/src/fontsize/fontsizeediting.js b/src/fontsize/fontsizeediting.js
new file mode 100644
index 0000000..b2719f4
--- /dev/null
+++ b/src/fontsize/fontsizeediting.js
@@ -0,0 +1,68 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontsize/fontsizeediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+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.
+ *
+ * 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
+ */
+export default class FontSizeEditing extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ constructor( editor ) {
+ super( editor );
+
+ // Define default configuration using named presets.
+ editor.config.define( FONT_SIZE, {
+ options: [
+ 'tiny',
+ 'small',
+ 'default',
+ 'big',
+ 'huge'
+ ]
+ } );
+
+ // Define view to model conversion.
+ const options = normalizeOptions( this.editor.config.get( 'fontSize.options' ) ).filter( item => item.model );
+
+ // Set-up the two-way conversion.
+ attributeToElement( editor.conversion, FONT_SIZE, options );
+
+ // Add FontSize command.
+ editor.commands.add( FONT_SIZE, new FontSizeCommand( editor ) );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ init() {
+ const editor = this.editor;
+
+ // Allow fontSize attribute on text nodes.
+ editor.model.schema.extend( '$text', { allowAttributes: FONT_SIZE } );
+ }
+}
diff --git a/src/fontsize/fontsizeui.js b/src/fontsize/fontsizeui.js
new file mode 100644
index 0000000..5af87dd
--- /dev/null
+++ b/src/fontsize/fontsizeui.js
@@ -0,0 +1,135 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontsize/fontsizeui
+ */
+
+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, addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
+import { normalizeOptions } from '../fontsize/utils';
+import fontSizeIcon from '../../theme/icons/font-size.svg';
+
+/**
+ * The font family UI plugin. It introduces the `'fontSize'` drop-down.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class FontSizeUI extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ init() {
+ const editor = this.editor;
+ const t = editor.t;
+
+ const options = this._getLocalizedOptions();
+
+ const command = editor.commands.get( 'fontSize' );
+
+ // Register UI component.
+ editor.ui.componentFactory.add( 'fontSize', locale => {
+ const dropdownView = createDropdown( locale );
+ addListToDropdown( dropdownView, _prepareListOptions( options, command ) );
+
+ // Create dropdown model.
+ dropdownView.buttonView.set( {
+ label: t( 'Font Size' ),
+ icon: fontSizeIcon,
+ tooltip: true
+ } );
+
+ dropdownView.extendTemplate( {
+ attributes: {
+ class: [
+ 'ck-font-size-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 } );
+ editor.editing.view.focus();
+ } );
+
+ return dropdownView;
+ } );
+ }
+
+ /**
+ * Returns options as defined in `config.fontSize.options` but processed to consider
+ * 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.}.
+ */
+ _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' )
+ };
+
+ const options = normalizeOptions( editor.config.get( 'fontSize.options' ) );
+
+ return options.map( option => {
+ const title = localizedTitles[ option.title ];
+
+ if ( title && title != option.title ) {
+ // Clone the option to avoid altering the original `namedPresets` from `./utils.js`.
+ option = Object.assign( {}, option, { title } );
+ }
+
+ return option;
+ } );
+ }
+}
+
+// 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/src/fontsize/utils.js b/src/fontsize/utils.js
new file mode 100644
index 0000000..9472b6e
--- /dev/null
+++ b/src/fontsize/utils.js
@@ -0,0 +1,114 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module font/fontsize/utils
+ */
+
+/**
+ * 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.}
+ */
+export function normalizeOptions( configuredOptions ) {
+ // 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',
+ model: 'tiny',
+ view: {
+ name: 'span',
+ class: 'text-tiny'
+ }
+ },
+ small: {
+ title: 'Small',
+ model: 'small',
+ view: {
+ name: 'span',
+ class: 'text-small'
+ }
+ },
+ big: {
+ title: 'Big',
+ model: 'big',
+ view: {
+ name: 'span',
+ class: 'text-big'
+ }
+ },
+ huge: {
+ title: 'Huge',
+ model: 'huge',
+ view: {
+ name: 'span',
+ class: 'text-huge'
+ }
+ }
+};
+
+// 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 {undefined|module:font/fontsize~FontSizeOption}
+function getOptionDefinition( option ) {
+ // Treat any object as full item definition provided by user in configuration.
+ if ( typeof option === 'object' ) {
+ return option;
+ }
+
+ // Item is a named preset.
+ if ( namedPresets[ option ] ) {
+ return namedPresets[ option ];
+ }
+
+ // 'Default' font size. It will be used to remove the fontSize attribute.
+ if ( option === 'default' ) {
+ return {
+ model: undefined,
+ title: 'Default'
+ };
+ }
+
+ // At this stage we probably have numerical value to generate a preset so parse it's value.
+ const sizePreset = parseFloat( option );
+
+ // Discard any faulty values.
+ if ( isNaN( sizePreset ) ) {
+ return;
+ }
+
+ // Return font size definition from size value.
+ return generatePixelPreset( sizePreset );
+}
+
+// Creates a predefined preset for pixel size.
+//
+// @param {Number} size Font size in pixels.
+// @returns {module:font/fontsize~FontSizeOption}
+function generatePixelPreset( size ) {
+ const sizeName = String( size );
+
+ return {
+ title: sizeName,
+ model: size,
+ view: {
+ name: 'span',
+ style: {
+ 'font-size': `${ size }px`
+ }
+ }
+ };
+}
diff --git a/tests/font.js b/tests/font.js
new file mode 100644
index 0000000..07e02ed
--- /dev/null
+++ b/tests/font.js
@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+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( [ FontFamily, FontSize ] );
+ } );
+
+ it( 'defines plugin name', () => {
+ expect( Font.pluginName ).to.equal( 'Font' );
+ } );
+} );
diff --git a/tests/fontcommand.js b/tests/fontcommand.js
new file mode 100644
index 0000000..3eff014
--- /dev/null
+++ b/tests/fontcommand.js
@@ -0,0 +1,279 @@
+/**
+ * @license Copyright (c) 2003-2018, 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';
+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;
+
+ 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' } );
+ } );
+ } );
+
+ 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$text> ' );
+
+ 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 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]obar$text>xyz ' );
+
+ 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]obar$text>xyz ' );
+ } );
+
+ 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> ' +
+ '<$text font="foo">foofoofoo$text> ' +
+ '<$text font="foo">barbar$text>]bar '
+ );
+ } );
+
+ it( 'should change font attribute on selected nodes', () => {
+ setData(
+ model,
+ 'abc[abc<$text font="text-small">abc$text> ' +
+ '<$text font="text-small">foofoofoo$text> ' +
+ '<$text font="text-small">bar]bar$text>bar '
+ );
+
+ command.execute( { value: 'foo' } );
+
+ expect( command.value ).to.equal( 'foo' );
+
+ expect( getData( model ) ).to.equal(
+ 'abc[<$text font="foo">abcabc$text> ' +
+ '<$text font="foo">foofoofoo$text> ' +
+ '<$text font="foo">bar$text>]<$text font="text-small">bar$text>bar '
+ );
+ } );
+
+ it( 'should remove font attribute on selected nodes when passing undefined value', () => {
+ setData(
+ model,
+ 'abcabc[<$text font="foo">abc$text> ' +
+ '<$text font="foo">foofoofoo$text> ' +
+ '<$text font="foo">barbar$text>]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 '
+ );
+ } );
+
+ it( 'should change selection attribute if selection is collapsed in non-empty parent', () => {
+ setData( model, 'a[]bc<$text font="foo">foobar$text>xyz ' );
+
+ 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">foobar$text>xyz ' );
+
+ command.execute( { value: 'foo' } );
+
+ // It should not save that bold was executed at position ( root, [ 0, 1 ] ).
+
+ model.change( writer => {
+ // Simulate clicking right arrow key by changing selection ranges.
+ writer.setSelection( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] );
+
+ // Get back to previous selection.
+ writer.setSelection( [ 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">foobar$text>xyz [] ' );
+
+ 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( 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( writer => {
+ writer.setSelection( [ 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">foobar$text>xy ]z ' );
+
+ expect( command.isEnabled ).to.be.true;
+
+ command.execute( { value: 'foo' } );
+
+ expect( getData( model ) ).to.equal(
+ 'ab[<$text font="foo">c$text> <$text font="foo">foobarxy$text> ]z '
+ );
+ } );
+
+ it( 'should use parent batch for storing undo steps', () => {
+ setData( model, 'a[bc<$text font="foo">fo]obar$text>xyz ' );
+
+ 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]obar$text>xyz ' );
+ } );
+
+ 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.js b/tests/fontfamily.js
new file mode 100644
index 0000000..b8b37d5
--- /dev/null
+++ b/tests/fontfamily.js
@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import FontFamily from './../src/fontfamily';
+import FontFamilyEditing from './../src/fontfamily/fontfamilyediting';
+import FontFamilyUI from '../src/fontfamily/fontfamilyui';
+
+describe( 'FontFamily', () => {
+ it( 'requires FontFamilyEditing and FontFamilyUI', () => {
+ expect( FontFamily.requires ).to.deep.equal( [ FontFamilyEditing, FontFamilyUI ] );
+ } );
+
+ it( 'defines plugin name', () => {
+ expect( FontFamily.pluginName ).to.equal( 'FontFamily' );
+ } );
+} );
diff --git a/tests/fontfamily/fontfamilycommand.js b/tests/fontfamily/fontfamilycommand.js
new file mode 100644
index 0000000..f5974f3
--- /dev/null
+++ b/tests/fontfamily/fontfamilycommand.js
@@ -0,0 +1,35 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import FontFamilyCommand from '../../src/fontfamily/fontfamilycommand';
+import FontCommand from '../../src/fontcommand';
+
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+
+describe( 'FontFamilyCommand', () => {
+ let editor, command;
+
+ beforeEach( () => {
+ return ModelTestEditor.create()
+ .then( newEditor => {
+ editor = newEditor;
+
+ command = new FontFamilyCommand( editor );
+ } );
+ } );
+
+ afterEach( () => {
+ editor.destroy();
+ } );
+
+ it( 'is a FontCommand', () => {
+ expect( FontFamilyCommand.prototype ).to.be.instanceOf( FontCommand );
+ expect( command ).to.be.instanceOf( FontCommand );
+ } );
+
+ it( 'operates on fontFamily attribute', () => {
+ expect( command ).to.have.property( 'attributeKey', 'fontFamily' );
+ } );
+} );
diff --git a/tests/fontfamily/fontfamilyediting.js b/tests/fontfamily/fontfamilyediting.js
new file mode 100644
index 0000000..7669fbc
--- /dev/null
+++ b/tests/fontfamily/fontfamilyediting.js
@@ -0,0 +1,201 @@
+/**
+ * @license Copyright (c) 2003-2018, 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';
+import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+describe( 'FontFamilyEditing', () => {
+ let editor, doc;
+
+ beforeEach( () => {
+ return VirtualTestEditor
+ .create( {
+ plugins: [ FontFamilyEditing, Paragraph ]
+ } )
+ .then( newEditor => {
+ editor = newEditor;
+
+ doc = editor.document;
+ } );
+ } );
+
+ afterEach( () => {
+ editor.destroy();
+ } );
+
+ it( 'should set proper schema rules', () => {
+ 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', () => {
+ describe( 'default value', () => {
+ it( 'should be set', () => {
+ expect( editor.config.get( 'fontFamily.options' ) ).to.deep.equal( [
+ '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'
+ ] );
+ } );
+ } );
+ } );
+
+ describe( 'editing pipeline conversion', () => {
+ beforeEach( () => {
+ return VirtualTestEditor
+ .create( {
+ plugins: [ FontFamilyEditing, Paragraph ],
+ fontFamily: {
+ options: [
+ 'Arial',
+ 'Lucida Sans Unicode, Lucida Grande, sans-serif',
+ {
+ title: 'My font',
+ model: 'my',
+ view: {
+ name: 'mark',
+ class: 'my-style'
+ }
+ }
+ ]
+ }
+ } )
+ .then( newEditor => {
+ editor = newEditor;
+
+ doc = editor.model;
+ } );
+ } );
+
+ it( 'should discard unknown fontFamily attribute values', () => {
+ setModelData( doc, 'f<$text fontFamily="foo-bar">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'foo
' );
+ } );
+
+ it( 'should convert fontFamily attribute to configured simple preset', () => {
+ setModelData( doc, 'f<$text fontFamily="Arial">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+
+ it( 'should convert fontFamily attribute to configured complex preset', () => {
+ setModelData( doc, 'f<$text fontFamily="Lucida Sans Unicode">o$text>o ' );
+
+ expect( editor.getData() )
+ .to.equal( 'fo o
' );
+ } );
+
+ it( 'should convert fontFamily attribute from user defined settings', () => {
+ setModelData( doc, 'f<$text fontFamily="my">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+ } );
+
+ describe( 'data pipeline conversions', () => {
+ beforeEach( () => {
+ return VirtualTestEditor
+ .create( {
+ plugins: [ FontFamilyEditing, Paragraph ],
+ fontFamily: {
+ options: [
+ 'Lucida Sans Unicode, Lucida Grande, sans-serif',
+ {
+ title: 'My other setting',
+ model: 'my-other',
+ view: {
+ name: 'span',
+ style: { 'font-family': 'Other' }
+ }
+ },
+ {
+ title: 'My setting',
+ model: 'my',
+ view: {
+ name: 'mark',
+ style: { 'font-family': 'Verdana' },
+ class: 'my-style'
+ }
+ },
+ {
+ title: 'Hybrid',
+ model: 'complex',
+ view: {
+ name: 'span',
+ class: [ 'text-complex' ]
+ },
+ upcastAlso: [
+ { name: 'span', style: { 'font-family': 'Arial' } },
+ { name: 'span', style: { 'font-family': 'Arial,sans-serif' } },
+ { name: 'span', attribute: { 'data-font': 'Arial' } }
+ ]
+ }
+ ]
+ }
+ } )
+ .then( newEditor => {
+ editor = newEditor;
+
+ doc = editor.model;
+ } );
+ } );
+
+ it( 'should convert from element with defined style when with other styles', () => {
+ const data = 'fo o
';
+
+ editor.setData( data );
+
+ expect( getModelData( doc ) ).to.equal( '[]f<$text fontFamily="my-other">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+
+ it( 'should convert from user defined element', () => {
+ const data = 'fo o
';
+
+ editor.setData( data );
+
+ expect( getModelData( doc ) ).to.equal( '[]f<$text fontFamily="my">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( data );
+ } );
+
+ it( 'should convert from complex definitions', () => {
+ editor.setData(
+ 'fo o
' +
+ 'fo o
' +
+ 'ba r
' +
+ 'ba z
'
+ );
+
+ expect( getModelData( doc ) ).to.equal(
+ '[]f<$text fontFamily="complex">o$text>o ' +
+ 'f<$text fontFamily="complex">o$text>o ' +
+ 'b<$text fontFamily="complex">a$text>r ' +
+ 'b<$text fontFamily="complex">a$text>z '
+ );
+
+ expect( editor.getData() ).to.equal(
+ 'fo o
' +
+ 'fo o
' +
+ 'ba r
' +
+ 'ba z
'
+ );
+ } );
+ } );
+} );
diff --git a/tests/fontfamily/fontfamilyui.js b/tests/fontfamily/fontfamilyui.js
new file mode 100644
index 0000000..37e27d2
--- /dev/null
+++ b/tests/fontfamily/fontfamilyui.js
@@ -0,0 +1,163 @@
+/**
+ * @license Copyright (c) 2003-2018, 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 '../../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 );
+
+ 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( 'label', 'Font Family' );
+ expect( button ).to.have.property( 'tooltip', true );
+ expect( button ).to.have.property( 'icon', fontFamilyIcon );
+ } );
+
+ 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 );
+ } );
+
+ 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;
+
+ expect( dropdown.buttonView.isEnabled ).to.be.false;
+
+ command.isEnabled = true;
+ 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.label ).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/fontfamily/utils.js b/tests/fontfamily/utils.js
new file mode 100644
index 0000000..ffb4947
--- /dev/null
+++ b/tests/fontfamily/utils.js
@@ -0,0 +1,86 @@
+/**
+ * @license Copyright (c) 2003-2018, 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 unsupported 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'
+ }
+ }
+ },
+ {
+ title: 'Comic Sans MS',
+ model: 'Comic Sans MS',
+ view: {
+ name: 'span',
+ style: {
+ 'font-family': '\'Comic Sans MS\', sans-serif'
+ }
+ }
+ },
+ {
+ title: 'Lucida Console',
+ model: 'Lucida Console',
+ view: {
+ name: 'span',
+ style: {
+ 'font-family': '\'Lucida Console\', \'Courier New\', Courier, monospace'
+ }
+ }
+ }
+ ] );
+ } );
+ } );
+ } );
+} );
diff --git a/tests/fontsize.js b/tests/fontsize.js
new file mode 100644
index 0000000..f7f42d1
--- /dev/null
+++ b/tests/fontsize.js
@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import FontSize from './../src/fontsize';
+import FontSizeEditing from './../src/fontsize/fontsizeediting';
+import FontSizeUI from './../src/fontsize/fontsizeui';
+
+describe( 'FontSize', () => {
+ it( 'requires FontSizeEditing & FontSizeUI', () => {
+ expect( FontSize.requires ).to.deep.equal( [ FontSizeEditing, FontSizeUI ] );
+ } );
+
+ it( 'defines plugin name', () => {
+ expect( FontSize.pluginName ).to.equal( 'FontSize' );
+ } );
+} );
diff --git a/tests/fontsize/fontsizecommand.js b/tests/fontsize/fontsizecommand.js
new file mode 100644
index 0000000..6fa67a4
--- /dev/null
+++ b/tests/fontsize/fontsizecommand.js
@@ -0,0 +1,35 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import FontSizeCommand from '../../src/fontsize/fontsizecommand';
+import FontCommand from '../../src/fontcommand';
+
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+
+describe( 'FontSizeCommand', () => {
+ let editor, command;
+
+ beforeEach( () => {
+ return ModelTestEditor.create()
+ .then( newEditor => {
+ editor = newEditor;
+
+ command = new FontSizeCommand( editor );
+ } );
+ } );
+
+ afterEach( () => {
+ editor.destroy();
+ } );
+
+ it( 'is a FontCommand', () => {
+ expect( FontSizeCommand.prototype ).to.be.instanceOf( FontCommand );
+ expect( command ).to.be.instanceOf( FontCommand );
+ } );
+
+ it( 'operates on fontSize attribute', () => {
+ expect( command ).to.have.property( 'attributeKey', 'fontSize' );
+ } );
+} );
diff --git a/tests/fontsize/fontsizeediting.js b/tests/fontsize/fontsizeediting.js
new file mode 100644
index 0000000..8107966
--- /dev/null
+++ b/tests/fontsize/fontsizeediting.js
@@ -0,0 +1,220 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+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 '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+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( 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', () => {
+ describe( 'default value', () => {
+ it( 'should be set', () => {
+ expect( editor.config.get( 'fontSize.options' ) ).to.deep.equal( [ 'tiny', 'small', 'default', 'big', 'huge' ] );
+ } );
+ } );
+ } );
+
+ describe( 'editing pipeline conversion', () => {
+ beforeEach( () => {
+ return VirtualTestEditor
+ .create( {
+ plugins: [ FontSizeEditing, Paragraph ],
+ fontSize: {
+ options: [
+ 'tiny',
+ 'default',
+ 18,
+ {
+ title: 'My setting',
+ model: 'my',
+ view: {
+ name: 'mark',
+ style: { 'font-size': '30px' },
+ class: 'my-style'
+ }
+ }
+ ]
+ }
+ } )
+ .then( newEditor => {
+ editor = newEditor;
+
+ doc = editor.model;
+ } );
+ } );
+
+ it( 'should discard unknown fontSize attribute values', () => {
+ setModelData( doc, 'f<$text fontSize="foo-bar">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'foo
' );
+ } );
+
+ it( 'should convert fontSize attribute to predefined named preset', () => {
+ setModelData( doc, 'f<$text fontSize="tiny">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+
+ it( 'should convert fontSize attribute to predefined pixel size preset', () => {
+ setModelData( doc, 'f<$text fontSize="18">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+
+ it( 'should convert fontSize attribute from user defined settings', () => {
+ setModelData( doc, 'f<$text fontSize="my">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+ } );
+
+ describe( 'data pipeline conversions', () => {
+ beforeEach( () => {
+ return VirtualTestEditor
+ .create( {
+ plugins: [ FontSizeEditing, Paragraph ],
+ fontSize: {
+ options: [
+ 'tiny',
+ 'default',
+ 18,
+ {
+ title: 'My setting',
+ model: 'my',
+ view: {
+ name: 'mark',
+ style: { 'font-size': '30px' },
+ class: 'my-style'
+ }
+ },
+ {
+ title: 'Big multiple classes',
+ model: 'big-multiple',
+ view: {
+ name: 'span',
+ class: [ 'foo', 'foo-big' ]
+ }
+ },
+ {
+ title: 'Hybrid',
+ model: 'complex',
+ view: {
+ name: 'span',
+ class: [ 'text-complex' ]
+ },
+ upcastAlso: [
+ { name: 'span', style: { 'font-size': '77em' } },
+ { name: 'span', attribute: { 'data-size': '77em' } }
+ ]
+ }
+ ]
+ }
+ } )
+ .then( newEditor => {
+ editor = newEditor;
+
+ doc = editor.model;
+ } );
+ } );
+
+ it( 'should convert from element with defined class', () => {
+ const data = 'fo o
';
+
+ editor.setData( data );
+
+ expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="tiny">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+
+ it( 'should convert from element with defined multiple classes', () => {
+ const data = 'fo o
';
+
+ editor.setData( data );
+
+ expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="big-multiple">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+
+ it( 'should convert from element with defined style', () => {
+ const data = 'fo o
';
+
+ editor.setData( data );
+
+ expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="18">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( data );
+ } );
+
+ it( 'should convert from element with defined style when with other styles', () => {
+ const data = 'fo o
';
+
+ editor.setData( data );
+
+ expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="18">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( 'fo o
' );
+ } );
+
+ it( 'should convert from user defined element', () => {
+ const data = 'fo o
';
+
+ editor.setData( data );
+
+ expect( getModelData( doc ) ).to.equal( '[]f<$text fontSize="my">o$text>o ' );
+
+ expect( editor.getData() ).to.equal( data );
+ } );
+
+ it( 'should convert from complex definitions', () => {
+ editor.setData(
+ 'fo o
' +
+ 'ba r
' +
+ 'ba z
'
+ );
+
+ expect( getModelData( doc ) ).to.equal(
+ '[]f<$text fontSize="complex">o$text>o ' +
+ 'b<$text fontSize="complex">a$text>r ' +
+ 'b<$text fontSize="complex">a$text>z '
+ );
+
+ expect( editor.getData() ).to.equal(
+ 'fo o
' +
+ 'ba r
' +
+ 'ba z
'
+ );
+ } );
+ } );
+} );
diff --git a/tests/fontsize/fontsizeui.js b/tests/fontsize/fontsizeui.js
new file mode 100644
index 0000000..5787c04
--- /dev/null
+++ b/tests/fontsize/fontsizeui.js
@@ -0,0 +1,257 @@
+/**
+ * @license Copyright (c) 2003-2018, 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 '../../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',
+ 'Default': 'Default',
+ 'Tiny': 'Tiny',
+ 'Small': 'Small',
+ 'Big': 'Big',
+ 'Huge': 'Huge'
+ } );
+
+ addTranslations( 'pl', {
+ 'Font Size': 'Rozmiar czcionki',
+ 'Default': 'Domyślny',
+ 'Tiny': 'Tyci',
+ 'Small': 'Mały',
+ 'Big': 'Duży',
+ 'Huge': 'Ogromny'
+ } );
+ } );
+
+ after( () => {
+ clearTranslations();
+ } );
+
+ 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( 'label', 'Font Size' );
+ expect( button ).to.have.property( 'tooltip', true );
+ expect( button ).to.have.property( 'icon', fontSizeIcon );
+ } );
+
+ it( 'should add custom CSS class to dropdown', () => {
+ 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' );
+
+ dropdown.commandName = 'fontSize';
+ dropdown.fire( 'execute' );
+
+ sinon.assert.calledOnce( focusSpy );
+ } );
+
+ it( 'should activate current option in dropdown', () => {
+ const listView = dropdown.listView;
+
+ command.value = undefined;
+
+ // 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';
+
+ // 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;
+
+ expect( dropdown.buttonView.isEnabled ).to.be.false;
+
+ command.isEnabled = true;
+ expect( dropdown.buttonView.isEnabled ).to.be.true;
+ } );
+ } );
+
+ 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', 'default', '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 numerical values', () => {
+ beforeEach( () => {
+ element = document.createElement( 'div' );
+ document.body.appendChild( element );
+
+ return ClassicTestEditor
+ .create( element, {
+ plugins: [ FontSizeEditing, FontSizeUI ],
+ fontSize: {
+ options: [ 10, 12, 'default', 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', 'default', 'big', 'huge' ] );
+ } );
+
+ it( 'does not alter normalizeOptions() internals', () => {
+ 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: 'Default', model: undefined },
+ { title: 'Big', model: 'big', view: { name: 'span', class: 'text-big' } },
+ { title: 'Huge', model: 'huge', view: { name: 'span', class: 'text-huge' } }
+ ] );
+ } );
+
+ it( 'works for the #buttonView', () => {
+ const buttonView = dropdown.buttonView;
+
+ expect( buttonView.label ).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',
+ 'Domyślny',
+ '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();
+ } );
+ }
+ } );
+ } );
+} );
diff --git a/tests/fontsize/utils.js b/tests/fontsize/utils.js
new file mode 100644
index 0000000..94069a5
--- /dev/null
+++ b/tests/fontsize/utils.js
@@ -0,0 +1,52 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import { normalizeOptions } from '../../src/fontsize/utils';
+
+describe( 'FontSizeEditing Utils', () => {
+ describe( 'normalizeOptions()', () => {
+ it( 'should discard unsupported values', () => {
+ expect( normalizeOptions( [ () => {}, 'default', 'unknown' ] ) ).to.deep.equal( [ { title: 'Default', 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', '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: 'Default', model: undefined },
+ { title: 'Big', model: 'big', view: { name: 'span', class: 'text-big' } },
+ { title: 'Huge', model: 'huge', view: { name: 'span', class: 'text-huge' } }
+ ] );
+ } );
+ } );
+
+ 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' } } },
+ { title: '12', model: 12, view: { name: 'span', style: { 'font-size': '12px' } } },
+ { 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-family.html b/tests/manual/font-family.html
new file mode 100644
index 0000000..e35a8fd
--- /dev/null
+++ b/tests/manual/font-family.html
@@ -0,0 +1,45 @@
+
+
Font Family feature sample.
+
+
Arial, Helvetica, sans-serif:
+
+ 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:
+
+ Jujubes sweet wafer. Pastry cotton candy sweet muffin dessert cookie. Chocolate cake candy canes dragée wafer donut bear claw.
+
+
+
Georgia, serif:
+
+ Chocolate bar candy fruitcake fruitcake. Lollipop gingerbread pudding sweet roll biscuit halvah marzipan croissant soufflé.
+
+
+
Lucida Sans Unicode, Lucida Grande, sans-serif:
+
+ 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:
+
+ 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:
+
+ Jujubes candy lollipop. Tootsie roll cookie chocolate gummies cupcake sweet fruitcake oat cake danish.
+
+
+
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.
+
+
+
+
+
+ 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..396dd02
--- /dev/null
+++ b/tests/manual/font-family.js
@@ -0,0 +1,24 @@
+/**
+ * @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 FontFamily from '../../src/fontfamily';
+
+ClassicEditor
+ .create( document.querySelector( '#editor' ), {
+ plugins: [ ArticlePluginSet, FontFamily ],
+ toolbar: [
+ 'headings', '|', 'fontFamily', '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..bdd7ecb
--- /dev/null
+++ b/tests/manual/font-family.md
@@ -0,0 +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
+
+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-numeric.html b/tests/manual/font-size-numeric.html
new file mode 100644
index 0000000..6ef262a
--- /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 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
new file mode 100644
index 0000000..b6a4c72
--- /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, 'default', 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..bf60baf
--- /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, default, 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-presets.html b/tests/manual/font-size-presets.html
new file mode 100644
index 0000000..a757a47
--- /dev/null
+++ b/tests/manual/font-size-presets.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
+
+
+
+
+
+ Sample image with c r a zy caption.
+
+
+
diff --git a/tests/manual/font-size-presets.js b/tests/manual/font-size-presets.js
new file mode 100644
index 0000000..8af7685
--- /dev/null
+++ b/tests/manual/font-size-presets.js
@@ -0,0 +1,24 @@
+/**
+ * @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'
+ ]
+ } )
+ .then( editor => {
+ window.editor = editor;
+ } )
+ .catch( err => {
+ console.error( err.stack );
+ } );
diff --git a/tests/manual/font-size-presets.md b/tests/manual/font-size-presets.md
new file mode 100644
index 0000000..34c2488
--- /dev/null
+++ b/tests/manual/font-size-presets.md
@@ -0,0 +1,15 @@
+### 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
+
+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/sample.jpg b/tests/manual/sample.jpg
new file mode 100644
index 0000000..b77d07e
Binary files /dev/null and b/tests/manual/sample.jpg differ
diff --git a/theme/icons/font-family.svg b/theme/icons/font-family.svg
new file mode 100644
index 0000000..330647f
--- /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..bbae46a
--- /dev/null
+++ b/theme/icons/font-size.svg
@@ -0,0 +1 @@
+
\ No newline at end of file