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

+ 23 - 12
packages/ckeditor5-engine/src/treecontroller/ViewConversionController.js

@@ -5,24 +5,35 @@
 
 
 'use strict';
 'use strict';
 
 
+import ViewElement from '../treview/element.js';
+import ViewText from '../treview/text.js';
+
 export default class ViewConversionController {
 export default class ViewConversionController {
-	constructor( context ) {
-		this.context = context;
-	}
+	constructor( dispatcher, consumable ) {
+		this.consumable = consumable;
 
 
-	convert( source ) {
-		// data.consumable()
-		// data.convert
+		this._dispatcher = dispatcher;
+	}
 
 
+	convert( input, context ) {
 		const data = {
 		const data = {
-			source: source,
-			target: null
+			input: input,
+			output: null,
+			context: context
 		};
 		};
 
 
-		this.fire( 'element:' + source.name, data, this.context );
+		if ( input instanceof ViewElement ) {
+			this._dispatcher.fire( 'element:' + input.name, data, this );
+		} else if ( input instanceof ViewText ) {
+			this._dispatcher.fire( 'text', data, this );
+		} else { // DocumentFragment
+			this._dispatcher.fire( 'documentFragment', data, this );
+		}
 
 
-		this.fire( 'post', data.target );
+		return data.output;
+	}
 
 
-		return data.target;
+	convertChildren( input, context ) {
+		return Array.from( input.getChildren() ).map( viewChild => this.convert( viewChild, context ) );
 	}
 	}
-}
+}

+ 19 - 20
packages/ckeditor5-engine/src/treecontroller/datacontroller.js

@@ -9,9 +9,10 @@ import Mapper from './mapper.js';
 import ModelConversionDispatcher from './modelconversiondispatcher.js';
 import ModelConversionDispatcher from './modelconversiondispatcher.js';
 import ViewConversionController from './viewconversioncontroller.js';
 import ViewConversionController from './viewconversioncontroller.js';
 import { insertText } from './model-to-view.js';
 import { insertText } from './model-to-view.js';
+import { convertText, convertChildren } from './view-to-model.js';
 
 
 import Writer from '../treview/writer.js';
 import Writer from '../treview/writer.js';
-import ViewElement from '../treview/element.js';
+import ViewDocumentFragment from '../treview/documentfragment.js';
 import DomConverter from '../treview/domconverter.js';
 import DomConverter from '../treview/domconverter.js';
 
 
 import ModelRange from '../treemodel/range.js';
 import ModelRange from '../treemodel/range.js';
