Browse Source

Introduce EditableElement and RootEditableElement.

Piotr Jasiun 9 years ago
parent
commit
6c54c83d44

+ 7 - 8
packages/ckeditor5-engine/src/view/document.js

@@ -9,7 +9,7 @@ import Selection from './selection.js';
 import Renderer from './renderer.js';
 import Writer from './writer.js';
 import DomConverter from './domconverter.js';
-import ContainerElement from './containerelement.js';
+import RootEditableElement from './rooteditableelement.js';
 import { injectQuirksHandling } from './filler.js';
 
 import mix from '../../utils/mix.js';
@@ -152,20 +152,19 @@ export default class Document {
 	 * @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.ContainerElement} The created view root element.
+	 * @returns {engine.view.RootEditableElement} The created view root element.
 	 */
 	createRoot( domRoot, name = 'main' ) {
 		const rootTag = typeof domRoot == 'string' ? domRoot : domRoot.tagName;
 
-		const viewRoot = new ContainerElement( rootTag );
-		viewRoot.setDocument( this );
+		const viewRoot = new RootEditableElement( this, rootTag, name );
 
 		this.roots.set( name, viewRoot );
 
 		// Mark changed nodes in the renderer.
-		viewRoot.on( 'change', ( evt, type, node ) => {
-			this.renderer.markToSync( type, node );
-		} );
+		viewRoot.on( 'change:children', ( evt, type, node ) => this.renderer.markToSync( 'CHILDREN', node ) );
+		viewRoot.on( 'change:attribute', ( evt, type, node ) => this.renderer.markToSync( 'ATTRIBTE', node ) );
+		viewRoot.on( 'change:text', ( evt, type, node ) => this.renderer.markToSync( 'TEXT', node ) );
 
 		if ( domRoot instanceof HTMLElement ) {
 			this.attachDomRoot( domRoot, name );
@@ -204,7 +203,7 @@ export default class Document {
 	 * specific "main" root is returned.
 	 *
 	 * @param {String} [name='main'] Name of the root.
-	 * @returns {engine.view.ContainerElement} The view root element with the specified name.
+	 * @returns {engine.view.RootEditableElement} The view root element with the specified name.
 	 */
 	getRoot( name = 'main' ) {
 		return this.roots.get( name );

+ 29 - 0
packages/ckeditor5-engine/src/view/editableelement.js

@@ -0,0 +1,29 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import ContainerElement from './containerelement.js';
+
+import mix from '../../utils/mix.js';
+import ObservableMixin from '../../utils/observablemixin.js';
+
+export default class EditableElement extends ContainerElement {
+	/**
+	 * Creates an editable element.
+	 */
+	constructor( name, attrs, children ) {
+		super( name, attrs, children );
+		/**
+		 * Whether the editable is in read-write or read-only mode.
+		 *
+		 * @observable
+		 * @member {Boolean} engine.view.EditableElement#isEditable
+		 */
+		this.set( 'isReadOnly', false );
+	}
+}
+
+mix( EditableElement, ObservableMixin );

+ 20 - 26
packages/ckeditor5-engine/src/view/node.js

@@ -29,14 +29,6 @@ export default class Node {
 		 * @member {engine.view.Element|engine.view.DocumentFragment|null} engine.view.Node#parent
 		 */
 		this.parent = null;
-
-		/**
-		 * {@link engine.view.Document} reference.
-		 *
-		 * @protected
-		 * @member {engine.view.Document} engine.view.Node#_document
-		 */
-		this._document = null;
 	}
 
 	/**
@@ -89,16 +81,13 @@ export default class Node {
 	}
 
 	/**
-	 * Gets {@link engine.view.Document} reference. If the node has {@link engine.view.Document}, assign by
-	 * {@link engine.view.Node#setDocument} it will be returned. Otherwise {@link engine.view.Document} of the parents node
-	 * will be returned. If node has no parent, `null` will be returned.
+	 * Gets {@link engine.view.Document} reference, from the {@link engine.view.Node#getRoot root} or
+	 * returns null if the root has no reference to the {@link engine.view.Document}.
 	 *
-	 * @returns {engine.view.Document|null} Tree view of the node, tree view of the parent or null.
+	 * @returns {engine.view.Document|null} View Document of the node or null.
 	 */
 	getDocument() {
-		if ( this._document ) {
-			return this._document;
-		} else if ( this.parent ) {
+		if ( this.parent ) {
 			return this.parent.getDocument();
 		} else {
 			return null;
@@ -106,6 +95,21 @@ export default class Node {
 	}
 
 	/**
+	 * Gets the top parent for the node. If node has no parent it is the root itself.
+	 *
+	 * @returns {engine.view.Node}
+	 */
+	getRoot() {
+		let root = this;
+
+		while ( root.parent ) {
+			root = root.parent;
+		}
+
+		return root;
+	}
+
+	/**
 	 * Returns ancestors array of this node.
 	 *
 	 * @param {Object} options Options object.
@@ -127,16 +131,6 @@ export default class Node {
 	}
 
 	/**
-	 * Sets the {@link engine.view.Document} of the node. Note that not all of nodes need to have {@link engine.view.Document}
-	 * assigned, see {@link engine.view.Node#getDocument}.
-	 *
-	 * @param {engine.view.Document} document Document.
-	 */
-	setDocument( document ) {
-		this._document = document;
-	}
-
-	/**
 	 * Removes node from parent.
 	 */
 	remove() {
@@ -149,7 +143,7 @@ export default class Node {
 	 * @fires engine.view.Node#change
 	 */
 	_fireChange( type, node ) {
-		this.fire( 'change', type, node );
+		this.fire( 'change' + type.toLowerCase(), type, node );
 
 		if ( this.parent ) {
 			this.parent._fireChange( type, node );

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

@@ -22,8 +22,20 @@ export default class FocusObserver extends DomEventObserver {
 		this.domEventType = [ 'focus', 'blur' ];
 	}
 
-	onDomEvent( domEvt ) {
-		this.fire( domEvt.type, domEvt );
+	onDomEvent( domEvent ) {
+		const target = this.document.domConverter.getCorrespondingViewElement( domEvent.target );
+
+		for ( let root of this.document.roots.values() ) {
+			if ( target === root ) {
+				if ( domEvent.type == 'focus' ) {
+					root.isFocused = true;
+				} else {
+					root.isFocused = false;
+				}
+			}
+		}
+
+		this.fire( domEvent.type, domEvent );
 	}
 }
 

+ 51 - 0
packages/ckeditor5-engine/src/view/rooteditableelement.js

@@ -0,0 +1,51 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import EditableElement from './editableelement.js';
+
+export default class RootEditableElement extends EditableElement {
+	/**
+	 * Creates an root editable element.
+	 */
+	constructor( document, name, rootName ) {
+		super( name );
+
+		/**
+		 * {@link engine.view.Document} that is an owner of this root.
+		 *
+		 * @private
+		 * @member {engine.view.Document} engine.view.RootEditableElement#_document
+		 */
+		this._document = document;
+
+		/**
+		 * Name of this root inside {@link engine.view.Document} that is an owner of this root.
+		 *
+		 * @readonly
+		 * @member {String} engine.view.RootEditableElement#rootName
+		 */
+		this.rootName = rootName;
+
+		/**
+		 * Whether the editable is focused.
+		 *
+		 * @readonly
+		 * @observable
+		 * @member {Boolean} engine.view.RootEditableElement#isFocused
+		 */
+		this.set( 'isFocused', false );
+	}
+
+	/**
+	 * Gets {@link engine.view.Document} reference.
+	 *
+	 * @returns {engine.view.Document|null} View Document of the node or null.
+	 */
+	getDocument() {
+		return this._document;
+	}
+}

+ 10 - 4
packages/ckeditor5-engine/tests/view/manual/focusobserver.js

@@ -12,10 +12,16 @@ import FocusObserver from '/ckeditor5/engine/view/observer/focusobserver.js';
 
 const viewDocument = new Document();
 
-viewDocument.on( 'focus', ( evt, data ) => console.log( 'focus', data.domTarget ) );
-viewDocument.on( 'blur', ( evt, data ) => console.log( 'blur', data.domTarget ) );
+viewDocument.on( 'focus', ( evt, data ) => console.log( 'event:focus', data.domTarget ) );
+viewDocument.on( 'blur', ( evt, data ) => console.log( 'event:blur', data.domTarget ) );
 
 viewDocument.addObserver( FocusObserver );
 
-viewDocument.createRoot( document.getElementById( 'editable1' ), 'editable1' );
-viewDocument.createRoot( document.getElementById( 'editable2' ), 'editable2' );
+const domEditable1 = document.getElementById( 'editable1' );
+const domEditable2 = document.getElementById( 'editable2' );
+
+const editable1 = viewDocument.createRoot( domEditable1, 'editable1' );
+const editable2 = viewDocument.createRoot( domEditable2, 'editable2' );
+
+editable1.on( 'change:isFocused', () => domEditable1.style.border = editable1.isFocused ? 'solid 1px green' : 'solid 1px red' );
+editable2.on( 'change:isFocused', () => domEditable2.style.border = editable2.isFocused ? 'solid 1px green' : 'solid 1px red' );