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

Merge branch 'master' into t/ckeditor5/742

Kamil Piechaczek 7 лет назад
Родитель
Сommit
f71b5a8e6f

+ 2 - 4
packages/ckeditor5-editor-decoupled/.travis.yml

@@ -24,7 +24,5 @@ after_success:
 - ckeditor5-dev-tests-save-revision
 - ckeditor5-dev-tests-save-revision
 env:
 env:
   global:
   global:
-  - secure: yGjqvN+FNsbRuAneBgIi9AyraRbhCSm8MEd7FPq/exSTjfsWofOq5i9O7o7/02bJVZ8rveD71OaqAKQCWVG8BD4umbpu0N0PpkJbjVi8jmytzN28mUxgrmZkFbr96C5YnGqvdxf4AB9wT5ExtzTfp5XUO383/WoGPu9qt6rmb5II1PHwhT1WzXql/pCU5eGLccCYtlEr3ybWD9n3wiC4fM90M5axuSu9EwpvTEfMJQbeqEv7jSu+qlPUAJ4rSTmgt1pGIMxJgUpDi76qnn4VdTxRUZy+4heAFT9c/96tPV0QAz8cpVM3qGKSTuF8FAY71sddF4PX8wfG/1GjO+dHEmbop5Kz8UXnS8rnp9gx4Q5be8iW0DfPCqjc8a2ULWgUMr4jBnbZKpMHNT83Li1TfGflIiDksh68EKMRHHoVzxKJ2ysBSMzPqLfLXSyY9Jl/j5OBAiVl1n22oq5t9AlT6JEm0bYcgIwVIRpJqPMZMkZBCoLZHSyVfdIqdT+bcNgSEG5iLrXS9FlqsOS9FB4U3DbvPZbNBq+uoEuAyk5w+T5OkqIhXBddUG9emaa6TuQNNcATyucwWxB3HC1UAK8s5XTWVUzQ86nUShzH7aHJky+PJN0hkYAxwjldwtzTWwKdw+SBjk3Cl5h8JEepJSX0Lqtwci4XGoHsJ4JL5UCU5OM=
-  - secure: KDj0ramEp45sMIyRogmNesjH5O2q7k5kopvDjG5+O1PWjMc+moxoSLYdcqknINLdHkid/SfwyfDppO6rvsVuLR9nvq7SPsM/xDGtLMoLYwUoWPrK57tQ1exl1FUWHzZs/7UJOwC+GnqqtL6bN6PAAj4aSLoTai/xDiDBTRMrVzDmYGeHaHihI44jtqsfBEkCGd9vfB0JDZvhncZqQrEubTwFnh+QwZXTnVR837p+EWhT87HvH/OPioXPnEF3O3N72u/WpqmQKrgM2vUWOfjHzZTEleBBJZnbrFFKzoOczig2a4I0hLiaqtK/bsSpjMpyKVzW+Jh+bQiKLk2B3DlUL+/6RH/dioo0DM438dI5sT46dNDl8k3mXCfJyGpn1by3EKdjEkkgLz095YzaCIEKBLVqKky4FhqRduEGpmXXMJw5xJQuaLOqC8jg0bgAl/E6q0uMyVPJuaC1ewGOHSx33j09ZXrOCAk57KzXgJzVi3BJR2kf4x2KygRqqVtFVYjOyvdAS2EieytMKwMVfQfrtmgJU5KCcbYzNX6ZEKEdS7SxTlyPtDdJssxvuuRU8xxgkswBxTkFGFG77dy0BTsa0xmAdiJnb/Z3F9hJP3VySOKj/lnATO3Yzr5v5l0jD1SQqfG9IYvDE/fDULs7/cFuj46voHIVBkziQ+IAqv/3vYo=
-  - secure: Gx267GbmaD2jMWCirha+1wQ/A7kJnHMGIkzUUBgzsy8CszGfQv8WZe8KnSeiLjV/+/gXg9v+/0LPULDjs9e2d8FdpGdA+7iE3HPCXijR/LWCJq9ZiMN6QrCSaLI5o6mpkUB29sfgKOVfC6CEjyd00sK3LzcjSJeApSgPY90g1FOsbMg/t1PIzFUrJytvFnjUr2cOPay2ZeXrXFLox2je8n1YvtCmWt6QgMGmT0bBQRjYFHhiw7IsGZ9aT36Um4qx9hXADixIXfE+RRxhdrVcFhcmESfr7US9inZKMDGQa48VYUX8kdPr073bi9Wh8c5WsmiIT4cspviyLhWfHvq5hwhGt1FTbfcuQIeq172hFoLRphwWujENrzJnXjSbKIDFHqv08p0TJJLe/Q2af0imeWSL9+hszIN7Z7S12VYxbixAMmxaB9DXhqdVN7qhNKV4e/KhZv+60DLUPIrP7iLYNpb2TK1D9tr5rnF3v0FbJTCCr1XyCRbZSdGi9HnAm5bw7n5rllCW7NB6QUIPrBwBu7cJ37PqFeJXZDIXlmBEGP+ZBV4u7vIBBO8CXaaVCEvflIGqE15PbGvSc8wPiP4X4tLjnpgUY6OP67sCNRbuH0XeuvhGPRf3C15GNp14qM7cFtmoKSKxy+sUQBtx5LCpESI/JMEUiotqk232l0vqyR8=
-  - secure: eqj8DaCp7F6/XXy6npJ4YO7jfQaAbMBYo2rsvYAk0lubYSL0fJsbtMR1Qm8kbXFENxFKhpcU6fI9pAPKBT2fvn3/dnIHTng4xklpWWdl8CgojGJ8wPu/RjTa9UxDXvsDaiixipg/uPfc9oReCzEhulNVB6V6iq9jHeBJaQqgI5R1b3GB3U/sIF+Y7vWhICjM9s7WE/ESqADVHMpSQnzMIAiFJwJgqB1zu9bkwSsjhMgNscgifWuB8+AoXVeCesSYyJQsG7PHSoY+JlzGUw05xtSc6x+3bivs7dyrt68bkaFkVN2YmAstxl45R0Itd5dgPt/bvXKo63CTtkNAsyvUrK8Z3F2VqnqqTJ+3exQbkHCxHW2Pd7QOyxVAy00OCdIcjEAtSQp5HrI9ZAipP/9JJq5KzdaNzpSLwuL6jt9WysnMQBF3LMB1ONWRlrCcGdkUfTt2cjBMR3yiaaPJK+Bk93eSNPDJ9BaQbIGtsI6p2aojlgYzz7Uvon9d1lY9mlZi5BnyWwiOzfpmdALZlnl9yl6Bh/PI7SfW0zWRYOWS33pSWt9nkoNre9uTOdAhlJpS1PohNraEzw1AYR1j6hRZszqpf8JpMIrdoUOdf4QMh1LIJZW7EbEULsvU9fDzLZucjQyYpZLvFjXQ15UR+u4iTsC8c9rNGdDJkuBAsg81zis=
+  - secure: czAq3wmkippaXuAaoiibg+6vtcfF6ylU6QgzLIABhegKQaOARSB6Cae7WeveCBKu03aiVxWSTzPtAkNnq4qGK8mxEh012r1F50zKBInR9QiEB8HHkiUrkO2cVt21hX/PzdCJ+Ys4Cjthm1xsQwZ8vrfXsZnyahkQCzLxqkQR7mYNiQGPRaGvxaJmgMlfNNgoRFO7KzcmTNX9OeU+4k2BJOuua8NuoZx01aCktaC6qGpYLFE9Kj+oDpWMcQXp8y20hIDeY7GOh2zRDlQqXQV9FslxOykHE0uJ2dRa6Wvf65PTQknrpkBLHQgdaIM0e/E4+xtKMxl7676P1gbpQAntyCD8QybE3N+ldJqrOzzLmQ5hTxKjHxzU3/rtXqowRCtJS1a9/gV6ABbE3PKzfBi1fhkx7uyzc84owKTuR8VMTuXwPqjAuBbbD2Limv1GbS+Of6VPlaM7FGg22ZIjWr5H/kEIydvB9+9ffIOCjwi+5LObss1MiqZTn6tYwJvq6ELTj4a/6rXF4r2G4GLqPfheNxad3FXrTGsT2BYkrp8J1YiNwHVPnaec16S8tzRS7dQjKQiTinp/3wtOsdJ87XY+Z0wKFwv1nvn9wFWpTLo2Lh9a1grxW05HvrrKr0MIwlVdieHHsjtaBMYbwS1WtIoOMA1znsghM6ZPUVGzYX5mn5w=
+  - secure: VJkWZMdkuuvwdHMyhJdTiJ9jjAtuVOQn7sQjpBAPzfLyjE7DliaS0gYHz5KG8RLs1Qn99ShNMr6PXcuZ7tMVF4YMSLU4AHh4+wg+EYXxWDbiiz3AbSPa2ScRVQrqayEdvdHv77ydPSXRj7wY2wWzpSQml6zdPHNhLRi9czKxPmehEloZKZfBiJY9/CmeiIF/Fv1KK/OsuOgafyOOG/ffJeqiI4agYW8PgxrMRl71YITl9IOWDZw093x4AqlXFG8JUxYk0JqiSJDWJCSxID7A/12lOe/ExsPPRQ69EKXMsMxsVLKhNlHpo3GHMXO0bf461Y0Y/1exCbeKP+F2ILaBJOKxhPD6ulWLTBcB/58XV/ke8G8cwYyFKl3nNxcejK1bR8fE9/4q+vsjnCTNq4tJxRN1RW324uC7AbL8f/Mur5TIWUGftmjhFLU+bjKkuTADhbwB9XYU2KhmAN96fpEvQ4RIz4/nIs9dbfKY/Hj2YAyblfXeWLMPxmhOXTScR7dkw+TWDiU2gUCw2MjU8AyOhB3Iwenxgpe+jGObvt+zPZz8UWgyJYb+KtZsNlmr+vvISa5m+jAukOcY2afH0tH/mMvwdXntLH7A+X40BWrP/14cA609clyw5ou5Jaxo+XPVX0GAsR/B99NkJbUGLKUgGP4fWf3WfGra1WVZZ9PKmuo=

