Piotr Jasiun пре 9 година
родитељ
комит
2969b618e0

+ 43 - 0
packages/ckeditor5-engine/src/conversion/view-selection-to-model-converters.js

@@ -0,0 +1,43 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+/**
+ * Contains {@link engine.view.Selection view selection} to {@link engine.model.Selection model selection} conversion
+ * helper.
+ *
+ * @namespace engine.conversion.viewSelectionToModel
+ */
+
+/**
+ * Function factory, creates a callback function which converts a {@link engine.view.Selection view selection} taken
+ * from the {@link engine.view.Document#selectionChange} event and set in on the
+ * {@link engine.model.Document#selection model}.
+ *
+ * Note that because there is not view selection change dispatcher nor any other advance view selection to model
+ * conversion mechanism, this method is simple event listener.
+ *
+ *		view.document.on( 'selectionChange', convertSelectionChange( model, mapper ) );
+ *
+ * @function engine.conversion.viewSelectionToModel.convertSelectionChange
+ * @param {engine.model.Document} model Model document on which selection should be updated.
+ * @param {engine.conversion.Mapper} mapper Conversion mapper.
+ * @returns {Function} {@link engine.view.Document#selectionChange} callback function.
+ */
+export function convertSelectionChange( model, mapper ) {
+	return ( evt, data ) => {
+		model.enqueueChanges( () => {
+			const viewSelection = data.newSelection;
+
+			model.selection.removeAllRanges();
+
+			for ( let viewRange of viewSelection.getRanges() ) {
+				const modelRange = mapper.toModelRange( viewRange );
+				model.selection.addRange( modelRange, viewSelection.isBackward );
+			}
+		} );
+	};
+}

+ 4 - 5
packages/ckeditor5-engine/src/datacontroller.js

