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

Manual test for mutation observer and debugging.

Piotr Jasiun 10 лет назад
Родитель
Сommit
c631096bf7

+ 4 - 5
packages/ckeditor5-engine/src/treeview/element.js

@@ -25,7 +25,7 @@ export default class Element extends Node {
 			this._attrs = new Map( attrs );
 		}
 
-		this._children = langUtils.clone( children );
+		this._children = langUtils.clone( children ) || [];
 
 		this.domElement = null;
 	}
@@ -128,10 +128,9 @@ export default class Element extends Node {
 	cloneDOMAttrs( element ) {
 		this.markToSync( 'ATTRIBUTES_NEED_UPDATE' );
 
-		const attrKeys = element.attributes;
-
-		for ( let key of attrKeys ) {
-			this.setAttr( key, element.getAttribute( key ) );
+		for ( let i = element.attributes.length - 1; i >= 0; i-- ) {
+			let attr = element.attributes[ i ];
+			this.setAttr( attr.name, attr.value );
 		}
 	}
 

+ 0 - 5
packages/ckeditor5-engine/src/treeview/node.js

@@ -53,11 +53,6 @@ export default class Node {
 	}
 
 	markToSync( type ) {
-		// If the node has no DOM element it is not rendered yet, its children/attributes do not need to be marked to be sync.
-		if ( !this.DOMElement ) {
-			return;
-		}
-
 		const treeView = this.getTreeView();
 
 		// If element is not attached to the Tree view it is a child of the detached subtree and will be rendered anyway with this

+ 6 - 1
packages/ckeditor5-engine/src/treeview/observer/mutationobserver.js

@@ -9,6 +9,8 @@ import diff from '../../utils-diff.js';
 import Observer from './observer.js';
 import ViewElement from '../element.js';
 import ViewText from '../text.js';
+import objectUtils from '../../lib/lodash/object.js';
+import EmitterMixin from '../../emittermixin.js';
 
 export default class MutationObserver extends Observer {
 	constructor() {
@@ -27,6 +29,7 @@ export default class MutationObserver extends Observer {
 	 * @param {treeView.TreeView}
 	 */
 	init( treeView ) {
+		this.treeView = treeView;
 		this.domRoot = treeView.domRoot;
 
 		this._mutationObserver = new window.MutationObserver( this._onMutations.bind( this ) );
@@ -63,7 +66,7 @@ export default class MutationObserver extends Observer {
 
 		for ( let mutation of domMutations ) {
 			if ( mutation.type === 'characterData' ) {
-				const text = Text.getCorespondingText( mutation.target );
+				const text = ViewText.getCorespondingText( mutation.target );
 
 				if ( text && !mutatedElements.has( text.parent ) ) {
 					mutatedTexts.add( {
@@ -124,3 +127,5 @@ export default class MutationObserver extends Observer {
 		}
 	}
 }
+
+objectUtils.extend( MutationObserver.prototype, EmitterMixin );

+ 16 - 8
packages/ckeditor5-engine/src/treeview/renderer.js

@@ -21,12 +21,21 @@ export default class Renderer {
 	}
 
 	markToSync( node, type ) {
-		if ( type === 'ATTRIBUTES_NEED_UPDATE' ) {
-			this.markedAttrs.push( node );
-		} else if ( type === 'CHILDREN_NEED_UPDATE' ) {
-			this.markedChildren.push( node );
-		} else if ( type === 'TEXT_NEEDS_UPDATE' ) {
-			this.markedTexts.push( node );
+		if ( type === 'TEXT_NEEDS_UPDATE' ) {
+			if ( node.parent.domElement ) {
+				this.markedTexts.add( node );
+			}
+		} else {
+			// If the node has no DOM element it is not rendered yet, its children/attributes do not need to be marked to be sync.
+			if ( !node.domElement ) {
+				return;
+			}
+
+			if ( type === 'ATTRIBUTES_NEED_UPDATE' ) {
+				this.markedAttrs.add( node );
+			} else if ( type === 'CHILDREN_NEED_UPDATE' ) {
+				this.markedChildren.add( node );
+			}
 		}
 	}
 
@@ -76,10 +85,9 @@ export default class Renderer {
 		function updateChildren( viewElement ) {
 			const domElement = viewElement.domElement;
 			const domChildren = domElement.childNodes;
-			const viewChildren = viewElement.getChildren();
+			const viewChildren = Array.from( viewElement.getChildren() );
 
 			const actions = diff( domChildren, viewChildren, compareNodes );
-
 			let i = 0;
 
 			for ( let action of actions ) {

+ 2 - 6
packages/ckeditor5-engine/src/treeview/text.js

@@ -49,17 +49,13 @@ export default class Text extends Node {
 				return viewElement.getNextSibling;
 			}
 		} else {
-			const viewElement = Element.getCorespondingElement( this.parent );
+			const viewElement = Element.getCorespondingElement( domText.parentElement );
 
 			if ( viewElement ) {
-				return viewElement.getChild[ 0 ];
+				return viewElement.getChild( 0 );
 			}
 		}
 
-		if ( !previousSibling && this.parent.domElement ) {
-			return this.parent.domElement.childNodes[ 0 ];
-		}
-
 		return null;
 	}
 }

+ 12 - 10
packages/ckeditor5-engine/src/treeview/treeview.js

@@ -5,19 +5,13 @@
 
 'use strict';
 
-import utils from '../utils.js';
-import EmitterMixin from './emittermixin.js';
+import objectUtils from '../lib/lodash/object.js';
+import EmitterMixin from '../emittermixin.js';
 import RootElement from './rootelement.js';
 import Renderer from './renderer.js';
 
 export default class TreeView {
 	constructor( domElement ) {
-		/**
-		 * Root of the view
-		 */
-		this.viewRoot = new RootElement( this, domElement.name );
-		this.viewRoot.cloneDOMAttrs();
-
 		/**
 		 * Root of the DOM.
 		 */
@@ -26,10 +20,18 @@ export default class TreeView {
 		this.observers = new Set();
 
 		this.renderer = new Renderer( this );
+
+		/**
+		 * Root of the view
+		 */
+		this.viewRoot = new RootElement( this, domElement.name );
+		this.viewRoot.cloneDOMAttrs( domElement );
+		this.viewRoot.setDomElement( domElement );
+		this.viewRoot.markToSync( 'CHILDREN_NEED_UPDATE' );
 	}
 
 	addObserver( observer ) {
-		this.observers.push( observer );
+		this.observers.add( observer );
 		observer.init( this );
 		observer.attach();
 	}
@@ -47,4 +49,4 @@ export default class TreeView {
 	}
 }
 
-utils.extend( Document.prototype, EmitterMixin );
+objectUtils.extend( TreeView.prototype, EmitterMixin );

+ 106 - 110
packages/ckeditor5-engine/src/utils-diff.js

@@ -7,124 +7,120 @@
 
 // the following code is based on the "O(NP) Sequence Comparison Algorithm" by Sun Wu, Udi Manber, Gene Myers, Webb Miller
 
-CKEDITOR.define( [], () => {
-	// action types
-	var INSERT = 1,
-		DELETE = -1,
-		EQUAL = 0;
-
-	/**
-	 * Calculates the difference between two arrays producing an object containing a list of actions
-	 * necessary to transform one array into another.
-	 *
-	 *		diff( 'aba', 'acca' ); // [ EQUAL, INSERT, INSERT, DELETE, EQUAL ]
-	 *
-	 * @param {Array} a Input array
-	 * @param {Array} b Output array
-	 * @param {Function} [cmp] Optional function used to compare array values, by default === is used
-	 * @return {Array}
-	 */
-	function diff( a, b, cmp ) {
-		// Set the comparator function.
-		cmp = cmp || function( a, b ) {
-				return a === b;
-			};
-
-		// Temporary action type statics.
-		var _INSERT, _DELETE;
-
-		// Swapped the arrays to use the shorter one as the first one.
-		if ( b.length < a.length ) {
-			var tmp = a;
-
-			a = b;
-			b = tmp;
-
-			// We swap the action types as well.
-			_INSERT = DELETE;
-			_DELETE = INSERT;
-		} else {
-			_INSERT = INSERT;
-			_DELETE = DELETE;
+// action types
+var INSERT = 1,
+	DELETE = -1,
+	EQUAL = 0;
+
+/**
+ * Calculates the difference between two arrays producing an object containing a list of actions
+ * necessary to transform one array into another.
+ *
+ *		diff( 'aba', 'acca' ); // [ EQUAL, INSERT, INSERT, DELETE, EQUAL ]
+ *
+ * @param {Array} a Input array
+ * @param {Array} b Output array
+ * @param {Function} [cmp] Optional function used to compare array values, by default === is used
+ * @return {Array}
+ */
+ export default function diff( a, b, cmp ) {
+	// Set the comparator function.
+	cmp = cmp || function( a, b ) {
+			return a === b;
+		};
+
+	// Temporary action type statics.
+	var _INSERT, _DELETE;
+
+	// Swapped the arrays to use the shorter one as the first one.
+	if ( b.length < a.length ) {
+		var tmp = a;
+
+		a = b;
+		b = tmp;
+
+		// We swap the action types as well.
+		_INSERT = DELETE;
+		_DELETE = INSERT;
+	} else {
+		_INSERT = INSERT;
+		_DELETE = DELETE;
+	}
+
+	var m = a.length,
+		n = b.length,
+		delta = n - m;
+
+	// Edit scripts, for each diagonal.
+	var es = {};
+	// Furthest points, the furthest y we can get on each diagonal.
+	var fp = {};
+
+	function snake( k ) {
+		// We use -1 as an alternative below to handle initial values ( instead of filling the fp with -1 first ).
+		// Furthest points (y) on the diagonal below k.
+		var y1 = ( fp[ k - 1 ] !== undefined ? fp[ k - 1 ] : -1 ) + 1;
+		// Furthest points (y) on the diagonal above k.
+		var y2 = fp[ k + 1 ] !== undefined ? fp[ k + 1 ] : -1;
+		// The way we should go to get further.
+		var dir = y1 > y2 ? -1 : 1;
+
+		// Clone previous actions array (if any).
+		if ( es[ k + dir ] ) {
+			es[ k ] = es[ k + dir ].slice( 0 );
 		}
 
-		var m = a.length,
-			n = b.length,
-			delta = n - m;
-
-		// Edit scripts, for each diagonal.
-		var es = {};
-		// Furthest points, the furthest y we can get on each diagonal.
-		var fp = {};
-
-		function snake( k ) {
-			// We use -1 as an alternative below to handle initial values ( instead of filling the fp with -1 first ).
-			// Furthest points (y) on the diagonal below k.
-			var y1 = ( fp[ k - 1 ] !== undefined ? fp[ k - 1 ] : -1 ) + 1;
-			// Furthest points (y) on the diagonal above k.
-			var y2 = fp[ k + 1 ] !== undefined ? fp[ k + 1 ] : -1;
-			// The way we should go to get further.
-			var dir = y1 > y2 ? -1 : 1;
-
-			// Clone previous actions array (if any).
-			if ( es[ k + dir ] ) {
-				es[ k ] = es[ k + dir ].slice( 0 );
-			}
-
-			// Create actions array.
-			if ( !es[ k ] ) {
-				es[ k ] = [];
-			}
-
-			// Push the action.
-			es[ k ].push( y1 > y2 ? _INSERT : _DELETE );
-
-			// Set the beginning coordinates.
-			var y = Math.max( y1, y2 ),
-				x = y - k;
-
-			// Traverse the diagonal as long as the values match.
-			while ( x < m && y < n && cmp( a[ x ], b[ y ] ) ) {
-				x++;
-				y++;
-				// Push no change action.
-				es[ k ].push( EQUAL );
-			}
-
-			return y;
+		// Create actions array.
+		if ( !es[ k ] ) {
+			es[ k ] = [];
 		}
 
-		var p = 0,
-			k;
+		// Push the action.
+		es[ k ].push( y1 > y2 ? _INSERT : _DELETE );
 
-		// Traverse the graph until we reach the end of the longer string.
-		do {
-			// Updates furthest points and edit scripts for diagonals below delta.
-			for ( k = -p; k < delta; k++ ) {
-				fp[ k ] = snake( k );
-			}
+		// Set the beginning coordinates.
+		var y = Math.max( y1, y2 ),
+			x = y - k;
 
-			// Updates furthest points and edit scripts for diagonals above delta.
-			for ( k = delta + p; k > delta; k-- ) {
-				fp[ k ] = snake( k );
-			}
+		// Traverse the diagonal as long as the values match.
+		while ( x < m && y < n && cmp( a[ x ], b[ y ] ) ) {
+			x++;
+			y++;
+			// Push no change action.
+			es[ k ].push( EQUAL );
+		}
 
-			// Updates furthest point and edit script for the delta diagonal.
-			// note that the delta diagonal is the one which goes through the sink (m, n).
-			fp[ delta ] = snake( delta );
+		return y;
+	}
 
-			p++;
-		} while ( fp[ delta ] !== n );
+	var p = 0,
+		k;
 
-		// Return the final list of edit actions.
-		// We remove the first item that represents the action for the injected nulls.
-		return es[ delta ].slice( 1 );
-	}
+	// Traverse the graph until we reach the end of the longer string.
+	do {
+		// Updates furthest points and edit scripts for diagonals below delta.
+		for ( k = -p; k < delta; k++ ) {
+			fp[ k ] = snake( k );
+		}
+
+		// Updates furthest points and edit scripts for diagonals above delta.
+		for ( k = delta + p; k > delta; k-- ) {
+			fp[ k ] = snake( k );
+		}
+
+		// Updates furthest point and edit script for the delta diagonal.
+		// note that the delta diagonal is the one which goes through the sink (m, n).
+		fp[ delta ] = snake( delta );
+
+		p++;
+	} while ( fp[ delta ] !== n );
 
-	// Expose action types.
-	diff.INSERT = INSERT;
-	diff.DELETE = DELETE;
-	diff.EQUAL = EQUAL;
+	// Return the final list of edit actions.
+	// We remove the first item that represents the action for the injected nulls.
+	return es[ delta ].slice( 1 );
+}
 
-	return diff;
-} );
+// Expose action types.
+diff.INSERT = INSERT;
+diff.DELETE = DELETE;
+diff.EQUAL = EQUAL;

+ 22 - 1
packages/ckeditor5-engine/tests/treeview/manual/mutationobserver.html

@@ -1,7 +1,28 @@
-<div contenteditable="true">
+<div contenteditable="true" id="editor">
 	<p>Foo</p>
 	<p>Bar</p>
 </div>
 <script>
+	'use strict';
 
+	bender.amd.manualRequire( [
+		'core/treeview/treeview',
+		'core/treeview/observer/mutationobserver',
+		'core/treeview/text'
+	], function( TreeView, MutationObserver, Text ) {
+		const treeView = new TreeView( document.getElementById( 'editor' ) );
+		const mutationObserver = new MutationObserver();
+
+		mutationObserver.on( 'mutations', ( evt, mutations ) => {
+			console.log( mutations );
+		} );
+		
+		treeView.addObserver( mutationObserver );
+
+		treeView.viewRoot.insertChildren( 0, new Text( 'bom' ) );
+
+		treeView.render();
+
+		console.log( treeView );
+	} );	
 </script>

+ 1 - 0
packages/ckeditor5-engine/tests/treeview/manual/mutationobserver.md

@@ -1,3 +1,4 @@
+@bender-ui: collapsed
 @bender-tags: treeview
 
 ## Mutation Observer ##