8
0
Просмотр исходного кода

Merge tag 'v1.0.0-beta.1' into stable

Release: v1.0.0-beta.1.
Piotrek Koszuliński 7 лет назад
Родитель
Сommit
1e348c12f9
61 измененных файлов с 1516 добавлено и 1086 удалено
  1. 1 1
      packages/ckeditor5-heading/.eslintrc.js
  2. 15 11
      packages/ckeditor5-heading/.travis.yml
  3. 19 3
      packages/ckeditor5-heading/CHANGELOG.md
  4. 1 1
      packages/ckeditor5-heading/LICENSE.md
  5. 3 1
      packages/ckeditor5-heading/README.md
  6. 13 0
      packages/ckeditor5-heading/docs/_snippets/features/custom-heading-elements.html
  7. 39 0
      packages/ckeditor5-heading/docs/_snippets/features/custom-heading-elements.js
  8. 7 4
      packages/ckeditor5-heading/docs/_snippets/features/custom-heading-levels.js
  9. 59 8
      packages/ckeditor5-heading/docs/features/headings.md
  10. 6 6
      packages/ckeditor5-heading/lang/contexts.json
  11. 7 7
      packages/ckeditor5-heading/lang/translations/bg.po
  12. 7 7
      packages/ckeditor5-heading/lang/translations/da.po
  13. 7 7
      packages/ckeditor5-heading/lang/translations/de.po
  14. 7 7
      packages/ckeditor5-heading/lang/translations/el.po
  15. 7 7
      packages/ckeditor5-heading/lang/translations/en-au.po
  16. 7 7
      packages/ckeditor5-heading/lang/translations/en.po
  17. 7 7
      packages/ckeditor5-heading/lang/translations/eo.po
  18. 7 7
      packages/ckeditor5-heading/lang/translations/es.po
  19. 30 0
      packages/ckeditor5-heading/lang/translations/et.po
  20. 30 0
      packages/ckeditor5-heading/lang/translations/eu.po
  21. 7 7
      packages/ckeditor5-heading/lang/translations/fi.po
  22. 7 7
      packages/ckeditor5-heading/lang/translations/fr.po
  23. 7 7
      packages/ckeditor5-heading/lang/translations/gl.po
  24. 7 7
      packages/ckeditor5-heading/lang/translations/hr.po
  25. 7 7
      packages/ckeditor5-heading/lang/translations/hu.po
  26. 7 7
      packages/ckeditor5-heading/lang/translations/it.po
  27. 7 7
      packages/ckeditor5-heading/lang/translations/ja.po
  28. 7 7
      packages/ckeditor5-heading/lang/translations/km.po
  29. 7 7
      packages/ckeditor5-heading/lang/translations/kn.po
  30. 7 7
      packages/ckeditor5-heading/lang/translations/ko.po
  31. 7 7
      packages/ckeditor5-heading/lang/translations/ku.po
  32. 7 7
      packages/ckeditor5-heading/lang/translations/nb.po
  33. 30 0
      packages/ckeditor5-heading/lang/translations/nl.po
  34. 7 7
      packages/ckeditor5-heading/lang/translations/pl.po
  35. 7 7
      packages/ckeditor5-heading/lang/translations/pt-br.po
  36. 7 7
      packages/ckeditor5-heading/lang/translations/pt.po
  37. 7 7
      packages/ckeditor5-heading/lang/translations/ro.po
  38. 9 9
      packages/ckeditor5-heading/lang/translations/ru.po
  39. 7 7
      packages/ckeditor5-heading/lang/translations/sk.po
  40. 30 0
      packages/ckeditor5-heading/lang/translations/sq.po
  41. 7 7
      packages/ckeditor5-heading/lang/translations/sv.po
  42. 7 7
      packages/ckeditor5-heading/lang/translations/tr.po
  43. 30 0
      packages/ckeditor5-heading/lang/translations/ug.po
  44. 7 7
      packages/ckeditor5-heading/lang/translations/zh-cn.po
  45. 9 9
      packages/ckeditor5-heading/lang/translations/zh.po
  46. 16 15
      packages/ckeditor5-heading/package.json
  47. 31 150
      packages/ckeditor5-heading/src/heading.js
  48. 28 26
      packages/ckeditor5-heading/src/headingcommand.js
  49. 95 0
      packages/ckeditor5-heading/src/headingediting.js
  50. 0 101
      packages/ckeditor5-heading/src/headingengine.js
  51. 138 0
      packages/ckeditor5-heading/src/headingui.js
  52. 7 251
      packages/ckeditor5-heading/tests/heading.js
  53. 133 99
      packages/ckeditor5-heading/tests/headingcommand.js
  54. 166 0
      packages/ckeditor5-heading/tests/headingediting.js
  55. 0 127
      packages/ckeditor5-heading/tests/headingengine.js
  56. 283 0
      packages/ckeditor5-heading/tests/headingui.js
  57. 62 16
      packages/ckeditor5-heading/tests/integration.js
  58. 2 2
      packages/ckeditor5-heading/tests/manual/heading.js
  59. 8 8
      packages/ckeditor5-heading/tests/tickets/40.js
  60. 20 0
      packages/ckeditor5-heading/theme/heading.css
  61. 0 42
      packages/ckeditor5-heading/theme/theme.scss

+ 1 - 1
packages/ckeditor5-heading/.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.
  */
 

+ 15 - 11
packages/ckeditor5-heading/.travis.yml

@@ -1,24 +1,28 @@
 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
+env:
+  global:
+  - secure: OGymTwGxIFRhkUdU4o3NaEWRh+HkzQXy4TlHROj/ZPxmX67ptAgPwNxIrotckNzQuzHZPWNeTdrzqvBcyOAtvqyBA/MzDTTIHRs5KOqsyxge09H/x3wODGjdDmmwZ9eOLKERwtdDdx/3g8iTwjslLOMOGtCzmXlBWBlZ/r2cZMtrXUwkOR5gBxAiqTyOaEILJ1iQxSrJG2oZftuQaUKs1akbPDGRVKrB1rmx1lDJYtAFlB5lihqcFwlunr998PueGziGeHx9ars/7TULFvCNKf0UIQ9cT2FUPc9Ua/TnNLedAn4XPV2UpjrIBPIyNDah4gEv7v0GJoptJ6umc9NUdZLjkdBuunQrtjpyw6gvSMjarK/BT/IcLKMRuQXNGWLAGupe9TJu0VBu6mJK3ov9Btkya6NjQUa5OfDt6PrJTakqkDpR0Xz6EJVZv6FdRs/lXMZhH1lODL2I6qq18TUulbc7ZfexOLE/n+8vfrzG+xxr0dSnem6T1JmXuonelYWj5XcGEsnYpDsaMOoJzLOLeeaivOmvEVp1Q3aHXFRKC2iwuepqrpW8bR/rGpRPTqiNmD1uaj7MKq0Dlkz7P4tygstv5zDOw6Ef26z81wqNm/j2vkPscV3mL7nK2vTtpoXqBgtl5YR5QyHHK+esS/YWW5zxaOF5LTc91obqiDjtcck=
+  - secure: LayNm8w86mtAwrHSsWtwQkH6FWtgzUEoc6dETTFYSFEGIjvZoI+IDKuOXJQY+l6JGshbEhyYMmWbwujp17iIwGtxJkg0UZTDEqu24o+UvnygmSYXMulEnbX/DVUeZ/oN1bqLiQY3euqVKIFD35kG84l4p40Dl2FM4s4hEpztW5FC2vOOjTu3aj17Lwvky76qZgWj1Vfi83F2ZLeXM9dqFo7ZMwFzK2uukyzWnNa5fXPIMEIQ8fo7/ZdkiQ9+C+fy35wEy6PIpN03Bjoe+VOoNL//fM+I00Ua2u3ObcqJWrD514Y7pssccRl5Zne4PUnCwWxMtwlfdsyU5z59tzE9qygRZKUItmYeabjaxt1dtAY2MprBl2gaBjwkSG+ZzgOs4jfOHUiDMxE/8R+QSCTPF6koyfvN0iBglPye3tkGtUiLUeztdQph+m+rwkTB2FOVHH29wD7C1DfMwx1D1HdOkV+OZExbtubU3QD9rCzzGgtv77zL8PhbDZ2GKdUedywBVvHMGtqy4GMHNd+qedH5NfTM1VKS3Et3Qqh9NwiYC2S4LlzE3iw0MJREcSqo8f4WhNdJE/n8IJzJxhHpRuxlOahmRXCcuz6x3uIHKSnEa5mLBzf9KDDTFXVvWt+e1L5iI15UZ8JfyTE5E12LwNrdUa2zN517rS5UAenLoY5I7Tw=

+ 19 - 3
packages/ckeditor5-heading/CHANGELOG.md

@@ -1,6 +1,22 @@
 Changelog
 =========
 