@@ -21,19 +22,23 @@ export default class DataController {
 		this.model = modelDocument;
 		this.model = modelDocument;
 
 
 		this.mapper = new Mapper();
 		this.mapper = new Mapper();
+
 		this.writer = new Writer();
 		this.writer = new Writer();
 
 
+		this.domConverter = new DomConverter();
+
+		this.dataProcessor = dataProcessor;
+
 		this.toView = new ModelConversionDispatcher( {
 		this.toView = new ModelConversionDispatcher( {
 			writer: this.writer,
 			writer: this.writer,
 			mapper: this.mapper
 			mapper: this.mapper
 		} );
 		} );
+		this.toView.on( 'insert:text', insertText() );
 
 
 		this.toModel = new ViewConversionController();
 		this.toModel = new ViewConversionController();
-
-		this.domConverter = new DomConverter();
-		this.dataProcessor = dataProcessor;
-
-		this.toView.on( 'insert:text', insertText() );
+		this.toModel.on( 'text', convertText() );
+		this.toModel.on( 'element', convertChildren(), 9999 );
+		this.toModel.on( 'documentFragment', convertChildren(), 9999 );
 	}
 	}
 
 
 	get( rootName ) {
 	get( rootName ) {
@@ -42,17 +47,15 @@ export default class DataController {
 		const modelRange = ModelRange.createFromElement( modelRootElement );
 		const modelRange = ModelRange.createFromElement( modelRootElement );
 
 
 		// model -> view
 		// model -> view
-		const viewElement = new ViewElement(); // ViewDocumentFragment?
-		this.mapper.bindElements( modelRootElement, viewElement );
+		const viewDocumentFragment = new ViewDocumentFragment();
+		this.mapper.bindElements( modelRootElement, viewDocumentFragment );
 
 
 		this.toView.convertInsert( modelRange );
 		this.toView.convertInsert( modelRange );
 
 
-		this.mapper.unbindElements( modelRootElement, viewElement );
+		this.mapper.unbindElements( modelRootElement, viewDocumentFragment );
 
 
 		// view -> DOM
 		// view -> DOM
-		const domElement = this.domConverter.viewToDom( viewElement, document ); // TODO new document
-
-		const domDocumentFragment = domElement; // TODO
+		const domDocumentFragment = this.domConverter.viewToDom( viewDocumentFragment, document );
 
 
 		// DOM -> data
 		// DOM -> data
 		return this.dataProcessor.toData( domDocumentFragment );
 		return this.dataProcessor.toData( domDocumentFragment );
@@ -62,21 +65,17 @@ export default class DataController {
 		// data -> DOM
 		// data -> DOM
 		const domDocumentFragment = this.dataProcessor.toDom( data );
 		const domDocumentFragment = this.dataProcessor.toDom( data );
 
 
-		// view -> DOM
+		// DOM -> view
 		const viewDocumentFragment = this.domConverter.domToView( domDocumentFragment );
 		const viewDocumentFragment = this.domConverter.domToView( domDocumentFragment );
 
 
-		// view	-> model
-		this.fire( 'view', viewDocumentFragment ); // ?
-
-		const modelNodeList = this.toModel.convert( viewDocumentFragment );
-
-		this.fire( 'model', modelNodeList ); // ?
+		// view -> model
+		const modelDocumentFragment = this.toModel.convert( viewDocumentFragment );
 
 
 		// Save to model
 		// Save to model
 		const modelRoot = this.model.getRoot( rootName );
 		const modelRoot = this.model.getRoot( rootName );
 
 
 		this.model.batch()
 		this.model.batch()
 			.removeChildren( 0, modelRoot.getChildCount() )
 			.removeChildren( 0, modelRoot.getChildCount() )
-			.appendChildren( modelRoot, rootName );
+			.appendChildren( modelRoot, modelDocumentFragment );
 	}
 	}
 }
 }

+ 9 - 3
packages/ckeditor5-engine/src/treecontroller/editingcontroller.js

@@ -5,6 +5,12 @@
 
 
 'use strict';
 'use strict';
 
 