@@ -37,18 +37,17 @@ export default class DataController {
 	/**
 	 * Creates data controller instance.
 	 *
-	 *
-	 * @param {engine.model.Document} modelDocument Model document.
+	 * @param {engine.model.Document} model Model document.
 	 * @param {engine.dataProcessor.DataProcessor} dataProcessor Data processor which should used by the controller.
 	 */
-	constructor( modelDocument, dataProcessor ) {
+	constructor( model, dataProcessor ) {
 		/**
 		 * Model document.
 		 *
 		 * @readonly
 		 * @member {engine.model.document} engine.DataController#model
 		 */
-		this.model = modelDocument;
+		this.model = model;
 
 		/**
 		 * Data processor used during the conversion.
@@ -117,7 +116,7 @@ export default class DataController {
 		 * @member {engine.conversion.ViewConversionDispatcher} engine.DataController#viewToModel
 		 */
 		this.viewToModel = new ViewConversionDispatcher( {
-			schema: modelDocument.schema
+			schema: model.schema
 		} );
 
 		// Define default converters for text and elements.

+ 90 - 24
packages/ckeditor5-engine/src/editingcontroller.js

@@ -6,7 +6,6 @@
 'use strict';
 
 import ViewDocument from './view/document.js';
-
 import MutationObserver from './view/observer/mutationobserver.js';
 import SelectionObserver from './view/observer/selectionobserver.js';
 import FocusObserver from './view/observer/focusobserver.js';
@@ -14,51 +13,94 @@ import KeyObserver from './view/observer/keyobserver.js';
 
 import Mapper from './conversion/mapper.js';
 import ModelConversionDispatcher from './conversion/modelconversiondispatcher.js';
-
+import { insertText, remove, move } from './conversion/model-to-view-converters.js';
+import { convertSelectionChange } from './conversion/view-selection-to-model-converters.js';
 import {
 	convertRangeSelection,
 	convertCollapsedSelection,
 	clearAttributes
 } from './conversion/model-selection-to-view-converters.js';
 
-import {
-	insertText,
-	remove,
-	move
-} from './conversion/model-to-view-converters.js';
-
 import EmitterMixin from '../utils/emittermixin.js';
 
+/**
+ * Controller for the editing pipeline. The editing pipeline controls {@link engine.EditingController#model model} rendering,
+ * including selection handling. It also creates {@link engine.EditingController#view view document} which build a
+ * browser-independent virtualization over the DOM elements. Editing controller also attach default converters and
+ * observers.
+ *
+ * Note that the following observers are attached by the controller and are always available:
+ * * {@link view.observer.MutationObserver},
+ * * {@link view.observer.SelectionObserver},
+ * * {@link view.observer.FocusObserver},
+ * * {@link view.observer.KeyObserver}.
+ *
+ * @memberOf engine
+ */
 export default class EditingController {
+	/**
+	 * Creates editing controller instance.
+	 *
+	 * @param {engine.model.Document} model Model document.
+	 */
 	constructor( model ) {
+		/**
+		 * Property keeping all listenters attached by controller on other objects, so it can
+		 * stop listening on {@link engine.EditingController#destroy}.
+		 *
+		 * @private
+		 * @member {utils.EmitterMixin} engine.EditingController#_listenters
+		 */
 		this._listenters = Object.create( EmitterMixin );
 
+		/**
+		 * Model document.
+		 *
+		 * @readonly
+		 * @member {engine.model.document} engine.EditingController#model
+		 */
 		this.model = model;
 
-		const view = new ViewDocument();
-		this.view = view;
+		/**
+		 * View document.
+		 *
+		 * @readonly
+		 * @member {engine.view.document} engine.EditingController#view
+		 */
+		this.view = new ViewDocument();
 
+		// Attach default observers.
 		this.view.addObserver( MutationObserver );
 		this.view.addObserver( SelectionObserver );
 		this.view.addObserver( FocusObserver );
 		this.view.addObserver( KeyObserver );
 
+		/**
+		 * Mapper which describe model-view binding.
+		 *
+		 * @readonly
+		 * @member {engine.conversion.Mapper} engine.EditingController#mapper
+		 */
 		this.mapper = new Mapper();
 
-		// Move selection change to model
-		this._listenters.listenTo( this.view, 'selectionChange', ( evt, data ) => {
-			model.enqueueChanges( () => {
-				const viewSelection = data.newSelection;
-
-				model.selection.removeAllRanges();
-
-				for ( let viewRange of viewSelection.getRanges() ) {
-					const modelRange = this.mapper.toModelRange( viewRange );
-					model.selection.addRange( modelRange, viewSelection.isBackward );
-				}
-			} );
-		} );
-
+		// Convert view selection to model.
+		this._listenters.listenTo( this.view, 'selectionChange', convertSelectionChange( model, this.mapper ) );
+
+		/**
+		 * Model to view conversion dispatcher, which converts changes from the model to
+		 * {@link engine.EditingController#view editing view}.
+		 *
+		 * To attach model to view converter to the editing pipeline you need to add lister to this property:
+		 *
+		 *		editing.modelToView( 'insert:$element', customInsertConverter );
+		 *
+		 * Or use {@link engine.conversion.ModelConverterBuilder}:
+		 *
+		 *		BuildModelConverterFor( editing.modelToView ).fromAttribute( 'bold' ).toElement( 'b' );
+		 *
+		 * @readonly
+		 * @member {engine.conversion.ModelConversionDispatcher} engine.EditingController#modelToView
+		 */
 		this.modelToView = new ModelConversionDispatcher( {
 			writer: this.view.writer,
 			mapper: this.mapper,
@@ -74,22 +116,46 @@ export default class EditingController {
 			this.view.render();
 		} );
 
+		// Attach default content converters.
 		this.modelToView.on( 'insert:$text', insertText() );
 		this.modelToView.on( 'remove', remove() );
 		this.modelToView.on( 'move', move() );
 
+		// Attach default selection converters.
 		this.modelToView.on( 'selection', clearAttributes() );
 		this.modelToView.on( 'selection', convertRangeSelection() );
 		this.modelToView.on( 'selection', convertCollapsedSelection() );
 	}
 
+	/**
+	 * {@link engine.view.Document#createRoot Creates} a view root and {@link engine.conversion.Mapper#bindElements binds}
+	 * the model root with view root and and view root with DOM element:
+	 *
+	 *		editing.createRoot( document.querySelector( div#editor ) );
+	 *
+	 * If the DOM element is not available at the time you want to create a view root, for instance it is iframe body
+	 * element, it is possible to create view element and bind the DOM element later:
+	 *
+	 *		editing.createRoot( 'body' );
+	 *		editing.view.attachDomRoot( iframe.contentDocument.body );
+	 *
+	 * @param {Element|String} domRoot DOM root element or the name of view root element if the DOM element will be
+	 * attached later.
+	 * @param {String} [name='main'] Root name.
+	 * @returns {engine.view.ContainerElement} View root element.
+	 */
 	createRoot( domRoot, name = 'main' ) {
 		const viewRoot = this.view.createRoot( domRoot, name );
 		const modelRoot = this.model.getRoot( name );
 
 		this.mapper.bindElements( modelRoot, viewRoot );
+
+		return viewRoot;
 	}
 
+	/**
+	 * Removes all event listeners attached by the EditingController.
+	 */
 	destroy() {
 		this._listenters.stopListening();
 	}

+ 38 - 11
packages/ckeditor5-engine/src/view/document.js

@@ -136,17 +136,23 @@ export default class Document {
 	}
 
 	/**
-	 * Creates a root for the HTMLElement. It adds elements to {@link engine.view.Document#domRoots} and
-	 * {@link engine.view.Document#viewRoots}.
+	 * Creates a {@link engine.view.Document#viewRoots view root element}.
 	 *
-	 * The constructor copies the element name and attributes to create the
-	 * root of the view, but does not copy its children. This means that while
-	 * {@link engine.view.Document#render rendering}, the whole content of this
-	 * root element will be removed but the root name and attributes will be preserved.
+	 * If the DOM element will be passed as a first parameter it will be automatically
+	 * {@link engine.view.Document#attachDomRoot attached}:
 	 *
-	 * @param {HTMLElement} domRoot DOM element in which the tree view should do change.
+	 *		document.createRoot( document.querySelector( div#editor ) ); // will call document.attachDomRoot
+	 *
+	 * However, if the string will be passed, then only the view element will be created and the DOM element have to be
+	 * attached separately:
+	 *
+	 *		document.createRoot( 'body' );
+	 *		document.attachDomRoot( document.querySelector( body#editor ) );
+	 *
+	 * @param {Element|String} domRoot DOM root element or the tag name of view root element if the DOM element will be
+	 * attached later.
 	 * @param {String} [name='main'] Name of the root.
-	 * @returns {engine.view.element} The created view root element.
+	 * @returns {engine.view.ContainerElement} The created view root element.
 	 */
 	createRoot( domRoot, name = 'main' ) {
 		const rootTag = typeof domRoot == 'string' ? domRoot : domRoot.tagName;
@@ -168,6 +174,17 @@ export default class Document {
 		return viewRoot;
 	}
 
+	/**
+	 * Attaches DOM root element to the view element and enable all observers on that element. This method also
+	 * {@link engine.view.Renderer#markToSync mark element} to be synchronize with the view what means that all child
+	 * nodes will be removed and replaced with content of the view root.
+	 *
+	 * Note that {@link engine.view.Document#createRoot} will call this method automatically if the DOM element will be
+	 * passed to it.
+	 *
+	 * @param {Element|String} domRoot DOM root element.
+	 * @param {String} [name='main'] Name of the root.
+	 */
 	attachDomRoot( domRoot, name = 'main' ) {
 		const viewRoot = this.getRoot( name );
 
@@ -183,16 +200,26 @@ export default class Document {
 	}
 
 	/**
-	 * Get a {@link engine.view.Document#viewRoots view root element} with the specified name. If the name is not
+	 * Gets a {@link engine.view.Document#viewRoots view root element} with the specified name. If the name is not
 	 * specific "main" root is returned.
 	 *
-	 * @param {String} [name='main']  Name of the root.
-	 * @returns {engine.view.element} The view root element with the specified name.
+	 * @param {String} [name='main'] Name of the root.
+	 * @returns {engine.view.ContainerElement} The view root element with the specified name.
 	 */
 	getRoot( name = 'main' ) {
 		return this.viewRoots.get( name );
 	}
 
+	/**
+	 * Gets DOM root element.
+	 *
+	 * @param {String} [name='main']  Name of the root.
+	 * @returns {Element} DOM root element instance.
+	 */
+	getDomRoot(  name = 'main'  ) {
+		return this.domRoots.get( name );
+	}
+
 	/**
 	 * Renders all changes. In order to avoid triggering the observers (e.g. mutations) all observers all detached
 	 * before rendering and reattached after that.

+ 14 - 0
packages/ckeditor5-engine/src/view/observer/focusobserver.js

@@ -10,6 +10,8 @@ import DomEventObserver from './domeventobserver.js';
 /**
  * {@link engine.view.Document#focus Focus} and {@link engine.view.Document#blur blur} events observer.
  *
+ * Note that this observer is attached by the {@link engine.EditingController} and should be available by default.
+ *
  * @memberOf engine.view.observer
  * @extends engine.view.observer.DomEventObserver
  */
@@ -28,6 +30,12 @@ export default class FocusObserver extends DomEventObserver {
 /**
  * Fired when one of the editables gets focus.
  *
+ * Introduced by {@link engine.view.observer.FocusObserver}.
+ *
+ * Note that because {@link engine.view.observer.FocusObserver} is attached by the {@link engine.EditingController}
+ * this event should be available by default.
+ *
+ * @see engine.view.observer.FocusObserver
  * @event engine.view.Document#focus
  * @param {engine.view.observer.DomEventData} data Event data.
  */
@@ -35,6 +43,12 @@ export default class FocusObserver extends DomEventObserver {
 /**
  * Fired when one of the editables loses focus.
  *
+ * Introduced by {@link engine.view.observer.FocusObserver}.
+ *
+ * Note that because {@link engine.view.observer.FocusObserver} is attached by the {@link engine.EditingController}
+ * this event should be available by default.
+ *
+ * @see engine.view.observer.FocusObserver
  * @event engine.view.Document#blur
  * @param {engine.view.observer.DomEventData} data Event data.
  */

+ 8 - 0
packages/ckeditor5-engine/src/view/observer/keyobserver.js

@@ -11,6 +11,8 @@ import { getCode } from '../../../utils/keyboard.js';
 /**
  * {@link engine.view.Document#keydown Key down} event observer.
  *
+ * Note that this observer is attached by the {@link engine.EditingController} and should be available by default.
+ *
  * @memberOf engine.view.observer
  * @extends engine.view.observer.DomEventObserver
  */
@@ -39,6 +41,12 @@ export default class KeyObserver extends DomEventObserver {
 /**
  * Fired when a key has been pressed.
  *
+ * Introduced by {@link engine.view.observer.KeyObserver}.
+ *
+ * Note that because {@link engine.view.observer.KeyObserver} is attached by the {@link engine.EditingController}
+ * this event should be available by default.
+ *
+ * @see engine.view.observer.KeyObserver
  * @event engine.view.Document#keydown
  * @param {engine.view.observer.keyObserver.KeyEventData} keyEventData
  */

+ 8 - 0
packages/ckeditor5-engine/src/view/observer/mutationobserver.js

@@ -19,6 +19,8 @@ import { startsWithFiller, getDataWithoutFiller } from '../filler.js';
  * mutations on elements which do not have corresponding view elements. Also
  * {@link engine.view.Document.MutatatedText text mutation} is fired only if parent element do not change child list.
  *
+ * Note that this observer is attached by the {@link engine.EditingController} and should be available by default.
+ *
  * @memberOf engine.view.observer
  * @extends engine.view.observer.Observer
  */
@@ -201,6 +203,12 @@ export default class MutationObserver extends Observer {
  * Fired when mutation occurred. If tree view is not changed on this event, DOM will be reverter to the state before
  * mutation, so all changes which should be applied, should be handled on this event.
  *
+ * Introduced by {@link engine.view.observer.MutationObserver}.
+ *
+ * Note that because {@link engine.view.observer.MutationObserver} is attached by the {@link engine.EditingController}
+ * this event should be available by default.
+ *
+ * @see engine.view.observer.MutationObserver
  * @event engine.view.Document#mutations
  * @param {Array.<engine.view.Document~MutatatedText|engine.view.Document~MutatatedChildren>} viewMutations
  * Array of mutations.

+ 8 - 0
packages/ckeditor5-engine/src/view/observer/selectionobserver.js

@@ -15,6 +15,8 @@ import MutationObserver from './mutationobserver.js';
  * {@link engine.view.Document#selectionChange} event only if selection change was the only change in the document
  * and DOM selection is different then the view selection.
  *
+ * Note that this observer is attached by the {@link engine.EditingController} and should be available by default.
+ *
  * @see engine.view.MutationObserver
  * @memberOf engine.view.observer
  * @extends engine.view.observer.Observer
@@ -122,6 +124,12 @@ export default class SelectionObserver extends Observer {
  * Fired when selection has changed. This event is fired only when the selection change was the only change that happened
  * in the document, and old selection is different then the new selection.
  *
+ * Introduced by {@link engine.view.observer.SelectionObserver}.
+ *
+ * Note that because {@link engine.view.observer.SelectionObserver} is attached by the {@link engine.EditingController}
+ * this event should be available by default.
+ *
+ * @see engine.view.observer.SelectionObserver
  * @event engine.view.Document#selectionChange
  * @param {Object} data
  * @param {engine.view.Selection} data.oldSelection Old View selection which is