+## [1.0.0-beta.1](https://github.com/ckeditor/ckeditor5-heading/compare/v1.0.0-alpha.2...v1.0.0-beta.1) (2018-03-15)
+
+### Other changes
+
+* Aligned feature class naming to the new scheme. ([511a9d8](https://github.com/ckeditor/ckeditor5-heading/commit/511a9d8))
+* Migrated package styles to PostCSS. Moved visual styles to `@ckeditor/ckeditor5-theme-lark` (see [ckeditor/ckeditor5-ui#144](https://github.com/ckeditor/ckeditor5-ui/issues/144)). ([965179e](https://github.com/ckeditor/ckeditor5-heading/commit/965179e))
+* Updated naming of UI components & commands. ([72ee3d6](https://github.com/ckeditor/ckeditor5-heading/commit/72ee3d6))
+
+### BREAKING CHANGES
+
+* Renamed the `'headings'` dropdown UI component to `'heading'`.
+* The `'heading1'`, `'heading2'` and `'heading3'` commands are no longer available. They were replaced by the `'heading'` command that accepts heading model element name as a value.
+* The `HeadingCommand#value` is no longer a boolean only. Now it stores a name of the heading model element when selection is inside a heading.
+* The `HeadingCommand` constructor's second parameter is now an array of supported model elements.
+
+
 ## [1.0.0-alpha.2](https://github.com/ckeditor/ckeditor5-heading/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) (2017-11-14)
 
 ### Other changes
@@ -47,12 +63,12 @@ Internal changes only (updated dependencies, documentation, etc.).
 
 ### Bug fixes
 
-* Changed the default heading drop-down title to a more meaningful one. Closes [#68](https://github.com/ckeditor/ckeditor5-heading/issues/68). Closes [#62](https://github.com/ckeditor/ckeditor5-heading/issues/62). ([1c16e96](https://github.com/ckeditor/ckeditor5-heading/commit/1c16e96)) and ([e58dadc](https://github.com/ckeditor/ckeditor5-heading/commit/e58dadc))
-* Drop-down should be inactive when none of the commands can be applied to the current selection. Closes [#66](https://github.com/ckeditor/ckeditor5-heading/issues/66). ([0ebd5cd](https://github.com/ckeditor/ckeditor5-heading/commit/0ebd5cd))
+* Changed the default heading dropdown title to a more meaningful one. Closes [#68](https://github.com/ckeditor/ckeditor5-heading/issues/68). Closes [#62](https://github.com/ckeditor/ckeditor5-heading/issues/62). ([1c16e96](https://github.com/ckeditor/ckeditor5-heading/commit/1c16e96)) and ([e58dadc](https://github.com/ckeditor/ckeditor5-heading/commit/e58dadc))
+* Dropdown should be inactive when none of the commands can be applied to the current selection. Closes [#66](https://github.com/ckeditor/ckeditor5-heading/issues/66). ([0ebd5cd](https://github.com/ckeditor/ckeditor5-heading/commit/0ebd5cd))
 
 ### Features
 
-* Active heading is marked in the drop-down list. Closes [#26](https://github.com/ckeditor/ckeditor5-heading/issues/26). ([39ba14b](https://github.com/ckeditor/ckeditor5-heading/commit/39ba14b))
+* Active heading is marked in the dropdown list. Closes [#26](https://github.com/ckeditor/ckeditor5-heading/issues/26). ([39ba14b](https://github.com/ckeditor/ckeditor5-heading/commit/39ba14b))
 * Enabled the tooltip for the 'headings' component in editor.ui#componentFactory. Closes [#55](https://github.com/ckeditor/ckeditor5-heading/issues/55). ([794e6df](https://github.com/ckeditor/ckeditor5-heading/commit/794e6df))
 * Named existing plugin(s). ([7d512cd](https://github.com/ckeditor/ckeditor5-heading/commit/7d512cd))
 * Split "heading" command into independent commands. Closes [#53](https://github.com/ckeditor/ckeditor5-heading/issues/53). Closes [#56](https://github.com/ckeditor/ckeditor5-heading/issues/56). Closes [#52](https://github.com/ckeditor/ckeditor5-heading/issues/52). ([7a8f6f0](https://github.com/ckeditor/ckeditor5-heading/commit/7a8f6f0))

+ 1 - 1
packages/ckeditor5-heading/LICENSE.md

@@ -2,7 +2,7 @@ Software License Agreement
 ==========================
 
 **CKEditor 5 Headings Feature** – https://github.com/ckeditor/ckeditor5-heading <br>
-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:
 

+ 3 - 1
packages/ckeditor5-heading/README.md

@@ -4,7 +4,9 @@ CKEditor 5 headings feature
 [![Join the chat at https://gitter.im/ckeditor/ckeditor5](https://badges.gitter.im/ckeditor/ckeditor5.svg)](https://gitter.im/ckeditor/ckeditor5?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-heading.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-heading)
 [![Build Status](https://travis-ci.org/ckeditor/ckeditor5-heading.svg?branch=master)](https://travis-ci.org/ckeditor/ckeditor5-heading)
-[![Test Coverage](https://codeclimate.com/github/ckeditor/ckeditor5-heading/badges/coverage.svg)](https://codeclimate.com/github/ckeditor/ckeditor5-heading/coverage)
+[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0)](https://www.browserstack.com/automate/public-build/d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0)
+[![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5-heading/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5-heading?branch=master)
+<br>
 [![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-heading/status.svg)](https://david-dm.org/ckeditor/ckeditor5-heading)
 [![devDependency Status](https://david-dm.org/ckeditor/ckeditor5-heading/dev-status.svg)](https://david-dm.org/ckeditor/ckeditor5-heading?type=dev)
 

+ 13 - 0
packages/ckeditor5-heading/docs/_snippets/features/custom-heading-elements.html

@@ -0,0 +1,13 @@
+<style>
+	h2.fancy, .ck-heading_heading2_fancy {
+		color: #ff0050;
+		font-size: 17px;
+	}
+</style>
+
+<div id="snippet-custom-heading-elements">
+	<h1>Heading 1</h1>
+	<h2>Heading 2</h2>
+	<h2 class="fancy">Fancy Heading 2</h2>
+	<p>This is <a href="https://ckeditor5.github.io">CKEditor 5</a>.</p>
+</div>

+ 39 - 0
packages/ckeditor5-heading/docs/_snippets/features/custom-heading-elements.js

@@ -0,0 +1,39 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals ClassicEditor, console, window, document */
+
+import { CS_CONFIG } from '@ckeditor/ckeditor5-cloudservices/tests/_utils/cloudservices-config';
+
+ClassicEditor
+	.create( document.querySelector( '#snippet-custom-heading-elements' ), {
+		cloudServices: CS_CONFIG,
+		heading: {
+			options: [
+				{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+				{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
+				{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
+				{
+					model: 'headingFancy',
+					view: {
+						name: 'h2',
+						class: 'fancy'
+					},
+					title: 'Heading 2 (fancy)',
+					class: 'ck-heading_heading2_fancy',
+					priority: 'high'
+				}
+			]
+		},
+		toolbar: {
+			viewportTopOffset: 60
+		}
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );

+ 7 - 4
packages/ckeditor5-heading/docs/_snippets/features/custom-heading-levels.js

@@ -1,17 +1,20 @@
 /**
- * @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.
  */
 
 /* globals ClassicEditor, console, window, document */
 
+import { CS_CONFIG } from '@ckeditor/ckeditor5-cloudservices/tests/_utils/cloudservices-config';
+
 ClassicEditor
 	.create( document.querySelector( '#snippet-custom-heading-levels' ), {
+		cloudServices: CS_CONFIG,
 		heading: {
 			options: [
-				{ modelElement: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
-				{ modelElement: 'heading1', viewElement: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
-				{ modelElement: 'heading2', viewElement: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }
+				{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+				{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
+				{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }
 			]
 		},
 		toolbar: {

+ 59 - 8
packages/ckeditor5-heading/docs/features/headings.md

@@ -25,7 +25,6 @@ It is, of course, possible to configure which heading levels the editor should s
 
 For example, the following editor will support only two levels of headings &mdash; `<h1>` and `<h2>`:
 
-
 ```html
 <div id="editor">
 	<h1>Heading 1</h1>
@@ -39,9 +38,9 @@ ClassicEditor
 	.create( document.querySelector( '#editor' ), {
 		heading: {
 			options: [
-				{ modelElement: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
-				{ modelElement: 'heading1', viewElement: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
-				{ modelElement: 'heading2', viewElement: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }
+				{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+				{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
+				{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }
 			]
 		}
 	} )
@@ -51,6 +50,58 @@ ClassicEditor
 
 {@snippet features/custom-heading-levels}
 
+### Configuring custom heading elements
+
+It is also possible to define fully custom elements for headings by using the {@link module:engine/view/elementdefinition~ElementDefinition advanced format} of the {@link module:heading/heading~HeadingConfig#options `heading.options`} configuration option.
+
+For example, the following editor will support the following two heading options at the same time: `<h2 class="fancy">` and `<h2>`:
+
+```html
+<style>
+	// Styles for the heading in the content and for the dropdown item.
+	h2.fancy, .ck-heading_heading2_fancy {
+		color: #ff0050;
+		font-size: 17px;
+	}
+</style>
+
+<div id="snippet-custom-heading-levels">
+	<h1>Heading 1</h1>
+	<h2>Heading 2</h2>
+	<h2 class="fancy">Fancy Heading 2</h2>
+	<p>This is <a href="https://ckeditor5.github.io">CKEditor 5</a>.</p>
+</div>
+```
+
+```js
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		heading: {
+			options: [
+				{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+				{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
+				{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
+				{
+					model: 'headingFancy',
+					view: {
+						name: 'h2',
+						class: 'fancy'
+					},
+					title: 'Heading 2 (fancy)',
+					class: 'ck-heading_heading2_fancy',
+
+					// It needs to be converted before the standard 'heading2'.
+					priority: 'high'
+				}
+			]
+		}
+	} )
+	.then( ... )
+	.catch( ... );
+```
+
+{@snippet features/custom-heading-elements}
+
 ## Installation
 
 <info-box info>
@@ -71,7 +122,7 @@ import Heading from '@ckeditor/ckeditor5-heading/src/heading';
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
 		plugins: [ Heading, ... ],
-		toolbar: [ 'headings', ... ]
+		toolbar: [ 'heading', ... ]
 	} )
 	.then( ... )
 	.catch( ... );
@@ -81,13 +132,13 @@ ClassicEditor
 
 The {@link module:heading/heading~Heading} plugin registers:
 
-* The `'headings'` dropdown.
-* The `'heading1'`, `'heading2'`, ..., `'headingN'` commands based on the {@link module:heading/heading~HeadingConfig#options `heading.options`} configuration option.
+* The `'heading'` dropdown.
+* The `'heading'` command that accepts value based on the {@link module:heading/heading~HeadingConfig#options `heading.options`} configuration option.
 
 	You can turn the currently selected block(s) to headings by executing one of these commands:
 
 	```js
-	editor.execute( 'heading2' );
+	editor.execute( 'heading', { value: 'heading2' } );
 	```
 
 ## Contribute

+ 6 - 6
packages/ckeditor5-heading/lang/contexts.json

@@ -1,8 +1,8 @@
 {
-	"Paragraph": "Drop-down option label for the paragraph format.",
-	"Heading": "Tooltip for the heading drop-down.",
-	"Choose heading": "Default label for the heading drop-down.",
-	"Heading 1": "Drop-down option label for the heading level 1 format.",
-	"Heading 2": "Drop-down option label for the heading level 2 format.",
-	"Heading 3": "Drop-down option label for the heading level 3 format."
+	"Paragraph": "Dropdown option label for the paragraph format.",
+	"Heading": "Tooltip for the heading dropdown.",
+	"Choose heading": "Default label for the heading dropdown.",
+	"Heading 1": "Dropdown option label for the heading level 1 format.",
+	"Heading 2": "Dropdown option label for the heading level 2 format.",
+	"Heading 3": "Dropdown option label for the heading level 3 format."
 }

+ 7 - 7
packages/ckeditor5-heading/lang/translations/bg.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: bg\n"
 "Language-Team: Bulgarian (https://www.transifex.com/ckeditor/teams/11143/bg/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Параграф"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr ""
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr ""
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr ""
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr ""
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr ""

+ 7 - 7
packages/ckeditor5-heading/lang/translations/da.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: da\n"
 "Language-Team: Danish (https://www.transifex.com/ckeditor/teams/11143/da/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Formattering"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Overskrift"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Vælg overskrift"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Overskrift 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Overskrift 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Overskrift 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/de.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: de\n"
 "Language-Team: German (https://www.transifex.com/ckeditor/teams/11143/de/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Absatz"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Überschrift"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Überschrift auswählen"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Überschrift 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Überschrift 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Überschrift 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/el.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: el\n"
 "Language-Team: Greek (https://www.transifex.com/ckeditor/teams/11143/el/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Παράγραφος"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Κεφαλίδα"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Επιλέξτε κεφαλίδα"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Κεφαλίδα 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Κεφαλίδα 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Κεφαλίδα 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/en-au.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: en_AU\n"
 "Language-Team: English (Australia) (https://www.transifex.com/ckeditor/teams/11143/en_AU/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragraph"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Heading"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Choose heading"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Heading 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Heading 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Heading 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/en.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: \n"
 "Language-Team: \n"
 "Plural-Forms: \n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragraph"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Heading"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Choose heading"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Heading 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Heading 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Heading 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/eo.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: eo\n"
 "Language-Team: Esperanto (https://www.transifex.com/ckeditor/teams/11143/eo/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragrafo"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Ĉapo"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Elektu ĉapon"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Ĉapo 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Ĉapo 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Ĉapo 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/es.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: es\n"
 "Language-Team: Spanish (https://www.transifex.com/ckeditor/teams/11143/es/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Párrafo"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Encabezado"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Elegir Encabezado"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Encabezado 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Encabezado 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Encabezado 3"

+ 30 - 0
packages/ckeditor5-heading/lang/translations/et.po

@@ -0,0 +1,30 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: et\n"
+"Language-Team: Estonian (https://www.transifex.com/ckeditor/teams/11143/et/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Dropdown option label for the paragraph format."
+msgid "Paragraph"
+msgstr "Lõik"
+
+msgctxt "Tooltip for the heading dropdown."
+msgid "Heading"
+msgstr "Pealkiri"
+
+msgctxt "Default label for the heading dropdown."
+msgid "Choose heading"
+msgstr "Vali pealkiri"
+
+msgctxt "Dropdown option label for the heading level 1 format."
+msgid "Heading 1"
+msgstr "Pealkiri 1"
+
+msgctxt "Dropdown option label for the heading level 2 format."
+msgid "Heading 2"
+msgstr "Pealkiri 2"
+
+msgctxt "Dropdown option label for the heading level 3 format."
+msgid "Heading 3"
+msgstr "Pealkiri 3"

+ 30 - 0
packages/ckeditor5-heading/lang/translations/eu.po

@@ -0,0 +1,30 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: eu\n"
+"Language-Team: Basque (https://www.transifex.com/ckeditor/teams/11143/eu/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Dropdown option label for the paragraph format."
+msgid "Paragraph"
+msgstr "Paragrafoa"
+
+msgctxt "Tooltip for the heading dropdown."
+msgid "Heading"
+msgstr "Izenburua"
+
+msgctxt "Default label for the heading dropdown."
+msgid "Choose heading"
+msgstr "Aukeratu izenburua"
+
+msgctxt "Dropdown option label for the heading level 1 format."
+msgid "Heading 1"
+msgstr "Izenburua 1"
+
+msgctxt "Dropdown option label for the heading level 2 format."
+msgid "Heading 2"
+msgstr "Izenburua 2"
+
+msgctxt "Dropdown option label for the heading level 3 format."
+msgid "Heading 3"
+msgstr "Izenburua 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/fi.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: fi\n"
 "Language-Team: Finnish (https://www.transifex.com/ckeditor/teams/11143/fi/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Kappale"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Otsikkotyyli"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Valitse tyyli"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Otsikko 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Otsikko 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Otsikko 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/fr.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: fr\n"
 "Language-Team: French (https://www.transifex.com/ckeditor/teams/11143/fr/)\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragraphe"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "En-tête"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Choisir le titre"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Titre 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Titre 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Titre 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/gl.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: gl\n"
 "Language-Team: Galician (https://www.transifex.com/ckeditor/teams/11143/gl/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Parágrafo"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Título"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Escolla o título"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Título 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Título 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Título 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/hr.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: hr\n"
 "Language-Team: Croatian (https://www.transifex.com/ckeditor/teams/11143/hr/)\n"
 "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragraf"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Naslov"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Odaberite naslov"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Naslov 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Naslov 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Naslov 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/hu.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: hu\n"
 "Language-Team: Hungarian (https://www.transifex.com/ckeditor/teams/11143/hu/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Bekezdés"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Stílusok"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Stílus megadása"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Címsor 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Címsor 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Címsor 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/it.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: it\n"
 "Language-Team: Italian (https://www.transifex.com/ckeditor/teams/11143/it/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragrafo"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Intestazione"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Seleziona intestazione"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Intestazione 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Intestazione 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Intestazione 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/ja.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ja\n"
 "Language-Team: Japanese (https://www.transifex.com/ckeditor/teams/11143/ja/)\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "パラグラフ"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "見出し"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "見出しを選択"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "見出し1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "見出し2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "見出し3 "

+ 7 - 7
packages/ckeditor5-heading/lang/translations/km.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: km\n"
 "Language-Team: Khmer (https://www.transifex.com/ckeditor/teams/11143/km/)\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "កថាខណ្ឌ"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "ក្បាលអត្ថបទ"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "ជ្រើសរើស​ក្បាលអត្ថបទ"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "ក្បាលអត្ថបទ 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "ក្បាលអត្ថបទ 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "ក្បាលអត្ថបទ 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/kn.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: kn\n"
 "Language-Team: Kannada (https://www.transifex.com/ckeditor/teams/11143/kn/)\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "ಪ್ಯಾರಾಗ್ರಾಫ್"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "ಶೀರ್ಷಿಕೆ"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "ಶೀರ್ಷಿಕೆ ಆಯ್ಕೆಮಾಡು"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "ಶೀರ್ಷಿಕೆ 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "ಶೀರ್ಷಿಕೆ 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "ಶೀರ್ಷಿಕೆ 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/ko.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ko\n"
 "Language-Team: Korean (https://www.transifex.com/ckeditor/teams/11143/ko/)\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "문단"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "제목"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "제목 선택"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "제목1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "제목2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "제목3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/ku.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ku\n"
 "Language-Team: Kurdish (https://www.transifex.com/ckeditor/teams/11143/ku/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "پەراگراف"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "سەرنووسە"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "سەرنووسە هەڵبژێرە"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "سەرنووسەی 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "سەرنووسەی 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "سەرنووسەی 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/nb.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: nb\n"
 "Language-Team: Norwegian Bokmål (https://www.transifex.com/ckeditor/teams/11143/nb/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Avsnitt"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Overskrift"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Velg overskrift"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Overskrift 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Overskrift 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Overskrift 3"

+ 30 - 0
packages/ckeditor5-heading/lang/translations/nl.po

@@ -0,0 +1,30 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: nl\n"
+"Language-Team: Dutch (https://www.transifex.com/ckeditor/teams/11143/nl/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Dropdown option label for the paragraph format."
+msgid "Paragraph"
+msgstr "Paragraaf"
+
+msgctxt "Tooltip for the heading dropdown."
+msgid "Heading"
+msgstr "Koppen"
+
+msgctxt "Default label for the heading dropdown."
+msgid "Choose heading"
+msgstr "Kies kop"
+
+msgctxt "Dropdown option label for the heading level 1 format."
+msgid "Heading 1"
+msgstr "Kop 1"
+
+msgctxt "Dropdown option label for the heading level 2 format."
+msgid "Heading 2"
+msgstr "Kop 2"
+
+msgctxt "Dropdown option label for the heading level 3 format."
+msgid "Heading 3"
+msgstr "Kop 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/pl.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: pl\n"
 "Language-Team: Polish (https://www.transifex.com/ckeditor/teams/11143/pl/)\n"
 "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Akapit"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Nagłówek"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Wybierz nagłówek"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Nagłówek 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Nagłówek 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Nagłówek 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/pt-br.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: pt_BR\n"
 "Language-Team: Portuguese (Brazil) (https://www.transifex.com/ckeditor/teams/11143/pt_BR/)\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Parágrafo"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Titulo"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Escolha o título"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Título 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Título 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Título 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/pt.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: pt\n"
 "Language-Team: Portuguese (https://www.transifex.com/ckeditor/teams/11143/pt/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Parágrafo"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Título"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr ""
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Título 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Título 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Título 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/ro.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ro\n"
 "Language-Team: Romanian (https://www.transifex.com/ckeditor/teams/11143/ro/)\n"
 "Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragraf"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Titlu rubrică"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Alege titlu rubrică"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Titlu rubrică 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Titlu rubrică 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Titlu rubrică 3"

+ 9 - 9
packages/ckeditor5-heading/lang/translations/ru.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ru\n"
 "Language-Team: Russian (https://www.transifex.com/ckeditor/teams/11143/ru/)\n"
 "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
-msgstr ""
+msgstr "Параграф"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Заголовок"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
-msgstr ""
+msgstr "Выбрать заголовок"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Заголовок 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Заголовок 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Заголовок 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/sk.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: sk\n"
 "Language-Team: Slovak (https://www.transifex.com/ckeditor/teams/11143/sk/)\n"
 "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragraf"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Nadpis"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Vyberte nadpis"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Nadpis 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Nadpis 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Nadpis 3"

+ 30 - 0
packages/ckeditor5-heading/lang/translations/sq.po

@@ -0,0 +1,30 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: sq\n"
+"Language-Team: Albanian (https://www.transifex.com/ckeditor/teams/11143/sq/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Dropdown option label for the paragraph format."
+msgid "Paragraph"
+msgstr "Paragrafi"
+
+msgctxt "Tooltip for the heading dropdown."
+msgid "Heading"
+msgstr "Nëntitulli"
+
+msgctxt "Default label for the heading dropdown."
+msgid "Choose heading"
+msgstr "Përzgjidh nëntitullin"
+
+msgctxt "Dropdown option label for the heading level 1 format."
+msgid "Heading 1"
+msgstr "Nëntitulli 1"
+
+msgctxt "Dropdown option label for the heading level 2 format."
+msgid "Heading 2"
+msgstr "Nëntitulli 2"
+
+msgctxt "Dropdown option label for the heading level 3 format."
+msgid "Heading 3"
+msgstr "Nëntitulli 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/sv.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: sv\n"
 "Language-Team: Swedish (https://www.transifex.com/ckeditor/teams/11143/sv/)\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragraf"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Rubrik"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Välj rubrik"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "Rubrik 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "Rubrik 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "Rubrik 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/tr.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: tr\n"
 "Language-Team: Turkish (https://www.transifex.com/ckeditor/teams/11143/tr/)\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "Paragraf"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "Başlık"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "Başlık tipi seç"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "1. Seviye Başlık"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "2. Seviye Başlık"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "3. Seviye Başlık"

+ 30 - 0
packages/ckeditor5-heading/lang/translations/ug.po

@@ -0,0 +1,30 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: ug\n"
+"Language-Team: Uighur (https://www.transifex.com/ckeditor/teams/11143/ug/)\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Dropdown option label for the paragraph format."
+msgid "Paragraph"
+msgstr "بۆلەك"
+
+msgctxt "Tooltip for the heading dropdown."
+msgid "Heading"
+msgstr "تېما"
+
+msgctxt "Default label for the heading dropdown."
+msgid "Choose heading"
+msgstr "تېما تاللاش"
+
+msgctxt "Dropdown option label for the heading level 1 format."
+msgid "Heading 1"
+msgstr "تېما 1"
+
+msgctxt "Dropdown option label for the heading level 2 format."
+msgid "Heading 2"
+msgstr "تېما 2"
+
+msgctxt "Dropdown option label for the heading level 3 format."
+msgid "Heading 3"
+msgstr "تېما 3"

+ 7 - 7
packages/ckeditor5-heading/lang/translations/zh-cn.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: zh_CN\n"
 "Language-Team: Chinese (China) (https://www.transifex.com/ckeditor/teams/11143/zh_CN/)\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "段落"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "标题"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "标题类型"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "标题 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "标题 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "标题 3"

+ 9 - 9
packages/ckeditor5-heading/lang/translations/zh.po

@@ -1,30 +1,30 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
-"Language: zh_TW\n"
-"Language-Team: Chinese (Taiwan) (https://www.transifex.com/ckeditor/teams/11143/zh_TW/)\n"
+"Language: zh\n"
+"Language-Team: Chinese (https://www.transifex.com/ckeditor/teams/11143/zh/)\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-msgctxt "Drop-down option label for the paragraph format."
+msgctxt "Dropdown option label for the paragraph format."
 msgid "Paragraph"
 msgstr "段落"
 
-msgctxt "Tooltip for the heading drop-down."
+msgctxt "Tooltip for the heading dropdown."
 msgid "Heading"
 msgstr "標題"
 
-msgctxt "Default label for the heading drop-down."
+msgctxt "Default label for the heading dropdown."
 msgid "Choose heading"
 msgstr "選取標題"
 
-msgctxt "Drop-down option label for the heading level 1 format."
+msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgstr "標題 1"
 
-msgctxt "Drop-down option label for the heading level 2 format."
+msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgstr "標題 2"
 
-msgctxt "Drop-down option label for the heading level 3 format."
+msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgstr "標題 3"

+ 16 - 15
packages/ckeditor5-heading/package.json

@@ -1,29 +1,30 @@
 {
   "name": "@ckeditor/ckeditor5-heading",
-  "version": "1.0.0-alpha.2",
+  "version": "1.0.0-beta.1",
   "description": "Headings feature for CKEditor 5.",
   "keywords": [
     "ckeditor5",
     "ckeditor5-feature"
   ],
   "dependencies": {
-    "@ckeditor/ckeditor5-core": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-ui": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-utils": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-engine": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-paragraph": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-theme-lark": "^1.0.0-alpha.2"
+    "@ckeditor/ckeditor5-core": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-ui": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-utils": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-engine": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-paragraph": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-theme-lark": "^1.0.0-beta.1"
   },
   "devDependencies": {
-    "@ckeditor/ckeditor5-editor-classic": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-enter": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-image": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-typing": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-undo": "^1.0.0-alpha.2",
-    "eslint": "^4.8.0",
-    "eslint-config-ckeditor5": "^1.0.6",
+    "@ckeditor/ckeditor5-cloudservices": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-editor-classic": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-enter": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-image": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-typing": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-undo": "^1.0.0-beta.1",
+    "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",

+ 31 - 150
packages/ckeditor5-heading/src/heading.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.
  */
 
@@ -7,18 +7,17 @@
  * @module heading/heading
  */
 
-import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-import HeadingEngine from './headingengine';
+import HeadingEditing from './headingediting';
+import HeadingUI from './headingui';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import Model from '@ckeditor/ckeditor5-ui/src/model';
-import createListDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/list/createlistdropdown';
-import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 
-import '../theme/theme.scss';
+import '../theme/heading.css';
 
 /**
- * The headings feature. It introduces the `headings` drop-down and the `heading1`-`headingN` commands which allow
- * to convert paragraphs into headings.
+ * The headings feature.
+ *
+ * It loads the {@link module:heading/headingediting~HeadingEditing heading editing feature}
+ * and {@link module:heading/headingui~HeadingUI heading UI feature}.
  *
  * For a detailed overview, check the {@glink features/headings Headings feature documentation}.
  *
@@ -29,7 +28,7 @@ export default class Heading extends Plugin {
 	 * @inheritDoc
 	 */
 	static get requires() {
-		return [ Paragraph, HeadingEngine ];
+		return [ HeadingEditing, HeadingUI ];
 	}
 
 	/**
@@ -38,141 +37,10 @@ export default class Heading extends Plugin {
 	static get pluginName() {
 		return 'Heading';
 	}
-
-	/**
-	 * @inheritDoc
-	 */
-	init() {
-		const editor = this.editor;
-		const dropdownItems = new Collection();
-		const options = this._getLocalizedOptions();
-		const commands = [];
-		const t = editor.t;
-		const defaultTitle = t( 'Choose heading' );
-		const dropdownTooltip = t( 'Heading' );
-
-		for ( const option of options ) {
-			const command = editor.commands.get( option.modelElement );
-			const itemModel = new Model( {
-				commandName: option.modelElement,
-				label: option.title,
-				class: option.class
-			} );
-
-			itemModel.bind( 'isActive' ).to( command, 'value' );
-
-			// Add the option to the collection.
-			dropdownItems.add( itemModel );
-
-			commands.push( command );
-		}
-
-		// Create dropdown model.
-		const dropdownModel = new Model( {
-			withText: true,
-			items: dropdownItems,
-			tooltip: dropdownTooltip
-		} );
-
-		dropdownModel.bind( 'isEnabled' ).to(
-			// Bind to #isEnabled of each command...
-			...getCommandsBindingTargets( commands, 'isEnabled' ),
-			// ...and set it true if any command #isEnabled is true.
-			( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled )
-		);
-
-		dropdownModel.bind( 'label' ).to(
-			// Bind to #value of each command...
-			...getCommandsBindingTargets( commands, 'value' ),
-			// ...and chose the title of the first one which #value is true.
-			( ...areActive ) => {
-				const index = areActive.findIndex( value => value );
-
-				// If none of the commands is active, display default title.
-				return options[ index ] ? options[ index ].title : defaultTitle;
-			}
-		);
-
-		// Register UI component.
-		editor.ui.componentFactory.add( 'headings', locale => {
-			const dropdown = createListDropdown( dropdownModel, locale );
-
-			dropdown.extendTemplate( {
-				attributes: {
-					class: [
-						'ck-heading-dropdown'
-					]
-				}
-			} );
-
-			// Execute command when an item from the dropdown is selected.
-			this.listenTo( dropdown, 'execute', evt => {
-				editor.execute( evt.source.commandName );
-				editor.editing.view.focus();
-			} );
-
-			return dropdown;
-		} );
-	}
-
-	/**
-	 * Returns heading options as defined in `config.heading.options` but processed to consider
-	 * editor localization, i.e. to display {@link module:heading/heading~HeadingOption}
-	 * in the correct language.
-	 *
-	 * Note: The reason behind this method is that there's no way to use {@link module:utils/locale~Locale#t}
-	 * when the user config is defined because the editor does not exist yet.
-	 *
-	 * @private
-	 * @returns {Array.<module:heading/heading~HeadingOption>}.
-	 */
-	_getLocalizedOptions() {
-		const editor = this.editor;
-		const t = editor.t;
-		const localizedTitles = {
-			Paragraph: t( 'Paragraph' ),
-			'Heading 1': t( 'Heading 1' ),
-			'Heading 2': t( 'Heading 2' ),
-			'Heading 3': t( 'Heading 3' )
-		};
-
-		return editor.config.get( 'heading.options' ).map( option => {
-			const title = localizedTitles[ option.title ];
-
-			if ( title && title != option.title ) {
-				// Clone the option to avoid altering the original `config.heading.options`.
-				option = Object.assign( {}, option, { title } );
-			}
-
-			return option;
-		} );
-	}
 }
 
-// Returns an array of binding components for
-// {@link module:utils/observablemixin~Observable#bind} from a set of iterable
-// commands.
-//
-// @private
-// @param {Iterable.<module:core/command~Command>} commands
-// @param {String} attribute
-// @returns {Array.<String>}
-function getCommandsBindingTargets( commands, attribute ) {
-	return Array.prototype.concat( ...commands.map( c => [ c, attribute ] ) );
-}
-
-/**
- * Heading option descriptor.
- *
- * @typedef {Object} module:heading/heading~HeadingOption
- * @property {String} modelElement Element's name in the model.
- * @property {String} viewElement The name of the view element that will be used to represent the model element in the view.
- * @property {String} title The user-readable title of the option.
- * @property {String} class The class which will be added to the dropdown item representing this option.
- */
-
 /**
- * The configuration of the heading feature. Introduced by the {@link module:heading/headingengine~HeadingEngine} feature.
+ * The configuration of the heading feature. Introduced by the {@link module:heading/headingediting~HeadingEditing} feature.
  *
  * Read more in {@link module:heading/heading~HeadingConfig}.
  *
@@ -181,7 +49,7 @@ function getCommandsBindingTargets( commands, attribute ) {
 
 /**
  * The configuration of the heading feature.
- * The option is used by the {@link module:heading/headingengine~HeadingEngine} feature.
+ * The option is used by the {@link module:heading/headingediting~HeadingEditing} feature.
  *
  *		ClassicEditor
  *			.create( {
@@ -202,10 +70,10 @@ function getCommandsBindingTargets( commands, attribute ) {
  *
  *		const headingConfig = {
  *			options: [
- *				{ modelElement: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
- *				{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
- *				{ modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
- *				{ modelElement: 'heading3', viewElement: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
+ *				{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+ *				{ model: 'heading1', view: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
+ *				{ model: 'heading2', view: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
+ *				{ model: 'heading3', view: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
  *			]
  *		};
  *
@@ -218,16 +86,29 @@ function getCommandsBindingTargets( commands, attribute ) {
  * Usually, the first option in the headings dropdown is the "Paragraph" option, hence it's also defined on the list.
  * However, you don't need to define its view representation because it's handled by
  * the {@link module:paragraph/paragraph~Paragraph} feature (which is required by
- * the {@link module:heading/headingengine~HeadingEngine} feature).
+ * the {@link module:heading/headingediting~HeadingEditing} feature).
+ *
+ * You can **read more** about configuring heading levels and **see more examples** in
+ * the {@glink features/headings Headings} guide.
  *
  * Note: In the model you should always start from `heading1`, regardless of how the headings are represented in the view.
  * That's assumption is used by features like {@link module:autoformat/autoformat~Autoformat} to know which element
  * they should use when applying the first level heading.
  *
- * The defined headings are also available in {@link module:core/commandcollection~CommandCollection} under their model names.
+ * The defined headings are also available as values passed to `heading` command under their model names.
  * For example, the below code will apply `<heading1>` to the current selection:
  *
- *		editor.execute( 'heading1' );
+ *		editor.execute( 'heading', { value: 'heading1' } );
  *
  * @member {Array.<module:heading/heading~HeadingOption>} module:heading/heading~HeadingConfig#options
  */
+
+/**
+ * Heading option descriptor.
+ *
+ * @typedef {Object} module:heading/heading~HeadingOption
+ * @property {String} model Name of the model element to convert.
+ * @property {module:engine/view/elementdefinition~ElementDefinition} view Definition of a view element to convert from/to.
+ * @property {String} title The user-readable title of the option.
+ * @property {String} class The class which will be added to the dropdown item representing this option.
+ */

+ 28 - 26
packages/ckeditor5-heading/src/headingcommand.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.
  */
 
@@ -8,7 +8,6 @@
  */
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
-import Position from '@ckeditor/ckeditor5-engine/src/model/position';
 import first from '@ckeditor/ckeditor5-utils/src/first';
 
 /**
@@ -21,62 +20,68 @@ export default class HeadingCommand extends Command {
 	 * Creates an instance of the command.
 	 *
 	 * @param {module:core/editor/editor~Editor} editor Editor instance.
-	 * @param {String} modelElement Name of the element which this command will apply in the model.
+	 * @param {Array.<String>} modelElements Names of the element which this command can apply in the model.
 	 */
-	constructor( editor, modelElement ) {
+	constructor( editor, modelElements ) {
 		super( editor );
 
 		/**
-		 * Whether the selection starts in a heading of {@link #modelElement this level}.
+		 * If the selection starts in a heading (which {@link #modelElements is supported by this command})
+		 * the value is set to the name of that heading model element.
+		 * It is  set to `false` otherwise.
 		 *
 		 * @observable
 		 * @readonly
-		 * @member {Boolean} #value
+		 * @member {Boolean|String} #value
 		 */
 
 		/**
-		 * Unique identifier of the command, also element's name in the model.
+		 * Set of defined model's elements names that this command support.
 		 * See {@link module:heading/heading~HeadingOption}.
 		 *
 		 * @readonly
-		 * @member {String}
+		 * @member {Array.<String>}
 		 */
-		this.modelElement = modelElement;
+		this.modelElements = modelElements;
 	}
 
 	/**
 	 * @inheritDoc
 	 */
 	refresh() {
-		const block = first( this.editor.document.selection.getSelectedBlocks() );
+		const block = first( this.editor.model.document.selection.getSelectedBlocks() );
 
-		this.value = !!block && block.is( this.modelElement );
-		this.isEnabled = !!block && checkCanBecomeHeading( block, this.modelElement, this.editor.document.schema );
+		this.value = !!block && this.modelElements.includes( block.name ) && block.name;
+		this.isEnabled = !!block && this.modelElements.some( heading => checkCanBecomeHeading( block, heading, this.editor.model.schema ) );
 	}
 
 	/**
 	 * Executes the command. Applies the heading to the selected blocks or, if the first selected
 	 * block is a heading already, turns selected headings (of this level only) to paragraphs.
 	 *
+	 * @param {Object} options
+	 * @param {String} options.value Name of the element which this command will apply in the model.
 	 * @fires execute
-	 * @param {Object} [options] Options for executed command.
-	 * @param {module:engine/model/batch~Batch} [options.batch] Batch to collect all the change steps.
-	 * New batch will be created if this option is not set.
 	 */
 	execute( options = {} ) {
-		const editor = this.editor;
-		const document = editor.document;
+		const model = this.editor.model;
+		const document = model.document;
 
-		document.enqueueChanges( () => {
-			const batch = options.batch || document.batch();
+		const modelElement = options.value;
+
+		if ( !this.modelElements.includes( modelElement ) ) {
+			return;
+		}
+
+		model.change( writer => {
 			const blocks = Array.from( document.selection.getSelectedBlocks() )
 				.filter( block => {
-					return checkCanBecomeHeading( block, this.modelElement, document.schema );
+					return checkCanBecomeHeading( block, modelElement, model.schema );
 				} );
 
 			for ( const block of blocks ) {
-				if ( !block.is( this.modelElement ) ) {
-					batch.rename( block, this.modelElement );
+				if ( !block.is( modelElement ) ) {
+					writer.rename( block, modelElement );
 				}
 			}
 		} );
@@ -91,8 +96,5 @@ export default class HeadingCommand extends Command {
 // @param {module:engine/model/schema~Schema} schema The schema of the document.
 // @returns {Boolean}
 function checkCanBecomeHeading( block, heading, schema ) {
-	return schema.check( {
-		name: heading,
-		inside: Position.createBefore( block )
-	} );
+	return schema.checkChild( block.parent, heading ) && !schema.isObject( block );
 }

+ 95 - 0
packages/ckeditor5-heading/src/headingediting.js

@@ -0,0 +1,95 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module heading/headingediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import HeadingCommand from './headingcommand';
+
+const defaultModelElement = 'paragraph';
+
+/**
+ * The headings engine feature. It handles switching between block formats &ndash; headings and paragraph.
+ * This class represents the engine part of the heading feature. See also {@link module:heading/heading~Heading}.
+ * It introduces `heading1`-`headingN` commands which allow to convert paragraphs into headings.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class HeadingEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	constructor( editor ) {
+		super( editor );
+
+		editor.config.define( 'heading', {
+			options: [
+				{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+				{ model: 'heading1', view: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
+				{ model: 'heading2', view: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
+				{ model: 'heading3', view: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
+			]
+		} );
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ Paragraph ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const options = editor.config.get( 'heading.options' );
+
+		const modelElements = [];
+
+		for ( const option of options ) {
+			// Skip paragraph - it is defined in required Paragraph feature.
+			if ( option.model !== defaultModelElement ) {
+				// Schema.
+				editor.model.schema.register( option.model, {
+					inheritAllFrom: '$block'
+				} );
+
+				editor.conversion.elementToElement( option );
+
+				modelElements.push( option.model );
+			}
+		}
+
+		// Register the heading command for this option.
+		editor.commands.add( 'heading', new HeadingCommand( editor, modelElements ) );
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	afterInit() {
+		// If the enter command is added to the editor, alter its behavior.
+		// Enter at the end of a heading element should create a paragraph.
+		const editor = this.editor;
+		const enterCommand = editor.commands.get( 'enter' );
+		const options = editor.config.get( 'heading.options' );
+
+		if ( enterCommand ) {
+			this.listenTo( enterCommand, 'afterExecute', ( evt, data ) => {
+				const positionParent = editor.model.document.selection.getFirstPosition().parent;
+				const isHeading = options.some( option => positionParent.is( option.model ) );
+
+				if ( isHeading && !positionParent.is( defaultModelElement ) && positionParent.childCount === 0 ) {
+					data.writer.rename( positionParent, defaultModelElement );
+				}
+			} );
+		}
+	}
+}

+ 0 - 101
packages/ckeditor5-heading/src/headingengine.js

@@ -1,101 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module heading/headingengine
- */
-
-import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
-import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
-import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-import HeadingCommand from './headingcommand';
-
-const defaultModelElement = 'paragraph';
-
-/**
- * The headings engine feature. It handles switching between block formats &ndash; headings and paragraph.
- * This class represents the engine part of the heading feature. See also {@link module:heading/heading~Heading}.
- *
- * @extends module:core/plugin~Plugin
- */
-export default class HeadingEngine extends Plugin {
-	/**
-	 * @inheritDoc
-	 */
-	constructor( editor ) {
-		super( editor );
-
-		editor.config.define( 'heading', {
-			options: [
-				{ modelElement: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
-				{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
-				{ modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
-				{ modelElement: 'heading3', viewElement: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
-			]
-		} );
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	static get requires() {
-		return [ Paragraph ];
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	init() {
-		const editor = this.editor;
-		const data = editor.data;
-		const editing = editor.editing;
-		const options = editor.config.get( 'heading.options' );
-
-		for ( const option of options ) {
-			// Skip paragraph - it is defined in required Paragraph feature.
-			if ( option.modelElement !== defaultModelElement ) {
-				// Schema.
-				editor.document.schema.registerItem( option.modelElement, '$block' );
-
-				// Build converter from model to view for data and editing pipelines.
-				buildModelConverter().for( data.modelToView, editing.modelToView )
-					.fromElement( option.modelElement )
-					.toElement( option.viewElement );
-
-				// Build converter from view to model for data pipeline.
-				buildViewConverter().for( data.viewToModel )
-					.fromElement( option.viewElement )
-					.toElement( option.modelElement );
-
-				// Register the heading command for this option.
-				editor.commands.add( option.modelElement, new HeadingCommand( editor, option.modelElement ) );
-			}
-		}
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	afterInit() {
-		// If the enter command is added to the editor, alter its behavior.
-		// Enter at the end of a heading element should create a paragraph.
-		const editor = this.editor;
-		const enterCommand = editor.commands.get( 'enter' );
-		const options = editor.config.get( 'heading.options' );
-
-		if ( enterCommand ) {
-			this.listenTo( enterCommand, 'afterExecute', ( evt, data ) => {
-				const positionParent = editor.document.selection.getFirstPosition().parent;
-				const batch = data.batch;
-				const isHeading = options.some( option => positionParent.is( option.modelElement ) );
-
-				if ( isHeading && !positionParent.is( defaultModelElement ) && positionParent.childCount === 0 ) {
-					batch.rename( positionParent, defaultModelElement );
-				}
-			} );
-		}
-	}
-}

+ 138 - 0
packages/ckeditor5-heading/src/headingui.js

@@ -0,0 +1,138 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module heading/headingui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import Model from '@ckeditor/ckeditor5-ui/src/model';
+
+import { createDropdown, addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
+
+import Collection from '@ckeditor/ckeditor5-utils/src/collection';
+
+import '../theme/heading.css';
+
+/**
+ * The headings UI feature. It introduces the `headings` dropdown.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class HeadingUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+		const options = this._getLocalizedOptions();
+		const defaultTitle = t( 'Choose heading' );
+		const dropdownTooltip = t( 'Heading' );
+
+		// Register UI component.
+		editor.ui.componentFactory.add( 'heading', locale => {
+			const titles = {};
+			const dropdownItems = new Collection();
+
+			const headingCommand = editor.commands.get( 'heading' );
+			const paragraphCommand = editor.commands.get( 'paragraph' );
+
+			const commands = [ headingCommand ];
+
+			for ( const option of options ) {
+				const itemModel = new Model( {
+					label: option.title,
+					class: option.class
+				} );
+
+				if ( option.model === 'paragraph' ) {
+					itemModel.bind( 'isActive' ).to( paragraphCommand, 'value' );
+					itemModel.set( 'commandName', 'paragraph' );
+					commands.push( paragraphCommand );
+				} else {
+					itemModel.bind( 'isActive' ).to( headingCommand, 'value', value => value === option.model );
+					itemModel.set( {
+						commandName: 'heading',
+						commandValue: option.model
+					} );
+				}
+
+				// Add the option to the collection.
+				dropdownItems.add( itemModel );
+
+				titles[ option.model ] = option.title;
+			}
+
+			const dropdownView = createDropdown( locale );
+			addListToDropdown( dropdownView, dropdownItems );
+
+			dropdownView.buttonView.set( {
+				isOn: false,
+				withText: true,
+				tooltip: dropdownTooltip
+			} );
+
+			dropdownView.extendTemplate( {
+				attributes: {
+					class: [
+						'ck-heading-dropdown'
+					]
+				}
+			} );
+
+			dropdownView.bind( 'isEnabled' ).toMany( commands, 'isEnabled', ( ...areEnabled ) => {
+				return areEnabled.some( isEnabled => isEnabled );
+			} );
+
+			dropdownView.buttonView.bind( 'label' ).to( headingCommand, 'value', paragraphCommand, 'value', ( value, para ) => {
+				const whichModel = value || para && 'paragraph';
+				// If none of the commands is active, display default title.
+				return titles[ whichModel ] ? titles[ whichModel ] : defaultTitle;
+			} );
+
+			// Execute command when an item from the dropdown is selected.
+			this.listenTo( dropdownView, 'execute', evt => {
+				editor.execute( evt.source.commandName, evt.source.commandValue ? { value: evt.source.commandValue } : undefined );
+				editor.editing.view.focus();
+			} );
+
+			return dropdownView;
+		} );
+	}
+
+	/**
+	 * Returns heading options as defined in `config.heading.options` but processed to consider
+	 * editor localization, i.e. to display {@link module:heading/heading~HeadingOption}
+	 * in the correct language.
+	 *
+	 * Note: The reason behind this method is that there's no way to use {@link module:utils/locale~Locale#t}
+	 * when the user config is defined because the editor does not exist yet.
+	 *
+	 * @private
+	 * @returns {Array.<module:heading/heading~HeadingOption>}.
+	 */
+	_getLocalizedOptions() {
+		const editor = this.editor;
+		const t = editor.t;
+		const localizedTitles = {
+			Paragraph: t( 'Paragraph' ),
+			'Heading 1': t( 'Heading 1' ),
+			'Heading 2': t( 'Heading 2' ),
+			'Heading 3': t( 'Heading 3' )
+		};
+
+		return editor.config.get( 'heading.options' ).map( option => {
+			const title = localizedTitles[ option.title ];
+
+			if ( title && title != option.title ) {
+				// Clone the option to avoid altering the original `config.heading.options`.
+				option = Object.assign( {}, option, { title } );
+			}
+
+			return option;
+		} );
+	}
+}

+ 7 - 251
packages/ckeditor5-heading/tests/heading.js

@@ -1,262 +1,18 @@
 /**
- * @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.
  */
 
-/* globals document */
-
-import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 import Heading from '../src/heading';
-import HeadingEngine from '../src/headingengine';
-import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
-import { add } from '@ckeditor/ckeditor5-utils/src/translation-service';
-import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
-import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
-
-add( 'pl', {
-	'Choose heading': 'Wybierz nagłówek',
-	'Paragraph': 'Akapit',
-	'Heading': 'Nagłówek',
-	'Heading 1': 'Nagłówek 1',
-	'Heading 2': 'Nagłówek 2',
-} );
-
-testUtils.createSinonSandbox();
+import HeadingEditing from '../src/headingediting';
+import HeadingUI from '../src/headingui';
 
 describe( 'Heading', () => {
-	let editor, editorElement, dropdown;
-
-	beforeEach( () => {
-		editorElement = document.createElement( 'div' );
-		document.body.appendChild( editorElement );
-
-		return ClassicTestEditor
-			.create( editorElement, {
-				plugins: [ Heading ],
-				toolbar: [ 'heading' ]
-			} )
-			.then( newEditor => {
-				editor = newEditor;
-				dropdown = editor.ui.componentFactory.create( 'headings' );
-
-				// Set data so the commands will be enabled.
-				setData( editor.document, '<paragraph>f{}oo</paragraph>' );
-			} );
-	} );
-
-	afterEach( () => {
-		editorElement.remove();
-
-		return editor.destroy();
-	} );
-
-	it( 'should be loaded', () => {
-		expect( editor.plugins.get( Heading ) ).to.be.instanceOf( Heading );
-	} );
-
-	it( 'should load HeadingEngine', () => {
-		expect( editor.plugins.get( HeadingEngine ) ).to.be.instanceOf( HeadingEngine );
+	it( 'should require HeadingEditing and HeadingUI', () => {
+		expect( Heading.requires ).to.deep.equal( [ HeadingEditing, HeadingUI ] );
 	} );
 
-	describe( 'init()', () => {
-		it( 'should register options feature component', () => {
-			const dropdown = editor.ui.componentFactory.create( 'headings' );
-
-			expect( dropdown ).to.be.instanceOf( DropdownView );
-			expect( dropdown.buttonView.isEnabled ).to.be.true;
-			expect( dropdown.buttonView.isOn ).to.be.undefined;
-			expect( dropdown.buttonView.label ).to.equal( 'Paragraph' );
-			expect( dropdown.buttonView.tooltip ).to.equal( 'Heading' );
-		} );
-
-		it( 'should execute format command on model execute event', () => {
-			const executeSpy = testUtils.sinon.spy( editor, 'execute' );
-			const dropdown = editor.ui.componentFactory.create( 'headings' );
-
-			dropdown.commandName = 'paragraph';
-			dropdown.fire( 'execute' );
-
-			sinon.assert.calledOnce( executeSpy );
-			sinon.assert.calledWithExactly( executeSpy, 'paragraph' );
-		} );
-
-		it( 'should focus view after command execution', () => {
-			const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
-			const dropdown = editor.ui.componentFactory.create( 'headings' );
-
-			dropdown.commandName = 'paragraph';
-			dropdown.fire( 'execute' );
-
-			sinon.assert.calledOnce( focusSpy );
-		} );
-
-		it( 'should add custom CSS class to dropdown', () => {
-			const dropdown = editor.ui.componentFactory.create( 'headings' );
-
-			dropdown.render();
-
-			expect( dropdown.element.classList.contains( 'ck-heading-dropdown' ) ).to.be.true;
-		} );
-
-		describe( 'model to command binding', () => {
-			let commands;
-
-			beforeEach( () => {
-				commands = {};
-
-				editor.config.get( 'heading.options' ).forEach( ( { modelElement } ) => {
-					commands[ modelElement ] = editor.commands.get( modelElement );
-				} );
-			} );
-
-			it( 'isEnabled', () => {
-				for ( const name in commands ) {
-					commands[ name ].isEnabled = false;
-				}
-
-				expect( dropdown.buttonView.isEnabled ).to.be.false;
-
-				commands.heading2.isEnabled = true;
-				expect( dropdown.buttonView.isEnabled ).to.be.true;
-			} );
-
-			it( 'label', () => {
-				for ( const name in commands ) {
-					commands[ name ].value = false;
-				}
-
-				expect( dropdown.buttonView.label ).to.equal( 'Choose heading' );
-
-				commands.heading2.value = true;
-				expect( dropdown.buttonView.label ).to.equal( 'Heading 2' );
-			} );
-		} );
-
-		describe( 'localization', () => {
-			let commands, editor, dropdown;
-
-			beforeEach( () => {
-				return localizedEditor( [
-					{ modelElement: 'paragraph', title: 'Paragraph' },
-					{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' },
-					{ modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2' }
-				] );
-			} );
-
-			it( 'does not alter the original config', () => {
-				expect( editor.config.get( 'heading.options' ) ).to.deep.equal( [
-					{ modelElement: 'paragraph', title: 'Paragraph' },
-					{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' },
-					{ modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2' }
-				] );
-			} );
-
-			it( 'works for the #buttonView', () => {
-				const buttonView = dropdown.buttonView;
-
-				// Setting manually paragraph.value to `false` because there might be some content in editor
-				// after initialisation (for example empty <p></p> inserted when editor is empty).
-				commands.paragraph.value = false;
-				expect( buttonView.label ).to.equal( 'Wybierz nagłówek' );
-				expect( buttonView.tooltip ).to.equal( 'Nagłówek' );
-
-				commands.paragraph.value = true;
-				expect( buttonView.label ).to.equal( 'Akapit' );
-
-				commands.paragraph.value = false;
-				commands.heading1.value = true;
-				expect( buttonView.label ).to.equal( 'Nagłówek 1' );
-			} );
-
-			it( 'works for the listView#items in the panel', () => {
-				const listView = dropdown.listView;
-
-				expect( listView.items.map( item => item.label ) ).to.deep.equal( [
-					'Akapit',
-					'Nagłówek 1',
-					'Nagłówek 2'
-				] );
-			} );
-
-			it( 'allows custom titles', () => {
-				return localizedEditor( [
-					{ modelElement: 'paragraph', title: 'Custom paragraph title' },
-					{ modelElement: 'heading1', title: 'Custom heading1 title' }
-				] ).then( () => {
-					const listView = dropdown.listView;
-
-					expect( listView.items.map( item => item.label ) ).to.deep.equal( [
-						'Custom paragraph title',
-						'Custom heading1 title',
-					] );
-				} );
-			} );
-
-			it( 'translates default using the the locale', () => {
-				return localizedEditor( [
-					{ modelElement: 'paragraph', title: 'Paragraph' }
-				] ).then( () => {
-					const listView = dropdown.listView;
-
-					expect( listView.items.map( item => item.label ) ).to.deep.equal( [
-						'Akapit'
-					] );
-				} );
-			} );
-
-			function localizedEditor( options ) {
-				const editorElement = document.createElement( 'div' );
-				document.body.appendChild( editorElement );
-
-				return ClassicTestEditor
-					.create( editorElement, {
-						plugins: [ Heading ],
-						toolbar: [ 'heading' ],
-						lang: 'pl',
-						heading: {
-							options
-						}
-					} )
-					.then( newEditor => {
-						editor = newEditor;
-						dropdown = editor.ui.componentFactory.create( 'headings' );
-						commands = {};
-
-						editor.config.get( 'heading.options' ).forEach( ( { modelElement } ) => {
-							commands[ modelElement ] = editor.commands.get( modelElement );
-						} );
-
-						editorElement.remove();
-
-						return editor.destroy();
-					} );
-			}
-		} );
-
-		describe( 'class', () => {
-			it( 'is set for the listView#items in the panel', () => {
-				const listView = dropdown.listView;
-
-				expect( listView.items.map( item => item.class ) ).to.deep.equal( [
-					'ck-heading_paragraph',
-					'ck-heading_heading1',
-					'ck-heading_heading2',
-					'ck-heading_heading3'
-				] );
-			} );
-
-			it( 'reflects the #value of the commands', () => {
-				const listView = dropdown.listView;
-
-				setData( editor.document, '<heading2>f{}oo</heading2>' );
-
-				expect( listView.items.map( item => item.isActive ) ).to.deep.equal( [
-					false,
-					false,
-					true,
-					false
-				] );
-			} );
-		} );
+	it( 'should be named', () => {
+		expect( Heading.pluginName ).to.equal( 'Heading' );
 	} );
 } );

+ 133 - 99
packages/ckeditor5-heading/tests/headingcommand.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.
  */
 
@@ -10,46 +10,46 @@ import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
 const options = [
-	{ modelElement: 'heading1', viewElement: 'h2', title: 'H2' },
-	{ modelElement: 'heading2', viewElement: 'h3', title: 'H3' },
-	{ modelElement: 'heading3', viewElement: 'h4', title: 'H4' }
+	{ model: 'heading1', view: { name: 'h2' }, title: 'H2' },
+	{ model: 'heading2', view: { name: 'h3' }, title: 'H3' },
+	{ model: 'heading3', view: { name: 'h4' }, title: 'H4' }
 ];
 
 describe( 'HeadingCommand', () => {
-	let editor, document, commands, root, schema;
+	let editor, model, document, command, root, schema;
 
 	beforeEach( () => {
 		return ModelTestEditor.create().then( newEditor => {
 			editor = newEditor;
-			document = editor.document;
-			commands = {};
-			schema = document.schema;
+			model = editor.model;
+			document = model.document;
+			schema = model.schema;
 
 			editor.commands.add( 'paragraph', new ParagraphCommand( editor ) );
-			schema.registerItem( 'paragraph', '$block' );
+			schema.register( 'paragraph', { inheritAllFrom: '$block' } );
+
+			const modelElements = [];
 
 			for ( const option of options ) {
-				commands[ option.modelElement ] = new HeadingCommand( editor, option.modelElement );
-				schema.registerItem( option.modelElement, '$block' );
+				modelElements.push( option.model );
+				schema.register( option.model, { inheritAllFrom: '$block' } );
 			}
 
-			schema.registerItem( 'notBlock' );
-			schema.allow( { name: 'notBlock', inside: '$root' } );
-			schema.allow( { name: '$text', inside: 'notBlock' } );
+			command = new HeadingCommand( editor, modelElements );
+			editor.commands.add( 'heading', command );
+			schema.register( 'heading', { inheritAllFrom: '$block' } );
+
+			schema.register( 'notBlock' );
+			schema.extend( 'notBlock', { allowIn: '$root' } );
+			schema.extend( '$text', { allowIn: 'notBlock' } );
 
 			root = document.getRoot();
 		} );
 	} );
 
-	afterEach( () => {
-		for ( const modelElement in commands ) {
-			commands[ modelElement ].destroy();
-		}
-	} );
-
-	describe( 'modelElement', () => {
+	describe( 'modelElements', () => {
 		it( 'is set', () => {
-			expect( commands.heading1.modelElement ).to.equal( 'heading1' );
+			expect( command.modelElements ).to.deep.equal( [ 'heading1', 'heading2', 'heading3' ] );
 		} );
 	} );
 
@@ -58,98 +58,138 @@ describe( 'HeadingCommand', () => {
 			test( option );
 		}
 
-		function test( { modelElement } ) {
+		function test( { model: modelElement } ) {
 			it( `equals ${ modelElement } when collapsed selection is placed inside ${ modelElement } element`, () => {
-				setData( document, `<${ modelElement }>foobar</${ modelElement }>` );
+				setData( model, `<${ modelElement }>foobar</${ modelElement }>` );
 				const element = root.getChild( 0 );
-				document.selection.addRange( Range.createFromParentsAndOffsets( element, 3, element, 3 ) );
-
-				expect( commands[ modelElement ].value ).to.be.true;
+				model.change( writer => {
+					const ranges = [
+						...model.document.selection.getRanges(),
+						Range.createFromParentsAndOffsets( element, 3, element, 3 )
+					];
+					writer.setSelection( ranges );
+				} );
+				expect( command.value ).to.equal( modelElement );
 			} );
 
 			it( 'equals false if inside to non-block element', () => {
-				setData( document, '<notBlock>[foo]</notBlock>' );
+				setData( model, '<notBlock>[foo]</notBlock>' );
 
-				expect( commands[ modelElement ].value ).to.be.false;
+				expect( command.value ).to.be.false;
 			} );
 
 			it( `equals false if moved from ${ modelElement } to non-block element`, () => {
-				setData( document, `<${ modelElement }>[foo]</${ modelElement }><notBlock>foo</notBlock>` );
+				setData( model, `<${ modelElement }>[foo]</${ modelElement }><notBlock>foo</notBlock>` );
 				const element = document.getRoot().getChild( 1 );
 
-				document.enqueueChanges( () => {
-					document.selection.setRanges( [ Range.createIn( element ) ] );
+				model.change( writer => {
+					writer.setSelection( Range.createIn( element ) );
 				} );
 
-				expect( commands[ modelElement ].value ).to.be.false;
+				expect( command.value ).to.be.false;
 			} );
 
 			it( 'should be refreshed after calling refresh()', () => {
-				const command = commands[ modelElement ];
-				setData( document, `<${ modelElement }>[foo]</${ modelElement }><notBlock>foo</notBlock>` );
+				setData( model, `<${ modelElement }>[foo]</${ modelElement }><notBlock>foo</notBlock>` );
 				const element = document.getRoot().getChild( 1 );
 
-				// Purposely not putting it in `document.enqueueChanges` to update command manually.
-				document.selection.setRanges( [ Range.createIn( element ) ] );
+				model.change( writer => {
+					writer.setSelection( Range.createIn( element ) );
 
-				expect( command.value ).to.be.true;
-				command.refresh();
-				expect( command.value ).to.be.false;
+					expect( command.value ).to.equal( modelElement );
+					command.refresh();
+					expect( command.value ).to.be.false;
+				} );
 			} );
 		}
 	} );
 
 	describe( 'execute()', () => {
 		it( 'should update value after execution', () => {
-			const command = commands.heading1;
-
-			setData( document, '<paragraph>[]</paragraph>' );
-			command.execute();
+			setData( model, '<paragraph>[]</paragraph>' );
+			command.execute( { value: 'heading1' } );
 
-			expect( getData( document ) ).to.equal( '<heading1>[]</heading1>' );
-			expect( command.value ).to.be.true;
+			expect( getData( model ) ).to.equal( '<heading1>[]</heading1>' );
+			expect( command.value ).to.equal( 'heading1' );
 		} );
 
 		// https://github.com/ckeditor/ckeditor5-heading/issues/73
-		it( 'should not rename blocks which cannot become headings', () => {
-			document.schema.registerItem( 'restricted' );
-			document.schema.allow( { name: 'restricted', inside: '$root' } );
-			document.schema.disallow( { name: 'heading1', inside: 'restricted' } );
+		it( 'should not rename blocks which cannot become headings (heading is not allowed in their parent)', () => {
+			schema.register( 'restricted' );
+			schema.extend( 'restricted', { allowIn: '$root' } );
 
-			document.schema.registerItem( 'fooBlock', '$block' );
-			document.schema.allow( { name: 'fooBlock', inside: 'restricted' } );
+			schema.register( 'fooBlock', { inheritAllFrom: '$block' } );
+			schema.extend( 'fooBlock', {
+				allowIn: [ 'restricted', '$root' ]
+			} );
 
 			setData(
-				document,
+				model,
 				'<paragraph>a[bc</paragraph>' +
 				'<restricted><fooBlock></fooBlock></restricted>' +
-				'<paragraph>de]f</paragraph>'
+				'<fooBlock>de]f</fooBlock>'
 			);
 
-			commands.heading1.execute();
+			command.execute( { value: 'heading1' } );
 
-			expect( getData( document ) ).to.equal(
+			expect( getData( model ) ).to.equal(
 				'<heading1>a[bc</heading1>' +
 				'<restricted><fooBlock></fooBlock></restricted>' +
 				'<heading1>de]f</heading1>'
 			);
 		} );
 
-		describe( 'custom options', () => {
-			it( 'should use provided batch', () => {
-				const batch = editor.document.batch();
-				const command = commands.heading1;
+		it( 'should not rename blocks which cannot become headings (block is an object)', () => {
+			schema.register( 'image', {
+				isBlock: true,
+				isObject: true,
+				allowIn: '$root'
+			} );
 
-				setData( document, '<paragraph>foo[]bar</paragraph>' );
+			setData(
+				model,
+				'<paragraph>a[bc</paragraph>' +
+				'<image></image>' +
+				'<paragraph>de]f</paragraph>'
+			);
 
-				expect( batch.deltas.length ).to.equal( 0 );
+			command.execute( { value: 'heading1' } );
 
-				command.execute( { batch } );
+			expect( getData( model ) ).to.equal(
+				'<heading1>a[bc</heading1>' +
+				'<image></image>' +
+				'<heading1>de]f</heading1>'
+			);
+		} );
 
-				expect( batch.deltas.length ).to.be.above( 0 );
+		it( 'should use parent batch', () => {
+			setData( model, '<paragraph>foo[]bar</paragraph>' );
+
+			model.change( writer => {
+				expect( writer.batch.deltas.length ).to.equal( 0 );
+
+				command.execute( { value: 'heading1' } );
+
+				expect( writer.batch.deltas.length ).to.be.above( 0 );
 			} );
 		} );
 
+		it( 'should do nothing on non-registered model elements', () => {
+			setData( model, '<heading1>[]</heading1>' );
+			command.execute( { value: 'paragraph' } );
+
+			expect( getData( model ) ).to.equal( '<heading1>[]</heading1>' );
+			expect( command.value ).to.equal( 'heading1' );
+		} );
+
+		it( 'should do nothing when empty value is passed', () => {
+			setData( model, '<heading1>[]</heading1>' );
+			command.execute();
+
+			expect( getData( model ) ).to.equal( '<heading1>[]</heading1>' );
+			expect( command.value ).to.equal( 'heading1' );
+		} );
+
 		describe( 'collapsed selection', () => {
 			let convertTo = options[ options.length - 1 ];
 
@@ -159,28 +199,28 @@ describe( 'HeadingCommand', () => {
 			}
 
 			it( 'does nothing when executed with already applied option', () => {
-				setData( document, '<heading1>foo[]bar</heading1>' );
+				setData( model, '<heading1>foo[]bar</heading1>' );
 
-				commands.heading1.execute();
-				expect( getData( document ) ).to.equal( '<heading1>foo[]bar</heading1>' );
+				command.execute( { value: 'heading1' } );
+				expect( getData( model ) ).to.equal( '<heading1>foo[]bar</heading1>' );
 			} );
 
 			it( 'converts topmost blocks', () => {
-				schema.registerItem( 'inlineImage', '$inline' );
-				schema.allow( { name: '$text', inside: 'inlineImage' } );
+				schema.register( 'inlineImage', { allowWhere: '$text' } );
+				schema.extend( '$text', { allowIn: 'inlineImage' } );
 
-				setData( document, '<paragraph><inlineImage>foo[]</inlineImage>bar</paragraph>' );
-				commands.heading1.execute();
+				setData( model, '<paragraph><inlineImage>foo[]</inlineImage>bar</paragraph>' );
+				command.execute( { value: 'heading1' } );
 
-				expect( getData( document ) ).to.equal( '<heading1><inlineImage>foo[]</inlineImage>bar</heading1>' );
+				expect( getData( model ) ).to.equal( '<heading1><inlineImage>foo[]</inlineImage>bar</heading1>' );
 			} );
 
 			function test( from, to ) {
-				it( `converts ${ from.modelElement } to ${ to.modelElement } on collapsed selection`, () => {
-					setData( document, `<${ from.modelElement }>foo[]bar</${ from.modelElement }>` );
-					commands[ to.modelElement ].execute();
+				it( `converts ${ from.model } to ${ to.model } on collapsed selection`, () => {
+					setData( model, `<${ from.model }>foo[]bar</${ from.model }>` );
+					command.execute( { value: to.model } );
 
-					expect( getData( document ) ).to.equal( `<${ to.modelElement }>foo[]bar</${ to.modelElement }>` );
+					expect( getData( model ) ).to.equal( `<${ to.model }>foo[]bar</${ to.model }>` );
 				} );
 			}
 		} );
@@ -194,43 +234,43 @@ describe( 'HeadingCommand', () => {
 			}
 
 			it( 'converts all elements where selection is applied', () => {
-				setData( document, '<heading1>foo[</heading1><heading2>bar</heading2><heading3>baz]</heading3>' );
+				setData( model, '<heading1>foo[</heading1><heading2>bar</heading2><heading3>baz]</heading3>' );
 
-				commands.heading3.execute();
+				command.execute( { value: 'heading3' } );
 
-				expect( getData( document ) ).to.equal(
+				expect( getData( model ) ).to.equal(
 					'<heading3>foo[</heading3><heading3>bar</heading3><heading3>baz]</heading3>'
 				);
 			} );
 
 			it( 'does nothing to the elements with same option (#1)', () => {
-				setData( document, '<heading1>[foo</heading1><heading1>bar]</heading1>' );
-				commands.heading1.execute();
+				setData( model, '<heading1>[foo</heading1><heading1>bar]</heading1>' );
+				command.execute( { value: 'heading1' } );
 
-				expect( getData( document ) ).to.equal(
+				expect( getData( model ) ).to.equal(
 					'<heading1>[foo</heading1><heading1>bar]</heading1>'
 				);
 			} );
 
 			it( 'does nothing to the elements with same option (#2)', () => {
-				setData( document, '<heading1>[foo</heading1><heading1>bar</heading1><heading2>baz]</heading2>' );
-				commands.heading1.execute();
+				setData( model, '<heading1>[foo</heading1><heading1>bar</heading1><heading2>baz]</heading2>' );
+				command.execute( { value: 'heading1' } );
 
-				expect( getData( document ) ).to.equal(
+				expect( getData( model ) ).to.equal(
 					'<heading1>[foo</heading1><heading1>bar</heading1><heading1>baz]</heading1>'
 				);
 			} );
 
-			function test( { modelElement: fromElement }, { modelElement: toElement } ) {
+			function test( { model: fromElement }, { model: toElement } ) {
 				it( `converts ${ fromElement } to ${ toElement } on non-collapsed selection`, () => {
 					setData(
-						document,
+						model,
 						`<${ fromElement }>foo[bar</${ fromElement }><${ fromElement }>baz]qux</${ fromElement }>`
 					);
 
-					commands[ toElement ].execute();
+					command.execute( { value: toElement } );
 
-					expect( getData( document ) ).to.equal(
+					expect( getData( model ) ).to.equal(
 						`<${ toElement }>foo[bar</${ toElement }><${ toElement }>baz]qux</${ toElement }>`
 					);
 				} );
@@ -240,31 +280,25 @@ describe( 'HeadingCommand', () => {
 
 	describe( 'isEnabled', () => {
 		for ( const option of options ) {
-			test( option.modelElement );
+			test( option.model );
 		}
 
 		function test( modelElement ) {
-			let command;
-
-			beforeEach( () => {
-				command = commands[ modelElement ];
-			} );
-
 			describe( `${ modelElement } command`, () => {
 				it( 'should be enabled when inside another block', () => {
-					setData( document, '<paragraph>f{}oo</paragraph>' );
+					setData( model, '<paragraph>f{}oo</paragraph>' );
 
 					expect( command.isEnabled ).to.be.true;
 				} );
 
 				it( 'should be disabled if inside non-block', () => {
-					setData( document, '<notBlock>f{}oo</notBlock>' );
+					setData( model, '<notBlock>f{}oo</notBlock>' );
 
 					expect( command.isEnabled ).to.be.false;
 				} );
 
 				it( 'should be disabled if selection is placed on non-block', () => {
-					setData( document, '[<notBlock>foo</notBlock>]' );
+					setData( model, '[<notBlock>foo</notBlock>]' );
 
 					expect( command.isEnabled ).to.be.false;
 				} );

+ 166 - 0
packages/ckeditor5-heading/tests/headingediting.js

@@ -0,0 +1,166 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import HeadingEditing from '../src/headingediting';
+import HeadingCommand from '../src/headingcommand';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import ParagraphCommand from '@ckeditor/ckeditor5-paragraph/src/paragraphcommand';
+import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import { getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+describe( 'HeadingEditing', () => {
+	let editor, model;
+
+	beforeEach( () => {
+		return VirtualTestEditor
+			.create( {
+				plugins: [ HeadingEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+			} );
+	} );
+
+	it( 'should be loaded', () => {
+		expect( editor.plugins.get( HeadingEditing ) ).to.be.instanceOf( HeadingEditing );
+	} );
+
+	it( 'should load paragraph feature', () => {
+		expect( editor.plugins.get( Paragraph ) ).to.be.instanceOf( Paragraph );
+	} );
+
+	it( 'should set proper schema rules', () => {
+		expect( model.schema.isRegistered( 'heading1' ) ).to.be.true;
+		expect( model.schema.isRegistered( 'heading2' ) ).to.be.true;
+		expect( model.schema.isRegistered( 'heading3' ) ).to.be.true;
+
+		expect( model.schema.checkChild( [ '$root' ], 'heading1' ) ).to.be.true;
+		expect( model.schema.checkChild( [ 'heading1' ], '$text' ) ).to.be.true;
+
+		expect( model.schema.checkChild( [ '$root' ], 'heading2' ) ).to.be.true;
+		expect( model.schema.checkChild( [ 'heading2' ], '$text' ) ).to.be.true;
+
+		expect( model.schema.checkChild( [ '$root' ], 'heading3' ) ).to.be.true;
+		expect( model.schema.checkChild( [ 'heading3' ], '$text' ) ).to.be.true;
+	} );
+
+	it( 'should register #commands', () => {
+		expect( editor.commands.get( 'paragraph' ) ).to.be.instanceOf( ParagraphCommand );
+		expect( editor.commands.get( 'heading' ) ).to.be.instanceOf( HeadingCommand );
+	} );
+
+	it( 'should convert heading1', () => {
+		editor.setData( '<h2>foobar</h2>' );
+
+		expect( getData( model, { withoutSelection: true } ) ).to.equal( '<heading1>foobar</heading1>' );
+		expect( editor.getData() ).to.equal( '<h2>foobar</h2>' );
+	} );
+
+	it( 'should convert heading2', () => {
+		editor.setData( '<h3>foobar</h3>' );
+
+		expect( getData( model, { withoutSelection: true } ) ).to.equal( '<heading2>foobar</heading2>' );
+		expect( editor.getData() ).to.equal( '<h3>foobar</h3>' );
+	} );
+
+	it( 'should convert heading3', () => {
+		editor.setData( '<h4>foobar</h4>' );
+
+		expect( getData( model, { withoutSelection: true } ) ).to.equal( '<heading3>foobar</heading3>' );
+		expect( editor.getData() ).to.equal( '<h4>foobar</h4>' );
+	} );
+
+	describe( 'user defined', () => {
+		beforeEach( () => {
+			return VirtualTestEditor
+				.create( {
+					plugins: [ HeadingEditing ],
+					heading: {
+						options: [
+							{ model: 'paragraph', title: 'paragraph' },
+							{
+								model: 'heading1',
+								view: 'h1',
+								upcastAlso: [
+									{ name: 'p', attribute: { 'data-heading': 'h1' } }
+								],
+								title: 'User H1',
+								priority: 'high'
+							}
+						]
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+					model = editor.model;
+				} );
+		} );
+
+		it( 'should convert from defined h element', () => {
+			editor.setData( '<h1>foobar</h1>' );
+
+			expect( getData( model, { withoutSelection: true } ) ).to.equal( '<heading1>foobar</heading1>' );
+			expect( editor.getData() ).to.equal( '<h1>foobar</h1>' );
+		} );
+
+		it( 'should convert from defined paragraph with attributes', () => {
+			editor.setData( '<p data-heading="h1">foobar</p><p>Normal paragraph</p>' );
+
+			expect( getData( model, { withoutSelection: true } ) )
+				.to.equal( '<heading1>foobar</heading1><paragraph>Normal paragraph</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<h1>foobar</h1><p>Normal paragraph</p>' );
+		} );
+	} );
+
+	it( 'should not blow up if there\'s no enter command in the editor', () => {
+		return VirtualTestEditor
+			.create( {
+				plugins: [ HeadingEditing ]
+			} );
+	} );
+
+	describe( 'config', () => {
+		describe( 'options', () => {
+			describe( 'default value', () => {
+				it( 'should be set', () => {
+					expect( editor.config.get( 'heading.options' ) ).to.deep.equal( [
+						{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+						{ model: 'heading1', view: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
+						{ model: 'heading2', view: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
+						{ model: 'heading3', view: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
+					] );
+				} );
+			} );
+
+			it( 'should customize options', () => {
+				const options = [
+					{ model: 'paragraph', title: 'Paragraph' },
+					{ model: 'h4', view: { name: 'h4' }, title: 'H4' }
+				];
+
+				return VirtualTestEditor
+					.create( {
+						plugins: [ HeadingEditing ],
+						heading: {
+							options
+						}
+					} )
+					.then( editor => {
+						model = editor.model;
+
+						expect( model.schema.isRegistered( 'paragraph' ) ).to.be.true;
+						expect( model.schema.isRegistered( 'h4' ) ).to.be.true;
+
+						expect( model.schema.isRegistered( 'heading1' ) ).to.be.false;
+						expect( model.schema.isRegistered( 'heading2' ) ).to.be.false;
+						expect( model.schema.isRegistered( 'heading3' ) ).to.be.false;
+					} );
+			} );
+		} );
+	} );
+} );
+

+ 0 - 127
packages/ckeditor5-heading/tests/headingengine.js

@@ -1,127 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-import HeadingEngine from '../src/headingengine';
-import HeadingCommand from '../src/headingcommand';
-import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-import ParagraphCommand from '@ckeditor/ckeditor5-paragraph/src/paragraphcommand';
-import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
-import { getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
-
-describe( 'HeadingEngine', () => {
-	let editor, document;
-
-	beforeEach( () => {
-		return VirtualTestEditor
-			.create( {
-				plugins: [ HeadingEngine ]
-			} )
-			.then( newEditor => {
-				editor = newEditor;
-				document = editor.document;
-			} );
-	} );
-
-	it( 'should be loaded', () => {
-		expect( editor.plugins.get( HeadingEngine ) ).to.be.instanceOf( HeadingEngine );
-	} );
-
-	it( 'should load paragraph feature', () => {
-		expect( editor.plugins.get( Paragraph ) ).to.be.instanceOf( Paragraph );
-	} );
-
-	it( 'should set proper schema rules', () => {
-		expect( document.schema.hasItem( 'heading1' ) ).to.be.true;
-		expect( document.schema.hasItem( 'heading2' ) ).to.be.true;
-		expect( document.schema.hasItem( 'heading3' ) ).to.be.true;
-
-		expect( document.schema.check( { name: 'heading1', inside: '$root' } ) ).to.be.true;
-		expect( document.schema.check( { name: '$inline', inside: 'heading1' } ) ).to.be.true;
-
-		expect( document.schema.check( { name: 'heading2', inside: '$root' } ) ).to.be.true;
-		expect( document.schema.check( { name: '$inline', inside: 'heading2' } ) ).to.be.true;
-
-		expect( document.schema.check( { name: 'heading3', inside: '$root' } ) ).to.be.true;
-		expect( document.schema.check( { name: '$inline', inside: 'heading3' } ) ).to.be.true;
-	} );
-
-	it( 'should register #commands', () => {
-		expect( editor.commands.get( 'paragraph' ) ).to.be.instanceOf( ParagraphCommand );
-		expect( editor.commands.get( 'heading1' ) ).to.be.instanceOf( HeadingCommand );
-		expect( editor.commands.get( 'heading2' ) ).to.be.instanceOf( HeadingCommand );
-		expect( editor.commands.get( 'heading3' ) ).to.be.instanceOf( HeadingCommand );
-	} );
-
-	it( 'should convert heading1', () => {
-		editor.setData( '<h2>foobar</h2>' );
-
-		expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading1>foobar</heading1>' );
-		expect( editor.getData() ).to.equal( '<h2>foobar</h2>' );
-	} );
-
-	it( 'should convert heading2', () => {
-		editor.setData( '<h3>foobar</h3>' );
-
-		expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading2>foobar</heading2>' );
-		expect( editor.getData() ).to.equal( '<h3>foobar</h3>' );
-	} );
-
-	it( 'should convert heading3', () => {
-		editor.setData( '<h4>foobar</h4>' );
-
-		expect( getData( document, { withoutSelection: true } ) ).to.equal( '<heading3>foobar</heading3>' );
-		expect( editor.getData() ).to.equal( '<h4>foobar</h4>' );
-	} );
-
-	it( 'should not blow up if there\'s no enter command in the editor', () => {
-		return VirtualTestEditor
-			.create( {
-				plugins: [ HeadingEngine ]
-			} );
-	} );
-
-	describe( 'config', () => {
-		describe( 'options', () => {
-			describe( 'default value', () => {
-				it( 'should be set', () => {
-					expect( editor.config.get( 'heading.options' ) ).to.deep.equal( [
-						{ modelElement: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
-						{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
-						{ modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
-						{ modelElement: 'heading3', viewElement: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
-					] );
-				} );
-			} );
-
-			it( 'should customize options', () => {
-				const options = [
-					{ modelElement: 'paragraph', title: 'Paragraph' },
-					{ modelElement: 'h4', viewElement: 'h4', title: 'H4' }
-				];
-
-				return VirtualTestEditor
-					.create( {
-						plugins: [ HeadingEngine ],
-						heading: {
-							options
-						}
-					} )
-					.then( editor => {
-						document = editor.document;
-
-						expect( editor.commands.get( 'h4' ) ).to.be.instanceOf( HeadingCommand );
-						expect( editor.commands.get( 'paragraph' ) ).to.be.instanceOf( ParagraphCommand );
-
-						expect( document.schema.hasItem( 'paragraph' ) ).to.be.true;
-						expect( document.schema.hasItem( 'h4' ) ).to.be.true;
-
-						expect( document.schema.hasItem( 'heading1' ) ).to.be.false;
-						expect( document.schema.hasItem( 'heading2' ) ).to.be.false;
-						expect( document.schema.hasItem( 'heading3' ) ).to.be.false;
-					} );
-			} );
-		} );
-	} );
-} );

+ 283 - 0
packages/ckeditor5-heading/tests/headingui.js

@@ -0,0 +1,283 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals document */
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import Heading from '../src/heading';
+import HeadingEditing from '../src/headingediting';
+import HeadingUI from '../src/headingui';
+import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
+import { add as addTranslations, _clear as clearTranslations } from '@ckeditor/ckeditor5-utils/src/translation-service';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+testUtils.createSinonSandbox();
+
+describe( 'HeadingUI', () => {
+	let editor, editorElement, dropdown;
+
+	before( () => {
+		addTranslations( 'en', {
+			'Choose heading': 'Choose heading',
+			'Paragraph': 'Paragraph',
+			'Heading': 'Heading',
+			'Heading 1': 'Heading 1',
+			'Heading 2': 'Heading 2',
+		} );
+
+		addTranslations( 'pl', {
+			'Choose heading': 'Wybierz nagłówek',
+			'Paragraph': 'Akapit',
+			'Heading': 'Nagłówek',
+			'Heading 1': 'Nagłówek 1',
+			'Heading 2': 'Nagłówek 2',
+		} );
+	} );
+
+	after( () => {
+		clearTranslations();
+	} );
+
+	beforeEach( () => {
+		editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicTestEditor
+			.create( editorElement, {
+				plugins: [ HeadingUI, HeadingEditing ],
+				toolbar: [ 'heading' ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				dropdown = editor.ui.componentFactory.create( 'heading' );
+
+				// Set data so the commands will be enabled.
+				setData( editor.model, '<paragraph>f{}oo</paragraph>' );
+			} );
+	} );
+
+	afterEach( () => {
+		editorElement.remove();
+
+		return editor.destroy();
+	} );
+
+	describe( 'init()', () => {
+		it( 'should register options feature component', () => {
+			const dropdown = editor.ui.componentFactory.create( 'heading' );
+
+			expect( dropdown ).to.be.instanceOf( DropdownView );
+			expect( dropdown.buttonView.isEnabled ).to.be.true;
+			expect( dropdown.buttonView.isOn ).to.be.false;
+			expect( dropdown.buttonView.label ).to.equal( 'Paragraph' );
+			expect( dropdown.buttonView.tooltip ).to.equal( 'Heading' );
+		} );
+
+		it( 'should execute format command on model execute event for paragraph', () => {
+			const executeSpy = testUtils.sinon.spy( editor, 'execute' );
+			const dropdown = editor.ui.componentFactory.create( 'heading' );
+
+			dropdown.commandName = 'paragraph';
+			dropdown.fire( 'execute' );
+
+			sinon.assert.calledOnce( executeSpy );
+			sinon.assert.calledWithExactly( executeSpy, 'paragraph', undefined );
+		} );
+
+		it( 'should execute format command on model execute event for heading', () => {
+			const executeSpy = testUtils.sinon.spy( editor, 'execute' );
+			const dropdown = editor.ui.componentFactory.create( 'heading' );
+
+			dropdown.commandName = 'heading';
+			dropdown.commandValue = 'heading1';
+			dropdown.fire( 'execute' );
+
+			sinon.assert.calledOnce( executeSpy );
+			sinon.assert.calledWithExactly( executeSpy, 'heading', { value: 'heading1' } );
+		} );
+
+		it( 'should focus view after command execution', () => {
+			const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
+			const dropdown = editor.ui.componentFactory.create( 'heading' );
+
+			dropdown.commandName = 'paragraph';
+			dropdown.fire( 'execute' );
+
+			sinon.assert.calledOnce( focusSpy );
+		} );
+
+		it( 'should add custom CSS class to dropdown', () => {
+			const dropdown = editor.ui.componentFactory.create( 'heading' );
+
+			dropdown.render();
+
+			expect( dropdown.element.classList.contains( 'ck-heading-dropdown' ) ).to.be.true;
+		} );
+
+		describe( 'model to command binding', () => {
+			let command, paragraphCommand;
+
+			beforeEach( () => {
+				command = editor.commands.get( 'heading' );
+				paragraphCommand = editor.commands.get( 'paragraph' );
+			} );
+
+			it( 'isEnabled', () => {
+				command.isEnabled = false;
+				paragraphCommand.isEnabled = false;
+
+				expect( dropdown.buttonView.isEnabled ).to.be.false;
+
+				command.isEnabled = true;
+				expect( dropdown.buttonView.isEnabled ).to.be.true;
+
+				command.isEnabled = false;
+				expect( dropdown.buttonView.isEnabled ).to.be.false;
+
+				paragraphCommand.isEnabled = true;
+				expect( dropdown.buttonView.isEnabled ).to.be.true;
+			} );
+
+			it( 'label', () => {
+				command.value = false;
+				paragraphCommand.value = false;
+
+				expect( dropdown.buttonView.label ).to.equal( 'Choose heading' );
+
+				command.value = 'heading2';
+				expect( dropdown.buttonView.label ).to.equal( 'Heading 2' );
+				command.value = false;
+
+				paragraphCommand.value = true;
+				expect( dropdown.buttonView.label ).to.equal( 'Paragraph' );
+			} );
+		} );
+
+		describe( 'localization', () => {
+			let command, paragraphCommand, editor, dropdown;
+
+			beforeEach( () => {
+				return localizedEditor( [
+					{ model: 'paragraph', title: 'Paragraph' },
+					{ model: 'heading1', view: { name: 'h2' }, title: 'Heading 1' },
+					{ model: 'heading2', view: { name: 'h3' }, title: 'Heading 2' }
+				] );
+			} );
+
+			it( 'does not alter the original config', () => {
+				expect( editor.config.get( 'heading.options' ) ).to.deep.equal( [
+					{ model: 'paragraph', title: 'Paragraph' },
+					{ model: 'heading1', view: { name: 'h2' }, title: 'Heading 1' },
+					{ model: 'heading2', view: { name: 'h3' }, title: 'Heading 2' }
+				] );
+			} );
+
+			it( 'works for the #buttonView', () => {
+				const buttonView = dropdown.buttonView;
+
+				// Setting manually paragraph.value to `false` because there might be some content in editor
+				// after initialisation (for example empty <p></p> inserted when editor is empty).
+				paragraphCommand.value = false;
+				expect( buttonView.label ).to.equal( 'Wybierz nagłówek' );
+				expect( buttonView.tooltip ).to.equal( 'Nagłówek' );
+
+				paragraphCommand.value = true;
+				expect( buttonView.label ).to.equal( 'Akapit' );
+
+				paragraphCommand.value = false;
+				command.value = 'heading1';
+				expect( buttonView.label ).to.equal( 'Nagłówek 1' );
+			} );
+
+			it( 'works for the listView#items in the panel', () => {
+				const listView = dropdown.listView;
+
+				expect( listView.items.map( item => item.label ) ).to.deep.equal( [
+					'Akapit',
+					'Nagłówek 1',
+					'Nagłówek 2'
+				] );
+			} );
+
+			it( 'allows custom titles', () => {
+				return localizedEditor( [
+					{ model: 'paragraph', title: 'Custom paragraph title' },
+					{ model: 'heading1', view: { name: 'h1' }, title: 'Custom heading1 title' }
+				] ).then( () => {
+					const listView = dropdown.listView;
+
+					expect( listView.items.map( item => item.label ) ).to.deep.equal( [
+						'Custom paragraph title',
+						'Custom heading1 title',
+					] );
+				} );
+			} );
+
+			it( 'translates default using the the locale', () => {
+				return localizedEditor( [
+					{ model: 'paragraph', title: 'Paragraph' }
+				] ).then( () => {
+					const listView = dropdown.listView;
+
+					expect( listView.items.map( item => item.label ) ).to.deep.equal( [
+						'Akapit'
+					] );
+				} );
+			} );
+
+			function localizedEditor( options ) {
+				const editorElement = document.createElement( 'div' );
+				document.body.appendChild( editorElement );
+
+				return ClassicTestEditor
+					.create( editorElement, {
+						plugins: [ Heading ],
+						toolbar: [ 'heading' ],
+						language: 'pl',
+						heading: {
+							options
+						}
+					} )
+					.then( newEditor => {
+						editor = newEditor;
+						dropdown = editor.ui.componentFactory.create( 'heading' );
+						command = editor.commands.get( 'heading' );
+						paragraphCommand = editor.commands.get( 'paragraph' );
+
+						editorElement.remove();
+
+						return editor.destroy();
+					} );
+			}
+		} );
+
+		describe( 'class', () => {
+			it( 'is set for the listView#items in the panel', () => {
+				const listView = dropdown.listView;
+
+				expect( listView.items.map( item => item.class ) ).to.deep.equal( [
+					'ck-heading_paragraph',
+					'ck-heading_heading1',
+					'ck-heading_heading2',
+					'ck-heading_heading3'
+				] );
+			} );
+
+			it( 'reflects the #value of the commands', () => {
+				const listView = dropdown.listView;
+
+				setData( editor.model, '<heading2>f{}oo</heading2>' );
+
+				expect( listView.items.map( item => item.isActive ) ).to.deep.equal( [
+					false,
+					false,
+					true,
+					false
+				] );
+			} );
+		} );
+	} );
+} );

+ 62 - 16
packages/ckeditor5-heading/tests/integration.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.
  */
 
@@ -17,7 +17,7 @@ import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictest
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
 describe( 'Heading integration', () => {
-	let editor, doc, element;
+	let editor, model, doc, element;
 
 	beforeEach( () => {
 		element = document.createElement( 'div' );
@@ -29,7 +29,8 @@ describe( 'Heading integration', () => {
 			} )
 			.then( newEditor => {
 				editor = newEditor;
-				doc = editor.document;
+				model = editor.model;
+				doc = model.document;
 			} );
 	} );
 
@@ -42,28 +43,34 @@ describe( 'Heading integration', () => {
 	describe( 'with the enter key', () => {
 		it( 'should make the "enter" command insert a default heading block if the selection ended at the end of a heading block', () => {
 			editor.setData( '<h2>foobar</h2>' );
-			doc.selection.setCollapsedAt( doc.getRoot().getChild( 0 ), 'end' );
+
+			model.change( writer => {
+				writer.setSelection( doc.getRoot().getChild( 0 ), 'end' );
+			} );
 
 			editor.execute( 'enter' );
 
-			expect( getModelData( doc ) ).to.equal( '<heading1>foobar</heading1><paragraph>[]</paragraph>' );
+			expect( getModelData( model ) ).to.equal( '<heading1>foobar</heading1><paragraph>[]</paragraph>' );
 		} );
 
 		it( 'should not alter the "enter" command if selection not ended at the end of a heading block', () => {
 			// This test is to fill code coverage.
 			editor.setData( '<h2>foobar</h2>' );
-			doc.selection.setCollapsedAt( doc.getRoot().getChild( 0 ), 3 );
+
+			model.change( writer => {
+				writer.setSelection( doc.getRoot().getChild( 0 ), 3 );
+			} );
 
 			editor.execute( 'enter' );
 
-			expect( getModelData( doc ) ).to.equal( '<heading1>foo</heading1><heading1>[]bar</heading1>' );
+			expect( getModelData( model ) ).to.equal( '<heading1>foo</heading1><heading1>[]bar</heading1>' );
 		} );
 	} );
 
 	describe( 'with the image feature', () => {
 		// https://github.com/ckeditor/ckeditor5-heading/issues/73
 		it( 'should not destroy the image when a selection converted to a heading', () => {
-			setModelData( editor.document,
+			setModelData( model,
 				'<paragraph>fo[o</paragraph>' +
 				'<image src="foo.png">' +
 					'<caption>xxx</caption>' +
@@ -71,9 +78,9 @@ describe( 'Heading integration', () => {
 				'<paragraph>b]ar</paragraph>'
 			);
 
-			editor.execute( 'heading1' );
+			editor.execute( 'heading', { value: 'heading1' } );
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<heading1>fo[o</heading1>' +
 				'<image src="foo.png">' +
 					'<caption>xxx</caption>' +
@@ -85,21 +92,60 @@ describe( 'Heading integration', () => {
 
 	describe( 'with the undo feature', () => {
 		it( 'does not create undo steps when applied to an existing heading (collapsed selection)', () => {
-			setModelData( editor.document, '<heading1>foo[]bar</heading1>' );
+			setModelData( model, '<heading1>foo[]bar</heading1>' );
 
-			editor.execute( 'heading1' );
-			expect( getModelData( editor.document ) ).to.equal( '<heading1>foo[]bar</heading1>' );
+			editor.execute( 'heading', { value: 'heading1' } );
+			expect( getModelData( model ) ).to.equal( '<heading1>foo[]bar</heading1>' );
 
 			expect( editor.commands.get( 'undo' ).isEnabled ).to.be.false;
 		} );
 
 		it( 'does not create undo steps when applied to an existing heading (non–collapsed selection)', () => {
-			setModelData( editor.document, '<heading1>[foo</heading1><heading1>bar]</heading1>' );
+			setModelData( model, '<heading1>[foo</heading1><heading1>bar]</heading1>' );
 
-			editor.execute( 'heading1' );
-			expect( getModelData( editor.document ) ).to.equal( '<heading1>[foo</heading1><heading1>bar]</heading1>' );
+			editor.execute( 'heading', { value: 'heading1' } );
+			expect( getModelData( model ) ).to.equal( '<heading1>[foo</heading1><heading1>bar]</heading1>' );
 
 			expect( editor.commands.get( 'undo' ).isEnabled ).to.be.false;
 		} );
 	} );
+
+	// Remember to sync docs/_snippets/features/custom-heading-elements.js and docs/features/headings.md
+	// with this test when changing it.
+	describe( 'fancy heading sample in the docs', () => {
+		it( 'upcasts the <h2> and <h2 class=fancy> elements when configured to do so', () => {
+			const element = document.createElement( 'div' );
+			document.body.appendChild( element );
+
+			return ClassicTestEditor
+				.create( element, {
+					plugins: [ Paragraph, Heading ],
+					heading: {
+						options: [
+							{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+							{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
+							{
+								model: 'headingFancy',
+								view: {
+									name: 'h2',
+									class: 'fancy'
+								},
+								title: 'Heading 2 (fancy)',
+								class: 'ck-heading_heading2_fancy',
+								priority: 'high'
+							}
+						]
+					}
+				} )
+				.then( editor => {
+					editor.setData( '<h2>Heading 2</h2><h2 class="fancy">Fancy Heading 2</h2>' );
+
+					expect( editor.getData() )
+						.to.equal( '<h2>Heading 2</h2><h2 class="fancy">Fancy Heading 2</h2>' );
+
+					editor.destroy();
+					element.remove();
+				} );
+		} );
+	} );
 } );

+ 2 - 2
packages/ckeditor5-heading/tests/manual/heading.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.
  */
 
@@ -15,7 +15,7 @@ import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
 		plugins: [ Enter, Typing, Undo, Heading, Paragraph ],
-		toolbar: [ 'headings', 'undo', 'redo' ]
+		toolbar: [ 'heading', '|', 'undo', 'redo' ]
 	} )
 	.then( editor => {
 		window.editor = editor;

+ 8 - 8
packages/ckeditor5-heading/tests/tickets/40.js

@@ -1,9 +1,9 @@
 /**
- * @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.
  */
 
-import HeadingEngine from '../../src/headingengine';
+import HeadingEditing from '../../src/headingediting';
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import Enter from '@ckeditor/ckeditor5-enter/src/enter';
 import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -18,32 +18,32 @@ describe( 'Bug ckeditor5-heading#40', () => {
 	it( 'enter at the end of a heading creates a paragraph, when heading was loaded before enter', () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ HeadingEngine, Enter ]
+				plugins: [ HeadingEditing, Enter ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
 
-				setData( editor.document, '<heading1>foo[]</heading1>' );
+				setData( editor.model, '<heading1>foo[]</heading1>' );
 
 				editor.execute( 'enter' );
 
-				expect( getData( editor.document ) ).to.equal( '<heading1>foo</heading1><paragraph>[]</paragraph>' );
+				expect( getData( editor.model ) ).to.equal( '<heading1>foo</heading1><paragraph>[]</paragraph>' );
 			} );
 	} );
 
 	it( 'enter at the end of a heading creates a paragraph, when enter was loaded before heading', () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ Enter, HeadingEngine ]
+				plugins: [ Enter, HeadingEditing ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
 
-				setData( editor.document, '<heading1>foo[]</heading1>' );
+				setData( editor.model, '<heading1>foo[]</heading1>' );
 
 				editor.execute( 'enter' );
 
-				expect( getData( editor.document ) ).to.equal( '<heading1>foo</heading1><paragraph>[]</paragraph>' );
+				expect( getData( editor.model ) ).to.equal( '<heading1>foo</heading1><paragraph>[]</paragraph>' );
 			} );
 	} );
 } );

+ 20 - 0
packages/ckeditor5-heading/theme/heading.css

@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+.ck-heading_heading1 {
+	font-size: 20px;
+}
+
+.ck-heading_heading2 {
+	font-size: 17px;
+}
+
+.ck-heading_heading3 {
+	font-size: 14px;
+}
+
+[class*="ck-heading_heading"] {
+	font-weight: bold;
+}

+ 0 - 42
packages/ckeditor5-heading/theme/theme.scss

@@ -1,42 +0,0 @@
-// Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
-// For licensing, see LICENSE.md or http://ckeditor.com/license
-
-@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_spacing';
-@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_fonts';
-
-.ck-heading_heading {
-	&1 {
-		font-size: 1.5em;
-	}
-
-	&2 {
-		font-size: 1.3em;
-	}
-
-	&3 {
-		font-size: 1.1em;
-	}
-}
-
-// Using absolute units to make sure each element has the same height and padding.
-// https://github.com/ckeditor/ckeditor5-heading/issues/63
-[class*="ck-heading_"] {
-	line-height: 1.8 * $ck-font-size-base;
-	// Strip the units from $ck-def-spacing here.
-	padding: $ck-def-spacing / ( $ck-def-spacing * 0 + 1 ) * $ck-font-size-base;
-}
-
-[class*="ck-heading_heading"] {
-	font-weight: bold;
-}
-
-// Resize dropdown's button label.
-.ck-dropdown {
-	&.ck-heading-dropdown {
-		.ck-dropdown__button {
-			.ck-button__label {
-				width: 8em;
-			}
-		}
-	}
-}