+import Mapper from './mapper.js';
+import ModelConversionDispatcher from './modelconversiondispatcher.js';
+import { insertText, remove, move } from './model-to-view.js';
+
+import TreeView from '../treview/treview.js';
+
 export default class EditingController {
 export default class EditingController {
 	constructor( modelDocument ) {
 	constructor( modelDocument ) {
 		this.model = modelDocument;
 		this.model = modelDocument;
@@ -21,9 +27,9 @@ export default class EditingController {
 			this.toView.convertChange( type, changeInfo );
 			this.toView.convertChange( type, changeInfo );
 		} );
 		} );
 
 
-		toView.on( 'insert:text', insertText() );
-		toView.on( 'remove', remove() );
-		toView.on( 'move', move() );
+		this.toView.on( 'insert:text', insertText() );
+		this.toView.on( 'remove', remove() );
+		this.toView.on( 'move', move() );
 	}
 	}
 
 
 	createRoot( element, name ) {
 	createRoot( element, name ) {

+ 47 - 44
packages/ckeditor5-engine/src/treecontroller/modelconversiondispatcher.js

@@ -9,8 +9,8 @@ import Consumable from './modelconsumable.js';
 import TextFragment from '../treemodel/textfragment.js';
 import TextFragment from '../treemodel/textfragment.js';
 
 
 export default class ModelConversionDispatcher {
 export default class ModelConversionDispatcher {
-	constructor( context ) {
-		this.context = context;
+	constructor( controller ) {
+		this.controller = controller;
 	}
 	}
 
 
 	convertChange( type, changeInfo ) {
 	convertChange( type, changeInfo ) {
@@ -26,38 +26,23 @@ export default class ModelConversionDispatcher {
 	}
 	}
 
 
 	convertInsert( range ) {
 	convertInsert( range ) {
-		const consumable = new Consumable();
-		const values = [];
+		this.controller.consumable = this._createInsertConsumable();
 
 
 		for ( let value of range ) {
 		for ( let value of range ) {
-			values.push( value );
-
-			const item = value.item;
-
-			consumable.add( item, 'insert' );
-
-			for ( let key of item.getAttributes() ) {
-				consumable.add( item, 'addAttribute:' + key );
-			}
-		}
-
-		for ( let value of values ) {
 			const item = value.item;
 			const item = value.item;
 			const range = Range.createFromPositionAndShift( value.previousPosition, value.length );
 			const range = Range.createFromPositionAndShift( value.previousPosition, value.length );
-			const model = {
-				item,
-				range,
-				consumable
-			};
+			const model = { item, range };
 
 
-			this._testAndFire( 'insert', model, this.context );
+			this._testAndFire( 'insert', model, this.controller );
 
 
 			for ( let key of item.getAttributes() ) {
 			for ( let key of item.getAttributes() ) {
 				model.attributeKey = key;
 				model.attributeKey = key;
 
 
-				this._testAndFire( 'addAttribute:' + key, model, this.context );
+				this._testAndFire( 'addAttribute:' + key, model, this.controller );
 			}
 			}
 		}
 		}
+
+		this.controller.consumable = undefined;
 	}
 	}
 
 
 	convertMove( range, sourcePosition ) {
 	convertMove( range, sourcePosition ) {
@@ -66,7 +51,7 @@ export default class ModelConversionDispatcher {
 			sourcePosition: sourcePosition,
 			sourcePosition: sourcePosition,
 		};
 		};
 
 
-		this.fire( 'move', model, this.context );
+		this.fire( 'move', model, this.controller );
 	}
 	}
 
 
 	convertRemove( range, sourcePosition ) {
 	convertRemove( range, sourcePosition ) {
@@ -75,24 +60,13 @@ export default class ModelConversionDispatcher {
 			sourcePosition: sourcePosition,
 			sourcePosition: sourcePosition,
 		};
 		};
 
 
-		this.fire( 'remove', model, this.context );
+		this.fire( 'remove', model, this.controller );
 	}
 	}
 
 
 	convertAttribute( type, range, key, oldValue, newValue ) {
 	convertAttribute( type, range, key, oldValue, newValue ) {
-		const consumable = new Consumable();
-		const values = [];
+		this.controller.consumable = this._createAttributeConsumable( type, range, key );
 
 
 		for ( let value of range ) {
 		for ( let value of range ) {
-			values.push( value );
-
-			const item = value.item;
-
-			for ( let key of item.getAttributes() ) {
-				consumable.add( item, type + ':' + key );
-			}
-		}
-
-		for ( let value of values ) {
 			const item = value.item;
 			const item = value.item;
 			const range = Range.createFromPositionAndShift( value.previousPosition, value.length );
 			const range = Range.createFromPositionAndShift( value.previousPosition, value.length );
 			const model = {
 			const model = {
@@ -100,15 +74,44 @@ export default class ModelConversionDispatcher {
 				range: range,
 				range: range,
 				attributeKey: key,
 				attributeKey: key,
 				attributeOldValue: oldValue,
 				attributeOldValue: oldValue,
-				attributeNewValue: newValue,
-				consumable: consumable
+				attributeNewValue: newValue
 			};
 			};
 
 
-			this._testAndFire( type + ':' + key, model, this.context );
+			this._testAndFire( type + ':' + key, model, this.controller );
+		}
+
+		this.controller.consumable = undefined;
+	}
+
+	_createInsertConsumable( range ) {
+		const consumable = new Consumable();
+
+		for ( let value of range ) {
+			const item = value.item;
+
+			consumable.add( item, 'insert' );
+
+			for ( let key of item.getAttributes() ) {
+				consumable.add( item, 'addAttribute:' + key );
+			}
 		}
 		}
+
+		return consumable;
+	}
+
+	_createAttributeConsumable( type, range, key ) {
+		const consumable = new Consumable();
+
+		for ( let value of range ) {
+			const item = value.item;
+
+			consumable.add( item, type + ':' + key );
+		}
+
+		return consumable;
 	}
 	}
 
 
-	_testAndFire( type, model, context ) {
+	_testAndFire( type, model, controller ) {
 		if ( !model.consumable.test( model.item, type ) ) {
 		if ( !model.consumable.test( model.item, type ) ) {
 			// Do not fire event if the item was consumed.
 			// Do not fire event if the item was consumed.
 			return;
 			return;
@@ -117,14 +120,14 @@ export default class ModelConversionDispatcher {
 		if ( type === 'insert' ) {
 		if ( type === 'insert' ) {
 			if ( model.item instanceof TextFragment ) {
 			if ( model.item instanceof TextFragment ) {
 				// e.g. insert:text
 				// e.g. insert:text
-				this.fire( type + ':text', model, context );
+				this.fire( type + ':text', model, controller );
 			} else {
 			} else {
 				// e.g. insert:element:p
 				// e.g. insert:element:p
-				this.fire( type + ':element:' + model.item.name, model, context );
+				this.fire( type + ':element:' + model.item.name, model, controller );
 			}
 			}
 		} else {
 		} else {
 			// e.g. addAttribute:alt:img
 			// e.g. addAttribute:alt:img
-			this.fire( type + ':' + model.item.name, model, context );
+			this.fire( type + ':' + model.item.name, model, controller );
 		}
 		}
 	}
 	}
 }
 }

+ 25 - 0
packages/ckeditor5-engine/src/treecontroller/view-to-model-converters.js

@@ -0,0 +1,25 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import ModelDocumentFragment from '../treemodel/documentfragment.js';
+import ModelText from '../treemodel/text.js';
+
+export function convertChildren() {
+	return ( data, controller ) => {
+		if ( !data.output && controller.consumable.test( data.input ) ) {
+			data.output = new ModelDocumentFragment( controller.convertChildren( data.input, data.context ) );
+		}
+	};
+}
+
+export function convertText() {
+	return ( data, controller ) => {
+		if ( controller.consumable.consume( data.input ) ) {
+			data.output = new ModelText( data.input.data );
+		}
+	};
+}

+ 39 - 0
packages/ckeditor5-engine/src/treecontroller/viewconversioncontroller.js

@@ -0,0 +1,39 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import ViewElement from '../treview/element.js';
+import ViewText from '../treview/text.js';
+
+export default class ViewConversionController {
+	constructor( dispatcher, consumable ) {
+		this.consumable = consumable;
+
+		this._dispatcher = dispatcher;
+	}
+
+	convert( input, context ) {
+		const data = {
+			input: input,
+			output: null,
+			context: context
+		};
+
+		if ( input instanceof ViewElement ) {
+			this._dispatcher.fire( 'element:' + input.name, data, this );
+		} else if ( input instanceof ViewText ) {
+			this._dispatcher.fire( 'text', data, this );
+		} else { // DocumentFragment
+			this._dispatcher.fire( 'documentFragment', data, this );
+		}
+
+		return data.output;
+	}
+
+	convertChildren( input, context ) {
+		return Array.from( input.getChildren() ).map( viewChild => this.convert( viewChild, context ) );
+	}
+}

+ 58 - 0
packages/ckeditor5-engine/src/treecontroller/viewconversiondispatcher.js

@@ -0,0 +1,58 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import Consumable from './viewconsumable.js';
+import ConversionController from './viewconversioncontroller.js';
+import EmitterMixin from '../../utils/emittermixin.js';
+import utils from '../../utils/utils.js';
+
+/**
+ *
+ *		coversionDispatcher.on( 'element:p', ( data, controller ) => {
+ *			const paragraph = new ModelElement( 'paragraph' );
+ *			const schemaQuery = {
+ *				name: 'paragraph',
+ *				inside: data.context
+ *			};
+ *
+ *			if ( controller.schema.checkQuery( schemaQuery ) ) {
+ *				if ( !controller.consumable.consume( data.input, { name: true } ) ) {
+ *					const context = data.context.concat( paragraph );
+ *					const children = controller.convertToFragment( data.input, context );
+ *					paragraph.appendChildren( children );
+ *					data.output = paragraph;
+ *				}
+ *			}
+ *		} );
+ *
+ *		coversionDispatcher.on( 'element:a', ( data, controller ) => {
+ *			if ( controller.consumable.consume( data.input, { name: true, attributes: [ 'href' ] } ) ) {
+ *				for ( let item of Range.createFrom( data.output ) ) {
+ *					const schemaQuery = {
+ *						name: item.name || '$text',
+ *						attribute: 'link',
+ *						inside: data.context
+ *					};
+ *					if ( controller.schema.checkQuery( schemaQuery ) ) {
+ *						item.setAttribute( 'link', data.input.getAttribute( 'href' ) );
+ *					}
+ *				}
+ *			}
+ *		}
+ */
+
+export default class ViewConversionDispatcher {
+	convert( viewDocumentFragment ) {
+		const consumable = Consumable.createFrom( viewDocumentFragment );
+
+		const conversionContext = new ConversionController( this, consumable );
+
+		return conversionContext.convert( viewDocumentFragment );
+	}
+}
+
+utils.mix( ViewConversionDispatcher, EmitterMixin );