浏览代码

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

Release: v1.0.0-beta.1.
Piotrek Koszuliński 7 年之前
父节点
当前提交
a078d5d740
共有 53 个文件被更改,包括 577 次插入470 次删除
  1. 1 1
      packages/ckeditor5-block-quote/.eslintrc.js
  2. 15 11
      packages/ckeditor5-block-quote/.travis.yml
  3. 8 0
      packages/ckeditor5-block-quote/CHANGELOG.md
  4. 1 1
      packages/ckeditor5-block-quote/LICENSE.md
  5. 3 1
      packages/ckeditor5-block-quote/README.md
  6. 1 1
      packages/ckeditor5-block-quote/lang/translations/bg.po
  7. 1 1
      packages/ckeditor5-block-quote/lang/translations/da.po
  8. 1 1
      packages/ckeditor5-block-quote/lang/translations/de.po
  9. 1 1
      packages/ckeditor5-block-quote/lang/translations/el.po
  10. 1 1
      packages/ckeditor5-block-quote/lang/translations/en-au.po
  11. 1 1
      packages/ckeditor5-block-quote/lang/translations/en.po
  12. 1 1
      packages/ckeditor5-block-quote/lang/translations/es.po
  13. 10 0
      packages/ckeditor5-block-quote/lang/translations/et.po
  14. 10 0
      packages/ckeditor5-block-quote/lang/translations/eu.po
  15. 1 1
      packages/ckeditor5-block-quote/lang/translations/fi.po
  16. 1 1
      packages/ckeditor5-block-quote/lang/translations/fr.po
  17. 1 1
      packages/ckeditor5-block-quote/lang/translations/gl.po
  18. 1 1
      packages/ckeditor5-block-quote/lang/translations/gu.po
  19. 1 1
      packages/ckeditor5-block-quote/lang/translations/hr.po
  20. 1 1
      packages/ckeditor5-block-quote/lang/translations/hu.po
  21. 1 1
      packages/ckeditor5-block-quote/lang/translations/it.po
  22. 1 1
      packages/ckeditor5-block-quote/lang/translations/ja.po
  23. 1 1
      packages/ckeditor5-block-quote/lang/translations/km.po
  24. 1 1
      packages/ckeditor5-block-quote/lang/translations/kn.po
  25. 1 1
      packages/ckeditor5-block-quote/lang/translations/ko.po
  26. 1 1
      packages/ckeditor5-block-quote/lang/translations/ku.po
  27. 1 1
      packages/ckeditor5-block-quote/lang/translations/nb.po
  28. 10 0
      packages/ckeditor5-block-quote/lang/translations/nl.po
  29. 1 1
      packages/ckeditor5-block-quote/lang/translations/pl.po
  30. 1 1
      packages/ckeditor5-block-quote/lang/translations/pt-br.po
  31. 1 1
      packages/ckeditor5-block-quote/lang/translations/ro.po
  32. 1 1
      packages/ckeditor5-block-quote/lang/translations/ru.po
  33. 1 1
      packages/ckeditor5-block-quote/lang/translations/sk.po
  34. 10 0
      packages/ckeditor5-block-quote/lang/translations/sq.po
  35. 1 1
      packages/ckeditor5-block-quote/lang/translations/sv.po
  36. 1 1
      packages/ckeditor5-block-quote/lang/translations/tr.po
  37. 10 0
      packages/ckeditor5-block-quote/lang/translations/ug.po
  38. 1 1
      packages/ckeditor5-block-quote/lang/translations/zh-cn.po
  39. 1 1
      packages/ckeditor5-block-quote/lang/translations/zh.po
  40. 16 15
      packages/ckeditor5-block-quote/package.json
  41. 6 64
      packages/ckeditor5-block-quote/src/blockquote.js
  42. 24 33
      packages/ckeditor5-block-quote/src/blockquotecommand.js
  43. 70 0
      packages/ckeditor5-block-quote/src/blockquoteediting.js
  44. 0 63
      packages/ckeditor5-block-quote/src/blockquoteengine.js
  45. 50 0
      packages/ckeditor5-block-quote/src/blockquoteui.js
  46. 5 71
      packages/ckeditor5-block-quote/tests/blockquote.js
  47. 82 86
      packages/ckeditor5-block-quote/tests/blockquotecommand.js
  48. 22 15
      packages/ckeditor5-block-quote/tests/blockquoteengine.js
  49. 76 0
      packages/ckeditor5-block-quote/tests/blockquoteui.js
  50. 103 66
      packages/ckeditor5-block-quote/tests/integration.js
  51. 3 3
      packages/ckeditor5-block-quote/tests/manual/blockquote.js
  52. 13 0
      packages/ckeditor5-block-quote/theme/blockquote.css
  53. 0 11
      packages/ckeditor5-block-quote/theme/theme.scss

+ 1 - 1
packages/ckeditor5-block-quote/.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-block-quote/.travis.yml


+ 8 - 0
packages/ckeditor5-block-quote/CHANGELOG.md

@@ -1,6 +1,14 @@
 Changelog
 =========
 
+## [1.0.0-beta.1](https://github.com/ckeditor/ckeditor5-block-quote/compare/v1.0.0-alpha.2...v1.0.0-beta.1) (2018-03-15)
+
+### Other changes
+
+* Aligned feature class naming to the new scheme. ([cc723c3](https://github.com/ckeditor/ckeditor5-block-quote/commit/cc723c3))
+* Migrated package styles to PostCSS. Moved the visual styles to `@ckeditor/ckeditor5-theme-lark` (see [ckeditor/ckeditor5-ui#144](https://github.com/ckeditor/ckeditor5-ui/issues/144)). ([c973931](https://github.com/ckeditor/ckeditor5-block-quote/commit/c973931))
+
+
 ## [1.0.0-alpha.2](https://github.com/ckeditor/ckeditor5-block-quote/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) (2017-11-14)
 
 ### Other changes

+ 1 - 1
packages/ckeditor5-block-quote/LICENSE.md

@@ -2,7 +2,7 @@ Software License Agreement
 ==========================
 
 **CKEditor 5 Block Quote Feature** – https://github.com/ckeditor/ckeditor5-paragraph <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-block-quote/README.md

@@ -4,7 +4,9 @@ CKEditor 5 block quote 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-block-quote.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-block-quote)
 [![Build Status](https://travis-ci.org/ckeditor/ckeditor5-block-quote.svg?branch=master)](https://travis-ci.org/ckeditor/ckeditor5-block-quote)
-[![Test Coverage](https://codeclimate.com/github/ckeditor/ckeditor5-block-quote/badges/coverage.svg)](https://codeclimate.com/github/ckeditor/ckeditor5-block-quote/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-block-quote/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5-block-quote?branch=master)
+<br>
 [![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-block-quote/status.svg)](https://david-dm.org/ckeditor/ckeditor5-block-quote)
 [![devDependency Status](https://david-dm.org/ckeditor/ckeditor5-block-quote/dev-status.svg)](https://david-dm.org/ckeditor/ckeditor5-block-quote?type=dev)
 

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/bg.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/da.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/de.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/el.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/en-au.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/en.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/es.po

@@ -1,4 +1,4 @@
-# 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"

+ 10 - 0
packages/ckeditor5-block-quote/lang/translations/et.po

@@ -0,0 +1,10 @@
+# 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 "Toolbar button tooltip for the Block quote feature."
+msgid "Block quote"
+msgstr "Tsitaat"

+ 10 - 0
packages/ckeditor5-block-quote/lang/translations/eu.po

@@ -0,0 +1,10 @@
+# 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 "Toolbar button tooltip for the Block quote feature."
+msgid "Block quote"
+msgstr "Aipua"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/fi.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/fr.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/gl.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/gu.po

@@ -1,4 +1,4 @@
-# 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: gu\n"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/hr.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/hu.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/it.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/ja.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/km.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/kn.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/ko.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/ku.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/nb.po

@@ -1,4 +1,4 @@
-# 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"

+ 10 - 0
packages/ckeditor5-block-quote/lang/translations/nl.po

@@ -0,0 +1,10 @@
+# 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 "Toolbar button tooltip for the Block quote feature."
+msgid "Block quote"
+msgstr "Blok citaat"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/pl.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/pt-br.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/ro.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/ru.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/sk.po

@@ -1,4 +1,4 @@
-# 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"

+ 10 - 0
packages/ckeditor5-block-quote/lang/translations/sq.po

@@ -0,0 +1,10 @@
+# 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 "Toolbar button tooltip for the Block quote feature."
+msgid "Block quote"
+msgstr "Thonjëzat"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/sv.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/tr.po

@@ -1,4 +1,4 @@
-# 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"

+ 10 - 0
packages/ckeditor5-block-quote/lang/translations/ug.po

@@ -0,0 +1,10 @@
+# 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 "Toolbar button tooltip for the Block quote feature."
+msgid "Block quote"
+msgstr "قىسمەن قوللىنىش"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/zh-cn.po

@@ -1,4 +1,4 @@
-# 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"

+ 1 - 1
packages/ckeditor5-block-quote/lang/translations/zh.po

@@ -1,4 +1,4 @@
-# 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"

+ 16 - 15
packages/ckeditor5-block-quote/package.json

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

+ 6 - 64
packages/ckeditor5-block-quote/src/blockquote.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,19 +8,14 @@
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import BlockQuoteEngine from './blockquoteengine';
-
-import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
-
-import quoteIcon from '@ckeditor/ckeditor5-core/theme/icons/quote.svg';
-import '../theme/theme.scss';
+import BlockQuoteEditing from './blockquoteediting';
+import BlockQuoteUI from './blockquoteui';
 
 /**
  * The block quote plugin.
  *
- * It introduces the `'blockQuote'` button and requires the {@link module:block-quote/blockquoteengine~BlockQuoteEngine}
- * plugin. It also changes <kbd>Enter</kbd> key behavior so it escapes block quotes when pressed in an
- * empty quoted block.
+ * It loads the {@link module:block-quote/blockquoteediting~BlockQuoteEditing block quote editing feature}
+ * and {@link module:block-quote/blockquoteui~BlockQuoteUI block quote UI feature}.
  *
  * @extends module:core/plugin~Plugin
  */
@@ -29,7 +24,7 @@ export default class BlockQuote extends Plugin {
 	 * @inheritDoc
 	 */
 	static get requires() {
-		return [ BlockQuoteEngine ];
+		return [ BlockQuoteEditing, BlockQuoteUI ];
 	}
 
 	/**
@@ -38,57 +33,4 @@ export default class BlockQuote extends Plugin {
 	static get pluginName() {
 		return 'BlockQuote';
 	}
-
-	/**
-	 * @inheritDoc
-	 */
-	init() {
-		const editor = this.editor;
-		const t = editor.t;
-		const command = editor.commands.get( 'blockQuote' );
-
-		editor.ui.componentFactory.add( 'blockQuote', locale => {
-			const buttonView = new ButtonView( locale );
-
-			buttonView.set( {
-				label: t( 'Block quote' ),
-				icon: quoteIcon,
-				tooltip: true
-			} );
-
-			// Bind button model to command.
-			buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
-
-			// Execute command.
-			this.listenTo( buttonView, 'execute', () => editor.execute( 'blockQuote' ) );
-
-			return buttonView;
-		} );
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	afterInit() {
-		const editor = this.editor;
-		const command = editor.commands.get( 'blockQuote' );
-
-		// Overwrite default Enter key behavior.
-		// If Enter key is pressed with selection collapsed in empty block inside a quote, break the quote.
-		// This listener is added in afterInit in order to register it after list's feature listener.
-		// We can't use a priority for this, because 'low' is already used by the enter feature, unless
-		// we'd use numeric priority in this case.
-		this.listenTo( this.editor.editing.view, 'enter', ( evt, data ) => {
-			const doc = this.editor.document;
-			const positionParent = doc.selection.getLastPosition().parent;
-
-			if ( doc.selection.isCollapsed && positionParent.isEmpty && command.value ) {
-				this.editor.execute( 'blockQuote' );
-				this.editor.editing.view.scrollToTheSelection();
-
-				data.preventDefault();
-				evt.stop();
-			}
-		} );
-	}
 }

+ 24 - 33
packages/ckeditor5-block-quote/src/blockquotecommand.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.
  */
 
@@ -42,19 +42,16 @@ export default class BlockQuoteCommand extends Command {
 	 * a block quote.
 	 *
 	 * @fires execute
-	 * @param {Object} [options] Options for executed command.
-	 * @param {module:engine/model/batch~Batch} [options.batch] Batch to collect all the change steps.
-	 * A new batch will be created if this option is not set.
 	 */
-	execute( options = {} ) {
-		const doc = this.editor.document;
-		const schema = doc.schema;
-		const batch = options.batch || doc.batch();
+	execute() {
+		const model = this.editor.model;
+		const doc = model.document;
+		const schema = model.schema;
 		const blocks = Array.from( doc.selection.getSelectedBlocks() );
 
-		doc.enqueueChanges( () => {
+		model.change( writer => {
 			if ( this.value ) {
-				this._removeQuote( batch, blocks.filter( findQuote ) );
+				this._removeQuote( writer, blocks.filter( findQuote ) );
 			} else {
 				const blocksToQuote = blocks.filter( block => {
 					// Already quoted blocks needs to be considered while quoting too
@@ -62,7 +59,7 @@ export default class BlockQuoteCommand extends Command {
 					return findQuote( block ) || checkCanBeQuoted( schema, block );
 				} );
 
-				this._applyQuote( batch, blocksToQuote );
+				this._applyQuote( writer, blocksToQuote );
 			}
 		} );
 	}
@@ -74,7 +71,7 @@ export default class BlockQuoteCommand extends Command {
 	 * @returns {Boolean} The current value.
 	 */
 	_getValue() {
-		const firstBlock = first( this.editor.document.selection.getSelectedBlocks() );
+		const firstBlock = first( this.editor.model.document.selection.getSelectedBlocks() );
 
 		// In the current implementation, the block quote must be an immediate parent of a block element.
 		return !!( firstBlock && findQuote( firstBlock ) );
@@ -91,8 +88,8 @@ export default class BlockQuoteCommand extends Command {
 			return true;
 		}
 
-		const selection = this.editor.document.selection;
-		const schema = this.editor.document.schema;
+		const selection = this.editor.model.document.selection;
+		const schema = this.editor.model.schema;
 
 		const firstBlock = first( selection.getSelectedBlocks() );
 
@@ -111,14 +108,14 @@ export default class BlockQuoteCommand extends Command {
 	 * will be moved out of it, so other quoted blocks remained quoted.
 	 *
 	 * @private
-	 * @param {module:engine/model/batch~Batch} batch
+	 * @param {module:engine/model/writer~Writer} writer
 	 * @param {Array.<module:engine/model/element~Element>} blocks
 	 */
-	_removeQuote( batch, blocks ) {
+	_removeQuote( writer, blocks ) {
 		// Unquote all groups of block. Iterate in the reverse order to not break following ranges.
 		getRangesOfBlockGroups( blocks ).reverse().forEach( groupRange => {
 			if ( groupRange.start.isAtStart && groupRange.end.isAtEnd ) {
-				batch.unwrap( groupRange.start.parent );
+				writer.unwrap( groupRange.start.parent );
 
 				return;
 			}
@@ -127,7 +124,7 @@ export default class BlockQuoteCommand extends Command {
 			if ( groupRange.start.isAtStart ) {
 				const positionBefore = Position.createBefore( groupRange.start.parent );
 
-				batch.move( groupRange, positionBefore );
+				writer.move( groupRange, positionBefore );
 
 				return;
 			}
@@ -135,14 +132,14 @@ export default class BlockQuoteCommand extends Command {
 			// The blocks are in the middle of an <bQ> so we need to split the <bQ> after the last block
 			// so we move the items there.
 			if ( !groupRange.end.isAtEnd ) {
-				batch.split( groupRange.end );
+				writer.split( groupRange.end );
 			}
 
 			// Now we are sure that groupRange.end.isAtEnd is true, so let's move the blocks right.
 
 			const positionAfter = Position.createAfter( groupRange.end.parent );
 
-			batch.move( groupRange, positionAfter );
+			writer.move( groupRange, positionAfter );
 		} );
 	}
 
@@ -150,10 +147,10 @@ export default class BlockQuoteCommand extends Command {
 	 * Applies the quote to given blocks.
 	 *
 	 * @private
-	 * @param {module:engine/model/batch~Batch} batch
+	 * @param {module:engine/model/writer~Writer} writer
 	 * @param {Array.<module:engine/model/element~Element>} blocks
 	 */
-	_applyQuote( batch, blocks ) {
+	_applyQuote( writer, blocks ) {
 		const quotesToMerge = [];
 
 		// Quote all groups of block. Iterate in the reverse order to not break following ranges.
@@ -163,7 +160,7 @@ export default class BlockQuoteCommand extends Command {
 			if ( !quote ) {
 				quote = new Element( 'blockQuote' );
 
-				batch.wrap( groupRange, quote );
+				writer.wrap( groupRange, quote );
 			}
 
 			quotesToMerge.push( quote );
@@ -175,7 +172,7 @@ export default class BlockQuoteCommand extends Command {
 		// we want to keep the reference to the first (furthest left) one.
 		quotesToMerge.reverse().reduce( ( currentQuote, nextQuote ) => {
 			if ( currentQuote.nextSibling == nextQuote ) {
-				batch.merge( Position.createAfter( currentQuote ) );
+				writer.merge( Position.createAfter( currentQuote ) );
 
 				return currentQuote;
 			}
@@ -223,15 +220,9 @@ function getRangesOfBlockGroups( blocks ) {
 
 // Checks whether <bQ> can wrap the block.
 function checkCanBeQuoted( schema, block ) {
-	const isBQAllowed = schema.check( {
-		name: 'blockQuote',
-		inside: Position.createBefore( block )
-	} );
-	const isBlockAllowedInBQ = schema.check( {
-		name: block.name,
-		attributes: Array.from( block.getAttributeKeys() ),
-		inside: 'blockQuote'
-	} );
+	// TMP will be replaced with schema.checkWrap().
+	const isBQAllowed = schema.checkChild( block.parent, 'blockQuote' );
+	const isBlockAllowedInBQ = schema.checkChild( [ '$root', 'blockQuote' ], block );
 
 	return isBQAllowed && isBlockAllowedInBQ;
 }

+ 70 - 0
packages/ckeditor5-block-quote/src/blockquoteediting.js

@@ -0,0 +1,70 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module block-quote/blockquoteediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import BlockQuoteCommand from './blockquotecommand';
+
+/**
+ * The block quote editing.
+ *
+ * Introduces the `'blockQuote'` command and the `'blockQuote'` model element.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class BlockQuoteEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const schema = editor.model.schema;
+
+		editor.commands.add( 'blockQuote', new BlockQuoteCommand( editor ) );
+
+		schema.register( 'blockQuote', {
+			allowWhere: '$block',
+			allowContentOf: '$root'
+		} );
+
+		// Disallow blockQuote in blockQuote.
+		schema.addChildCheck( ( ctx, childDef ) => {
+			if ( ctx.endsWith( 'blockQuote' ) && childDef.name == 'blockQuote' ) {
+				return false;
+			}
+		} );
+
+		editor.conversion.elementToElement( { model: 'blockQuote', view: 'blockquote' } );
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	afterInit() {
+		const editor = this.editor;
+		const command = editor.commands.get( 'blockQuote' );
+
+		// Overwrite default Enter key behavior.
+		// If Enter key is pressed with selection collapsed in empty block inside a quote, break the quote.
+		// This listener is added in afterInit in order to register it after list's feature listener.
+		// We can't use a priority for this, because 'low' is already used by the enter feature, unless
+		// we'd use numeric priority in this case.
+		this.listenTo( this.editor.editing.view.document, 'enter', ( evt, data ) => {
+			const doc = this.editor.model.document;
+			const positionParent = doc.selection.getLastPosition().parent;
+
+			if ( doc.selection.isCollapsed && positionParent.isEmpty && command.value ) {
+				this.editor.execute( 'blockQuote' );
+				this.editor.editing.view.scrollToTheSelection();
+
+				data.preventDefault();
+				evt.stop();
+			}
+		} );
+	}
+}

+ 0 - 63
packages/ckeditor5-block-quote/src/blockquoteengine.js

@@ -1,63 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module block-quote/blockquoteengine
- */
-
-import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-
-import BlockQuoteCommand from './blockquotecommand';
-
-import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
-import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
-
-/**
- * The block quote engine.
- *
- * Introduces the `'blockQuote'` command and the `'blockQuote'` model element.
- *
- * @extends module:core/plugin~Plugin
- */
-export default class BlockQuoteEngine extends Plugin {
-	/**
-	 * @inheritDoc
-	 */
-	init() {
-		const editor = this.editor;
-		const schema = editor.document.schema;
-
-		editor.commands.add( 'blockQuote', new BlockQuoteCommand( editor ) );
-
-		schema.registerItem( 'blockQuote' );
-		schema.allow( { name: 'blockQuote', inside: '$root' } );
-		schema.allow( { name: '$block', inside: 'blockQuote' } );
-
-		buildViewConverter().for( editor.data.viewToModel )
-			.fromElement( 'blockquote' )
-			.toElement( 'blockQuote' );
-
-		buildModelConverter().for( editor.data.modelToView, editor.editing.modelToView )
-			.fromElement( 'blockQuote' )
-			.toElement( 'blockquote' );
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	afterInit() {
-		const schema = this.editor.document.schema;
-
-		// TODO
-		// Workaround for https://github.com/ckeditor/ckeditor5-engine/issues/532#issuecomment-280924650.
-		if ( schema.hasItem( 'listItem' ) ) {
-			schema.allow( {
-				name: 'listItem',
-				inside: 'blockQuote',
-				attributes: [ 'type', 'indent' ]
-			} );
-		}
-	}
-}

+ 50 - 0
packages/ckeditor5-block-quote/src/blockquoteui.js

@@ -0,0 +1,50 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module block-quote/blockquoteui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+import quoteIcon from '@ckeditor/ckeditor5-core/theme/icons/quote.svg';
+import '../theme/blockquote.css';
+
+/**
+ * The block quote UI plugin.
+ *
+ * It introduces the `'blockQuote'` button.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class BlockQuoteUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+
+		editor.ui.componentFactory.add( 'blockQuote', locale => {
+			const command = editor.commands.get( 'blockQuote' );
+			const buttonView = new ButtonView( locale );
+
+			buttonView.set( {
+				label: t( 'Block quote' ),
+				icon: quoteIcon,
+				tooltip: true
+			} );
+
+			// Bind button model to command.
+			buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+			// Execute command.
+			this.listenTo( buttonView, 'execute', () => editor.execute( 'blockQuote' ) );
+
+			return buttonView;
+		} );
+	}
+}

+ 5 - 71
packages/ckeditor5-block-quote/tests/blockquote.js

@@ -1,80 +1,14 @@
 /**
- * @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.
  */
 
-/* global document */
-
 import BlockQuote from '../src/blockquote';
-import BlockQuoteEngine from '../src/blockquoteengine';
-
-import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import BlockQuoteEditing from '../src/blockquoteediting';
+import BlockQuoteUI from '../src/blockquoteui';
 
 describe( 'BlockQuote', () => {
-	let editor, command, element;
-
-	beforeEach( () => {
-		element = document.createElement( 'div' );
-		document.body.appendChild( element );
-
-		return ClassicTestEditor
-			.create( element, {
-				plugins: [ BlockQuote ]
-			} )
-			.then( newEditor => {
-				editor = newEditor;
-				command = editor.commands.get( 'blockQuote' );
-			} );
-	} );
-
-	afterEach( () => {
-		element.remove();
-
-		return editor.destroy();
-	} );
-
-	it( 'requires BlockQuoteEngine', () => {
-		expect( BlockQuote.requires ).to.deep.equal( [ BlockQuoteEngine ] );
-	} );
-
-	describe( 'blockQuote button', () => {
-		it( 'has the base properties', () => {
-			const button = editor.ui.componentFactory.create( 'blockQuote' );
-
-			expect( button ).to.have.property( 'label', 'Block quote' );
-			expect( button ).to.have.property( 'icon' );
-			expect( button ).to.have.property( 'tooltip', true );
-		} );
-
-		it( 'has isOn bound to command\'s value', () => {
-			const button = editor.ui.componentFactory.create( 'blockQuote' );
-
-			command.value = false;
-			expect( button ).to.have.property( 'isOn', false );
-
-			command.value = true;
-			expect( button ).to.have.property( 'isOn', true );
-		} );
-
-		it( 'has isEnabled bound to command\'s isEnabled', () => {
-			const button = editor.ui.componentFactory.create( 'blockQuote' );
-
-			command.isEnabled = true;
-			expect( button ).to.have.property( 'isEnabled', true );
-
-			command.isEnabled = false;
-			expect( button ).to.have.property( 'isEnabled', false );
-		} );
-
-		it( 'executes command when it\'s executed', () => {
-			const button = editor.ui.componentFactory.create( 'blockQuote' );
-
-			const spy = sinon.stub( editor, 'execute' );
-
-			button.fire( 'execute' );
-
-			expect( spy.calledOnce ).to.be.true;
-			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
-		} );
+	it( 'requires BlockQuoteEditing and BlockQuoteUI', () => {
+		expect( BlockQuote.requires ).to.deep.equal( [ BlockQuoteEditing, BlockQuoteUI ] );
 	} );
 } );

+ 82 - 86
packages/ckeditor5-block-quote/tests/blockquotecommand.js

@@ -1,12 +1,12 @@
 /**
- * @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 BlockQuoteEngine from '../src/blockquoteengine';
+import BlockQuoteEditing from '../src/blockquoteediting';
 import BlockQuoteCommand from '../src/blockquotecommand';
 
-import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
+import { downcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';
 
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -15,38 +15,31 @@ import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils
 import Command from '@ckeditor/ckeditor5-core/src/command';
 
 describe( 'BlockQuoteCommand', () => {
-	let editor, doc, command;
+	let editor, model, command;
 
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ BlockQuoteEngine ]
+				plugins: [ BlockQuoteEditing ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
 
-				doc = editor.document;
+				model = editor.model;
 
-				doc.schema.registerItem( 'paragraph', '$block' );
-				doc.schema.registerItem( 'heading', '$block' );
-				doc.schema.registerItem( 'widget' );
+				model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
+				model.schema.register( 'heading', { inheritAllFrom: '$block' } );
+				model.schema.register( 'widget' );
 
-				doc.schema.allow( { name: 'widget', inside: '$root' } );
-				doc.schema.allow( { name: '$text', inside: 'widget' } );
+				model.schema.extend( 'widget', {
+					allowIn: '$root',
+					isLimit: true
+				} );
+				model.schema.extend( '$text', { allowIn: 'widget' } );
 
-				doc.schema.limits.add( 'widget' );
-
-				buildModelConverter().for( editor.editing.modelToView )
-					.fromElement( 'paragraph' )
-					.toElement( 'p' );
-
-				buildModelConverter().for( editor.editing.modelToView )
-					.fromElement( 'heading' )
-					.toElement( 'h' );
-
-				buildModelConverter().for( editor.editing.modelToView )
-					.fromElement( 'widget' )
-					.toElement( 'widget' );
+				editor.conversion.for( 'downcast' ).add( downcastElementToElement( { model: 'paragraph', view: 'p' } ) );
+				editor.conversion.for( 'downcast' ).add( downcastElementToElement( { model: 'heading', view: 'h' } ) );
+				editor.conversion.for( 'downcast' ).add( downcastElementToElement( { model: 'widget', view: 'widget' } ) );
 
 				command = editor.commands.get( 'blockQuote' );
 			} );
@@ -63,33 +56,33 @@ describe( 'BlockQuoteCommand', () => {
 
 	describe( 'value', () => {
 		it( 'is false when selection is not in a block quote', () => {
-			setModelData( doc, '<paragraph>x[]x</paragraph>' );
+			setModelData( model, '<paragraph>x[]x</paragraph>' );
 
 			expect( command ).to.have.property( 'value', false );
 		} );
 
 		it( 'is false when start of the selection is not in a block quote', () => {
-			setModelData( doc, '<paragraph>x[x</paragraph><blockQuote><paragraph>y]y</paragraph></blockQuote>' );
+			setModelData( model, '<paragraph>x[x</paragraph><blockQuote><paragraph>y]y</paragraph></blockQuote>' );
 
 			expect( command ).to.have.property( 'value', false );
 		} );
 
 		it( 'is false when selection starts in a blockless space', () => {
-			doc.schema.allow( { name: '$text', inside: '$root' } );
+			model.schema.extend( '$text', { allowIn: '$root' } );
 
-			setModelData( doc, 'x[]x' );
+			setModelData( model, 'x[]x' );
 
 			expect( command ).to.have.property( 'value', false );
 		} );
 
 		it( 'is true when selection is in a block quote', () => {
-			setModelData( doc, '<blockQuote><paragraph>x[]x</paragraph></blockQuote>' );
+			setModelData( model, '<blockQuote><paragraph>x[]x</paragraph></blockQuote>' );
 
 			expect( command ).to.have.property( 'value', true );
 		} );
 
 		it( 'is true when selection starts in a block quote', () => {
-			setModelData( doc, '<blockQuote><paragraph>x[x</paragraph></blockQuote><paragraph>y]y</paragraph>' );
+			setModelData( model, '<blockQuote><paragraph>x[x</paragraph></blockQuote><paragraph>y]y</paragraph>' );
 
 			expect( command ).to.have.property( 'value', true );
 		} );
@@ -97,25 +90,25 @@ describe( 'BlockQuoteCommand', () => {
 
 	describe( 'isEnabled', () => {
 		it( 'is true when selection is in a block which can be wrapped with blockQuote', () => {
-			setModelData( doc, '<paragraph>x[]x</paragraph>' );
+			setModelData( model, '<paragraph>x[]x</paragraph>' );
 
 			expect( command ).to.have.property( 'isEnabled', true );
 		} );
 
 		it( 'is true when selection is in a block which is already in blockQuote', () => {
-			setModelData( doc, '<blockQuote><paragraph>x[]x</paragraph></blockQuote>' );
+			setModelData( model, '<blockQuote><paragraph>x[]x</paragraph></blockQuote>' );
 
 			expect( command ).to.have.property( 'isEnabled', true );
 		} );
 
 		it( 'is true when selection starts in a block which can be wrapped with blockQuote', () => {
-			setModelData( doc, '<paragraph>x[x</paragraph><widget>y]y</widget>' );
+			setModelData( model, '<paragraph>x[x</paragraph><widget>y]y</widget>' );
 
 			expect( command ).to.have.property( 'isEnabled', true );
 		} );
 
 		it( 'is false when selection is in an element which cannot be wrapped with blockQuote (because it cannot be its child)', () => {
-			setModelData( doc, '<widget>x[]x</widget>' );
+			setModelData( model, '<widget>x[]x</widget>' );
 
 			expect( command ).to.have.property( 'isEnabled', false );
 		} );
@@ -124,9 +117,13 @@ describe( 'BlockQuoteCommand', () => {
 			'is false when selection is in an element which cannot be wrapped with blockQuote' +
 			'(because mQ is not allowed in its parent)',
 			() => {
-				doc.schema.disallow( { name: 'blockQuote', inside: '$root' } );
+				model.schema.addChildCheck( ( ctx, childDef ) => {
+					if ( childDef.name == 'blockQuote' ) {
+						return false;
+					}
+				} );
 
-				setModelData( doc, '<paragraph>x[]x</paragraph>' );
+				setModelData( model, '<paragraph>x[]x</paragraph>' );
 
 				expect( command ).to.have.property( 'isEnabled', false );
 			}
@@ -134,7 +131,7 @@ describe( 'BlockQuoteCommand', () => {
 
 		// https://github.com/ckeditor/ckeditor5-engine/issues/826
 		// it( 'is false when selection starts in an element which cannot be wrapped with blockQuote', () => {
-		// 	setModelData( doc, '<widget>x[x</widget><paragraph>y]y</paragraph>' );
+		// 	setModelData( model, '<widget>x[x</widget><paragraph>y]y</paragraph>' );
 
 		// 	expect( command ).to.have.property( 'isEnabled', false );
 		// } );
@@ -144,7 +141,7 @@ describe( 'BlockQuoteCommand', () => {
 		describe( 'applying quote', () => {
 			it( 'should wrap a single block', () => {
 				setModelData(
-					doc,
+					model,
 					'<paragraph>abc</paragraph>' +
 					'<paragraph>x[]x</paragraph>' +
 					'<paragraph>def</paragraph>'
@@ -152,7 +149,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<paragraph>abc</paragraph>' +
 					'<blockQuote><paragraph>x[]x</paragraph></blockQuote>' +
 					'<paragraph>def</paragraph>'
@@ -165,7 +162,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should wrap multiple blocks', () => {
 				setModelData(
-					doc,
+					model,
 					'<heading>a[bc</heading>' +
 					'<paragraph>xx</paragraph>' +
 					'<paragraph>de]f</paragraph>'
@@ -173,7 +170,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote>' +
 						'<heading>a[bc</heading>' +
 						'<paragraph>xx</paragraph>' +
@@ -188,7 +185,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should merge with an existing quote', () => {
 				setModelData(
-					doc,
+					model,
 					'<heading>a[bc</heading>' +
 					'<blockQuote><paragraph>x]x</paragraph><paragraph>yy</paragraph></blockQuote>' +
 					'<paragraph>def</paragraph>'
@@ -196,7 +193,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote>' +
 						'<heading>a[bc</heading>' +
 						'<paragraph>x]x</paragraph>' +
@@ -212,14 +209,14 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should not merge with a quote preceding the current block', () => {
 				setModelData(
-					doc,
+					model,
 					'<blockQuote><paragraph>abc</paragraph></blockQuote>' +
 					'<paragraph>x[]x</paragraph>'
 				);
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote><paragraph>abc</paragraph></blockQuote>' +
 					'<blockQuote><paragraph>x[]x</paragraph></blockQuote>'
 				);
@@ -232,14 +229,14 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should not merge with a quote following the current block', () => {
 				setModelData(
-					doc,
+					model,
 					'<paragraph>x[]x</paragraph>' +
 					'<blockQuote><paragraph>abc</paragraph></blockQuote>'
 				);
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote><paragraph>x[]x</paragraph></blockQuote>' +
 					'<blockQuote><paragraph>abc</paragraph></blockQuote>'
 				);
@@ -252,7 +249,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should merge with an existing quote (more blocks)', () => {
 				setModelData(
-					doc,
+					model,
 					'<heading>a[bc</heading>' +
 					'<paragraph>def</paragraph>' +
 					'<blockQuote><paragraph>x]x</paragraph></blockQuote>' +
@@ -261,7 +258,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote>' +
 						'<heading>a[bc</heading>' +
 						'<paragraph>def</paragraph>' +
@@ -277,7 +274,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should not wrap non-block content', () => {
 				setModelData(
-					doc,
+					model,
 					'<paragraph>a[bc</paragraph>' +
 					'<widget>xx</widget>' +
 					'<paragraph>de]f</paragraph>'
@@ -285,7 +282,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote>' +
 						'<paragraph>a[bc</paragraph>' +
 					'</blockQuote>' +
@@ -302,7 +299,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should correctly wrap and merge groups of blocks', () => {
 				setModelData(
-					doc,
+					model,
 					'<paragraph>a[bc</paragraph>' +
 					'<widget>xx</widget>' +
 					'<paragraph>def</paragraph>' +
@@ -313,7 +310,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote><paragraph>a[bc</paragraph></blockQuote>' +
 					'<widget>xx</widget>' +
 					'<blockQuote><paragraph>def</paragraph><paragraph>ghi</paragraph></blockQuote>' +
@@ -332,7 +329,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should correctly merge a couple of subsequent quotes', () => {
 				setModelData(
-					doc,
+					model,
 					'<paragraph>x</paragraph>' +
 					'<paragraph>a[bc</paragraph>' +
 					'<blockQuote><paragraph>def</paragraph></blockQuote>' +
@@ -344,7 +341,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<paragraph>x</paragraph>' +
 					'<blockQuote>' +
 						'<paragraph>a[bc</paragraph>' +
@@ -371,14 +368,17 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should not wrap a block which can not be in a quote', () => {
 				// blockQuote is allowed in root, but fooBlock can not be inside blockQuote.
-				doc.schema.registerItem( 'fooBlock', '$block' );
-				doc.schema.disallow( { name: 'fooBlock', inside: 'blockQuote' } );
-				buildModelConverter().for( editor.editing.modelToView )
-					.fromElement( 'fooBlock' )
-					.toElement( 'fooblock' );
+				model.schema.register( 'fooBlock', { inheritAllFrom: '$block' } );
+				model.schema.addChildCheck( ( ctx, childDef ) => {
+					if ( ctx.endsWith( 'blockQuote' ) && childDef.name == 'fooBlock' ) {
+						return false;
+					}
+				} );
+
+				editor.conversion.for( 'downcast' ).add( downcastElementToElement( { model: 'fooBlock', view: 'fooblock' } ) );
 
 				setModelData(
-					doc,
+					model,
 					'<paragraph>a[bc</paragraph>' +
 					'<fooBlock>xx</fooBlock>' +
 					'<paragraph>de]f</paragraph>'
@@ -386,7 +386,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote>' +
 						'<paragraph>a[bc</paragraph>' +
 					'</blockQuote>' +
@@ -399,21 +399,17 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should not wrap a block which parent does not allow quote inside itself', () => {
 				// blockQuote is not be allowed in fooWrapper, but fooBlock can be inside blockQuote.
-				doc.schema.registerItem( 'fooWrapper' );
-				doc.schema.registerItem( 'fooBlock', '$block' );
+				model.schema.register( 'fooWrapper' );
+				model.schema.register( 'fooBlock', { inheritAllFrom: '$block' } );
 
-				doc.schema.allow( { name: 'fooWrapper', inside: '$root' } );
-				doc.schema.allow( { name: 'fooBlock', inside: 'fooWrapper' } );
+				model.schema.extend( 'fooWrapper', { allowIn: '$root' } );
+				model.schema.extend( 'fooBlock', { allowIn: 'fooWrapper' } );
 
-				buildModelConverter().for( editor.editing.modelToView )
-					.fromElement( 'fooWrapper' )
-					.toElement( 'foowrapper' );
-				buildModelConverter().for( editor.editing.modelToView )
-					.fromElement( 'fooBlock' )
-					.toElement( 'fooblock' );
+				editor.conversion.for( 'downcast' ).add( downcastElementToElement( { model: 'fooWrapper', view: 'foowrapper' } ) );
+				editor.conversion.for( 'downcast' ).add( downcastElementToElement( { model: 'fooBlock', view: 'fooblock' } ) );
 
 				setModelData(
-					doc,
+					model,
 					'<paragraph>a[bc</paragraph>' +
 					'<fooWrapper><fooBlock>xx</fooBlock></fooWrapper>' +
 					'<paragraph>de]f</paragraph>'
@@ -421,7 +417,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote>' +
 						'<paragraph>a[bc</paragraph>' +
 					'</blockQuote>' +
@@ -436,7 +432,7 @@ describe( 'BlockQuoteCommand', () => {
 		describe( 'removing quote', () => {
 			it( 'should unwrap a single block', () => {
 				setModelData(
-					doc,
+					model,
 					'<paragraph>abc</paragraph>' +
 					'<blockQuote><paragraph>x[]x</paragraph></blockQuote>' +
 					'<paragraph>def</paragraph>'
@@ -444,7 +440,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<paragraph>abc</paragraph>' +
 					'<paragraph>x[]x</paragraph>' +
 					'<paragraph>def</paragraph>'
@@ -457,7 +453,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should unwrap multiple blocks', () => {
 				setModelData(
-					doc,
+					model,
 					'<blockQuote>' +
 						'<paragraph>a[bc</paragraph>' +
 						'<paragraph>xx</paragraph>' +
@@ -467,7 +463,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<paragraph>a[bc</paragraph>' +
 					'<paragraph>xx</paragraph>' +
 					'<paragraph>de]f</paragraph>'
@@ -480,7 +476,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should unwrap only the selected blocks - at the beginning', () => {
 				setModelData(
-					doc,
+					model,
 					'<paragraph>xx</paragraph>' +
 					'<blockQuote>' +
 						'<paragraph>a[b]c</paragraph>' +
@@ -491,7 +487,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<paragraph>xx</paragraph>' +
 					'<paragraph>a[b]c</paragraph>' +
 					'<blockQuote>' +
@@ -507,7 +503,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should unwrap only the selected blocks - at the end', () => {
 				setModelData(
-					doc,
+					model,
 					'<blockQuote>' +
 						'<paragraph>abc</paragraph>' +
 						'<paragraph>x[x</paragraph>' +
@@ -517,7 +513,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<blockQuote>' +
 						'<paragraph>abc</paragraph>' +
 					'</blockQuote>' +
@@ -532,7 +528,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should unwrap only the selected blocks - in the middle', () => {
 				setModelData(
-					doc,
+					model,
 					'<paragraph>xx</paragraph>' +
 					'<blockQuote>' +
 						'<paragraph>abc</paragraph>' +
@@ -544,7 +540,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<paragraph>xx</paragraph>' +
 					'<blockQuote><paragraph>abc</paragraph></blockQuote>' +
 					'<paragraph>c[]de</paragraph>' +
@@ -563,7 +559,7 @@ describe( 'BlockQuoteCommand', () => {
 
 			it( 'should remove multiple quotes', () => {
 				setModelData(
-					doc,
+					model,
 					'<blockQuote><paragraph>a[bc</paragraph></blockQuote>' +
 					'<paragraph>xx</paragraph>' +
 					'<blockQuote><paragraph>def</paragraph><paragraph>ghi</paragraph></blockQuote>' +
@@ -573,7 +569,7 @@ describe( 'BlockQuoteCommand', () => {
 
 				editor.execute( 'blockQuote' );
 
-				expect( getModelData( doc ) ).to.equal(
+				expect( getModelData( model ) ).to.equal(
 					'<paragraph>a[bc</paragraph>' +
 					'<paragraph>xx</paragraph>' +
 					'<paragraph>def</paragraph><paragraph>ghi</paragraph>' +

+ 22 - 15
packages/ckeditor5-block-quote/tests/blockquoteengine.js

@@ -1,34 +1,33 @@
 /**
- * @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 BlockQuoteEngine from '../src/blockquoteengine';
+import BlockQuoteEditing from '../src/blockquoteediting';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-import ListEngine from '@ckeditor/ckeditor5-list/src/listengine';
+import ListEditing from '@ckeditor/ckeditor5-list/src/listediting';
 
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
 import BlockQuoteCommand from '../src/blockquotecommand';
 
-describe( 'BlockQuoteEngine', () => {
-	let editor, doc;
+describe( 'BlockQuoteEditing', () => {
+	let editor, model;
 
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ BlockQuoteEngine, Paragraph ]
+				plugins: [ BlockQuoteEditing, Paragraph ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
-
-				doc = editor.document;
+				model = editor.model;
 			} );
 	} );
 
 	afterEach( () => {
-		editor.destroy();
+		return editor.destroy();
 	} );
 
 	it( 'adds a blockQuote command', () => {
@@ -36,12 +35,20 @@ describe( 'BlockQuoteEngine', () => {
 	} );
 
 	it( 'allows for blockQuote in the $root', () => {
-		expect( doc.schema.check( { name: 'blockQuote', inside: '$root' } ) ).to.be.true;
+		expect( model.schema.checkChild( [ '$root' ], 'blockQuote' ) ).to.be.true;
 	} );
 
 	it( 'allows for $block in blockQuote', () => {
-		expect( doc.schema.check( { name: '$block', inside: 'blockQuote' } ) ).to.be.true;
-		expect( doc.schema.check( { name: 'paragraph', inside: 'blockQuote' } ) ).to.be.true;
+		expect( model.schema.checkChild( [ '$root', 'blockQuote' ], '$block' ) ).to.be.true;
+		expect( model.schema.checkChild( [ '$root', 'blockQuote' ], 'paragraph' ) ).to.be.true;
+	} );
+
+	it( 'does not allow for blockQuote in blockQuote', () => {
+		expect( model.schema.checkChild( [ '$root', 'blockQuote' ], 'blockQuote' ) ).to.be.false;
+	} );
+
+	it( 'does not break when checking an unregisterd item', () => {
+		expect( model.schema.checkChild( [ '$root', 'blockQuote' ], 'foo' ) ).to.be.false;
 	} );
 
 	it( 'adds converters to the data pipeline', () => {
@@ -49,12 +56,12 @@ describe( 'BlockQuoteEngine', () => {
 
 		editor.setData( data );
 
-		expect( getModelData( doc ) ).to.equal( '<blockQuote><paragraph>[]x</paragraph></blockQuote>' );
+		expect( getModelData( model ) ).to.equal( '<blockQuote><paragraph>[]x</paragraph></blockQuote>' );
 		expect( editor.getData() ).to.equal( data );
 	} );
 
 	it( 'adds a converter to the view pipeline', () => {
-		setModelData( doc, '<blockQuote><paragraph>x</paragraph></blockQuote>' );
+		setModelData( model, '<blockQuote><paragraph>x</paragraph></blockQuote>' );
 
 		expect( editor.getData() ).to.equal( '<blockquote><p>x</p></blockquote>' );
 	} );
@@ -62,7 +69,7 @@ describe( 'BlockQuoteEngine', () => {
 	it( 'allows list items inside blockQuote', () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ BlockQuoteEngine, Paragraph, ListEngine ]
+				plugins: [ BlockQuoteEditing, Paragraph, ListEditing ]
 			} )
 			.then( editor => {
 				editor.setData( '<blockquote><ul><li>xx</li></ul></blockquote>' );

+ 76 - 0
packages/ckeditor5-block-quote/tests/blockquoteui.js

@@ -0,0 +1,76 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* global document */
+
+import BlockQuoteEditing from '../src/blockquoteediting';
+import BlockQuoteUI from '../src/blockquoteui';
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+
+describe( 'BlockQuoteUI', () => {
+	let editor, command, element;
+
+	beforeEach( () => {
+		element = document.createElement( 'div' );
+		document.body.appendChild( element );
+
+		return ClassicTestEditor
+			.create( element, {
+				plugins: [ BlockQuoteEditing, BlockQuoteUI ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				command = editor.commands.get( 'blockQuote' );
+			} );
+	} );
+
+	afterEach( () => {
+		element.remove();
+
+		return editor.destroy();
+	} );
+
+	describe( 'blockQuote button', () => {
+		it( 'has the base properties', () => {
+			const button = editor.ui.componentFactory.create( 'blockQuote' );
+
+			expect( button ).to.have.property( 'label', 'Block quote' );
+			expect( button ).to.have.property( 'icon' );
+			expect( button ).to.have.property( 'tooltip', true );
+		} );
+
+		it( 'has isOn bound to command\'s value', () => {
+			const button = editor.ui.componentFactory.create( 'blockQuote' );
+
+			command.value = false;
+			expect( button ).to.have.property( 'isOn', false );
+
+			command.value = true;
+			expect( button ).to.have.property( 'isOn', true );
+		} );
+
+		it( 'has isEnabled bound to command\'s isEnabled', () => {
+			const button = editor.ui.componentFactory.create( 'blockQuote' );
+
+			command.isEnabled = true;
+			expect( button ).to.have.property( 'isEnabled', true );
+
+			command.isEnabled = false;
+			expect( button ).to.have.property( 'isEnabled', false );
+		} );
+
+		it( 'executes command when it\'s executed', () => {
+			const button = editor.ui.componentFactory.create( 'blockQuote' );
+
+			const spy = sinon.stub( editor, 'execute' );
+
+			button.fire( 'execute' );
+
+			expect( spy.calledOnce ).to.be.true;
+			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
+		} );
+	} );
+} );

+ 103 - 66
packages/ckeditor5-block-quote/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.
  */
 
@@ -12,12 +12,17 @@ import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
 import List from '@ckeditor/ckeditor5-list/src/list';
 import Enter from '@ckeditor/ckeditor5-enter/src/enter';
 import Delete from '@ckeditor/ckeditor5-typing/src/delete';
+import Heading from '@ckeditor/ckeditor5-heading/src/heading';
 
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
-import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import {
+	parse as parseModel,
+	getData as getModelData,
+	setData as setModelData
+} from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
-describe( 'BlockQuote', () => {
-	let editor, doc, element;
+describe( 'BlockQuote integration', () => {
+	let editor, model, element, viewDocument;
 
 	beforeEach( () => {
 		element = document.createElement( 'div' );
@@ -25,11 +30,12 @@ describe( 'BlockQuote', () => {
 
 		return ClassicTestEditor
 			.create( element, {
-				plugins: [ BlockQuote, Paragraph, Image, ImageCaption, List, Enter, Delete ]
+				plugins: [ BlockQuote, Paragraph, Image, ImageCaption, List, Enter, Delete, Heading ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
-				doc = editor.document;
+				model = editor.model;
+				viewDocument = editor.editing.view.document;
 			} );
 	} );
 
@@ -50,9 +56,9 @@ describe( 'BlockQuote', () => {
 			const data = fakeEventData();
 			const execSpy = sinon.spy( editor, 'execute' );
 
-			setModelData( doc, '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
+			setModelData( model, '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			// Only enter command should be executed.
 			expect( data.preventDefault.called ).to.be.true;
@@ -64,9 +70,9 @@ describe( 'BlockQuote', () => {
 			const data = fakeEventData();
 			const execSpy = sinon.spy( editor, 'execute' );
 
-			setModelData( doc, '<blockQuote><paragraph>xx[]</paragraph></blockQuote>' );
+			setModelData( model, '<blockQuote><paragraph>xx[]</paragraph></blockQuote>' );
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			// Only enter command should be executed.
 			expect( data.preventDefault.called ).to.be.true;
@@ -78,9 +84,9 @@ describe( 'BlockQuote', () => {
 			const data = fakeEventData();
 			const execSpy = sinon.spy( editor, 'execute' );
 
-			setModelData( doc, '<blockQuote><paragraph>[]xx</paragraph></blockQuote>' );
+			setModelData( model, '<blockQuote><paragraph>[]xx</paragraph></blockQuote>' );
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			// Only enter command should be executed.
 			expect( data.preventDefault.called ).to.be.true;
@@ -92,9 +98,9 @@ describe( 'BlockQuote', () => {
 			const data = fakeEventData();
 			const execSpy = sinon.spy( editor, 'execute' );
 
-			setModelData( doc, '<blockQuote><paragraph>[</paragraph><paragraph>]</paragraph></blockQuote>' );
+			setModelData( model, '<blockQuote><paragraph>[</paragraph><paragraph>]</paragraph></blockQuote>' );
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			// Only enter command should be executed.
 			expect( data.preventDefault.called ).to.be.true;
@@ -105,7 +111,7 @@ describe( 'BlockQuote', () => {
 		it( 'does not interfere with a similar handler in the list feature', () => {
 			const data = fakeEventData();
 
-			setModelData( doc,
+			setModelData( model,
 				'<paragraph>x</paragraph>' +
 				'<blockQuote>' +
 					'<listItem indent="0" type="bulleted">a</listItem>' +
@@ -114,11 +120,11 @@ describe( 'BlockQuote', () => {
 				'<paragraph>x</paragraph>'
 			);
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			expect( data.preventDefault.called ).to.be.true;
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<paragraph>x</paragraph>' +
 				'<blockQuote>' +
 					'<listItem indent="0" type="bulleted">a</listItem>' +
@@ -132,34 +138,34 @@ describe( 'BlockQuote', () => {
 			const data = fakeEventData();
 			const execSpy = sinon.spy( editor, 'execute' );
 
-			setModelData( doc, '<paragraph>x</paragraph><blockQuote><paragraph>[]</paragraph></blockQuote><paragraph>x</paragraph>' );
+			setModelData( model, '<paragraph>x</paragraph><blockQuote><paragraph>[]</paragraph></blockQuote><paragraph>x</paragraph>' );
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			expect( data.preventDefault.called ).to.be.true;
 			expect( execSpy.calledOnce ).to.be.true;
 			expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
 
-			expect( getModelData( doc ) ).to.equal( '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
+			expect( getModelData( model ) ).to.equal( '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
 		} );
 
 		it( 'escapes block quote if selection is in an empty block in the middle of a block quote', () => {
 			const data = fakeEventData();
 			const execSpy = sinon.spy( editor, 'execute' );
 
-			setModelData( doc,
+			setModelData( model,
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph><paragraph>b</paragraph></blockQuote>' +
 				'<paragraph>x</paragraph>'
 			);
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			expect( data.preventDefault.called ).to.be.true;
 			expect( execSpy.calledOnce ).to.be.true;
 			expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a</paragraph></blockQuote>' +
 				'<paragraph>[]</paragraph>' +
@@ -172,19 +178,19 @@ describe( 'BlockQuote', () => {
 			const data = fakeEventData();
 			const execSpy = sinon.spy( editor, 'execute' );
 
-			setModelData( doc,
+			setModelData( model,
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph></blockQuote>' +
 				'<paragraph>x</paragraph>'
 			);
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			expect( data.preventDefault.called ).to.be.true;
 			expect( execSpy.calledOnce ).to.be.true;
 			expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a</paragraph></blockQuote>' +
 				'<paragraph>[]</paragraph>' +
@@ -197,13 +203,13 @@ describe( 'BlockQuote', () => {
 			const execSpy = sinon.spy( editor, 'execute' );
 			const scrollSpy = sinon.stub( editor.editing.view, 'scrollToTheSelection' );
 
-			setModelData( doc,
+			setModelData( model,
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph></blockQuote>' +
 				'<paragraph>x</paragraph>'
 			);
 
-			editor.editing.view.fire( 'enter', data );
+			viewDocument.fire( 'enter', data );
 
 			sinon.assert.calledOnce( scrollSpy );
 			sinon.assert.callOrder( execSpy, scrollSpy );
@@ -222,15 +228,15 @@ describe( 'BlockQuote', () => {
 		it( 'merges paragraph into paragraph in the quote', () => {
 			const data = fakeEventData();
 
-			setModelData( doc,
+			setModelData( model,
 				'<blockQuote><paragraph>a</paragraph><paragraph>b</paragraph></blockQuote>' +
 				'<paragraph>[]c</paragraph>' +
 				'<paragraph>d</paragraph>'
 			);
 
-			editor.editing.view.fire( 'delete', data );
+			viewDocument.fire( 'delete', data );
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<blockQuote><paragraph>a</paragraph><paragraph>b[]c</paragraph></blockQuote>' +
 				'<paragraph>d</paragraph>'
 			);
@@ -239,15 +245,15 @@ describe( 'BlockQuote', () => {
 		it( 'merges paragraph from a quote into a paragraph before quote', () => {
 			const data = fakeEventData();
 
-			setModelData( doc,
+			setModelData( model,
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>[]a</paragraph><paragraph>b</paragraph></blockQuote>' +
 				'<paragraph>y</paragraph>'
 			);
 
-			editor.editing.view.fire( 'delete', data );
+			viewDocument.fire( 'delete', data );
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<paragraph>x[]a</paragraph>' +
 				'<blockQuote><paragraph>b</paragraph></blockQuote>' +
 				'<paragraph>y</paragraph>'
@@ -257,16 +263,16 @@ describe( 'BlockQuote', () => {
 		it( 'merges two quotes', () => {
 			const data = fakeEventData();
 
-			setModelData( doc,
+			setModelData( model,
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a</paragraph><paragraph>b</paragraph></blockQuote>' +
 				'<blockQuote><paragraph>[]c</paragraph><paragraph>d</paragraph></blockQuote>' +
 				'<paragraph>y</paragraph>'
 			);
 
-			editor.editing.view.fire( 'delete', data );
+			viewDocument.fire( 'delete', data );
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a</paragraph><paragraph>b[]c</paragraph><paragraph>d</paragraph></blockQuote>' +
 				'<paragraph>y</paragraph>'
@@ -276,16 +282,16 @@ describe( 'BlockQuote', () => {
 		it( 'removes empty quote when merging into another quote', () => {
 			const data = fakeEventData();
 
-			setModelData( doc,
+			setModelData( model,
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a</paragraph></blockQuote>' +
 				'<blockQuote><paragraph>[]</paragraph></blockQuote>' +
 				'<paragraph>y</paragraph>'
 			);
 
-			editor.editing.view.fire( 'delete', data );
+			viewDocument.fire( 'delete', data );
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>a[]</paragraph></blockQuote>' +
 				'<paragraph>y</paragraph>'
@@ -295,23 +301,25 @@ describe( 'BlockQuote', () => {
 		it( 'removes empty quote when merging into a paragraph', () => {
 			const data = fakeEventData();
 
-			setModelData( doc,
+			setModelData( model,
 				'<paragraph>x</paragraph>' +
 				'<blockQuote><paragraph>[]</paragraph></blockQuote>' +
 				'<paragraph>y</paragraph>'
 			);
 
-			editor.editing.view.fire( 'delete', data );
+			viewDocument.fire( 'delete', data );
 
-			expect( getModelData( doc ) ).to.equal(
+			expect( getModelData( model ) ).to.equal(
 				'<paragraph>x[]</paragraph>' +
 				'<paragraph>y</paragraph>'
 			);
 		} );
 	} );
 
+	// Historically, due to problems with schema, images were not quotable.
+	// These tests were left here to confirm that after schema was fixed, images are properly quotable.
 	describe( 'compatibility with images', () => {
-		it( 'does not quote a simple image', () => {
+		it( 'quotes a simple image', () => {
 			const element = document.createElement( 'div' );
 			document.body.appendChild( element );
 
@@ -321,7 +329,7 @@ describe( 'BlockQuote', () => {
 					plugins: [ BlockQuote, Paragraph, Image ]
 				} )
 				.then( editor => {
-					setModelData( editor.document,
+					setModelData( editor.model,
 						'<paragraph>fo[o</paragraph>' +
 						'<image src="foo.png"></image>' +
 						'<paragraph>b]ar</paragraph>'
@@ -329,19 +337,21 @@ describe( 'BlockQuote', () => {
 
 					editor.execute( 'blockQuote' );
 
-					expect( getModelData( editor.document ) ).to.equal(
-						'<blockQuote><paragraph>fo[o</paragraph></blockQuote>' +
-						'<image src="foo.png"></image>' +
-						'<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
+					expect( getModelData( editor.model ) ).to.equal(
+						'<blockQuote>' +
+							'<paragraph>fo[o</paragraph>' +
+							'<image src="foo.png"></image>' +
+							'<paragraph>b]ar</paragraph>' +
+						'</blockQuote>'
 					);
 
 					element.remove();
-					editor.destroy();
+					return editor.destroy();
 				} );
 		} );
 
-		it( 'does not quote an image with caption', () => {
-			setModelData( doc,
+		it( 'quotes an image with caption', () => {
+			setModelData( model,
 				'<paragraph>fo[o</paragraph>' +
 				'<image src="foo.png">' +
 					'<caption>xxx</caption>' +
@@ -351,17 +361,19 @@ describe( 'BlockQuote', () => {
 
 			editor.execute( 'blockQuote' );
 
-			expect( getModelData( doc ) ).to.equal(
-				'<blockQuote><paragraph>fo[o</paragraph></blockQuote>' +
-				'<image src="foo.png">' +
-					'<caption>xxx</caption>' +
-				'</image>' +
-				'<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
+			expect( getModelData( model ) ).to.equal(
+				'<blockQuote>' +
+					'<paragraph>fo[o</paragraph>' +
+					'<image src="foo.png">' +
+						'<caption>xxx</caption>' +
+					'</image>' +
+					'<paragraph>b]ar</paragraph>' +
+				'</blockQuote>'
 			);
 		} );
 
-		it( 'does not add an image to existing quote', () => {
-			setModelData( doc,
+		it( 'adds an image to an existing quote', () => {
+			setModelData( model,
 				'<paragraph>fo[o</paragraph>' +
 				'<image src="foo.png">' +
 					'<caption>xxx</caption>' +
@@ -371,12 +383,37 @@ describe( 'BlockQuote', () => {
 
 			editor.execute( 'blockQuote' );
 
-			expect( getModelData( doc ) ).to.equal(
-				'<blockQuote><paragraph>fo[o</paragraph></blockQuote>' +
-				'<image src="foo.png">' +
-					'<caption>xxx</caption>' +
-				'</image>' +
-				'<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
+			expect( getModelData( model ) ).to.equal(
+				'<blockQuote>' +
+					'<paragraph>fo[o</paragraph>' +
+					'<image src="foo.png">' +
+						'<caption>xxx</caption>' +
+					'</image>' +
+					'<paragraph>b]ar</paragraph>' +
+				'</blockQuote>'
+			);
+		} );
+	} );
+
+	// When blockQuote with a paragraph was pasted into a list item, the item contained the paragraph. It was invalid.
+	// There is a test which checks whether blockQuote will split the list items instead of merging with.
+	describe( 'compatibility with lists', () => {
+		it( 'does not merge the paragraph with list item', () => {
+			setModelData( model, '<listItem indent="0" type="bulleted">fo[]o</listItem>' );
+
+			const df = parseModel(
+				'<blockQuote><paragraph>xxx</paragraph></blockQuote><heading1>yyy</heading1>',
+				model.schema
+			);
+
+			model.insertContent( df, model.document.selection );
+
+			expect( getModelData( model ) ).to.equal(
+				'<listItem indent="0" type="bulleted">fo</listItem>' +
+				'<blockQuote>' +
+				'<paragraph>xxx</paragraph>' +
+				'</blockQuote>' +
+				'<heading1>yyy[]o</heading1>'
 			);
 		} );
 	} );

+ 3 - 3
packages/ckeditor5-block-quote/tests/manual/blockquote.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 @@ ClassicEditor
 		plugins: [
 			ArticlePluginSet
 		],
-		toolbar: [ 'headings', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ]
+		toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ]
 	} )
 	.then( editor => {
 		window.editor = editor;
@@ -25,5 +25,5 @@ ClassicEditor
 	} );
 
 window.setInterval( function() {
-	console.log( getData( window.editor.document ) );
+	console.log( getData( window.editor.model ) );
 }, 3000 );

+ 13 - 0
packages/ckeditor5-block-quote/theme/blockquote.css

@@ -0,0 +1,13 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+.ck-content blockquote {
+	/* See #12 */
+	overflow: hidden;
+	padding-left: 1.5em;
+	margin-left: 0;
+	font-style: italic;
+	border-left: solid 5px hsl(0, 0%, 80%);
+}

+ 0 - 11
packages/ckeditor5-block-quote/theme/theme.scss

@@ -1,11 +0,0 @@
-// Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
-// For licensing, see LICENSE.md or http://ckeditor.com/license
-
-blockquote {
-	border-left: solid 5px #CCC;
-	padding-left: 20px;
-	margin-left: 0;
-	font-style: italic;
-	// See #12.
-	overflow: hidden;
-}