+ 20 - 13
packages/ckeditor5-editor-decoupled/docs/framework/guides/document-editor.md

@@ -5,7 +5,7 @@ order: 30
 
 
 # Document editor
 # Document editor
 
 
-The {@link examples/builds/document-editor document editor example} showcases the {@link builds/guides/quick-start#document-editor document editor build} designed for document editing with a customized UI representing the layout of a sheet of paper. It was created on top of the {@link module:editor-decoupled/decouplededitor~DecoupledEditor `DecoupledEditor`} and makes the best of what it offers: the freedom to choose the location of the crucial UI elements in the application.
+The {@link examples/builds/document-editor document editor example} showcases the {@link builds/guides/quick-start#document-editor document editor build} designed for document editing with a customized UI representing the layout of a sheet of paper. It was created on top of the {@link module:editor-decoupled/decouplededitor~DecoupledEditor `DecoupledEditor`} class and makes the best of what it offers: the freedom to choose the location of the crucial UI elements in the application.
 
 
 In this tutorial you will learn how to create your own document editor with a customized user interface, step–by–step.
 In this tutorial you will learn how to create your own document editor with a customized user interface, step–by–step.
 
 
@@ -13,27 +13,32 @@ In this tutorial you will learn how to create your own document editor with a cu
 
 
 ## The editor
 ## The editor
 
 
-The `DecoupledDocumentEditor` includes all the necessary features for the task. All you need to do is import it and create a new instance.
+The document editor build includes all the necessary features for the task. All you need to do is import it and create a new instance.
 
 
 <info-box>
 <info-box>
 	See the {@link builds/guides/quick-start#document-editor quick start guide} to learn how to install the document editor build.
 	See the {@link builds/guides/quick-start#document-editor quick start guide} to learn how to install the document editor build.
 </info-box>
 </info-box>
 
 
-Unlike the {@link builds/guides/overview#classic-editor classic editor}, the document editor does not require any data container in the DOM. Instead, it accepts a string containing the initial data as the first argument of the static `create()` method. To get the output data, use the {@link module:core/editor/utils/dataapimixin~DataApi#getData `getData`} method.
+The document editor can be created using the existing data container in the DOM. It can also accept a raw data string and create the editable by itself. To get the output data, use the {@link module:core/editor/utils/dataapimixin~DataApi#getData `getData()`} method.
+
+<info-box>
+	See the {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`} to learn about different approaches to the initialization of the editor.
+</info-box>
 
 
 ```js
 ```js
 import DecoupledDocumentEditor from '@ckeditor/ckeditor5-build-decoupled-document/src/ckeditor';
 import DecoupledDocumentEditor from '@ckeditor/ckeditor5-build-decoupled-document/src/ckeditor';
 
 
 DecoupledDocumentEditor
 DecoupledDocumentEditor
-	.create( '<p>Initial editor data.</p>', {
-		toolbarContainer: document.querySelector( '.document-editor__toolbar' ),
-		editableContainer: document.querySelector( '.document-editor__editable' ),
-
+	.create( document.querySelector( '.document-editor__editable' ), {
 		cloudServices: {
 		cloudServices: {
 			....
 			....
 		}
 		}
 	} )
 	} )
 	.then( editor => {
 	.then( editor => {
+		const toolbarContainer = document.querySelector( '.document-editor__toolbar' );
+
+		toolbarContainer.appendChild( editor.ui.view.toolbar.element );
+
 		window.editor = editor;
 		window.editor = editor;
 	} )
 	} )
 	.catch( err => {
 	.catch( err => {
@@ -41,9 +46,7 @@ DecoupledDocumentEditor
 	} );
 	} );
 ```
 ```
 
 
-You may have noticed two configuration options used here: {@link module:core/editor/editorconfig~EditorConfig#toolbarContainer `config.toolbarContainer`} and {@link module:core/editor/editorconfig~EditorConfig#editableContainer `config.editableContainer`}. They specify the location of the editor toolbar and editable in your application.
-
-If you do not specify these configuration options, you have to make sure the editor UI is injected into your application after it fires the {@link module:core/editor/editorwithui~EditorWithUI#event:uiReady `uiReady`} event. The toolbar element is accessible via `editor.ui.view.toolbar.element` and the editable element can be found under `editor.ui.view.editable.element`.
+You may have noticed that you have to make sure the editor UI is injected into your application after it fires the {@link module:core/editor/editorwithui~EditorWithUI#event:uiReady `Editor#uiReady`} event. The toolbar element can be found under `editor.ui.view.toolbar.element`.
 
 
 <info-box>
 <info-box>
 	The document editor supports the Easy Image plugin provided by [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services/) out of the box. Please refer to the {@link features/image-upload#easy-image documentation} to learn more.
 	The document editor supports the Easy Image plugin provided by [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services/) out of the box. Please refer to the {@link features/image-upload#easy-image documentation} to learn more.
@@ -60,7 +63,11 @@ The following structure has two containers that correspond to the configuration
 ```html
 ```html
 <div class="document-editor">
 <div class="document-editor">
 	<div class="document-editor__toolbar"></div>
 	<div class="document-editor__toolbar"></div>
-	<div class="document-editor__editable"></div>
+	<div class="document-editor__editable-container">
+		<div class="document-editor__editable">
+			<p>The initial editor data.</p>
+		</div>
+	</div>
 </div>
 </div>
 ```
 ```
 
 
@@ -114,7 +121,7 @@ The editable should look like a sheet of paper, centered in its scrollable conta
 
 
 ```css
 ```css
 /* Make the editable container look like the inside of a native word processor application. */
 /* Make the editable container look like the inside of a native word processor application. */
-.document-editor__editable {
+.document-editor__editable-container {
 	padding: calc( 2 * var(--ck-spacing-large) );
 	padding: calc( 2 * var(--ck-spacing-large) );
 	background: var(--ck-color-base-foreground);
 	background: var(--ck-color-base-foreground);
 
 
@@ -122,7 +129,7 @@ The editable should look like a sheet of paper, centered in its scrollable conta
 	overflow-y: scroll;
 	overflow-y: scroll;
 }
 }
 
 
-.document-editor__editable .ck-editor__editable {
+.document-editor__editable-container .ck-editor__editable {
 	/* Set the dimensions of the "page". */
 	/* Set the dimensions of the "page". */
 	width: 15.8cm;
 	width: 15.8cm;
 	min-height: 21cm;
 	min-height: 21cm;

+ 73 - 81
packages/ckeditor5-editor-decoupled/src/decouplededitor.js

@@ -12,7 +12,10 @@ import DataApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin
 import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
 import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
 import DecoupledEditorUI from './decouplededitorui';
 import DecoupledEditorUI from './decouplededitorui';
 import DecoupledEditorUIView from './decouplededitoruiview';
 import DecoupledEditorUIView from './decouplededitoruiview';
+import getDataFromElement from '@ckeditor/ckeditor5-utils/src/dom/getdatafromelement';
+import setDataInElement from '@ckeditor/ckeditor5-utils/src/dom/setdatainelement';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
+import isElement from '@ckeditor/ckeditor5-utils/src/lib/lodash/isElement';
 
 
 /**
 /**
  * The {@glink builds/guides/overview#decoupled-editor decoupled editor} implementation.
  * The {@glink builds/guides/overview#decoupled-editor decoupled editor} implementation.
@@ -28,11 +31,11 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  * In order to create a decoupled editor instance, use the static
  * In order to create a decoupled editor instance, use the static
  * {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`} method.
  * {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`} method.
  *
  *
- * # Decoupled editor and document build
+ * # Decoupled editor and document editor build
  *
  *
  * The decoupled editor can be used directly from source (if you installed the
  * The decoupled editor can be used directly from source (if you installed the
  * [`@ckeditor/ckeditor5-editor-decoupled`](https://www.npmjs.com/package/@ckeditor/ckeditor5-editor-decoupled) package)
  * [`@ckeditor/ckeditor5-editor-decoupled`](https://www.npmjs.com/package/@ckeditor/ckeditor5-editor-decoupled) package)
- * but it is also available in the {@glink builds/guides/overview#document-editor document build}.
+ * but it is also available in the {@glink builds/guides/overview#document-editor document editor build}.
  *
  *
  * {@glink builds/guides/overview Builds} are ready-to-use editors with plugins bundled in. When using the editor from
  * {@glink builds/guides/overview Builds} are ready-to-use editors with plugins bundled in. When using the editor from
  * source you need to take care of loading all plugins by yourself
  * source you need to take care of loading all plugins by yourself
@@ -54,44 +57,80 @@ export default class DecoupledEditor extends Editor {
 	 * {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`} method instead.
 	 * {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`} method instead.
 	 *
 	 *
 	 * @protected
 	 * @protected
-	 * @param {String} data The data to be loaded into the editor.
+	 * @param {HTMLElement|String} elementOrData The DOM element that serves as an editable.
+	 * The data will be loaded from it and loaded back to it once the editor is destroyed.
+	 * Alternatively, a data string to be loaded into the editor.
 	 * @param {module:core/editor/editorconfig~EditorConfig} config The editor configuration.
 	 * @param {module:core/editor/editorconfig~EditorConfig} config The editor configuration.
 	 */
 	 */
-	constructor( config ) {
+	constructor( elementOrData, config ) {
 		super( config );
 		super( config );
 
 
+		if ( isElement( elementOrData ) ) {
+			/**
+			 * The element used as an editable. The data will be loaded from it and loaded back to
+			 * it once the editor is destroyed.
+			 *
+			 * **Note:** The property is available only when such element has been passed
+			 * to the {@link #constructor}.
+			 *
+			 * @readonly
+			 * @member {HTMLElement}
+			 */
+			this.element = elementOrData;
+		}
+
 		this.data.processor = new HtmlDataProcessor();
 		this.data.processor = new HtmlDataProcessor();
 
 
 		this.model.document.createRoot();
 		this.model.document.createRoot();
 
 
-		this.ui = new DecoupledEditorUI( this, new DecoupledEditorUIView( this.locale ) );
+		this.ui = new DecoupledEditorUI( this, new DecoupledEditorUIView( this.locale, this.element ) );
 	}
 	}
 
 
 	/**
 	/**
 	 * Destroys the editor instance, releasing all resources used by it.
 	 * Destroys the editor instance, releasing all resources used by it.
 	 *
 	 *
+	 * **Note**: The decoupled editor does not remove the toolbar and editable when destroyed. You can
+	 * do that yourself in the destruction chain:
+	 *
+	 *		editor.destroy()
+	 *			.then( () => {
+	 *				// Remove the toolbar from DOM.
+	 *				editor.ui.view.toolbar.element.remove();
+	 *
+	 *				// Remove the editable from DOM.
+	 *				editor.ui.view.editable.element.remove();
+	 *
+	 *				console.log( 'Editor was destroyed' );
+	 *			} );
+	 *
 	 * @returns {Promise}
 	 * @returns {Promise}
 	 */
 	 */
 	destroy() {
 	destroy() {
+		// Cache the data, then destroy.
+		// It's safe to assume that the model->view conversion will not work after super.destroy().
+		const data = this.getData();
+
 		this.ui.destroy();
 		this.ui.destroy();
 
 
-		return super.destroy();
+		return super.destroy()
+			.then( () => {
+				if ( this.element ) {
+					setDataInElement( this.element, data );
+				}
+			} );
 	}
 	}
 
 
 	/**
 	/**
 	 * Creates a decoupled editor instance.
 	 * Creates a decoupled editor instance.
 	 *
 	 *
-	 * Creating an instance when using the {@glink builds/index CKEditor build}:
+	 * Creating an instance when using the {@glink builds/index CKEditor 5 build}:
 	 *
 	 *
 	 *		DecoupledEditor
 	 *		DecoupledEditor
-	 *			.create( '<p>Editor data</p>', {
-	 *				// The location of the toolbar in the DOM.
-	 *				toolbarContainer: document.querySelector( 'body div.toolbar-container' ),
-	 *
-	 *				// The location of the editable in the DOM.
-	 *				editableContainer: document.querySelector( 'body div.editable-container' )
-	 *			} )
+	 *			.create( document.querySelector( '#editor' ) )
 	 *			.then( editor => {
 	 *			.then( editor => {
+	 *				// Append the toolbar to the <body> element.
+	 *				document.body.appendChild( editor.ui.view.toolbar.element );
+	 *
 	 *				console.log( 'Editor was initialized', editor );
 	 *				console.log( 'Editor was initialized', editor );
 	 *			} )
 	 *			} )
 	 *			.catch( err => {
 	 *			.catch( err => {
@@ -107,48 +146,48 @@ export default class DecoupledEditor extends Editor {
 	 *		import ...
 	 *		import ...
 	 *
 	 *
 	 *		DecoupledEditor
 	 *		DecoupledEditor
-	 *			.create( '<p>Editor data</p>', {
+	 *			.create( document.querySelector( '#editor' ), {
 	 *				plugins: [ Essentials, Bold, Italic, ... ],
 	 *				plugins: [ Essentials, Bold, Italic, ... ],
-	 *				toolbar: [ 'bold', 'italic', ... ],
-	 *
-	 *				// The location of the toolbar in the DOM.
-	 *				toolbarContainer: document.querySelector( 'div.toolbar-container' ),
-	 *
-	 *				// The location of the editable in the DOM.
-	 *				editableContainer: document.querySelector( 'div.editable-container' )
+	 *				toolbar: [ 'bold', 'italic', ... ]
 	 *			} )
 	 *			} )
 	 *			.then( editor => {
 	 *			.then( editor => {
+	 *				// Append the toolbar to the <body> element.
+	 *				document.body.appendChild( editor.ui.view.toolbar.element );
+	 *
 	 *				console.log( 'Editor was initialized', editor );
 	 *				console.log( 'Editor was initialized', editor );
 	 *			} )
 	 *			} )
 	 *			.catch( err => {
 	 *			.catch( err => {
 	 *				console.error( err.stack );
 	 *				console.error( err.stack );
 	 *			} );
 	 *			} );
 	 *
 	 *
-	 * **Note**: The {@link module:core/editor/editorconfig~EditorConfig#toolbarContainer `config.toolbarContainer`} and
-	 * {@link module:core/editor/editorconfig~EditorConfig#editableContainer `config.editableContainer`} settings are optional.
-	 * It is possible to define the location of the UI elements manually once the editor is up and running:
+	 * **Note**: It is possible to create the editor out of a pure data string. The editor will then render
+	 * an editable element that must be inserted into the DOM for the editor to work properly:
 	 *
 	 *
 	 *		DecoupledEditor
 	 *		DecoupledEditor
 	 *			.create( '<p>Editor data</p>' )
 	 *			.create( '<p>Editor data</p>' )
 	 *			.then( editor => {
 	 *			.then( editor => {
-	 *				console.log( 'Editor was initialized', editor );
-	 *
-	 *				// Append the toolbar and editable straight into the <body> element.
+	 *				// Append the toolbar to the <body> element.
 	 *				document.body.appendChild( editor.ui.view.toolbar.element );
 	 *				document.body.appendChild( editor.ui.view.toolbar.element );
+	 *
+	 *				// Append the editable to the <body> element.
 	 *				document.body.appendChild( editor.ui.view.editable.element );
 	 *				document.body.appendChild( editor.ui.view.editable.element );
+	 *
+	 *				console.log( 'Editor was initialized', editor );
 	 *			} )
 	 *			} )
 	 *			.catch( err => {
 	 *			.catch( err => {
 	 *				console.error( err.stack );
 	 *				console.error( err.stack );
 	 *			} );
 	 *			} );
 	 *
 	 *
-	 * @param {String} data The data to be loaded into the editor.
+	 * @param {HTMLElement|String} elementOrData The DOM element that serves as an editable.
+	 * The data will be loaded from it and loaded back to it once the editor is destroyed.
+	 * Alternatively, a data string to be loaded into the editor.
 	 * @param {module:core/editor/editorconfig~EditorConfig} config The editor configuration.
 	 * @param {module:core/editor/editorconfig~EditorConfig} config The editor configuration.
 	 * @returns {Promise} A promise resolved once the editor is ready.
 	 * @returns {Promise} A promise resolved once the editor is ready.
 	 * The promise returns the created {@link module:editor-decoupled/decouplededitor~DecoupledEditor} instance.
 	 * The promise returns the created {@link module:editor-decoupled/decouplededitor~DecoupledEditor} instance.
 	 */
 	 */
-	static create( data, config ) {
+	static create( elementOrData, config ) {
 		return new Promise( resolve => {
 		return new Promise( resolve => {
-			const editor = new this( config );
+			const editor = new this( elementOrData, config );
 
 
 			resolve(
 			resolve(
 				editor.initPlugins()
 				editor.initPlugins()
@@ -156,8 +195,9 @@ export default class DecoupledEditor extends Editor {
 						editor.ui.init();
 						editor.ui.init();
 						editor.fire( 'uiReady' );
 						editor.fire( 'uiReady' );
 					} )
 					} )
-					.then( () => editor.editing.view.attachDomRoot( editor.ui.view.editableElement ) )
-					.then( () => editor.data.init( data ) )
+					.then( () => {
+						editor.data.init( editor.element ? getDataFromElement( editor.element ) : elementOrData );
+					} )
 					.then( () => {
 					.then( () => {
 						editor.fire( 'dataReady' );
 						editor.fire( 'dataReady' );
 						editor.fire( 'ready' );
 						editor.fire( 'ready' );
@@ -169,51 +209,3 @@ export default class DecoupledEditor extends Editor {
 }
 }
 
 
 mix( DecoupledEditor, DataApiMixin );
 mix( DecoupledEditor, DataApiMixin );
-
-/**
- * The configuration of the {@link module:editor-decoupled/decouplededitor~DecoupledEditor}.
- *
- * When specified, it controls the location of the {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#toolbar}:
- *
- *		DecoupledEditor
- *			.create( '<p>Hello world!</p>', {
- *				// Append the toolbar to the <body> element.
- *				toolbarContainer: document.body
- *			} )
- *			.then( editor => {
- *				console.log( editor );
- *			} )
- *			.catch( error => {
- *				console.error( error );
- *			} );
- *
- * **Note**: If not specified, the toolbar must be manually injected into the DOM. See
- * {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`}
- * to learn more.
- *
- * @member {HTMLElement} module:core/editor/editorconfig~EditorConfig#toolbarContainer
- */
-
-/**
- * The configuration of the {@link module:editor-decoupled/decouplededitor~DecoupledEditor}.
- *
- * When specified, it controls the location of the {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#editable}:
- *
- *		DecoupledEditor
- *			.create( '<p>Hello world!</p>', {
- *				// Append the editable to the <body> element.
- *				editableContainer: document.body
- *			} )
- *			.then( editor => {
- *				console.log( editor );
- *			} )
- *			.catch( error => {
- *				console.error( error );
- *			} );
- *
- * **Note**: If not specified, the editable must be manually injected into the DOM. See
- * {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`}
- * to learn more.
- *
- * @member {HTMLElement} module:core/editor/editorconfig~EditorConfig#editableContainer
- */

+ 2 - 25
packages/ckeditor5-editor-decoupled/src/decouplededitorui.js

@@ -52,22 +52,6 @@ export default class DecoupledEditorUI {
 		 * @private
 		 * @private
 		 */
 		 */
 		this._toolbarConfig = normalizeToolbarConfig( editor.config.get( 'toolbar' ) );
 		this._toolbarConfig = normalizeToolbarConfig( editor.config.get( 'toolbar' ) );
-
-		/**
-		 * The container for the {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#toolbar}.
-		 *
-		 * @type {HTMLElement|String}
-		 * @private
-		 */
-		this._toolbarContainer = editor.config.get( 'toolbarContainer' );
-
-		/**
-		 * The container for the {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#editable}.
-		 *
-		 * @type {HTMLElement|String}
-		 * @private
-		 */
-		this._editableContainer = editor.config.get( 'editableContainer' );
 	}
 	}
 
 
 	/**
 	/**
@@ -83,19 +67,12 @@ export default class DecoupledEditorUI {
 		const editingRoot = editor.editing.view.document.getRoot();
 		const editingRoot = editor.editing.view.document.getRoot();
 		view.editable.bind( 'isReadOnly' ).to( editingRoot );
 		view.editable.bind( 'isReadOnly' ).to( editingRoot );
 		view.editable.bind( 'isFocused' ).to( editor.editing.view.document );
 		view.editable.bind( 'isFocused' ).to( editor.editing.view.document );
+		editor.editing.view.attachDomRoot( view.editableElement );
 		view.editable.name = editingRoot.rootName;
 		view.editable.name = editingRoot.rootName;
 
 
 		this.focusTracker.add( this.view.editableElement );
 		this.focusTracker.add( this.view.editableElement );
 		this.view.toolbar.fillFromConfig( this._toolbarConfig.items, this.componentFactory );
 		this.view.toolbar.fillFromConfig( this._toolbarConfig.items, this.componentFactory );
 
 
-		if ( this._toolbarContainer ) {
-			this._toolbarContainer.appendChild( view.toolbar.element );
-		}
-
-		if ( this._editableContainer ) {
-			this._editableContainer.appendChild( view.editable.element );
-		}
-
 		enableToolbarKeyboardFocus( {
 		enableToolbarKeyboardFocus( {
 			origin: editor.editing.view,
 			origin: editor.editing.view,
 			originFocusTracker: this.focusTracker,
 			originFocusTracker: this.focusTracker,
@@ -108,6 +85,6 @@ export default class DecoupledEditorUI {
 	 * Destroys the UI.
 	 * Destroys the UI.
 	 */
 	 */
 	destroy() {
 	destroy() {
-		this.view.destroy( !!this._toolbarContainer, !!this._editableContainer );
+		this.view.destroy();
 	}
 	}
 }
 }

+ 10 - 25
packages/ckeditor5-editor-decoupled/src/decouplededitoruiview.js

@@ -18,9 +18,8 @@ import Template from '@ckeditor/ckeditor5-ui/src/template';
  * {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#toolbar}, but without any
  * {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#toolbar}, but without any
  * specific arrangement of the components in the DOM.
  * specific arrangement of the components in the DOM.
  *
  *
- * See {@link module:core/editor/editorconfig~EditorConfig#toolbarContainer `config.toolbarContainer`} and
- * {@link module:core/editor/editorconfig~EditorConfig#editableContainer `config.editableContainer`} to
- * learn more about the UI of the decoupled editor.
+ * See {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`}
+ * to learn more about this view.
  *
  *
  * @extends module:ui/editorui/editoruiview~EditorUIView
  * @extends module:ui/editorui/editoruiview~EditorUIView
  */
  */
@@ -29,8 +28,9 @@ export default class DecoupledEditorUIView extends EditorUIView {
 	 * Creates an instance of the decoupled editor UI view.
 	 * Creates an instance of the decoupled editor UI view.
 	 *
 	 *
 	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
 	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
+	 * @param {HTMLElement} [editableElement] The DOM element to be used as editable.
 	 */
 	 */
-	constructor( locale ) {
+	constructor( locale, editableElement ) {
 		super( locale );
 		super( locale );
 
 
 		/**
 		/**
@@ -47,37 +47,22 @@ export default class DecoupledEditorUIView extends EditorUIView {
 		 * @readonly
 		 * @readonly
 		 * @member {module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}
 		 * @member {module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}
 		 */
 		 */
-		this.editable = new InlineEditableUIView( locale );
+		this.editable = new InlineEditableUIView( locale, editableElement );
 
 
 		// This toolbar may be placed anywhere in the page so things like font size need to be reset in it.
 		// This toolbar may be placed anywhere in the page so things like font size need to be reset in it.
+		// Also because of the above, make sure the toolbar supports rounded corners.
 		Template.extend( this.toolbar.template, {
 		Template.extend( this.toolbar.template, {
 			attributes: {
 			attributes: {
-				class: 'ck-reset_all'
+				class: [
+					'ck-reset_all',
+					'ck-rounded-corners'
+				]
 			}
 			}
 		} );
 		} );
 
 
 		this.registerChild( [ this.toolbar, this.editable ] );
 		this.registerChild( [ this.toolbar, this.editable ] );
 	}
 	}
 
 
-	/**
-	 * Destroys the view and removes the {@link #toolbar} and {@link #editable}
-	 * {@link module:ui/view~View#element `element`} from the DOM, if required.
-	 *
-	 * @param {Boolean} [removeToolbar] When `true`, remove the {@link #toolbar} element from the DOM.
-	 * @param {Boolean} [removeEditable] When `true`, remove the {@link #editable} element from the DOM.
-	 */
-	destroy( removeToolbar, removeEditable ) {
-		super.destroy();
-
-		if ( removeToolbar ) {
-			this.toolbar.element.remove();
-		}
-
-		if ( removeEditable ) {
-			this.editable.element.remove();
-		}
-	}
-
 	/**
 	/**
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */

+ 183 - 137
packages/ckeditor5-editor-decoupled/tests/decouplededitor.js

@@ -3,6 +3,8 @@
  * For licensing, see LICENSE.md.
  * For licensing, see LICENSE.md.
  */
  */
 
 
+/* globals document */
+
 import DecoupledEditorUI from '../src/decouplededitorui';
 import DecoupledEditorUI from '../src/decouplededitorui';
 import DecoupledEditorUIView from '../src/decouplededitoruiview';
 import DecoupledEditorUIView from '../src/decouplededitoruiview';
 
 
@@ -19,16 +21,19 @@ import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 
 testUtils.createSinonSandbox();
 testUtils.createSinonSandbox();
 
 
-describe( 'DecoupledEditor', () => {
-	let editor, editorData;
+const editorData = '<p><strong>foo</strong> bar</p>';
 
 
-	beforeEach( () => {
-		editorData = '<p><strong>foo</strong> bar</p>';
-	} );
+describe( 'DecoupledEditor', () => {
+	let editor;
 
 
 	describe( 'constructor()', () => {
 	describe( 'constructor()', () => {
 		beforeEach( () => {
 		beforeEach( () => {
 			editor = new DecoupledEditor();
 			editor = new DecoupledEditor();
+			editor.ui.init();
+		} );
+
+		afterEach( () => {
+			return editor.destroy();
 		} );
 		} );
 
 
 		it( 'uses HTMLDataProcessor', () => {
 		it( 'uses HTMLDataProcessor', () => {
@@ -52,173 +57,214 @@ describe( 'DecoupledEditor', () => {
 	} );
 	} );
 
 
 	describe( 'create()', () => {
 	describe( 'create()', () => {
-		beforeEach( () => {
-			return DecoupledEditor
-				.create( editorData, {
-					plugins: [ Paragraph, Bold ]
-				} )
-				.then( newEditor => {
-					editor = newEditor;
-				} );
+		describe( 'editor with data', () => {
+			test( () => editorData );
 		} );
 		} );
 
 
-		afterEach( () => {
-			return editor.destroy();
-		} );
+		describe( 'editor with editable element', () => {
+			let editableElement;
 
 
-		it( 'creates an instance which inherits from the DecoupledEditor', () => {
-			expect( editor ).to.be.instanceof( DecoupledEditor );
-		} );
+			beforeEach( () => {
+				editableElement = document.createElement( 'div' );
+				editableElement.innerHTML = editorData;
+			} );
 
 
-		it( 'loads the initial data', () => {
-			expect( editor.getData() ).to.equal( '<p><strong>foo</strong> bar</p>' );
+			test( () => editableElement );
 		} );
 		} );
 
 
-		// #53
-		it( 'creates an instance of a DecoupledEditor child class', () => {
-			class CustomDecoupledEditor extends DecoupledEditor {}
-
-			return CustomDecoupledEditor
-				.create( editorData, {
-					plugins: [ Paragraph, Bold ]
-				} )
-				.then( newEditor => {
-					expect( newEditor ).to.be.instanceof( CustomDecoupledEditor );
-					expect( newEditor ).to.be.instanceof( DecoupledEditor );
+		function test( getElementOrData ) {
+			it( 'creates an instance which inherits from the DecoupledEditor', () => {
+				return DecoupledEditor
+					.create( getElementOrData(), {
+						plugins: [ Paragraph, Bold ]
+					} )
+					.then( newEditor => {
+						expect( newEditor ).to.be.instanceof( DecoupledEditor );
 
 
-					expect( newEditor.getData() ).to.equal( '<p><strong>foo</strong> bar</p>' );
-
-					return newEditor.destroy();
-				} );
-		} );
-
-		// https://github.com/ckeditor/ckeditor5-editor-decoupled/issues/3
-		it( 'initializes the data controller', () => {
-			let dataInitSpy;
+						return newEditor.destroy();
+					} );
+			} );
 
 
-			class DataInitAssertPlugin extends Plugin {
-				constructor( editor ) {
-					super();
+			it( 'loads the initial data', () => {
+				return DecoupledEditor
+					.create( getElementOrData(), {
+						plugins: [ Paragraph, Bold ]
+					} )
+					.then( newEditor => {
+						expect( newEditor.getData() ).to.equal( '<p><strong>foo</strong> bar</p>' );
 
 
-					this.editor = editor;
-				}
+						return newEditor.destroy();
+					} );
+			} );
 
 
-				init() {
-					dataInitSpy = sinon.spy( this.editor.data, 'init' );
-				}
-			}
+			// https://github.com/ckeditor/ckeditor5-editor-classic/issues/53
+			it( 'creates an instance of a DecoupledEditor child class', () => {
+				class CustomDecoupledEditor extends DecoupledEditor {}
 
 
-			return DecoupledEditor
-				.create( editorData, {
-					plugins: [ Paragraph, Bold, DataInitAssertPlugin ]
-				} )
-				.then( newEditor => {
-					sinon.assert.calledOnce( dataInitSpy );
+				return CustomDecoupledEditor
+					.create( getElementOrData(), {
+						plugins: [ Paragraph, Bold ]
+					} )
+					.then( newEditor => {
+						expect( newEditor ).to.be.instanceof( CustomDecoupledEditor );
+						expect( newEditor ).to.be.instanceof( DecoupledEditor );
 
 
-					return newEditor.destroy();
-				} );
-		} );
+						expect( newEditor.getData() ).to.equal( '<p><strong>foo</strong> bar</p>' );
 
 
-		describe( 'ui', () => {
-			it( 'attaches editable UI as view\'s DOM root', () => {
-				expect( editor.editing.view.getDomRoot() ).to.equal( editor.ui.view.editable.element );
+						return newEditor.destroy();
+					} );
 			} );
 			} );
-		} );
-	} );
 
 
-	describe( 'create - events', () => {
-		afterEach( () => {
-			return editor.destroy();
-		} );
+			// https://github.com/ckeditor/ckeditor5-editor-decoupled/issues/3
+			it( 'initializes the data controller', () => {
+				let dataInitSpy;
 
 
-		it( 'fires all events in the right order', () => {
-			const fired = [];
+				class DataInitAssertPlugin extends Plugin {
+					constructor( editor ) {
+						super();
 
 
-			function spy( evt ) {
-				fired.push( evt.name );
-			}
+						this.editor = editor;
+					}
 
 
-			class EventWatcher extends Plugin {
-				init() {
-					this.editor.on( 'pluginsReady', spy );
-					this.editor.on( 'uiReady', spy );
-					this.editor.on( 'dataReady', spy );
-					this.editor.on( 'ready', spy );
+					init() {
+						dataInitSpy = sinon.spy( this.editor.data, 'init' );
+					}
 				}
 				}
-			}
 
 
-			return DecoupledEditor
-				.create( editorData, {
-					plugins: [ EventWatcher ]
-				} )
-				.then( newEditor => {
-					expect( fired ).to.deep.equal( [ 'pluginsReady', 'uiReady', 'dataReady', 'ready' ] );
+				return DecoupledEditor
+					.create( getElementOrData(), {
+						plugins: [ Paragraph, Bold, DataInitAssertPlugin ]
+					} )
+					.then( newEditor => {
+						sinon.assert.calledOnce( dataInitSpy );
+
+						return newEditor.destroy();
+					} );
+			} );
 
 
-					editor = newEditor;
+			describe( 'events', () => {
+				it( 'fires all events in the right order', () => {
+					const fired = [];
+
+					function spy( evt ) {
+						fired.push( evt.name );
+					}
+
+					class EventWatcher extends Plugin {
+						init() {
+							this.editor.on( 'pluginsReady', spy );
+							this.editor.on( 'uiReady', spy );
+							this.editor.on( 'dataReady', spy );
+							this.editor.on( 'ready', spy );
+						}
+					}
+
+					return DecoupledEditor
+						.create( getElementOrData(), {
+							plugins: [ EventWatcher ]
+						} )
+						.then( newEditor => {
+							expect( fired ).to.deep.equal( [ 'pluginsReady', 'uiReady', 'dataReady', 'ready' ] );
+
+							return newEditor.destroy();
+						} );
+				} );
+
+				it( 'fires dataReady once data is loaded', () => {
+					let data;
+
+					class EventWatcher extends Plugin {
+						init() {
+							this.editor.on( 'dataReady', () => {
+								data = this.editor.getData();
+							} );
+						}
+					}
+
+					return DecoupledEditor
+						.create( getElementOrData(), {
+							plugins: [ EventWatcher, Paragraph, Bold ]
+						} )
+						.then( newEditor => {
+							expect( data ).to.equal( '<p><strong>foo</strong> bar</p>' );
+
+							return newEditor.destroy();
+						} );
 				} );
 				} );
-		} );
 
 
-		it( 'fires dataReady once data is loaded', () => {
-			let data;
+				it( 'fires uiReady once UI is rendered', () => {
+					let isReady;
+
+					class EventWatcher extends Plugin {
+						init() {
+							this.editor.on( 'uiReady', () => {
+								isReady = this.editor.ui.view.isRendered;
+							} );
+						}
+					}
+
+					return DecoupledEditor
+						.create( getElementOrData(), {
+							plugins: [ EventWatcher ]
+						} )
+						.then( newEditor => {
+							expect( isReady ).to.be.true;
+
+							return newEditor.destroy();
+						} );
+				} );
+			} );
+		}
+	} );
 
 
-			class EventWatcher extends Plugin {
-				init() {
-					this.editor.on( 'dataReady', () => {
-						data = this.editor.getData();
+	describe( 'destroy', () => {
+		describe( 'editor with data', () => {
+			beforeEach( function() {
+				return DecoupledEditor
+					.create( editorData, { plugins: [ Paragraph ] } )
+					.then( newEditor => {
+						editor = newEditor;
 					} );
 					} );
-				}
-			}
-
-			return DecoupledEditor
-				.create( editorData, {
-					plugins: [ EventWatcher, Paragraph, Bold ]
-				} )
-				.then( newEditor => {
-					expect( data ).to.equal( '<p><strong>foo</strong> bar</p>' );
+			} );
 
 
-					editor = newEditor;
-				} );
+			test( () => editorData );
 		} );
 		} );
 
 
-		it( 'fires uiReady once UI is rendered', () => {
-			let isReady;
+		describe( 'editor with editable element', () => {
+			let editableElement;
+
+			beforeEach( function() {
+				editableElement = document.createElement( 'div' );
+				editableElement.innerHTML = editorData;
 
 
-			class EventWatcher extends Plugin {
-				init() {
-					this.editor.on( 'uiReady', () => {
-						isReady = this.editor.ui.view.isRendered;
+				return DecoupledEditor
+					.create( editableElement, { plugins: [ Paragraph ] } )
+					.then( newEditor => {
+						editor = newEditor;
 					} );
 					} );
-				}
-			}
+			} );
 
 
-			return DecoupledEditor
-				.create( editorData, {
-					plugins: [ EventWatcher ]
-				} )
-				.then( newEditor => {
-					expect( isReady ).to.be.true;
+			it( 'sets data back to the element', () => {
+				editor.setData( '<p>foo</p>' );
 
 
-					editor = newEditor;
-				} );
-		} );
-	} );
+				return editor.destroy()
+					.then( () => {
+						expect( editableElement.innerHTML ).to.equal( '<p>foo</p>' );
+					} );
+			} );
 
 
-	describe( 'destroy', () => {
-		beforeEach( function() {
-			return DecoupledEditor
-				.create( editorData, { plugins: [ Paragraph ] } )
-				.then( newEditor => {
-					editor = newEditor;
-				} );
+			test( () => editableElement );
 		} );
 		} );
 
 
-		it( 'destroys the UI', () => {
-			const spy = sinon.spy( editor.ui, 'destroy' );
+		function test() {
+			it( 'destroys the UI', () => {
+				const spy = sinon.spy( editor.ui, 'destroy' );
 
 
-			return editor.destroy()
-				.then( () => {
-					sinon.assert.calledOnce( spy );
-				} );
-		} );
+				return editor.destroy()
+					.then( () => {
+						sinon.assert.calledOnce( spy );
+					} );
+			} );
+		}
 	} );
 	} );
 } );
 } );

+ 5 - 76
packages/ckeditor5-editor-decoupled/tests/decouplededitorui.js

@@ -63,45 +63,6 @@ describe( 'DecoupledEditorUI', () => {
 			expect( view.isRendered ).to.be.true;
 			expect( view.isRendered ).to.be.true;
 		} );
 		} );
 
 
-		describe( 'config', () => {
-			it( 'does nothing if not specified', () => {
-				expect( view.toolbar.element.parentElement ).to.be.null;
-				expect( view.editable.element.parentElement ).to.be.null;
-			} );
-
-			it( 'allocates view#toolbar', () => {
-				return VirtualDecoupledTestEditor
-					.create( {
-						toolbar: [ 'foo', 'bar' ],
-						toolbarContainer: document.body
-					} )
-					.then( newEditor => {
-						expect( newEditor.ui.view.toolbar.element.parentElement ).to.equal( document.body );
-
-						return newEditor;
-					} )
-					.then( newEditor => {
-						newEditor.destroy();
-					} );
-			} );
-
-			it( 'allocates view#editable', () => {
-				return VirtualDecoupledTestEditor
-					.create( {
-						toolbar: [ 'foo', 'bar' ],
-						editableContainer: document.body
-					} )
-					.then( newEditor => {
-						expect( newEditor.ui.view.editable.element.parentElement ).to.equal( document.body );
-
-						return newEditor;
-					} )
-					.then( newEditor => {
-						newEditor.destroy();
-					} );
-			} );
-		} );
-
 		describe( 'editable', () => {
 		describe( 'editable', () => {
 			it( 'registers view.editable#element in editor focus tracker', () => {
 			it( 'registers view.editable#element in editor focus tracker', () => {
 				ui.focusTracker.isFocused = false;
 				ui.focusTracker.isFocused = false;
@@ -139,6 +100,10 @@ describe( 'DecoupledEditorUI', () => {
 					{ isReadOnly: true }
 					{ isReadOnly: true }
 				);
 				);
 			} );
 			} );
+
+			it( 'attaches editable UI as view\'s DOM root', () => {
+				expect( editor.editing.view.getDomRoot() ).to.equal( view.editable.element );
+			} );
 		} );
 		} );
 
 
 		describe( 'view.toolbar#items', () => {
 		describe( 'view.toolbar#items', () => {
@@ -207,43 +172,7 @@ describe( 'DecoupledEditorUI', () => {
 			return editor.destroy()
 			return editor.destroy()
 				.then( () => {
 				.then( () => {
 					sinon.assert.calledOnce( spy );
 					sinon.assert.calledOnce( spy );
-					sinon.assert.calledWithExactly( spy, false, false );
-				} );
-		} );
-
-		it( 'removes view#toolbar from DOM, if config.toolbarContainer is specified', () => {
-			let spy;
-
-			return VirtualDecoupledTestEditor
-				.create( {
-					toolbar: [ 'foo', 'bar' ],
-					toolbarContainer: document.body
-				} )
-				.then( newEditor => {
-					spy = sinon.spy( newEditor.ui.view, 'destroy' );
-
-					newEditor.destroy();
-				} )
-				.then( () => {
-					sinon.assert.calledWithExactly( spy, true, false );
-				} );
-		} );
-
-		it( 'removes view#editable from DOM, if config.editableContainer is specified', () => {
-			let spy;
-
-			return VirtualDecoupledTestEditor
-				.create( {
-					toolbar: [ 'foo', 'bar' ],
-					editableContainer: document.body
-				} )
-				.then( newEditor => {
-					spy = sinon.spy( newEditor.ui.view, 'destroy' );
-
-					newEditor.destroy();
-				} )
-				.then( () => {
-					sinon.assert.calledWithExactly( spy, false, true );
+					sinon.assert.calledWithExactly( spy );
 				} );
 				} );
 		} );
 		} );
 	} );
 	} );

+ 14 - 26
packages/ckeditor5-editor-decoupled/tests/decouplededitoruiview.js

@@ -43,8 +43,9 @@ describe( 'DecoupledEditorUIView', () => {
 				expect( view.toolbar.element.parentElement ).to.be.null;
 				expect( view.toolbar.element.parentElement ).to.be.null;
 			} );
 			} );
 
 
-			it( 'gets the .ck-reset_all class', () => {
+			it( 'gets the CSS classes', () => {
 				expect( view.toolbar.element.classList.contains( 'ck-reset_all' ) ).to.be.true;
 				expect( view.toolbar.element.classList.contains( 'ck-reset_all' ) ).to.be.true;
+				expect( view.toolbar.element.classList.contains( 'ck-rounded-corners' ) ).to.be.true;
 			} );
 			} );
 		} );
 		} );
 
 
@@ -53,7 +54,7 @@ describe( 'DecoupledEditorUIView', () => {
 				expect( view.editable ).to.be.instanceof( InlineEditableUIView );
 				expect( view.editable ).to.be.instanceof( InlineEditableUIView );
 			} );
 			} );
 
 
-			it( 'is given a locate object', () => {
+			it( 'is given a locale object', () => {
 				expect( view.editable.locale ).to.equal( locale );
 				expect( view.editable.locale ).to.equal( locale );
 			} );
 			} );
 
 
@@ -61,6 +62,17 @@ describe( 'DecoupledEditorUIView', () => {
 				expect( view.isRendered ).to.be.true;
 				expect( view.isRendered ).to.be.true;
 				expect( view.editable.element.parentElement ).to.be.null;
 				expect( view.editable.element.parentElement ).to.be.null;
 			} );
 			} );
+
+			it( 'can be created out of an existing DOM element', () => {
+				const editableElement = document.createElement( 'div' );
+				const testView = new DecoupledEditorUIView( locale, editableElement );
+
+				testView.render();
+
+				expect( testView.editable.element ).to.equal( editableElement );
+
+				testView.destroy();
+			} );
 		} );
 		} );
 	} );
 	} );
 
 
@@ -87,30 +99,6 @@ describe( 'DecoupledEditorUIView', () => {
 			view.toolbar.element.remove();
 			view.toolbar.element.remove();
 			view.editable.element.remove();
 			view.editable.element.remove();
 		} );
 		} );
-
-		it( 'removes toolbar#element on demand', () => {
-			document.body.appendChild( view.toolbar.element );
-			document.body.appendChild( view.editable.element );
-
-			view.destroy( true );
-
-			expect( view.toolbar.element.parentElement ).to.be.null;
-			expect( view.editable.element.parentElement ).to.equal( document.body );
-
-			view.editable.element.remove();
-		} );
-
-		it( 'removes editable#element on demand', () => {
-			document.body.appendChild( view.toolbar.element );
-			document.body.appendChild( view.editable.element );
-
-			view.destroy( false, true );
-
-			expect( view.toolbar.element.parentElement ).to.equal( document.body );
-			expect( view.editable.element.parentElement ).to.be.null;
-
-			view.toolbar.element.remove();
-		} );
 	} );
 	} );
 
 
 	describe( 'editableElement', () => {
 	describe( 'editableElement', () => {

+ 58 - 0
packages/ckeditor5-editor-decoupled/tests/manual/decouplededitor-editable.html

@@ -0,0 +1,58 @@
+<p>
+	<button id="destroyEditor">Destroy editor</button>
+	<button id="initEditor">Init editor</button>
+</p>
+
+<h2>The toolbar</h2>
+<div class="toolbar-container"></div>
+
+<h2>The editable</h2>
+<div class="editable-container">
+	<div class="editor__editable">
+		<h2>This element becomes the editable</h2>
+		<p>It has the initial editor data. It should keep it after the editor is destroyed too.</p>
+	</div>
+</div>
+
+<style>
+	.editable-container,
+	.toolbar-container {
+		position: relative;
+		border: 1px solid red;
+	}
+
+	.editable-container::after,
+	.toolbar-container::after {
+		content: attr(class);
+		position: absolute;
+		background: red;
+		color: #fff;
+		top: 0;
+		right: 0;
+		font: 10px/2 monospace;
+		padding: .1em .3em;
+	}
+
+	.toolbar-container {
+		padding: 1em;
+	}
+
+	.editable-container {
+		padding: 3em;
+		overflow-y: scroll;
+		max-height: 300px;
+	}
+
+	.editable-container div.editor__editable {
+		background: yellow;
+	}
+
+	.editable-container .ck-editor__editable {
+		min-height: 21cm;
+		padding: 2em;
+		border: 1px #D3D3D3 solid;
+		border-radius: var(--ck-border-radius);
+		background: white;
+		box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
+	}
+</style>

+ 57 - 0
packages/ckeditor5-editor-decoupled/tests/manual/decouplededitor-editable.js

@@ -0,0 +1,57 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console:false, document, window */
+
+import DecoupledEditor from '../../src/decouplededitor';
+import Enter from '@ckeditor/ckeditor5-enter/src/enter';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
+import Heading from '@ckeditor/ckeditor5-heading/src/heading';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Undo from '@ckeditor/ckeditor5-undo/src/undo';
+import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+import testUtils from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+
+let editor, editable, observer;
+
+function initEditor() {
+	DecoupledEditor
+		.create( document.querySelector( '.editor__editable' ), {
+			plugins: [ Enter, Typing, Paragraph, Undo, Heading, Bold, Italic ],
+			toolbar: [ 'heading', '|', 'bold', 'italic', 'undo', 'redo' ]
+		} )
+		.then( newEditor => {
+			console.log( 'Editor was initialized', newEditor );
+			console.log( 'You can now play with it using global `editor` and `editable` variables.' );
+
+			document.querySelector( '.toolbar-container' ).appendChild( newEditor.ui.view.toolbar.element );
+
+			window.editor = editor = newEditor;
+			window.editable = editable = editor.editing.view.document.getRoot();
+
+			observer = testUtils.createObserver();
+			observer.observe( 'Editable', editable, [ 'isFocused' ] );
+		} )
+		.catch( err => {
+			console.error( err.stack );
+		} );
+}
+
+function destroyEditor() {
+	editor.destroy()
+		.then( () => {
+			window.editor = editor = null;
+			window.editable = editable = null;
+
+			observer.stopListening();
+			observer = null;
+
+			console.log( 'Editor was destroyed' );
+		} );
+}
+
+document.getElementById( 'initEditor' ).addEventListener( 'click', initEditor );
+document.getElementById( 'destroyEditor' ).addEventListener( 'click', destroyEditor );

+ 20 - 0
packages/ckeditor5-editor-decoupled/tests/manual/decouplededitor-editable.md

@@ -0,0 +1,20 @@
+1. Click "Init editor".
+2. Expected:
+  * The toolbar container should get the toolbar.
+  * The toolbar should appear with "Heading", "Bold", "Italic", "Undo" and "Redo" buttons.
+  * **The yellow element should become an editable**.
+3. Do some editing and formatting.
+4. Click "Destroy editor".
+5. Expected:
+  * Editor should be destroyed.
+  * The toolbar should disappear from the container.
+  * **The editable must remain**.
+  * **The editable must retain the editor data**.
+  * The `.ck-body` region should be removed.
+
+## Notes:
+
+* You can play with:
+  * `editable.isReadOnly`,
+* Changes to `editable.isFocused` should be logged to the console.
+* Features should work.

+ 4 - 4
packages/ckeditor5-editor-decoupled/tests/manual/decouplededitor.js

@@ -22,15 +22,15 @@ function initEditor() {
 	DecoupledEditor
 	DecoupledEditor
 		.create( editorData, {
 		.create( editorData, {
 			plugins: [ Enter, Typing, Paragraph, Undo, Heading, Bold, Italic ],
 			plugins: [ Enter, Typing, Paragraph, Undo, Heading, Bold, Italic ],
-			toolbar: [ 'heading', '|', 'bold', 'italic', 'undo', 'redo' ],
-
-			toolbarContainer: document.querySelector( '.toolbar-container' ),
-			editableContainer: document.querySelector( '.editable-container' )
+			toolbar: [ 'heading', '|', 'bold', 'italic', 'undo', 'redo' ]
 		} )
 		} )
 		.then( newEditor => {
 		.then( newEditor => {
 			console.log( 'Editor was initialized', newEditor );
 			console.log( 'Editor was initialized', newEditor );
 			console.log( 'You can now play with it using global `editor` and `editable` variables.' );
 			console.log( 'You can now play with it using global `editor` and `editable` variables.' );
 
 
+			document.querySelector( '.toolbar-container' ).appendChild( newEditor.ui.view.toolbar.element );
+			document.querySelector( '.editable-container' ).appendChild( newEditor.ui.view.editable.element );
+
 			window.editor = editor = newEditor;
 			window.editor = editor = newEditor;
 			window.editable = editable = editor.editing.view.document.getRoot();
 			window.editable = editable = editor.editing.view.document.getRoot();
 
 

+ 2 - 2
packages/ckeditor5-editor-decoupled/tests/manual/decouplededitor.md

@@ -5,8 +5,8 @@
 3. Click "Destroy editor".
 3. Click "Destroy editor".
 4. Expected:
 4. Expected:
   * Editor should be destroyed.
   * Editor should be destroyed.
-  * The editor UI should disappear from the containers.
-  * The 'ck-body region' should be removed.
+  * **The editor UI should remain in the containers**.
+  * The `.ck-body` region should be removed.
 
 
 ## Notes:
 ## Notes: