99
1010import mix from '@ckeditor/ckeditor5-utils/src/mix' ;
1111import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin' ;
12+ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror' ;
1213
1314import Mapper from '../conversion/mapper' ;
1415
@@ -100,6 +101,8 @@ export default class DataController {
100101 this . upcastDispatcher . on ( 'text' , convertText ( ) , { priority : 'lowest' } ) ;
101102 this . upcastDispatcher . on ( 'element' , convertToModelFragment ( ) , { priority : 'lowest' } ) ;
102103 this . upcastDispatcher . on ( 'documentFragment' , convertToModelFragment ( ) , { priority : 'lowest' } ) ;
104+
105+ this . decorate ( 'init' ) ;
103106 }
104107
105108 /**
@@ -170,9 +173,42 @@ export default class DataController {
170173 return viewDocumentFragment ;
171174 }
172175
176+ /**
177+ * Sets initial input data parsed by the {@link #processor data processor} and
178+ * converted by the {@link #upcastDispatcher view-to-model converters}.
179+ * Initial data can be set only to document that {@link module:engine/model/document~Document#version} is equal 0.
180+ *
181+ * **Note** This method is {@link module:utils/observablemixin~ObservableMixin#decorate decorated} which is
182+ * used by e.g. collaborative editing plugin that syncs remote data on init.
183+ *
184+ * @fires init
185+ * @param {String } data Input data.
186+ * @param {String } [rootName='main'] Root name.
187+ */
188+ init ( data , rootName = 'main' ) {
189+ if ( this . model . document . version ) {
190+ /**
191+ * Cannot set initial data to not empty {@link module:engine/model/document~Document}.
192+ * Initial data should be set once, during {@link module:core/editor/editor~Editor} initialization,
193+ * when the {@link module:engine/model/document~Document#version} is equal 0.
194+ *
195+ * @error datacontroller-init-document-not-empty
196+ */
197+ throw new CKEditorError ( 'datacontroller-init-document-not-empty: Trying to set initial data to not empty document.' ) ;
198+ }
199+
200+ const modelRoot = this . model . document . getRoot ( rootName ) ;
201+
202+ this . model . enqueueChange ( 'transparent' , writer => {
203+ writer . insert ( this . parse ( data , modelRoot ) , modelRoot ) ;
204+ } ) ;
205+ }
206+
173207 /**
174208 * Sets input data parsed by the {@link #processor data processor} and
175209 * converted by the {@link #upcastDispatcher view-to-model converters}.
210+ * This method can be used any time to replace existing editor data by the new one without clearing the
211+ * {@link module:engine/model/document~Document#history document history}.
176212 *
177213 * This method also creates a batch with all the changes applied. If all you need is to parse data, use
178214 * the {@link #parse} method.
@@ -185,8 +221,6 @@ export default class DataController {
185221 const modelRoot = this . model . document . getRoot ( rootName ) ;
186222
187223 this . model . enqueueChange ( 'transparent' , writer => {
188- // Clearing selection is a workaround for ticket #569 (LiveRange loses position after removing data from document).
189- // After fixing it this code should be removed.
190224 writer . setSelection ( null ) ;
191225 writer . removeSelectionAttribute ( this . model . document . selection . getAttributeKeys ( ) ) ;
192226
@@ -237,6 +271,13 @@ export default class DataController {
237271 * Removes all event listeners set by the DataController.
238272 */
239273 destroy ( ) { }
274+
275+ /**
276+ * Event fired by decorated {@link #init} method.
277+ * See {@link module:utils/observablemixin~ObservableMixin.decorate} for more information and samples.
278+ *
279+ * @event init
280+ */
240281}
241282
242283mix ( DataController , ObservableMixin ) ;
0 commit comments