Преглед изворни кода

Merge pull request #258 from ckeditor/t/246

Split UI framework to a separate package.
Piotrek Koszuliński пре 9 година
родитељ
комит
1a5d06ff79
39 измењених фајлова са 16 додато и 5270 уклоњено
  1. 0 8
      packages/ckeditor5-engine/src/editable.jsdoc
  2. 0 105
      packages/ckeditor5-engine/src/editable/editable.js
  3. 0 54
      packages/ckeditor5-engine/src/editable/editableview.js
  4. 0 47
      packages/ckeditor5-engine/src/editorui/editorui.js
  5. 0 55
      packages/ckeditor5-engine/src/editorui/editoruiview.js
  6. 0 8
      packages/ckeditor5-engine/src/ui.jsdoc
  7. 0 42
      packages/ckeditor5-engine/src/ui/bindings/toolbar.js
  8. 0 82
      packages/ckeditor5-engine/src/ui/componentfactory.js
  9. 0 216
      packages/ckeditor5-engine/src/ui/controller.js
  10. 0 70
      packages/ckeditor5-engine/src/ui/controllercollection.js
  11. 0 278
      packages/ckeditor5-engine/src/ui/domemittermixin.js
  12. 0 38
      packages/ckeditor5-engine/src/ui/model.js
  13. 0 79
      packages/ckeditor5-engine/src/ui/region.js
  14. 0 370
      packages/ckeditor5-engine/src/ui/template.js
  15. 0 715
      packages/ckeditor5-engine/src/ui/view.js
  16. 2 2
      packages/ckeditor5-engine/tests/_utils/ui/boxededitorui/boxededitorui.js
  17. 1 1
      packages/ckeditor5-engine/tests/_utils/ui/boxededitorui/boxededitoruiview.js
  18. 3 3
      packages/ckeditor5-engine/tests/_utils/ui/boxlesseditorui/boxlesseditorui.js
  19. 1 1
      packages/ckeditor5-engine/tests/_utils/ui/editable/framed/framededitable.js
  20. 1 1
      packages/ckeditor5-engine/tests/_utils/ui/editable/framed/framededitableview.js
  21. 1 1
      packages/ckeditor5-engine/tests/_utils/ui/editable/inline/inlineeditable.js
  22. 1 1
      packages/ckeditor5-engine/tests/_utils/ui/editable/inline/inlineeditableview.js
  23. 1 1
      packages/ckeditor5-engine/tests/_utils/ui/floatingtoolbar/floatingtoolbar.js
  24. 2 2
      packages/ckeditor5-engine/tests/creator/manual/_utils/creator/classiccreator.js
  25. 2 2
      packages/ckeditor5-engine/tests/creator/manual/_utils/creator/inlinecreator.js
  26. 1 1
      packages/ckeditor5-engine/tests/creator/manual/_utils/imitatefeatures.js
  27. 0 79
      packages/ckeditor5-engine/tests/editable/editable.js
  28. 0 66
      packages/ckeditor5-engine/tests/editable/editableview.js
  29. 0 30
      packages/ckeditor5-engine/tests/editorui/editorui.js
  30. 0 38
      packages/ckeditor5-engine/tests/editorui/editoruiview.js
  31. 0 64
      packages/ckeditor5-engine/tests/ui/bindings/toolbar.js
  32. 0 59
      packages/ckeditor5-engine/tests/ui/componentfactory.js
  33. 0 366
      packages/ckeditor5-engine/tests/ui/controller.js
  34. 0 111
      packages/ckeditor5-engine/tests/ui/controllercollection.js
  35. 0 391
      packages/ckeditor5-engine/tests/ui/domemittermixin.js
  36. 0 43
      packages/ckeditor5-engine/tests/ui/model.js
  37. 0 128
      packages/ckeditor5-engine/tests/ui/region.js
  38. 0 525
      packages/ckeditor5-engine/tests/ui/template.js
  39. 0 1187
      packages/ckeditor5-engine/tests/ui/view.js

+ 0 - 8
packages/ckeditor5-engine/src/editable.jsdoc

@@ -1,8 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @namespace core.editable
- */

+ 0 - 105
packages/ckeditor5-engine/src/editable/editable.js

@@ -1,105 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import Controller from '../ui/controller.js';
-import Model from '../ui/model.js';
-import utils from '../../utils/utils.js';
-import ObservableMixin from '../../utils/observablemixin.js';
-
-/**
- * @memberOf core.editable
- * @extends core.ui.Controller
- * @mixes utils.ObservaleMixin
- */
-export default class Editable extends Controller {
-	/**
-	 * Creates a new instance of the Editable class.
-	 *
-	 * @param editor
-	 */
-	constructor( editor ) {
-		super();
-
-		this.editor = editor;
-
-		/**
-		 * Whether the editable is in read-write or read-only mode.
-		 *
-		 * @member {Boolean} core.editable.Editable#isEditable
-		 */
-		this.set( 'isEditable', true );
-
-		/**
-		 * Whether the editable is focused.
-		 *
-		 * @readonly
-		 * @member {Boolean} core.editable.Editable#isFocused
-		 */
-		this.set( 'isFocused', false );
-	}
-
-	/**
-	 * The model for the editable view.
-	 *
-	 * @readonly
-	 * @type {core.ui.Model}
-	 */
-	get viewModel() {
-		if ( this._viewModel ) {
-			return this._viewModel;
-		}
-
-		const viewModel = new Model( {
-			isFocused: this.isFocused
-		} );
-		this._viewModel = viewModel;
-
-		viewModel.bind( 'isEditable' ).to( this );
-		this.bind( 'isFocused' ).to( viewModel );
-
-		return viewModel;
-	}
-
-	/**
-	 * Temporary implementation (waiting for integration with the data model).
-	 *
-	 * @param {String} data HTML to be loaded.
-	 */
-	setData( data ) {
-		this.view.editableElement.innerHTML = data;
-	}
-
-	/**
-	 * Temporary implementation (waiting for integration with the data model).
-	 *
-	 * @returns {String} HTML string.
-	 */
-	getData() {
-		return this.view.editableElement.innerHTML;
-	}
-}
-
-utils.mix( Editable, ObservableMixin );
-
-/**
- * The editable model interface.
- *
- * @memberOf core.editable
- * @interface EditableModel
- */
-
-/**
- * Whether the editable has focus.
- *
- * @member {Boolean} core.editable.EditableModel#isFocused
- */
-
-/**
- * Whether the editable is not in read-only mode.
- *
- * @member {Boolean} core.editable.EditableModel#isEditable
- */

+ 0 - 54
packages/ckeditor5-engine/src/editable/editableview.js

@@ -1,54 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import View from '../ui/view.js';
-import CKEditorError from '../../utils/ckeditorerror.js';
-
-/**
- * @memberOf core.editable
- * @extends core.ui.View
- */
-export default class EditableView extends View {
-	/**
-	 * The element which is the main editable element (usually the one with `contentEditable="true"`).
-	 *
-	 * @readonly
-	 * @member {HTMLElement} core.editable.EditableView#editableElement
-	 */
-
-	/**
-	 * Sets the {@link #editableElement} property and applies necessary bindings to it.
-	 *
-	 * @param {HTMLElement} editableElement
-	 */
-	setEditableElement( editableElement ) {
-		const bind = this.attributeBinder;
-
-		if ( this.editableElement ) {
-			throw new CKEditorError(
-				'editableview-cannot-override-editableelement: The editableElement cannot be overriden.'
-			);
-		}
-
-		this.editableElement = editableElement;
-
-		this.applyTemplateToElement( editableElement, {
-			on: {
-				focus: () => {
-					this.model.isFocused = true;
-				},
-				blur: () => {
-					this.model.isFocused = false;
-				}
-			},
-
-			attributes: {
-				contentEditable: bind.to( 'isEditable' )
-			}
-		} );
-	}
-}

+ 0 - 47
packages/ckeditor5-engine/src/editorui/editorui.js

@@ -1,47 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import Controller from '../ui/controller.js';
-import ControllerCollection from '../ui/controllercollection.js';
-import ComponentFactory from '../ui/componentfactory.js';
-import ObservableMixin from '../../utils/observablemixin.js';
-import utils from '../../utils/utils.js';
-
-/**
- * Base class for the editor main view controllers.
- *
- * @memberOf core.editorUI
- * @extends core.ui.Controller
- * @mixes utils.ObservaleMixin
- */
-
-export default class EditorUI extends Controller {
-	/**
-	 * Creates an EditorUI instance.
-	 *
-	 * @param {core.Editor} editor
-	 */
-	constructor( editor ) {
-		super();
-
-		/**
-		 * @readonly
-		 * @member {core.Editor} core.editorUI.EditorUI#editor
-		 */
-		this.editor = editor;
-
-		/**
-		 * @readonly
-		 * @member {core.ui.ComponentFactory} core.editorUI.EditorUI#featureComponents
-		 */
-		this.featureComponents = new ComponentFactory( editor );
-
-		this.collections.add( new ControllerCollection( 'body' ) );
-	}
-}
-
-utils.mix( EditorUI, ObservableMixin );

+ 0 - 55
packages/ckeditor5-engine/src/editorui/editoruiview.js

@@ -1,55 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import View from '../ui/view.js';
-
-/**
- * Base class for the editor main views.
- *
- * @memberOf core.editorUI
- * @extends core.ui.View
- */
-
-export default class EditorUIView extends View {
-	constructor( model, locale ) {
-		super( model, locale );
-
-		this._createBodyRegion();
-
-		/**
-		 * The element holding elements of the 'body' region.
-		 *
-		 * @private
-		 * @member {HTMLElement} core.editorUI.EditorUIView#_bodyRegionContainer
-		 */
-	}
-
-	destroy() {
-		this._bodyRegionContainer.remove();
-		this._bodyRegionContainer = null;
-	}
-
-	/**
-	 * Creates and appends to `<body>` the 'body' region container.
-	 *
-	 * @private
-	 */
-	_createBodyRegion() {
-		const bodyElement = document.createElement( 'div' );
-		document.body.appendChild( bodyElement );
-
-		this.applyTemplateToElement( bodyElement, {
-			attributes: {
-				class: 'ck-body'
-			}
-		} );
-
-		this._bodyRegionContainer = bodyElement;
-
-		this.register( 'body', () => bodyElement );
-	}
-}

+ 0 - 8
packages/ckeditor5-engine/src/ui.jsdoc

@@ -1,8 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @namespace core.ui
- */

+ 0 - 42
packages/ckeditor5-engine/src/ui/bindings/toolbar.js

@@ -1,42 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import BaseToolbar from '../../../ui/toolbar/toolbar.js';
-
-/**
- * The editor toolbar controller class.
- *
- * @memberOf core.ui.bindings
- * @extends ui.toolbar.Toolbar
- */
-
-export default class Toolbar extends BaseToolbar {
-	/**
-	 * Creates a new toolbar instance.
-	 *
-	 * @param {core.ui.Model} model
-	 * @param {core.ui.View} view
-	 * @param {core.Editor} editor
-	 */
-	constructor( model, view, editor ) {
-		super( model, view );
-
-		this.editor = editor;
-	}
-
-	/**
-	 * Adds buttons to the toolbar. Buttons are taken from the {@link core.editorUI.EditorUI#featureComponents}
-	 * factory.
-	 *
-	 * @param {String[]} buttons The name of the buttons to add to the toolbar.
-	 */
-	addButtons( buttons ) {
-		for ( let button of buttons ) {
-			this.add( 'buttons', this.editor.ui.featureComponents.create( button ) );
-		}
-	}
-}

+ 0 - 82
packages/ckeditor5-engine/src/ui/componentfactory.js

@@ -1,82 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import CKEditorError from '../../utils/ckeditorerror.js';
-
-/**
- * Class implementing the UI component factory.
- *
- * Factories of specific UI components can be registered under their unique names. Registered
- * components can be later instantiated by providing the name of the component. The model is shared between all
- * instances of that component and has to be provided upon registering its factory.
- *
- * The main use case for the component factory is the {@link core.editorUI.EditorUI#featureComponents} factory.
- *
- * @memberOf core.ui
- */
-
-export default class ComponentFactory {
-	/**
-	 * Creates ComponentFactory instance.
-	 *
-	 * @constructor
-	 * @param {core.Editor} editor The editor instance.
-	 */
-	constructor( editor ) {
-		/**
-		 * @readonly
-		 * @member {core.Editor} core.ui.ComponentFactory#editor
-		 */
-		this.editor = editor;
-
-		/**
-		 * Registered component factories.
-		 *
-		 * @private
-		 * @member {Map} core.ui.ComponentFactory#_components
-		 */
-		this._components = new Map();
-	}
-
-	/**
-	 * Registers a component factory.
-	 *
-	 * @param {String} name The name of the component.
-	 * @param {Function} ControllerClass The component controller constructor.
-	 * @param {Function} ViewClass The component view constructor.
-	 * @param {core.ui.Model} model The model of the component.
-	 */
-	add( name, ControllerClass, ViewClass, model ) {
-		if ( this._components.get( name ) ) {
-			throw new CKEditorError(
-				'componentfactory-item-exists: The item already exists in the component factory.', { name }
-			);
-		}
-
-		this._components.set( name, {
-			ControllerClass,
-			ViewClass,
-			model
-		} );
-	}
-
-	/**
-	 * Creates a component instance.
-	 *
-	 * @param {String} name The name of the component.
-	 * @returns {core.ui.Controller} The instantiated component.
-	 */
-	create( name ) {
-		const component = this._components.get( name );
-
-		const model = component.model;
-		const view = new component.ViewClass( model, this.editor.locale );
-		const controller = new component.ControllerClass( model, view, this.editor );
-
-		return controller;
-	}
-}

+ 0 - 216
packages/ckeditor5-engine/src/ui/controller.js

@@ -1,216 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import Collection from '../../utils/collection.js';
-import CKEditorError from '../../utils/ckeditorerror.js';
-import EmitterMixin from '../../utils/emittermixin.js';
-import utils from '../../utils/utils.js';
-
-/**
- * Basic Controller class.
- *
- * @memberOf core.ui
- * @mixes core.EmitterMixin
- */
-
-export default class Controller {
-	/**
-	 * Creates an instance of the {@link core.ui.Controller} class.
-	 *
-	 * @param {core.ui.Model} [model] Model of this Controller.
-	 * @param {core.ui.View} [view] View instance of this Controller.
-	 */
-	constructor( model, view ) {
-		/**
-		 * Model of this controller.
-		 *
-		 * @member {core.ui.Model} core.ui.Controller#model
-		 */
-		this.model = model || null;
-
-		/**
-		 * Set `true` after {@link #init}.
-		 *
-		 * @member {Boolean} core.ui.Controller#ready
-		 */
-		this.ready = false;
-
-		/**
-		 * View of this controller.
-		 *
-		 * @member {core.ui.View} core.ui.Controller#view
-		 */
-		this.view = view || null;
-
-		/**
-		 * A collection of {@link ControllerCollection} instances containing
-		 * child controllers.
-		 *
-		 * @member {utils.Collection} core.ui.Controller#collections
-		 */
-		this.collections = new Collection( {
-			idProperty: 'name'
-		} );
-
-		// Listen to {@link ControllerCollection#add} and {@link ControllerCollection#remove}
-		// of newly added Collection to synchronize this controller's view and children
-		// controllers' views in the future.
-		this.collections.on( 'add', ( evt, collection ) => {
-			// Set the {@link ControllerCollection#parent} to this controller.
-			// It allows the collection to determine the {@link #ready} state of this controller
-			// and accordingly initialize a child controller when added.
-			collection.parent = this;
-
-			this.listenTo( collection, 'add', ( evt, childController, index ) => {
-				// Child view is added to corresponding region in this controller's view
-				// when a new Controller joins the collection.
-				if ( this.ready && childController.view ) {
-					this.view.regions.get( collection.name ).views.add( childController.view, index );
-				}
-			} );
-
-			this.listenTo( collection, 'remove', ( evt, childController ) => {
-				// Child view is removed from corresponding region in this controller's view
-				// when a new Controller is removed from the the collection.
-				if ( this.ready && childController.view ) {
-					this.view.regions.get( collection.name ).views.remove( childController.view );
-				}
-			} );
-		} );
-
-		this.collections.on( 'remove', ( evt, collection ) => {
-			// Release the collection. Once removed from {@link #collections}, it can be
-			// moved to another controller.
-			collection.parent = null;
-
-			this.stopListening( collection );
-		} );
-	}
-
-	/**
-	 * Initializes the controller instance. The process includes:
-	 *
-	 * 1. Initialization of the child {@link #view}.
-	 * 2. Initialization of child controllers in {@link #collections}.
-	 * 3. Setting {@link #ready} flag `true`.
-	 *
-	 * @returns {Promise} A Promise resolved when the initialization process is finished.
-	 */
-	init() {
-		if ( this.ready ) {
-			/**
-			 * This Controller already been initialized.
-			 *
-			 * @error ui-controller-init-reinit
-			 */
-			throw new CKEditorError( 'ui-controller-init-reinit: This Controller already been initialized.' );
-		}
-
-		return Promise.resolve()
-			.then( this._initView.bind( this ) )
-			.then( this._initCollections.bind( this ) )
-			.then( () => {
-				this.ready = true;
-			} );
-	}
-
-	/**
-	 * Destroys the controller instance. The process includes:
-	 *
-	 * 1. Destruction of the child {@link #view}.
-	 * 2. Destruction of child controllers in {@link #collections}.
-	 *
-	 * @returns {Promise} A Promise resolved when the destruction process is finished.
-	 */
-	destroy() {
-		let promises = [];
-		let collection, childController;
-
-		for ( collection of this.collections ) {
-			for ( childController of collection ) {
-				promises.push( childController.destroy() );
-				collection.remove( childController );
-			}
-
-			this.collections.remove( collection );
-		}
-
-		if ( this.view ) {
-			promises.push( Promise.resolve().then( () => {
-				return this.view.destroy();
-			} ) );
-		}
-
-		promises.push( Promise.resolve().then( () => {
-			this.model = this.ready = this.view = this.collections = null;
-		} ) );
-
-		return Promise.all( promises );
-	}
-
-	/**
-	 * Adds a child {@link Controller} instance to {@link #collections} at given index.
-	 *
-	 * @param {String} collectionName Name of the Controller Collection.
-	 * @param {core.ui.Controller} controller A controller instance to be added.
-	 * @param {Number} [index] An index in the collection.
-	 */
-	add( collectionName, controller, index ) {
-		this.collections.get( collectionName ).add( controller, index );
-	}
-
-	/**
-	 * Removes a child {@link Controller} instance from one of {@link #collections}.
-	 *
-	 * @param {String} collectionName Name of the Controller Collection.
-	 * @param {core.ui.Controller|Number} toRemove A Controller instance or index to be removed.
-	 */
-	remove( collectionName, toRemove ) {
-		return this.collections.get( collectionName ).remove( toRemove );
-	}
-
-	/**
-	 * Initializes the {@link #view} of this controller instance.
-	 *
-	 * @protected
-	 * @returns {Promise} A Promise resolved when initialization process is finished.
-	 */
-	_initView() {
-		let promise = Promise.resolve();
-
-		if ( this.view ) {
-			promise = promise.then( this.view.init.bind( this.view ) );
-		}
-
-		return promise;
-	}
-
-	/**
-	 * Initializes the {@link #collections} of this controller instance.
-	 *
-	 * @protected
-	 * @returns {Promise} A Promise resolved when initialization process is finished.
-	 */
-	_initCollections() {
-		const promises = [];
-		let collection, childController;
-
-		for ( collection of this.collections ) {
-			for ( childController of collection ) {
-				if ( this.view && childController.view ) {
-					this.view.regions.get( collection.name ).views.add( childController.view );
-				}
-
-				promises.push( childController.init() );
-			}
-		}
-
-		return Promise.all( promises );
-	}
-}
-
-utils.mix( Controller, EmitterMixin );

+ 0 - 70
packages/ckeditor5-engine/src/ui/controllercollection.js

@@ -1,70 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import Collection from '../../utils/collection.js';
-import CKEditorError from '../../utils/ckeditorerror.js';
-
-/**
- * Manages UI Controllers.
- *
- * @memberOf core.ui
- * @extends utils.Collection
- */
-export default class ControllerCollection extends Collection {
-	/**
-	 * Creates an instance of the ControllerCollection class, initializing it with a name.
-	 */
-	constructor( name ) {
-		super();
-
-		if ( !name ) {
-			/**
-			 * ControllerCollection must be initialized with a name.
-			 *
-			 * @error ui-controllercollection-no-name
-			 */
-			throw new CKEditorError( 'ui-controllercollection-no-name: ControllerCollection must be initialized with a name.' );
-		}
-
-		/**
-		 * Name of this collection.
-		 *
-		 * @member {String} core.ui.ControllerCollection#name
-		 */
-		this.name = name;
-
-		/**
-		 * Parent controller of this collection.
-		 *
-		 * @member {core.ui.Controller} core.ui.ControllerCollection#parent
-		 */
-		this.parent = null;
-	}
-
-	/**
-	 * Adds a child controller to the collection. If {@link #parent} {@link Controller}
-	 * instance is ready, the child view is initialized when added.
-	 *
-	 * @param {core.ui.Controller} controller A child controller.
-	 * @param {Number} [index] Index at which the child will be added to the collection.
-	 * @returns {Promise} A Promise resolved when the child {@link Controller#init} is done.
-	 */
-	add( controller, index ) {
-		super.add( controller, index );
-
-		// ChildController.init() returns Promise.
-		let promise = Promise.resolve();
-
-		if ( this.parent && this.parent.ready && !controller.ready ) {
-			promise = promise.then( () => {
-				return controller.init();
-			} );
-		}
-
-		return promise;
-	}
-}

+ 0 - 278
packages/ckeditor5-engine/src/ui/domemittermixin.js

@@ -1,278 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import EmitterMixin from '../../utils/emittermixin.js';
-import utils from '../../utils/utils.js';
-import extend from '../../utils/lib/lodash/extend.js';
-import log from '../../utils/log.js';
-
-/**
- * Creates a ProxyEmitter instance. Such an instance is a bridge between a DOM Node firing events
- * and any Host listening to them. It is backwards compatible with {@link EmitterMixin#on}.
- *
- * @memberOf core.ui
- * @mixes core.EmitterMixin
- * @implements core.ui.DOMEmitter
- */
-class ProxyEmitter {
-	/**
-	 * @param {Node} node DOM Node that fires events.
-	 * @returns {Object} ProxyEmitter instance bound to the DOM Node.
-	 */
-	constructor( node ) {
-		// Set emitter ID to match DOM Node "expando" property.
-		this._emitterId = getNodeUID( node );
-
-		// Remember the DOM Node this ProxyEmitter is bound to.
-		this._domNode = node;
-	}
-}
-
-extend( ProxyEmitter.prototype, EmitterMixin, {
-	/**
-	 * Collection of native DOM listeners.
-	 *
-	 * @private
-	 * @member {Object} core.ui.ProxyEmitter#_domListeners
-	 */
-
-	/**
-	 * Registers a callback function to be executed when an event is fired.
-	 *
-	 * It attaches a native DOM listener to the DOM Node. When fired,
-	 * a corresponding Emitter event will also fire with DOM Event object as an argument.
-	 *
-	 * @param {String} event The name of the event.
-	 * @param {Function} callback The function to be called on event.
-	 * @param {Object} [ctx] The object that represents `this` in the callback. Defaults to the object firing the
-	 * event.
-	 * @param {Number} [priority=10] The priority of this callback in relation to other callbacks to that same event.
-	 * Lower values are called first.
-	 *
-	 * @method core.ui.ProxyEmitter#on
-	 */
-	on( event ) {
-		// Execute parent class method first.
-		EmitterMixin.on.apply( this, arguments );
-
-		// If the DOM Listener for given event already exist it is pointless
-		// to attach another one.
-		if ( this._domListeners && this._domListeners[ event ] ) {
-			return;
-		}
-
-		const domListener = this._createDomListener( event );
-
-		// Attach the native DOM listener to DOM Node.
-		this._domNode.addEventListener( event, domListener );
-
-		if ( !this._domListeners ) {
-			this._domListeners = {};
-		}
-
-		// Store the native DOM listener in this ProxyEmitter. It will be helpful
-		// when stopping listening to the event.
-		this._domListeners[ event ] = domListener;
-	},
-
-	/**
-	 * Stops executing the callback on the given event.
-	 *
-	 * @param {String} event The name of the event.
-	 * @param {Function} callback The function to stop being called.
-	 * @param {Object} [ctx] The context object to be removed, pared with the given callback. To handle cases where
-	 * the same callback is used several times with different contexts.
-	 *
-	 * @method core.ui.ProxyEmitter#off
-	 */
-	off( event ) {
-		// Execute parent class method first.
-		EmitterMixin.off.apply( this, arguments );
-
-		let callbacks;
-
-		// Remove native DOM listeners which are orphans. If no callbacks
-		// are awaiting given event, detach native DOM listener from DOM Node.
-		// See: {@link on}.
-		if ( !( callbacks = this._events[ event ] ) || !callbacks.length ) {
-			this._domListeners[ event ].removeListener();
-		}
-	},
-
-	/**
-	 * Create a native DOM listener callback. When the native DOM event
-	 * is fired it will fire corresponding event on this ProxyEmitter.
-	 * Note: A native DOM Event is passed as an argument.
-	 *
-	 * @param {String} event
-	 *
-	 * @method core.ui.ProxyEmitter#_createDomListener
-	 * @returns {Function} The DOM listener callback.
-	 */
-	_createDomListener( event ) {
-		const domListener = domEvt => {
-			this.fire( event, domEvt );
-		};
-
-		// Supply the DOM listener callback with a function that will help
-		// detach it from the DOM Node, when it is no longer necessary.
-		// See: {@link off}.
-		domListener.removeListener = () => {
-			this._domNode.removeEventListener( event, domListener );
-			delete this._domListeners[ event ];
-		};
-
-		return domListener;
-	}
-} );
-
-/**
- * Mixin that injects the DOM events API into its host. It provides the API
- * compatible with {@link EmitterMixin}.
- *
- *                                  listenTo( click, ... )
- *                    +-----------------------------------------+
- *                    |              stopListening( ... )       |
- *     +----------------------------+                           |             addEventListener( click, ... )
- *     | Host                       |                           |   +---------------------------------------------+
- *     +----------------------------+                           |   |       removeEventListener( click, ... )     |
- *     | _listeningTo: {            |                +----------v-------------+                                   |
- *     |   UID: {                   |                | ProxyEmitter           |                                   |
- *     |     emitter: ProxyEmitter, |                +------------------------+                      +------------v----------+
- *     |     callbacks: {           |                | events: {              |                      | Node (HTMLElement)    |
- *     |       click: [ callbacks ] |                |   click: [ callbacks ] |                      +-----------------------+
- *     |     }                      |                | },                     |                      | data-cke-expando: UID |
- *     |   }                        |                | _domNode: Node,        |                      +-----------------------+
- *     | }                          |                | _domListeners: {},     |                                   |
- *     | +------------------------+ |                | _emitterId: UID        |                                   |
- *     | | DOMEmitterMixin        | |                +--------------^---------+                                   |
- *     | +------------------------+ |                           |   |                                             |
- *     +--------------^-------------+                           |   +---------------------------------------------+
- *                    |                                         |                  click (DOM Event)
- *                    +-----------------------------------------+
- *                                fire( click, DOM Event )
- *
- * @mixin core.ui.DOMEmitterMixin
- * @mixes core.EmitterMixin
- * @implements core.ui.DOMEmitter
- */
-const DOMEmitterMixin = extend( {}, EmitterMixin, {
-	/**
-	 * Registers a callback function to be executed when an event is fired in a specific Emitter or DOM Node.
-	 * It is backwards compatible with {@link core.EmitterMixin#listenTo}.
-	 *
-	 * @param {Emitter|Node} emitter The object that fires the event.
-	 * @param {String} event The name of the event.
-	 * @param {Function} callback The function to be called on event.
-	 * @param {Object} [ctx] The object that represents `this` in the callback. Defaults to `emitter`.
-	 * @param {Number} [priority=10] The priority of this callback in relation to other callbacks to that same event.
-	 * Lower values are called first.
-	 *
-	 * @method core.ui.DOMEmitterMixin#listenTo
-	 */
-	listenTo() {
-		const args = Array.prototype.slice.call( arguments );
-		const emitter = args[ 0 ];
-
-		// Check if emitter is an instance of DOM Node. If so, replace the argument with
-		// corresponding ProxyEmitter (or create one if not existing).
-		if ( isDomNode( emitter ) ) {
-			args[ 0 ] = this._getProxyEmitter( emitter ) || new ProxyEmitter( emitter );
-		}
-
-		// Execute parent class method with Emitter (or ProxyEmitter) instance.
-		EmitterMixin.listenTo.apply( this, args );
-	},
-
-	/**
-	 * Stops listening for events. It can be used at different levels:
-	 * It is backwards compatible with {@link EmitterMixin#listenTo}.
-	 *
-	 * * To stop listening to a specific callback.
-	 * * To stop listening to a specific event.
-	 * * To stop listening to all events fired by a specific object.
-	 * * To stop listening to all events fired by all object.
-	 *
-	 * @param {Emitter|Node} [emitter] The object to stop listening to. If omitted, stops it for all objects.
-	 * @param {String} [event] (Requires the `emitter`) The name of the event to stop listening to. If omitted, stops it
-	 * for all events from `emitter`.
-	 * @param {Function} [callback] (Requires the `event`) The function to be removed from the call list for the given
-	 * `event`.
-	 *
-	 * @method core.ui.DOMEmitterMixin#stopListening
-	 */
-	stopListening() {
-		const args = Array.prototype.slice.call( arguments );
-		const emitter = args[ 0 ];
-
-		// Check if emitter is an instance of DOM Node. If so, replace the argument with corresponding ProxyEmitter.
-		if ( isDomNode( emitter ) ) {
-			let proxy = this._getProxyEmitter( emitter );
-
-			if ( proxy ) {
-				args[ 0 ] = proxy;
-			} else {
-				log.error(
-					'domemittermixin-stoplistening: Stopped listening on a DOM Node that has no emitter or emitter is gone.',
-					emitter
-				);
-			}
-		}
-
-		// Execute parent class method with Emitter (or ProxyEmitter) instance.
-		EmitterMixin.stopListening.apply( this, args );
-	},
-
-	/**
-	 * Retrieves ProxyEmitter instance for given DOM Node residing in this Host.
-	 *
-	 * @param {Node} node DOM Node of the ProxyEmitter.
-	 * @method core.ui.DOMEmitterMixin#_getProxyEmitter
-	 * @return {ProxyEmitter} ProxyEmitter instance or null.
-	 */
-	_getProxyEmitter( node ) {
-		let proxy, emitters, emitterInfo;
-
-		// Get node UID. It allows finding Proxy Emitter for this DOM Node.
-		const uid = getNodeUID( node );
-
-		// Find existing Proxy Emitter for this DOM Node among emitters.
-		if ( ( emitters = this._listeningTo ) ) {
-			if ( ( emitterInfo = emitters[ uid ] ) ) {
-				proxy = emitterInfo.emitter;
-			}
-		}
-
-		return proxy || null;
-	}
-} );
-
-export default DOMEmitterMixin;
-
-// Gets an unique DOM Node identifier. The identifier will be set if not defined.
-//
-// @private
-// @param {Node} node
-// @return {Number} UID for given DOM Node.
-function getNodeUID( node ) {
-	return node[ 'data-ck-expando' ] || ( node[ 'data-ck-expando' ] = utils.uid() );
-}
-
-// Checks (naively) if given node is native DOM Node.
-//
-// @private
-// @param {Node} node
-// @return {Boolean} True when native DOM Node.
-function isDomNode( node ) {
-	return node && node.nodeType && ( typeof node.addEventListener == 'function' );
-}
-
-/**
- * Interface representing classes which mix in {@link core.ui.DOMEmitter}.
- *
- * @interface core.ui.DOMEmitter
- */

+ 0 - 38
packages/ckeditor5-engine/src/ui/model.js

@@ -1,38 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import extend from '../../utils/lib/lodash/extend.js';
-import utils from '../../utils/utils.js';
-import ObservableMixin from '../../utils/observablemixin.js';
-
-/**
- * The base MVC model class.
- *
- * @memberOf core.ui
- * @mixes utils.ObservaleMixin
- */
-export default class Model {
-	/**
-	 * Creates a new Model instance.
-	 *
-	 * @param {Object} [attributes] The model state attributes to be defined during the instance creation.
-	 * @param {Object} [properties] The (out of state) properties to be appended to the instance during creation.
-	 */
-	constructor( attributes, properties ) {
-		// Extend this instance with the additional (out of state) properties.
-		if ( properties ) {
-			extend( this, properties );
-		}
-
-		// Initialize the attributes.
-		if ( attributes ) {
-			this.set( attributes );
-		}
-	}
-}
-
-utils.mix( Model, ObservableMixin );

+ 0 - 79
packages/ckeditor5-engine/src/ui/region.js

@@ -1,79 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import Collection from '../../utils/collection.js';
-
-/**
- * Basic Region class.
- *
- * @memberOf core.ui
- */
-export default class Region {
-	/**
-	 * Creates an instance of the {@link Region} class.
-	 *
-	 * @param {String} name The name of the Region.
-	 */
-	constructor( name ) {
-		/**
-		 * The name of the region.
-		 *
-		 * @member {String} core.ui.Region#name
-		 */
-		this.name = name;
-
-		/**
-		 * Views which belong to the region.
-		 *
-		 * @member {utils.Collection} core.ui.Region#views
-		 */
-		this.views = new Collection();
-
-		/**
-		 * Element of this region (see {@link #init}).
-		 *
-		 * @member {HTMLElement} core.ui.Region#element
-		 */
-		this.element = null;
-	}
-
-	/**
-	 * Initializes region instance with an element. Usually it comes from {@link View#init}.
-	 *
-	 * @param {HTMLElement} regionElement Element of this region.
-	 */
-	init( regionElement ) {
-		this.element = regionElement;
-
-		if ( regionElement ) {
-			this.views.on( 'add', ( evt, childView, index ) => {
-				regionElement.insertBefore( childView.element, regionElement.childNodes[ index + 1 ] );
-			} );
-
-			this.views.on( 'remove', ( evt, childView ) => {
-				childView.element.remove();
-			} );
-		}
-	}
-
-	/**
-	 * Destroys region instance.
-	 */
-	destroy() {
-		if ( this.element ) {
-			for ( let view of this.views ) {
-				view.element.remove();
-				this.views.remove( view );
-			}
-		}
-
-		// Drop the reference to HTMLElement but don't remove it from DOM.
-		// Element comes as a parameter and it could be a part of the View.
-		// Then it's up to the View what to do with it when the View is destroyed.
-		this.element = this.views = null;
-	}
-}

+ 0 - 370
packages/ckeditor5-engine/src/ui/template.js

@@ -1,370 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* global document */
-
-'use strict';
-
-import CKEditorError from '../../utils/ckeditorerror.js';
-
-/**
- * Basic Template class.
- *
- * @memberOf core.ui
- */
-export default class Template {
-	/**
-	 * Creates an instance of the {@link Template} class.
-	 *
-	 * @param {core.ui.TemplateDefinition} def The definition of the template.
-	 */
-	constructor( def ) {
-		/**
-		 * Definition of this template.
-		 *
-		 * @type {core.ui.TemplateDefinition}
-		 */
-		this.definition = def;
-	}
-
-	/**
-	 * Renders DOM Node using {@link core.ui.Template#definition}.
-	 *
-	 * @see core.ui.Template#apply
-	 *
-	 * @returns {HTMLElement}
-	 */
-	render() {
-		return this._renderNode( this.definition, null, true );
-	}
-
-	/**
-	 * Applies template {@link core.ui.Template#def} to existing DOM tree.
-	 *
-	 * **Note:** No new DOM nodes (elements, text nodes) will be created.
-	 *
-	 * @see core.ui.Template#render
-	 * @see core.ui.View#applyTemplateToElement.
-	 *
-	 * @param {Node} element Root element for template to apply.
-	 */
-	apply( node ) {
-		if ( !node ) {
-			/**
-			 * No DOM Node specified.
-			 *
-			 * @error ui-template-wrong-syntax
-			 */
-			throw new CKEditorError( 'ui-template-wrong-node' );
-		}
-
-		return this._renderNode( this.definition, node );
-	}
-
-	/**
-	 * Renders a DOM Node from definition.
-	 *
-	 * @protected
-	 * @param {core.ui.TemplateDefinition} def Definition of a Node.
-	 * @param {Node} applyNode If specified, template `def` will be applied to existing DOM Node.
-	 * @param {Boolean} intoFragment If set, children are rendered into DocumentFragment.
-	 * @returns {HTMLElement} A rendered Node.
-	 */
-	_renderNode( def, applyNode, intoFragment ) {
-		const isText = def.text || typeof def == 'string';
-		let isInvalid;
-
-		if ( applyNode ) {
-			// When applying, a definition cannot have "tag" and "text" at the same time.
-			isInvalid = def.tag && isText;
-		} else {
-			// When rendering, a definition must have either "tag" or "text": XOR( def.tag, isText ).
-			isInvalid = def.tag ? isText : !isText;
-		}
-
-		if ( isInvalid ) {
-			/**
-			 * Node definition cannot have "tag" and "text" properties at the same time.
-			 * Node definition must have either "tag" or "text" when rendering new Node.
-			 *
-			 * @error ui-template-wrong-syntax
-			 */
-			throw new CKEditorError( 'ui-template-wrong-syntax' );
-		}
-
-		return isText ?
-			this._renderText( def, applyNode ) : this._renderElement( def, applyNode, intoFragment );
-	}
-
-	/**
-	 * Renders an HTMLElement from TemplateDefinition.
-	 *
-	 * @protected
-	 * @param {core.ui.TemplateDefinition} def Definition of an element.
-	 * @param {HTMLElement} applyElement If specified, template `def` will be applied to existing HTMLElement.
-	 * @param {Boolean} intoFragment If set, children are rendered into DocumentFragment.
-	 * @returns {HTMLElement} A rendered element.
-	 */
-	_renderElement( def, applyElement, intoFragment ) {
-		const el = applyElement || document.createElement( def.tag );
-
-		this._renderElementAttributes( def, el );
-
-		// Invoke children recursively.
-		if ( intoFragment ) {
-			const docFragment = document.createDocumentFragment();
-
-			this._renderElementChildren( def, docFragment );
-
-			el.appendChild( docFragment );
-		} else {
-			this._renderElementChildren( def, el, !!applyElement );
-		}
-
-		// Activate DOM bindings for event listeners.
-		this._activateElementListenerAttachers( def, el );
-
-		return el;
-	}
-
-	/**
-	 * Renders a Text from TemplateDefinition or String.
-	 *
-	 * @protected
-	 * @param {TemplateDefinition|String} def Definition of Text or its value.
-	 * @param {HTMLElement} applyText If specified, template `def` will be applied to existing Text Node.
-	 * @returns {Text} A rendered Text.
-	 */
-	_renderText( defOrText, applyText ) {
-		const textNode = applyText || document.createTextNode( '' );
-
-		// Check if there's a binder available for this Text Node.
-		const binder = defOrText._modelBinders && defOrText._modelBinders.text;
-
-		// Activate binder if one. Cases:
-		//		{ text: bind.to( ... ) }
-		//		{ text: [ 'foo', bind.to( ... ), ... ] }
-		if ( binder ) {
-			binder( textNode, getTextNodeUpdater( textNode ) );
-		}
-
-		// Simply set text. Cases:
-		// 		{ text: [ 'all', 'are', 'static' ] }
-		// 		{ text: 'foo' }
-		// 		'foo'
-		else {
-			textNode.textContent = defOrText.text || defOrText;
-		}
-
-		return textNode;
-	}
-
-	/**
-	 * Renders element attributes from definition.
-	 *
-	 * @protected
-	 * @param {core.ui.TemplateDefinition} def Definition of an element.
-	 * @param {HTMLElement} el Element which is rendered.
-	 */
-	_renderElementAttributes( def, el ) {
-		const attributes = def.attributes;
-		const binders = def._modelBinders && def._modelBinders.attributes;
-		let binder, attrName, attrValue;
-
-		if ( !attributes ) {
-			return;
-		}
-
-		for ( attrName in attributes ) {
-			// Check if there's a binder available for this attribute.
-			binder = binders && binders[ attrName ];
-
-			// Activate binder if one. Cases:
-			// 		{ class: [ 'bar', bind.to( ... ), 'baz' ] }
-			// 		{ class: bind.to( ... ) }
-			if ( binder ) {
-				binder( el, getElementAttributeUpdater( el, attrName ) );
-			}
-
-			// Otherwise simply set the attribute.
-			// 		{ class: [ 'all', 'are', 'static' ] }
-			// 		{ class: 'foo' }
-			else {
-				attrValue = attributes[ attrName ];
-
-				// Attribute can be an array. Merge array elements:
-				if ( Array.isArray( attrValue ) ) {
-					attrValue = attrValue.reduce( function binderValueReducer( prev, cur ) {
-						return prev === '' ? `${cur}` : `${prev} ${cur}`;
-					} );
-				}
-
-				el.setAttribute( attrName, attrValue );
-			}
-		}
-	}
-
-	/**
-	 * Recursively renders element children from definition by
-	 * calling {@link core.ui.Template#_renderElement}.
-	 *
-	 * @protected
-	 * @param {core.ui.TemplateDefinition} def Definition of an element.
-	 * @param {HTMLElement} el Element which is rendered.
-	 * @param {Boolean} isApply Traverse existing DOM structure only, don't modify DOM.
-	 */
-	_renderElementChildren( def, el, isApply ) {
-		if ( def.children ) {
-			def.children.forEach( ( childDef, index ) => {
-				if ( isApply ) {
-					this._renderNode( childDef, el.childNodes[ index ] );
-				} else {
-					el.appendChild( this._renderNode( childDef ) );
-				}
-			} );
-		}
-	}
-
-	/**
-	 * Activates element `on` listeners passed in element definition.
-	 *
-	 * @protected
-	 * @param {core.ui.TemplateDefinition} def Definition of an element.
-	 * @param {HTMLElement} el Element which is rendered.
-	 */
-	_activateElementListenerAttachers( def, el ) {
-		if ( !def.on ) {
-			return;
-		}
-
-		const attachers = def.on._listenerAttachers;
-
-		Object.keys( attachers )
-			.map( name => [ name, ...name.split( '@' ) ] )
-			.forEach( split => {
-				// TODO: ES6 destructuring.
-				const key = split[ 0 ];
-				const evtName = split[ 1 ];
-				const evtSelector = split[ 2 ] || null;
-
-				if ( Array.isArray( attachers[ key ] ) ) {
-					attachers[ key ].forEach( i => i( el, evtName, evtSelector ) );
-				} else {
-					attachers[ key ]( el, evtName, evtSelector );
-				}
-			} );
-	}
-}
-
-/**
- * Returns an object consisting of `set` and `remove` functions, which
- * can be used in the context of DOM Node to set or reset `textContent`.
- * @see core.ui.View#_getModelBinder
- *
- * @ignore
- * @private
- * @param {Node} node DOM Node to be modified.
- * @returns {Object}
- */
-function getTextNodeUpdater( node ) {
-	return {
-		set( value ) {
-			node.textContent = value;
-		},
-
-		remove() {
-			node.textContent = '';
-		}
-	};
-}
-
-/**
- * Returns an object consisting of `set` and `remove` functions, which
- * can be used in the context of DOM Node to set or reset an attribute.
- * @see core.ui.View#_getModelBinder
- *
- * @ignore
- * @private
- * @param {Node} node DOM Node to be modified.
- * @param {String} attrName Name of the attribute to be modified.
- * @returns {Object}
- */
-function getElementAttributeUpdater( el, attrName ) {
-	return {
-		set( value ) {
-			el.setAttribute( attrName, value );
-		},
-
-		remove() {
-			el.removeAttribute( attrName );
-		}
-	};
-}
-
-/**
- * Definition of {@link Template}.
- * See: {@link core.ui.TemplateValueSchema}.
- *
- *		{
- *			tag: 'p',
- *			children: [
- *				{
- *					tag: 'span',
- *					attributes: { ... },
- *					children: [ ... ],
- *					...
- *				},
- *				{
- *					text: 'static–text'
- *				},
- *				'also-static–text',
- *				...
- *			],
- *			attributes: {
- *				'class': [ 'class-a', 'class-b' ],
- *				id: 'element-id',
- *				style: callback,
- *				...
- *			},
- *			on: {
- *				'click': 'clicked'
- *				'mouseup': [ 'view-event-a', 'view-event-b', callback ],
- *				'keyup@selector': 'view-event',
- *				'focus@selector': [ 'view-event-a', 'view-event-b', callback ],
- *				...
- *			}
- *		}
- *
- * @typedef core.ui.TemplateDefinition
- * @type Object
- * @property {String} tag
- * @property {Array} [children]
- * @property {Object} [attributes]
- * @property {String} [text]
- * @property {Object} [on]
- * @property {Object} _modelBinders
- */
-
-/**
- * Describes a value of HTMLElement attribute or `textContent`.
- * See: {@link core.ui.TemplateDefinition}.
- *
- *		{
- *			tag: 'p',
- *			attributes: {
- *				// Plain String schema.
- *				class: 'class-foo'
- *
- *				// Object schema, a Model binding.
- *				class: { model: m, attribute: 'foo', callback... }
- *
- *				// Array schema, combines the above.
- *				class: [ 'foo', { model: m, attribute: 'bar' }, 'baz' ]
- *			}
- *		}
- *
- * @typedef core.ui.TemplateValueSchema
- * @type {Object|String|Array}
- */

+ 0 - 715
packages/ckeditor5-engine/src/ui/view.js

@@ -1,715 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import Collection from '../../utils/collection.js';
-import Region from './region.js';
-import Template from './template.js';
-import CKEditorError from '../../utils/ckeditorerror.js';
-import DOMEmitterMixin from './domemittermixin.js';
-import utils from '../../utils/utils.js';
-import isPlainObject from '../../utils/lib/lodash/isPlainObject.js';
-
-const bindToSymbol = Symbol( 'bindTo' );
-const bindIfSymbol = Symbol( 'bindIf' );
-
-/**
- * Basic View class.
- *
- * @memberOf core.ui
- * @mixes DOMEmitterMixin
- */
-export default class View {
-	/**
-	 * Creates an instance of the {@link View} class.
-	 *
-	 * @param {core.ui.Model} model (View)Model of this View.
-	 * @param {utils.Locale} [locale] The {@link core.Editor#locale editor's locale} instance.
-	 */
-	constructor( model, locale ) {
-		/**
-		 * Model of this view.
-		 *
-		 * @member {core.ui.Model} core.ui.View#model
-		 */
-		this.model = model;
-
-		/**
-		 * @readonly
-		 * @member {utils.Locale} core.ui.View#locale
-		 */
-		this.locale = locale;
-
-		/**
-		 * Shorthand for {@link utils.Locale#t}.
-		 *
-		 * Note: If locale instance hasn't been passed to the view this method may not be available.
-		 *
-		 * @see utils.Locale#t
-		 * @method core.ui.View#t
-		 */
-		this.t = locale && locale.t;
-
-		/**
-		 * Regions of this view. See {@link core.ui.View#register}.
-		 *
-		 * @member {utils.Collection} core.ui.View#regions
-		 */
-		this.regions = new Collection( {
-			idProperty: 'name'
-		} );
-
-		/**
-		 * Template of this view.
-		 *
-		 * @member {Object} core.ui.View#template
-		 */
-
-		/**
-		 * Region selectors of this view. See {@link core.ui.View#register}.
-		 *
-		 * @private
-		 * @member {Object} core.ui.View#_regionSelectors
-		 */
-		this._regionSelectors = {};
-
-		/**
-		 * Element of this view.
-		 *
-		 * @private
-		 * @member {HTMLElement} core.ui.View.#_element
-		 */
-
-		/**
-		 * An instance of Template to generate {@link core.ui.View#_el}.
-		 *
-		 * @private
-		 * @member {core.ui.Template} core.ui.View#_template
-		 */
-	}
-
-	/**
-	 * Element of this view. The element is rendered on first reference
-	 * using {@link core.ui.View#template} definition.
-	 *
-	 * @type {HTMLElement}
-	 */
-	get element() {
-		if ( this._element ) {
-			return this._element;
-		}
-
-		// No template means no element (a virtual view).
-		if ( !this.template ) {
-			return null;
-		}
-
-		// Prepare pre–defined listeners.
-		this._extendTemplateWithListenerAttachers( this.template );
-
-		// Prepare pre–defined attribute bindings.
-		this._extendTemplateWithModelBinders( this.template );
-
-		this._template = new Template( this.template );
-
-		return ( this._element = this._template.render() );
-	}
-
-	set element( el ) {
-		this._element = el;
-	}
-
-	/**
-	 * And entry point to the interface which allows binding attributes of {@link View#model}
-	 * to the DOM items like HTMLElement attributes or Text Node `textContent`, so their state
-	 * is synchronized with {@link View#model}.
-	 *
-	 * @readonly
-	 * @type core.ui.ViewModelBinding
-	 */
-	get attributeBinder() {
-		if ( this._attributeBinder ) {
-			return this._attributeBinder;
-		}
-
-		const model = this.model;
-		const binder = {
-			/**
-			 * Binds {@link View#model} to HTMLElement attribute or Text Node `textContent`
-			 * so remains in sync with the Model when it changes.
-			 *
-			 *		this.template = {
-			 *			tag: 'p',
-			 *			attributes: {
-			 *				// class="..." attribute gets bound to this.model.a
-			 *				'class': bind.to( 'a' )
-			 *			},
-			 *			children: [
-			 *				// <p>...</p> gets bound to this.model.b; always `toUpperCase()`.
-			 *				{ text: bind.to( 'b', ( value, node ) => value.toUpperCase() ) }
-			 *			]
-			 *		}
-			 *
-			 * @property {attributeBinder.to}
-			 * @param {String} attribute Name of {@link View#model} used in the binding.
-			 * @param {Function} [callback] Allows processing of the value. Accepts `Node` and `value` as arguments.
-			 * @return {core.ui.ViewModelBinding}
-			 */
-			to( attribute, callback ) {
-				return {
-					type: bindToSymbol,
-					model: model,
-					attribute,
-					callback
-				};
-			},
-
-			/**
-			 * Binds {@link View#model} to HTMLElement attribute or Text Node `textContent`
-			 * so remains in sync with the Model when it changes. Unlike {@link View#attributeBinder.to},
-			 * it controls the presence of the attribute/`textContent` depending on the "falseness" of
-			 * {@link View#model} attribute.
-			 *
-			 *		this.template = {
-			 *			tag: 'input',
-			 *			attributes: {
-			 *				// <input checked> this.model.a is not undefined/null/false/''
-			 *				// <input> this.model.a is undefined/null/false
-			 *				checked: bind.if( 'a' )
-			 *			},
-			 *			children: [
-			 *				{
-			 *					// <input>"b-is-not-set"</input> when this.model.b is undefined/null/false/''
-			 *					// <input></input> when this.model.b is not "falsy"
-			 *					text: bind.if( 'b', 'b-is-not-set', ( value, node ) => !value )
-			 *				}
-			 *			]
-			 *		}
-			 *
-			 * @property {attributeBinder.if}
-			 * @param {String} attribute Name of {@link View#model} used in the binding.
-			 * @param {String} [valueIfTrue] Value set when {@link View#model} attribute is not undefined/null/false/''.
-			 * @param {Function} [callback] Allows processing of the value. Accepts `Node` and `value` as arguments.
-			 * @return {core.ui.ViewModelBinding}
-			 */
-			if( attribute, valueIfTrue, callback ) {
-				return {
-					type: bindIfSymbol,
-					model: model,
-					attribute,
-					valueIfTrue,
-					callback
-				};
-			}
-		};
-
-		return ( this._attributeBinder = binder );
-	}
-
-	/**
-	 * Initializes the view.
-	 *
-	 * Note: {@link core.Controller} supports if a promise is returned by this method,
-	 * what means that view initialization may be asynchronous.
-	 */
-	init() {
-		this._initRegions();
-	}
-
-	/**
-	 * Registers a region in {@link core.ui.View#regions}.
-	 *
-	 *		let view = new View();
-	 *
-	 *		// region.name == "foo", region.element == view.element.firstChild
-	 *		view.register( 'foo', el => el.firstChild );
-	 *
-	 *		// region.name == "bar", region.element == view.element.querySelector( 'span' )
-	 *		view.register( new Region( 'bar' ), 'span' );
-	 *
-	 *		// region.name == "bar", region.element == view.element.querySelector( '#div#id' )
-	 *		view.register( 'bar', 'div#id', true );
-	 *
-	 *		// region.name == "baz", region.element == null
-	 *		view.register( 'baz', true );
-	 *
-	 * @param {String|Region} stringOrRegion The name or an instance of the Region
-	 * to be registered. If `String`, the region will be created on the fly.
-	 * @param {String|Function|true} regionSelector The selector to retrieve region's element
-	 * in DOM when the region instance is initialized (see {@link Region#init}, {@link core.ui.View#init}).
-	 * @param {Boolean} [override] When set `true` it will allow overriding of registered regions.
-	 */
-	register( ...args ) {
-		let region, regionName;
-
-		if ( typeof args[ 0 ] === 'string' ) {
-			regionName = args[ 0 ];
-			region = this.regions.get( regionName ) || new Region( regionName );
-		} else if ( args[ 0 ] instanceof Region ) {
-			regionName = args[ 0 ].name;
-			region = args[ 0 ];
-		} else {
-			/**
-			 * A name of the region or an instance of Region is required.
-			 *
-			 * @error ui-view-register-wrongtype
-			 */
-			throw new CKEditorError( 'ui-view-register-wrongtype' );
-		}
-
-		const regionSelector = args[ 1 ];
-
-		if ( !regionSelector || !isValidRegionSelector( regionSelector ) ) {
-			/**
-			 * The selector must be String, Function or `true`.
-			 *
-			 * @error ui-view-register-badselector
-			 */
-			throw new CKEditorError( 'ui-view-register-badselector' );
-		}
-
-		const registered = this.regions.get( regionName );
-
-		if ( !registered ) {
-			this.regions.add( region );
-		} else {
-			if ( registered !== region ) {
-				if ( !args[ 2 ] ) {
-					/**
-					 * Overriding is possible only when `override` flag is set.
-					 *
-					 * @error ui-view-register-override
-					 */
-					throw new CKEditorError( 'ui-view-register-override' );
-				}
-
-				this.regions.remove( registered );
-				this.regions.add( region );
-			}
-		}
-
-		this._regionSelectors[ regionName ] = regionSelector;
-	}
-
-	/**
-	 * Applies template to existing DOM element in the context of a View.
-	 *
-	 *		const element = document.createElement( 'div' );
-	 *		const view = new View( new Model( { divClass: 'my-div' } ) );
-	 *
-	 *		view.applyTemplateToElement( element, {
-	 *			attrs: {
-	 *				id: 'first-div',
-	 *				class: view.bindToAttribute( 'divClass' )
-	 *			},
-	 *			on: {
-	 *				click: 'elementClicked' // Will be fired by the View instance.
-	 *			}
-	 *			children: [
-	 *				'Div text.'
-	 *			]
-	 *		} );
-	 *
-	 *		element.outerHTML == "<div id="first-div" class="my-div">Div text.</div>"
-	 *
-	 * See: {@link Template#apply}.
-	 *
-	 * @param {DOMElement} element DOM Element to initialize.
-	 * @param {core.ui.TemplateDefinition} def Template definition to be applied.
-	 */
-	applyTemplateToElement( element, def ) {
-		// Prepare pre–defined listeners.
-		this._extendTemplateWithListenerAttachers( def );
-
-		// Prepare pre–defined attribute bindings.
-		this._extendTemplateWithModelBinders( def );
-
-		new Template( def ).apply( element );
-	}
-
-	/**
-	 * Destroys the view instance. The process includes:
-	 *
-	 * 1. Removal of child views from {@link core.ui.View#regions}.
-	 * 2. Destruction of the {@link core.ui.View#regions}.
-	 * 3. Removal of {#link #_el} from DOM.
-	 */
-	destroy() {
-		let childView;
-
-		this.stopListening();
-
-		for ( let region of this.regions ) {
-			while ( ( childView = region.views.get( 0 ) ) ) {
-				region.views.remove( childView );
-			}
-
-			this.regions.remove( region ).destroy();
-		}
-
-		if ( this.template ) {
-			this.element.remove();
-		}
-
-		this.model = this.regions = this.template = this.locale = this.t = null;
-		this._regionSelectors = this._element = this._template = null;
-	}
-
-	/**
-	 * Initializes {@link core.ui.View#regions} of this view by passing a DOM element
-	 * generated from {@link core.ui.View#_regionSelectors} into {@link Region#init}.
-	 *
-	 * @protected
-	 */
-	_initRegions() {
-		let region, regionEl, regionSelector;
-
-		for ( region of this.regions ) {
-			regionSelector = this._regionSelectors[ region.name ];
-
-			if ( typeof regionSelector == 'string' ) {
-				regionEl = this.element.querySelector( regionSelector );
-			} else if ( typeof regionSelector == 'function' ) {
-				regionEl = regionSelector( this.element );
-			} else {
-				regionEl = null;
-			}
-
-			region.init( regionEl );
-		}
-	}
-
-	/**
-	 * For a given event name or callback, returns a function which,
-	 * once executed in a context of an element, attaches native DOM listener
-	 * to the element. The listener executes given callback or fires View's event
-	 * of given name.
-	 *
-	 * @protected
-	 * @param {String|Function} evtNameOrCallback Event name to be fired on View or callback to execute.
-	 * @returns {Function} A listener attacher function to be executed in the context of an element.
-	 */
-	_getDOMListenerAttacher( evtNameOrCallback ) {
-		/**
-		 * Attaches a native DOM listener to given element. The listener executes the
-		 * callback or fires View's event.
-		 *
-		 * Note: If the selector is supplied, it narrows the scope to relevant targets only.
-		 * So instead of
-		 *
-		 *		children: [
-		 *			{ tag: 'span', on: { click: 'foo' } }
-		 *			{ tag: 'span', on: { click: 'foo' } }
-		 *		]
-		 *
-		 * a single, more efficient listener can be attached that uses **event delegation**:
-		 *
-		 *		children: [
-		 *			{ tag: 'span' }
-		 *			{ tag: 'span' }
-		 *		],
-		 *		on: {
-		 *			'click@span': 'foo',
-		 *		}
-		 *
-		 * @param {HTMLElement} el Element, to which the native DOM Event listener is attached.
-		 * @param {String} domEventName The name of native DOM Event.
-		 * @param {String} [selector] If provided, the selector narrows the scope to relevant targets only.
-		 */
-		return ( el, domEvtName, selector ) => {
-			// Use View's listenTo, so the listener is detached, when the View dies.
-			this.listenTo( el, domEvtName, ( evt, domEvt ) => {
-				if ( !selector || domEvt.target.matches( selector ) ) {
-					if ( typeof evtNameOrCallback == 'function' ) {
-						evtNameOrCallback( domEvt );
-					} else {
-						this.fire( evtNameOrCallback, domEvt );
-					}
-				}
-			} );
-		};
-	}
-
-	/**
-	 * For given {@link core.ui.TemplateValueSchema} found by (@link _extendTemplateWithModelBinders} containing
-	 * {@link core.ui.ViewModelBinding} it returns a function, which when called by {@link Template#render}
-	 * or {@link Template#apply} activates the binding and sets its initial value.
-	 *
-	 * Note: {@link core.ui.TemplateValueSchema} can be for HTMLElement attributes or Text Node `textContent`.
-	 *
-	 * @protected
-	 * @param {core.ui.TemplateValueSchema}
-	 * @return {Function}
-	 */
-	_getModelBinder( valueSchema ) {
-		valueSchema = normalizeBinderValueSchema( valueSchema );
-
-		/**
-		 * Assembles the value using {@link core.ui.TemplateValueSchema} and stores it in a form of
-		 * an Array. Each entry of an Array corresponds to one of {@link core.ui.TemplateValueSchema}
-		 * items.
-		 *
-		 * @private
-		 * @param {Node} node
-		 * @return {Array}
-		 */
-		const getBoundValue = ( node ) => {
-			let model, modelValue;
-
-			return valueSchema.map( schemaItem => {
-				model = schemaItem.model;
-
-				if ( model ) {
-					modelValue = model[ schemaItem.attribute ];
-
-					if ( schemaItem.callback ) {
-						modelValue = schemaItem.callback( modelValue, node );
-					}
-
-					return modelValue;
-				} else {
-					return schemaItem;
-				}
-			} );
-		};
-
-		/**
-		 * Attaches a listener to {@link View#model}, which updates DOM with a value constructed from
-		 * {@link core.ui.TemplateValueSchema} when {@link View#model} attribute value changes.
-		 *
-		 * This function is called by {@link Template#render} or {@link Template#apply}.
-		 *
-		 * @param {Node} node DOM Node to be updated when {@link View#model} changes.
-		 * @param {Function} domUpdater A function provided by {@link Template} which updates corresponding
-		 * DOM attribute or `textContent`.
-		 */
-		return ( node, domUpdater ) => {
-			// Check if valueSchema is a single bind.if, like:
-			//		{ class: bind.if( 'foo' ) }
-			const isPlainBindIf = valueSchema.length == 1 && valueSchema[ 0 ].type == bindIfSymbol;
-
-			// A function executed each time bound model attribute changes.
-			const onModelChange = () => {
-				let value = getBoundValue( node );
-
-				if ( isPlainBindIf ) {
-					value = value[ 0 ];
-				} else {
-					value = value.reduce( binderValueReducer, '' );
-				}
-
-				const isSet = isPlainBindIf ? !!value : value;
-
-				const valueToSet = isPlainBindIf ?
-					( valueSchema[ 0 ].valueIfTrue || '' ) : value;
-
-				if ( isSet ) {
-					domUpdater.set( valueToSet );
-				} else {
-					domUpdater.remove();
-				}
-			};
-
-			valueSchema
-				.filter( schemaItem => schemaItem.model )
-				.forEach( schemaItem => {
-					this.listenTo( schemaItem.model, 'change:' + schemaItem.attribute, onModelChange );
-				} );
-
-			// Set initial values.
-			onModelChange();
-		};
-	}
-
-	/**
-	 * Iterates over "attributes" and "text" properties in {@link TemplateDefinition} and
-	 * locates existing {@link core.ui.ViewModelBinding} created by {@link core.ui.View#attributeBinder}.
-	 * Then, for each such a binding, it creates corresponding entry in {@link Template#_modelBinders},
-	 * which can be then activated by {@link Template#render} or {@link Template#apply}.
-	 *
-	 * @protected
-	 * @param {core.ui.TemplateDefinition} def
-	 */
-	_extendTemplateWithModelBinders( def ) {
-		const attributes = def.attributes;
-		const text = def.text;
-		let binders = def._modelBinders;
-		let attrName, attrValue;
-
-		if ( !binders && isPlainObject( def ) ) {
-			Object.defineProperty( def, '_modelBinders', {
-				enumerable: false,
-				writable: true,
-				value: {
-					attributes: {}
-				}
-			} );
-
-			binders = def._modelBinders;
-		}
-
-		if ( attributes ) {
-			for ( attrName in attributes ) {
-				attrValue = attributes[ attrName ];
-
-				if ( hasModelBinding( attrValue ) ) {
-					binders.attributes[ attrName ] = this._getModelBinder( attrValue );
-				}
-			}
-		}
-
-		if ( text && hasModelBinding( text ) ) {
-			binders.text = this._getModelBinder( text );
-		}
-
-		// Repeat recursively for the children.
-		if ( def.children ) {
-			def.children.forEach( this._extendTemplateWithModelBinders, this );
-		}
-	}
-
-	/**
-	 * Iterates over "on" property in {@link TemplateDefinition} to recursively
-	 * replace each listener declaration with a function which, once executed in a context
-	 * of an element, attaches native DOM listener to that element.
-	 *
-	 * @protected
-	 * @param {core.ui.TemplateDefinition} def Template definition.
-	 */
-	_extendTemplateWithListenerAttachers( def ) {
-		const on = def.on;
-
-		// Don't create attachers if they're already here or in the context of the same (this) View instance.
-		if ( on && ( !on._listenerAttachers || on._listenerView != this ) ) {
-			let domEvtName, evtNameOrCallback;
-
-			Object.defineProperty( on, '_listenerAttachers', {
-				enumerable: false,
-				writable: true,
-				value: {}
-			} );
-
-			for ( domEvtName in on ) {
-				evtNameOrCallback = on[ domEvtName ];
-
-				// Listeners allow definition with an array:
-				//
-				//    on: {
-				//        'DOMEventName@selector': [ 'event1', callback ],
-				//        'DOMEventName': [ callback, 'event2', 'event3' ]
-				//        ...
-				//    }
-				if ( Array.isArray( evtNameOrCallback ) ) {
-					on._listenerAttachers[ domEvtName ] = on[ domEvtName ].map( this._getDOMListenerAttacher, this );
-				}
-				// Listeners allow definition with a string containing event name:
-				//
-				//    on: {
-				//       'DOMEventName@selector': 'event1',
-				//       'DOMEventName': 'event2'
-				//       ...
-				//    }
-				else {
-					on._listenerAttachers[ domEvtName ] = this._getDOMListenerAttacher( evtNameOrCallback );
-				}
-			}
-
-			// Set this property to be known that these attachers has already been created
-			// in the context of this particular View instance.
-			Object.defineProperty( on, '_listenerView', {
-				enumerable: false,
-				writable: true,
-				value: this
-			} );
-		}
-
-		// Repeat recursively for the children.
-		if ( def.children ) {
-			def.children.forEach( this._extendTemplateWithListenerAttachers, this );
-		}
-	}
-}
-
-utils.mix( View, DOMEmitterMixin );
-
-const validSelectorTypes = new Set( [ 'string', 'boolean', 'function' ] );
-
-/**
- * Check whether region selector is valid.
- *
- * @ignore
- * @private
- * @param {*} selector Selector to be checked.
- * @returns {Boolean}
- */
-function isValidRegionSelector( selector ) {
-	return validSelectorTypes.has( typeof selector ) && selector !== false;
-}
-
-/**
- * Normalizes given {@link core.ui.TemplateValueSchema} it's always in an Array–like format:
- *
- * 		{ attributeName/text: 'bar' } ->
- * 			{ attributeName/text: [ 'bar' ] }
- *
- * 		{ attributeName/text: { model: ..., modelAttributeName: ..., callback: ... } } ->
- * 			{ attributeName/text: [ { model: ..., modelAttributeName: ..., callback: ... } ] }
- *
- * 		{ attributeName/text: [ 'bar', { model: ..., modelAttributeName: ... }, 'baz' ] }
- *
- * @ignore
- * @private
- * @param {core.ui.TemplateValueSchema} valueSchema
- * @returns {Array}
- */
-function normalizeBinderValueSchema( valueSchema ) {
-	return Array.isArray( valueSchema ) ? valueSchema : [ valueSchema ];
-}
-
-/**
- * Checks whether given {@link core.ui.TemplateValueSchema} contains a
- * {@link core.ui.ViewModelBinding}.
- *
- * @ignore
- * @private
- * @param {core.ui.TemplateValueSchema} valueSchema
- * @returns {Boolean}
- */
-function hasModelBinding( valueSchema ) {
-	if ( Array.isArray( valueSchema ) ) {
-		return valueSchema.some( hasModelBinding );
-	} else if ( valueSchema.model ) {
-		return true;
-	}
-
-	return false;
-}
-
-/**
- * A helper which concatenates the value avoiding unwanted
- * leading white spaces.
- *
- * @ignore
- * @private
- * @param {String} prev
- * @param {String} cur
- * @returns {String}
- */
-function binderValueReducer( prev, cur ) {
-	return prev === '' ? `${cur}` : `${prev} ${cur}`;
-}
-
-/**
- * Describes Model binding created by {@link View#attributeBinder}.
- *
- * @typedef core.ui.ViewModelBinding
- * @type Object
- * @property {Symbol} type
- * @property {core.ui.Model} model
- * @property {String} attribute
- * @property {String} [valueIfTrue]
- * @property {Function} [callback]
- */

+ 2 - 2
packages/ckeditor5-engine/tests/_utils/ui/boxededitorui/boxededitorui.js

@@ -5,8 +5,8 @@
 
 'use strict';
 
-import EditorUI from '/ckeditor5/core/editorui/editorui.js';
-import ControllerCollection from '/ckeditor5/core/ui/controllercollection.js';
+import EditorUI from '/ckeditor5/ui/editorui/editorui.js';
+import ControllerCollection from '/ckeditor5/ui/controllercollection.js';
 
 export default class BoxedEditorUI extends EditorUI {
 	constructor( editor ) {

+ 1 - 1
packages/ckeditor5-engine/tests/_utils/ui/boxededitorui/boxededitoruiview.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-import EditorUIView from '/ckeditor5/core/editorui/editoruiview.js';
+import EditorUIView from '/ckeditor5/ui/editorui/editoruiview.js';
 
 export default class BoxedEditorUIView extends EditorUIView {
 	constructor( model, locale ) {

+ 3 - 3
packages/ckeditor5-engine/tests/_utils/ui/boxlesseditorui/boxlesseditorui.js

@@ -5,8 +5,8 @@
 
 'use strict';
 
-import EditorUI from '/ckeditor5/core/editorui/editorui.js';
-import ControllerCollection from '/ckeditor5/core/ui/controllercollection.js';
+import EditorUI from '/ckeditor5/ui/editorui/editorui.js';
+import ControllerCollection from '/ckeditor5/ui/controllercollection.js';
 
 export default class BoxlessEditorUI extends EditorUI {
 	constructor( editor ) {
@@ -16,7 +16,7 @@ export default class BoxlessEditorUI extends EditorUI {
 
 		/**
 		 * @private
-		 * @type {core.ui.View}
+		 * @type {ui.View}
 		 * @property _view
 		 */
 	}

+ 1 - 1
packages/ckeditor5-engine/tests/_utils/ui/editable/framed/framededitable.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-import Editable from '/ckeditor5/core/editable/editable.js';
+import Editable from '/ckeditor5/ui/editable/editable.js';
 
 export default class FramedEditable extends Editable {
 	constructor( editor ) {

+ 1 - 1
packages/ckeditor5-engine/tests/_utils/ui/editable/framed/framededitableview.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-import EditableView from '/ckeditor5/core/editable/editableview.js';
+import EditableView from '/ckeditor5/ui/editable/editableview.js';
 
 export default class FramedEditableView extends EditableView {
 	constructor( model, locale ) {

+ 1 - 1
packages/ckeditor5-engine/tests/_utils/ui/editable/inline/inlineeditable.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-import Editable from '/ckeditor5/core/editable/editable.js';
+import Editable from '/ckeditor5/ui/editable/editable.js';
 
 export default class InlineEditable extends Editable {
 }

+ 1 - 1
packages/ckeditor5-engine/tests/_utils/ui/editable/inline/inlineeditableview.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-import EditableView from '/ckeditor5/core/editable/editableview.js';
+import EditableView from '/ckeditor5/ui/editable/editableview.js';
 
 export default class InlineEditableView extends EditableView {
 	constructor( model, locale, editableElement ) {

+ 1 - 1
packages/ckeditor5-engine/tests/_utils/ui/floatingtoolbar/floatingtoolbar.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-import Toolbar from '/ckeditor5/core/ui/bindings/toolbar.js';
+import Toolbar from '/ckeditor5/ui/bindings/toolbar.js';
 
 export default class FloatingToolbar extends Toolbar {
 	constructor( model, view, editor ) {

+ 2 - 2
packages/ckeditor5-engine/tests/creator/manual/_utils/creator/classiccreator.js

@@ -10,8 +10,8 @@ import BoxedEditorUI from '/tests/core/_utils/ui/boxededitorui/boxededitorui.js'
 import BoxedEditorUIView from '/tests/core/_utils/ui/boxededitorui/boxededitoruiview.js';
 import FramedEditable from '/tests/core/_utils/ui/editable/framed/framededitable.js';
 import FramedEditableView from '/tests/core/_utils/ui/editable/framed/framededitableview.js';
-import Model from '/ckeditor5/core/ui/model.js';
-import Toolbar from '/ckeditor5/core/ui/bindings/toolbar.js';
+import Model from '/ckeditor5/ui/model.js';
+import Toolbar from '/ckeditor5/ui/bindings/toolbar.js';
 import ToolbarView from '/ckeditor5/ui/toolbar/toolbarview.js';
 import { imitateFeatures, imitateDestroyFeatures } from '../imitatefeatures.js';
 

+ 2 - 2
packages/ckeditor5-engine/tests/creator/manual/_utils/creator/inlinecreator.js

@@ -6,11 +6,11 @@
 'use strict';
 
 import Creator from '/ckeditor5/core/creator.js';
-import EditorUIView from '/ckeditor5/core/editorui/editoruiview.js';
+import EditorUIView from '/ckeditor5/ui/editorui/editoruiview.js';
 import BoxlessEditorUI from '/tests/core/_utils/ui/boxlesseditorui/boxlesseditorui.js';
 import InlineEditable from '/tests/core/_utils/ui/editable/inline/inlineeditable.js';
 import InlineEditableView from '/tests/core/_utils/ui/editable/inline/inlineeditableview.js';
-import Model from '/ckeditor5/core/ui/model.js';
+import Model from '/ckeditor5/ui/model.js';
 import FloatingToolbar from '/tests/core/_utils/ui/floatingtoolbar/floatingtoolbar.js';
 import FloatingToolbarView from '/tests/core/_utils/ui/floatingtoolbar/floatingtoolbarview.js';
 import { imitateFeatures, imitateDestroyFeatures } from '../imitatefeatures.js';

+ 1 - 1
packages/ckeditor5-engine/tests/creator/manual/_utils/imitatefeatures.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-import Model from '/ckeditor5/core/ui/model.js';
+import Model from '/ckeditor5/ui/model.js';
 import Button from '/ckeditor5/ui/button/button.js';
 import ButtonView from '/ckeditor5/ui/button/buttonview.js';
 

+ 0 - 79
packages/ckeditor5-engine/tests/editable/editable.js

@@ -1,79 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: editable */
-
-'use strict';
-
-import Editor from '/ckeditor5/core/editor.js';
-import Editable from '/ckeditor5/core/editable/editable.js';
-import Model from '/ckeditor5/core/ui/model.js';
-import testUtils from '/tests/utils/_utils/utils.js';
-
-describe( 'Editable', () => {
-	let editable, editor;
-
-	beforeEach( () => {
-		editor = new Editor();
-		editable = new Editable( editor );
-	} );
-
-	describe( 'constructor', () => {
-		it( 'sets all the properties', () => {
-			expect( editable ).to.have.property( 'editor', editor );
-			expect( editable ).to.have.property( 'isFocused', false );
-			expect( editable ).to.have.property( 'isEditable', true );
-		} );
-	} );
-
-	describe( 'viewModel', () => {
-		it( 'returns a model instance', () => {
-			expect( editable.viewModel ).to.be.an.instanceof( Model );
-		} );
-
-		it( 'always returns the same instance', () => {
-			expect( editable.viewModel ).to.equal( editable.viewModel );
-		} );
-
-		it( 'constains editable attributes', () => {
-			expect( editable.viewModel ).to.have.property( 'isEditable', true );
-			expect( editable.viewModel ).to.have.property( 'isFocused', false );
-		} );
-
-		it( 'binds this.isFocused to editable', () => {
-			testUtils.assertBinding(
-				editable,
-				{ isFocused: false },
-				[
-					[ editable.viewModel, { isFocused: true } ]
-				],
-				{ isFocused: true }
-			);
-		} );
-
-		it( 'binds editable.isEditable to itself', () => {
-			testUtils.assertBinding(
-				editable.viewModel,
-				{ isEditable: true },
-				[
-					[ editable, { isEditable: false } ]
-				],
-				{ isEditable: false }
-			);
-		} );
-	} );
-
-	// These are temporary implementation, so tests do nothing beside ensuring 100% CC.
-	describe( 'getData() and setData()', () => {
-		it( 'exist', () => {
-			editable.view = {
-				editableElement: document.createElement( 'div' )
-			};
-
-			editable.getData();
-			editable.setData();
-		} );
-	} );
-} );

+ 0 - 66
packages/ckeditor5-engine/tests/editable/editableview.js

@@ -1,66 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: editable */
-
-'use strict';
-
-import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
-import EditableView from '/ckeditor5/core/editable/editableview.js';
-import Model from '/ckeditor5/core/ui/model.js';
-
-describe( 'EditableView', () => {
-	let model, view, editableElement;
-
-	beforeEach( () => {
-		model = new Model( { isEditable: true, isFocused: false } );
-		view = new EditableView( model );
-		editableElement = document.createElement( 'div' );
-
-		return view.init();
-	} );
-
-	describe( 'setEditableElement', () => {
-		it( 'sets the editableElement property', () => {
-			view.setEditableElement( editableElement );
-
-			expect( view ).to.have.property( 'editableElement', editableElement );
-		} );
-
-		it( 'throws when trying to use it twice', () => {
-			view.setEditableElement( editableElement );
-
-			expect( view ).to.have.property( 'editableElement', editableElement );
-
-			expect( () => {
-				view.setEditableElement( editableElement );
-			} ).to.throw( CKEditorError, /^editableview-cannot-override-editableelement/ );
-		} );
-
-		it( 'sets the contentEditable attribute to model.isEditable', () => {
-			view.setEditableElement( editableElement );
-
-			expect( editableElement.contentEditable ).to.equal( 'true' );
-
-			model.isEditable = false;
-
-			expect( editableElement.contentEditable ).to.equal( 'false' );
-		} );
-
-		it( 'sets the model.isFocused based on element#focus and element#blur events', () => {
-			view.setEditableElement( editableElement );
-
-			expect( model.isFocused ).to.equal( false );
-
-			editableElement.dispatchEvent( new Event( 'focus' ) );
-
-			expect( model.isFocused ).to.equal( true );
-
-			editableElement.dispatchEvent( new Event( 'blur' ) );
-
-			expect( model.isFocused ).to.equal( false );
-		} );
-	} );
-} );

+ 0 - 30
packages/ckeditor5-engine/tests/editorui/editorui.js

@@ -1,30 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import Editor from '/ckeditor5/core/editor.js';
-import EditorUI from '/ckeditor5/core/editorui/editorui.js';
-import ComponentFactory from '/ckeditor5/core/ui/componentfactory.js';
-import ControllerCollection from '/ckeditor5/core/ui/controllercollection.js';
-
-describe( 'EditorUI', () => {
-	let editor, editorUI;
-
-	beforeEach( () => {
-		editor = new Editor();
-		editorUI = new EditorUI( editor );
-	} );
-
-	describe( 'constructor', () => {
-		it( 'sets all the properties', () => {
-			expect( editorUI ).to.have.property( 'editor', editor );
-
-			expect( editorUI.featureComponents ).to.be.instanceof( ComponentFactory );
-
-			expect( editorUI.collections.get( 'body' ) ).to.be.instanceof( ControllerCollection );
-		} );
-	} );
-} );

+ 0 - 38
packages/ckeditor5-engine/tests/editorui/editoruiview.js

@@ -1,38 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-import EditorUIView from '/ckeditor5/core/editorui/editoruiview.js';
-import Model from '/ckeditor5/core/ui/model.js';
-
-describe( 'EditorUIView', () => {
-	let editorUIView;
-
-	beforeEach( () => {
-		editorUIView = new EditorUIView( new Model() );
-
-		return editorUIView.init();
-	} );
-
-	describe( 'constructor', () => {
-		it( 'creates the body region', () => {
-			const el = editorUIView.regions.get( 'body' ).element;
-
-			expect( el.parentNode ).to.equal( document.body );
-			expect( el.nextSibling ).to.be.null;
-		} );
-	} );
-
-	describe( 'destroy', () => {
-		it( 'removes the body region container', () => {
-			const el = editorUIView.regions.get( 'body' ).element;
-
-			editorUIView.destroy();
-
-			expect( el.parentNode ).to.be.null;
-		} );
-	} );
-} );

+ 0 - 64
packages/ckeditor5-engine/tests/ui/bindings/toolbar.js

@@ -1,64 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: ui, toolbar */
-
-'use strict';
-
-import Editor from '/ckeditor5/core/editor.js';
-import Model from '/ckeditor5/core/ui/model.js';
-import View from '/ckeditor5/core/ui/view.js';
-import Controller from '/ckeditor5/core/ui/controller.js';
-import Toolbar from '/ckeditor5/core/ui/bindings/toolbar.js';
-
-describe( 'Toolbar', () => {
-	let toolbar, view, model, editor;
-
-	beforeEach( () => {
-		editor = new Editor();
-		model = new Model();
-		view = new View( model );
-		toolbar = new Toolbar( view, model, editor );
-	} );
-
-	describe( 'constructor', () => {
-		it( 'sets all the properties', () => {
-			expect( toolbar ).to.have.property( 'editor', editor );
-		} );
-	} );
-
-	describe( 'addButtons', () => {
-		it( 'creates buttons for each button name', () => {
-			const createSpy = sinon.spy( () => new Controller() );
-
-			editor.ui = {
-				featureComponents: {
-					create: createSpy
-				}
-			};
-
-			toolbar.addButtons( [ 'foo', 'bar' ] );
-
-			expect( createSpy.callCount ).to.equal( 2 );
-			expect( createSpy.firstCall.calledWith( 'foo' ) ).to.be.true;
-			expect( createSpy.secondCall.calledWith( 'bar' ) ).to.be.true;
-		} );
-
-		it( 'adds created compoments to the collection of buttons', () => {
-			const component = new Controller();
-			const createSpy = sinon.spy( () => component );
-
-			editor.ui = {
-				featureComponents: {
-					create: createSpy
-				}
-			};
-
-			toolbar.addButtons( [ 'foo' ] );
-
-			expect( toolbar.collections.get( 'buttons' ).get( 0 ) ).to.equal( component );
-		} );
-	} );
-} );

+ 0 - 59
packages/ckeditor5-engine/tests/ui/componentfactory.js

@@ -1,59 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: ui */
-
-'use strict';
-
-import Editor from '/ckeditor5/core/editor.js';
-import ComponentFactory from '/ckeditor5/core/ui/componentfactory.js';
-import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
-
-describe( 'ComponentFactory', () => {
-	let editor, factory;
-
-	beforeEach( () => {
-		editor = new Editor();
-		factory = new ComponentFactory( editor );
-	} );
-
-	describe( 'constructor', () => {
-		it( 'sets all the properties', () => {
-			expect( factory ).to.have.property( 'editor', editor );
-		} );
-	} );
-
-	describe( 'add', () => {
-		it( 'throws when trying to override already registered component', () => {
-			factory.add( 'foo', class {}, class {}, {} );
-
-			expect( () => {
-				factory.add( 'foo', class {}, class {}, {} );
-			} ).to.throw( CKEditorError, /^componentfactory-item-exists/ );
-		} );
-	} );
-
-	describe( 'create', () => {
-		it( 'creates an instance', () => {
-			class View {}
-
-			class Controller {
-				constructor( model, view, ed ) {
-					expect( model ).to.equal( model );
-					expect( view ).to.be.instanceof( View );
-					expect( ed ).to.equal( editor );
-				}
-			}
-
-			const model = {};
-
-			factory.add( 'foo', Controller, View, model );
-
-			const instance = factory.create( 'foo' );
-
-			expect( instance ).to.be.instanceof( Controller );
-		} );
-	} );
-} );

+ 0 - 366
packages/ckeditor5-engine/tests/ui/controller.js

@@ -1,366 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: ui */
-
-'use strict';
-
-import testUtils from '/tests/_utils/utils.js';
-import View from '/ckeditor5/core/ui/view.js';
-import Controller from '/ckeditor5/core/ui/controller.js';
-import ControllerCollection from '/ckeditor5/core/ui/controllercollection.js';
-import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
-import Model from '/ckeditor5/core/ui/model.js';
-import EventInfo from '/ckeditor5/utils/eventinfo.js';
-
-let ParentController, ParentView;
-
-testUtils.createSinonSandbox();
-
-describe( 'Controller', () => {
-	describe( 'constructor', () => {
-		it( 'defines basic properties', () => {
-			const controller = new Controller();
-
-			expect( controller.model ).to.be.null;
-			expect( controller.ready ).to.be.false;
-			expect( controller.view ).to.be.null;
-			expect( controller.collections.length ).to.be.equal( 0 );
-		} );
-
-		it( 'should accept model and view', () => {
-			const model = new Model();
-			const view = new View();
-			const controller = new Controller( model, view );
-
-			expect( controller.model ).to.be.equal( model );
-			expect( controller.view ).to.be.equal( view );
-		} );
-	} );
-
-	describe( 'init', () => {
-		it( 'should throw when already initialized', () => {
-			const controller = new Controller();
-
-			return controller.init()
-				.then( () => {
-					controller.init();
-
-					throw new Error( 'This should not be executed.' );
-				} )
-				.catch( ( err ) => {
-					expect( err ).to.be.instanceof( CKEditorError );
-					expect( err.message ).to.match( /ui-controller-init-re/ );
-				} );
-		} );
-
-		it( 'should set #ready flag', () => {
-			const controller = new Controller();
-
-			return controller.init().then( () => {
-				expect( controller.ready ).to.be.true;
-			} );
-		} );
-
-		it( 'should initialize own view', () => {
-			const view = new View();
-			const controller = new Controller( null, view );
-			const spy = testUtils.sinon.spy( view, 'init' );
-
-			return controller.init().then( () => {
-				sinon.assert.calledOnce( spy );
-			} );
-		} );
-
-		it( 'should initialize child controllers in own collections', () => {
-			const parentController = new Controller();
-			const buttonCollection = new ControllerCollection( 'buttons' );
-			parentController.collections.add( buttonCollection );
-
-			const childController1 = new Controller();
-			const childController2 = new Controller();
-			const spy1 = testUtils.sinon.spy( childController1, 'init' );
-			const spy2 = testUtils.sinon.spy( childController2, 'init' );
-
-			buttonCollection.add( childController1 );
-			buttonCollection.add( childController2 );
-
-			return parentController.init().then( () => {
-				expect( buttonCollection.get( 0 ) ).to.be.equal( childController1 );
-				expect( buttonCollection.get( 1 ) ).to.be.equal( childController2 );
-
-				sinon.assert.calledOnce( spy1 );
-				sinon.assert.calledOnce( spy2 );
-			} );
-		} );
-	} );
-
-	describe( 'add', () => {
-		beforeEach( defineParentControllerClass );
-
-		it( 'should add a controller to specific collection', () => {
-			const parentController = new ParentController();
-			const child1 = new Controller();
-			const child2 = new Controller();
-			const collection = parentController.collections.get( 'x' );
-
-			parentController.add( 'x', child1 );
-			parentController.add( 'x', child2 );
-
-			expect( collection ).to.have.length( 2 );
-			expect( collection.get( 0 ) ).to.be.equal( child1 );
-			expect( collection.get( 1 ) ).to.be.equal( child2 );
-		} );
-
-		it( 'should add a controller at specific index', () => {
-			const parentController = new ParentController();
-			const child1 = new Controller();
-			const child2 = new Controller();
-			const collection = parentController.collections.get( 'x' );
-
-			parentController.add( 'x', child1 );
-			parentController.add( 'x', child2, 0 );
-
-			expect( collection ).to.have.length( 2 );
-			expect( collection.get( 0 ) ).to.be.equal( child2 );
-			expect( collection.get( 1 ) ).to.be.equal( child1 );
-		} );
-	} );
-
-	describe( 'remove', () => {
-		beforeEach( defineParentControllerClass );
-
-		it( 'should remove a controller from specific collection – by instance', () => {
-			const parentController = new ParentController();
-			const child1 = new Controller();
-			const child2 = new Controller();
-			const child3 = new Controller();
-			const collection = parentController.collections.get( 'x' );
-
-			parentController.add( 'x', child1 );
-			parentController.add( 'x', child2 );
-			parentController.add( 'x', child3 );
-
-			const removed = parentController.remove( 'x', child2 );
-
-			expect( collection ).to.have.length( 2 );
-			expect( collection.get( 0 ) ).to.be.equal( child1 );
-			expect( collection.get( 1 ) ).to.be.equal( child3 );
-			expect( removed ).to.be.equal( child2 );
-		} );
-
-		it( 'should remove a controller from specific collection – by index', () => {
-			const parentController = new ParentController();
-			const child1 = new Controller();
-			const child2 = new Controller();
-			const child3 = new Controller();
-			const collection = parentController.collections.get( 'x' );
-
-			parentController.add( 'x', child1 );
-			parentController.add( 'x', child2 );
-			parentController.add( 'x', child3 );
-
-			const removed = parentController.remove( 'x', 1 );
-
-			expect( collection ).to.have.length( 2 );
-			expect( collection.get( 0 ) ).to.be.equal( child1 );
-			expect( collection.get( 1 ) ).to.be.equal( child3 );
-			expect( removed ).to.be.equal( child2 );
-		} );
-	} );
-
-	describe( 'collections', () => {
-		describe( 'add', () => {
-			beforeEach( defineParentViewClass );
-			beforeEach( defineParentControllerClass );
-
-			it( 'should add a child controller which has no view', () => {
-				const parentController = new ParentController( null, new ParentView() );
-				const collection = parentController.collections.get( 'x' );
-				const childController = new Controller();
-
-				return parentController.init()
-					.then( () => {
-						return collection.add( childController );
-					} )
-					.then( () => {
-						expect( collection.get( 0 ) ).to.be.equal( childController );
-					} );
-			} );
-
-			it( 'should append child controller\'s view to parent controller\'s view', () => {
-				const parentView = new ParentView();
-				const parentController = new ParentController( null, parentView );
-				const collection = parentController.collections.get( 'x' );
-				const childController = new Controller( null, new View() );
-				const spy = testUtils.sinon.spy();
-
-				parentView.regions.get( 'x' ).views.on( 'add', spy );
-
-				collection.add( childController );
-
-				sinon.assert.notCalled( spy );
-
-				collection.remove( childController );
-
-				return parentController.init()
-					.then( () => {
-						return collection.add( childController );
-					} )
-					.then( () => {
-						sinon.assert.calledOnce( spy );
-						sinon.assert.calledWithExactly( spy,
-							sinon.match.instanceOf( EventInfo ), childController.view, 0 );
-					} );
-			} );
-
-			it( 'should append child controller\'s view to parent controller\'s view at given index', () => {
-				const parentController = new ParentController( null, new ParentView() );
-				const collection = parentController.collections.get( 'x' );
-
-				const childView1 = new View();
-				const childController1 = new Controller( null, childView1 );
-				const childView2 = new View();
-				const childController2 = new Controller( null, childView2 );
-
-				return parentController.init()
-					.then( () => {
-						return collection.add( childController1 ).then( () => {
-							return collection.add( childController2, 0 );
-						} );
-					} )
-					.then( () => {
-						const region = parentController.view.regions.get( 'x' );
-
-						expect( region.views.get( 0 ) ).to.be.equal( childView2 );
-						expect( region.views.get( 1 ) ).to.be.equal( childView1 );
-					} );
-			} );
-		} );
-
-		describe( 'remove', () => {
-			beforeEach( defineParentViewClass );
-
-			it( 'should remove child controller\'s view from parent controller\'s view', () => {
-				const parentView = new ParentView();
-				const parentController = new ParentController( null, parentView );
-				const collection = parentController.collections.get( 'x' );
-				const childController = new Controller( null, new View() );
-				const spy = testUtils.sinon.spy();
-				parentView.regions.get( 'x' ).views.on( 'remove', spy );
-
-				collection.add( childController );
-
-				sinon.assert.notCalled( spy );
-
-				return parentController.init()
-					.then( () => {
-						collection.remove( childController );
-						sinon.assert.calledOnce( spy );
-						sinon.assert.calledWithExactly( spy,
-							sinon.match.instanceOf( EventInfo ), childController.view );
-					} );
-			} );
-		} );
-	} );
-
-	describe( 'destroy', () => {
-		beforeEach( defineParentViewClass );
-		beforeEach( defineParentControllerClass );
-
-		it( 'should destroy the controller', () => {
-			const view = new View();
-			const controller = new Controller( null, view );
-			const spy = testUtils.sinon.spy( view, 'destroy' );
-
-			return controller.init()
-				.then( () => {
-					return controller.destroy();
-				} )
-				.then( () => {
-					sinon.assert.calledOnce( spy );
-
-					expect( controller.model ).to.be.null;
-					expect( controller.ready ).to.be.null;
-					expect( controller.view ).to.be.null;
-					expect( controller.collections ).to.be.null;
-				} );
-		} );
-
-		it( 'should destroy the controller which has no view', () => {
-			const controller = new Controller( null, null );
-
-			return controller.init()
-				.then( () => {
-					return controller.destroy();
-				} )
-				.then( () => {
-					expect( controller.model ).to.be.null;
-					expect( controller.view ).to.be.null;
-					expect( controller.collections ).to.be.null;
-				} );
-		} );
-
-		it( 'should destroy child controllers in collections with their views', () => {
-			const parentController = new ParentController( null, new ParentView() );
-			const collection = parentController.collections.get( 'x' );
-			const childView = new View();
-			const childController = new Controller( null, childView );
-			const spy = testUtils.sinon.spy( childView, 'destroy' );
-
-			collection.add( childController );
-
-			return parentController.init()
-				.then( () => {
-					return parentController.destroy();
-				} )
-				.then( () => {
-					sinon.assert.calledOnce( spy );
-					expect( childController.model ).to.be.null;
-					expect( childController.view ).to.be.null;
-					expect( childController.collections ).to.be.null;
-				} );
-		} );
-
-		it( 'should destroy child controllers in collections when they have no views', () => {
-			const parentController = new ParentController( null, new ParentView() );
-			const collection = parentController.collections.get( 'x' );
-			const childController = new Controller( null, null );
-
-			collection.add( childController );
-
-			return parentController.init()
-				.then( () => {
-					return parentController.destroy();
-				} )
-				.then( () => {
-					expect( childController.model ).to.be.null;
-					expect( childController.view ).to.be.null;
-					expect( childController.collections ).to.be.null;
-				} );
-		} );
-	} );
-} );
-
-function defineParentViewClass() {
-	ParentView = class extends View {
-		constructor() {
-			super();
-
-			this.element = document.createElement( 'span' );
-			this.register( 'x', true );
-		}
-	};
-}
-
-function defineParentControllerClass() {
-	ParentController = class extends Controller {
-		constructor( ...args ) {
-			super( ...args );
-
-			this.collections.add( new ControllerCollection( 'x' ) );
-		}
-	};
-}

+ 0 - 111
packages/ckeditor5-engine/tests/ui/controllercollection.js

@@ -1,111 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: ui */
-
-'use strict';
-
-import testUtils from '/tests/_utils/utils.js';
-import ControllerCollection from '/ckeditor5/core/ui/controllercollection.js';
-import Controller from '/ckeditor5/core/ui/controller.js';
-import View from '/ckeditor5/core/ui/view.js';
-
-testUtils.createSinonSandbox();
-
-let ParentView;
-
-describe( 'ControllerCollection', () => {
-	beforeEach( defineParentViewClass );
-
-	describe( 'constructor', () => {
-		it( 'should throw when no name is passed', () => {
-			expect( () => {
-				new ControllerCollection();
-			} ).to.throw( /^ui-controllercollection-no-name/ );
-		} );
-	} );
-
-	describe( 'add', () => {
-		it( 'should add a child controller and return promise', () => {
-			const parentController = new Controller();
-			const childController = new Controller();
-			const collection = new ControllerCollection( 'x' );
-
-			parentController.collections.add( collection );
-
-			const returned = collection.add( childController );
-
-			expect( returned ).to.be.an.instanceof( Promise );
-			expect( collection.get( 0 ) ).to.be.equal( childController );
-		} );
-
-		it( 'should add a child controller at given position', () => {
-			const parentController = new Controller();
-			const childController1 = new Controller();
-			const childController2 = new Controller();
-			const collection = new ControllerCollection( 'x' );
-
-			parentController.collections.add( collection );
-
-			collection.add( childController1 );
-			collection.add( childController2, 0 );
-
-			expect( collection.get( 0 ) ).to.be.equal( childController2 );
-			expect( collection.get( 1 ) ).to.be.equal( childController1 );
-		} );
-
-		it( 'should initialize child controller if parent is ready', () => {
-			const parentController = new Controller( null, new ParentView() );
-			const childController = new Controller( null, new View() );
-			const spy = testUtils.sinon.spy( childController, 'init' );
-			const collection = new ControllerCollection( 'x' );
-
-			parentController.collections.add( collection );
-			collection.add( childController );
-			collection.remove( childController );
-
-			sinon.assert.notCalled( spy );
-
-			return parentController.init()
-				.then( () => {
-					return collection.add( childController );
-				} )
-				.then( () => {
-					sinon.assert.calledOnce( spy );
-				} );
-		} );
-
-		it( 'should not initialize child controller twice', () => {
-			const parentController = new Controller( null, new ParentView() );
-			const childController = new Controller( null, new View() );
-			const spy = testUtils.sinon.spy( childController, 'init' );
-			const collection = new ControllerCollection( 'x' );
-
-			parentController.collections.add( collection );
-
-			return parentController.init()
-				.then( () => {
-					return childController.init();
-				} )
-				.then( () => {
-					return collection.add( childController );
-				} )
-				.then( () => {
-					sinon.assert.calledOnce( spy );
-				} );
-		} );
-	} );
-} );
-
-function defineParentViewClass() {
-	ParentView = class extends View {
-		constructor() {
-			super();
-
-			this.element = document.createElement( 'span' );
-			this.register( 'x', true );
-		}
-	};
-}

+ 0 - 391
packages/ckeditor5-engine/tests/ui/domemittermixin.js

@@ -1,391 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* global document, Event, MouseEvent */
-/* bender-tags: ui */
-
-'use strict';
-
-import testUtils from '/tests/_utils/utils.js';
-import extend from '/ckeditor5/utils/lib/lodash/extend.js';
-import DOMEmitterMixin from '/ckeditor5/core/ui/domemittermixin.js';
-import EmitterMixin from '/ckeditor5/utils/emittermixin.js';
-
-let emitter, domEmitter, node;
-
-testUtils.createSinonSandbox();
-
-const getEmitterInstance = () => extend( {}, EmitterMixin );
-const getDOMEmitterInstance = () => extend( {}, DOMEmitterMixin );
-const getDOMNodeInstance = () => document.createElement( 'div' );
-
-function updateEmitterInstance() {
-	emitter = getEmitterInstance();
-}
-
-function updateDOMEmitterInstance() {
-	domEmitter = getDOMEmitterInstance();
-}
-
-function updateDOMNodeInstance() {
-	node = getDOMNodeInstance();
-}
-
-describe( 'DOMEmitterMixin', () => {
-	beforeEach( () => {
-		updateEmitterInstance();
-		updateDOMEmitterInstance();
-		updateDOMNodeInstance();
-	} );
-
-	describe( 'listenTo', () => {
-		it( 'should listen to EmitterMixin events', () => {
-			const spy = testUtils.sinon.spy();
-
-			domEmitter.listenTo( emitter, 'test', spy );
-			emitter.fire( 'test' );
-
-			sinon.assert.calledOnce( spy );
-		} );
-
-		it( 'should listen to native DOM events', () => {
-			const spy = testUtils.sinon.spy();
-
-			domEmitter.listenTo( node, 'test', spy );
-
-			node.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.calledOnce( spy );
-		} );
-
-		// #187
-		it( 'should work for DOM Nodes belonging to another window', () => {
-			const spy = testUtils.sinon.spy();
-			const iframe = document.createElement( 'iframe' );
-
-			document.body.appendChild( iframe );
-			const iframeNode = iframe.contentWindow.document.createElement( 'div' );
-
-			domEmitter.listenTo( iframeNode, 'test', spy );
-			iframeNode.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.calledOnce( spy );
-		} );
-	} );
-
-	describe( 'stopListening', () => {
-		it( 'should stop listening to a specific event callback', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-
-			domEmitter.listenTo( node, 'event1', spy1 );
-			domEmitter.listenTo( node, 'event2', spy2 );
-
-			node.dispatchEvent( new Event( 'event1' ) );
-			node.dispatchEvent( new Event( 'event2' ) );
-
-			domEmitter.stopListening( node, 'event1', spy1 );
-
-			node.dispatchEvent( new Event( 'event1' ) );
-			node.dispatchEvent( new Event( 'event2' ) );
-
-			sinon.assert.calledOnce( spy1 );
-			sinon.assert.calledTwice( spy2 );
-		} );
-
-		it( 'should stop listening to an specific event', () => {
-			const spy1a = testUtils.sinon.spy();
-			const spy1b = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-
-			domEmitter.listenTo( node, 'event1', spy1a );
-			domEmitter.listenTo( node, 'event1', spy1b );
-			domEmitter.listenTo( node, 'event2', spy2 );
-
-			node.dispatchEvent( new Event( 'event1' ) );
-			node.dispatchEvent( new Event( 'event2' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledOnce( spy2 );
-
-			domEmitter.stopListening( node, 'event1' );
-
-			node.dispatchEvent( new Event( 'event1' ) );
-			node.dispatchEvent( new Event( 'event2' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledTwice( spy2 );
-		} );
-
-		it( 'should stop listening to all events from a specific node', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-
-			domEmitter.listenTo( node, 'event1', spy1 );
-			domEmitter.listenTo( node, 'event2', spy2 );
-
-			node.dispatchEvent( new Event( 'event1' ) );
-			node.dispatchEvent( new Event( 'event2' ) );
-
-			domEmitter.stopListening( node );
-
-			node.dispatchEvent( new Event( 'event1' ) );
-			node.dispatchEvent( new Event( 'event2' ) );
-
-			sinon.assert.calledOnce( spy1 );
-			sinon.assert.calledOnce( spy2 );
-		} );
-
-		it( 'should stop listening to everything', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-
-			const node1 = getDOMNodeInstance();
-			const node2 = getDOMNodeInstance();
-
-			domEmitter.listenTo( node1, 'event1', spy1 );
-			domEmitter.listenTo( node2, 'event2', spy2 );
-
-			expect( domEmitter ).to.have.property( '_listeningTo' );
-
-			node1.dispatchEvent( new Event( 'event1' ) );
-			node2.dispatchEvent( new Event( 'event2' ) );
-
-			domEmitter.stopListening();
-
-			node1.dispatchEvent( new Event( 'event1' ) );
-			node2.dispatchEvent( new Event( 'event2' ) );
-
-			sinon.assert.calledOnce( spy1 );
-			sinon.assert.calledOnce( spy2 );
-
-			expect( domEmitter ).to.not.have.property( '_listeningTo' );
-		} );
-
-		it( 'should not stop other nodes when a non-listened node is provided', () => {
-			const spy = testUtils.sinon.spy();
-
-			const node1 = getDOMNodeInstance();
-			const node2 = getDOMNodeInstance();
-
-			domEmitter.listenTo( node1, 'test', spy );
-
-			domEmitter.stopListening( node2 );
-
-			node1.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.called( spy );
-		} );
-
-		it( 'should pass DOM Event data to the listener', () => {
-			const spy = testUtils.sinon.spy();
-
-			const node = getDOMNodeInstance();
-
-			domEmitter.listenTo( node, 'click', spy );
-
-			const mouseEvent = new MouseEvent( 'click', {
-				screenX: 10,
-				screenY: 20
-			} );
-
-			node.dispatchEvent( mouseEvent );
-
-			sinon.assert.calledOnce( spy );
-			expect( spy.args[ 0 ][ 1 ] ).to.be.equal( mouseEvent );
-		} );
-
-		it( 'should detach native DOM event listener proxy, specific event', () => {
-			const spy1a = testUtils.sinon.spy();
-			const spy1b = testUtils.sinon.spy();
-
-			domEmitter.listenTo( node, 'test', spy1a );
-
-			const proxyEmitter = domEmitter._getProxyEmitter( node );
-			const spy2 = testUtils.sinon.spy( proxyEmitter, 'fire' );
-
-			node.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy2 );
-
-			domEmitter.stopListening( node, 'test' );
-			node.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy2 );
-
-			// Attach same event again.
-			domEmitter.listenTo( node, 'test', spy1b );
-			node.dispatchEvent( new Event( 'test' ) );
-
-			expect( proxyEmitter ).to.be.equal( domEmitter._getProxyEmitter( node ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledTwice( spy2 );
-		} );
-
-		it( 'should detach native DOM event listener proxy, specific callback', () => {
-			const spy1a = testUtils.sinon.spy();
-			const spy1b = testUtils.sinon.spy();
-			const spy1c = testUtils.sinon.spy();
-
-			domEmitter.listenTo( node, 'test', spy1a );
-			domEmitter.listenTo( node, 'test', spy1b );
-
-			const proxyEmitter = domEmitter._getProxyEmitter( node );
-			const spy2 = testUtils.sinon.spy( proxyEmitter, 'fire' );
-
-			node.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledOnce( spy2 );
-
-			domEmitter.stopListening( node, 'test', spy1a );
-			node.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledTwice( spy1b );
-			sinon.assert.calledTwice( spy2 );
-
-			domEmitter.stopListening( node, 'test', spy1b );
-			node.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledTwice( spy1b );
-			sinon.assert.calledTwice( spy2 );
-
-			// Attach same event again.
-			domEmitter.listenTo( node, 'test', spy1c );
-			node.dispatchEvent( new Event( 'test' ) );
-
-			expect( proxyEmitter ).to.be.equal( domEmitter._getProxyEmitter( node ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledTwice( spy1b );
-			sinon.assert.calledOnce( spy1c );
-			sinon.assert.calledThrice( spy2 );
-		} );
-
-		it( 'should detach native DOM event listener proxy, specific emitter', () => {
-			const spy1a = testUtils.sinon.spy();
-			const spy1b = testUtils.sinon.spy();
-			const spy1c = testUtils.sinon.spy();
-			const spy1d = testUtils.sinon.spy();
-
-			domEmitter.listenTo( node, 'test1', spy1a );
-			domEmitter.listenTo( node, 'test2', spy1b );
-
-			const proxyEmitter = domEmitter._getProxyEmitter( node );
-			const spy2 = testUtils.sinon.spy( proxyEmitter, 'fire' );
-
-			node.dispatchEvent( new Event( 'test1' ) );
-			node.dispatchEvent( new Event( 'test2' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledTwice( spy2 );
-
-			domEmitter.stopListening( node );
-
-			node.dispatchEvent( new Event( 'test1' ) );
-			node.dispatchEvent( new Event( 'test2' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledTwice( spy2 );
-
-			// Attach same event again.
-			domEmitter.listenTo( node, 'test1', spy1c );
-			domEmitter.listenTo( node, 'test2', spy1d );
-
-			// Old proxy emitter died when stopped listening to the node.
-			const proxyEmitter2 = domEmitter._getProxyEmitter( node );
-			const spy3 = testUtils.sinon.spy( proxyEmitter2, 'fire' );
-
-			node.dispatchEvent( new Event( 'test1' ) );
-			node.dispatchEvent( new Event( 'test2' ) );
-
-			expect( proxyEmitter ).to.not.be.equal( proxyEmitter2 );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledOnce( spy1c );
-			sinon.assert.calledOnce( spy1d );
-			sinon.assert.calledTwice( spy2 );
-			sinon.assert.calledTwice( spy3 );
-		} );
-
-		it( 'should detach native DOM event listener proxy, everything', () => {
-			const spy1a = testUtils.sinon.spy();
-			const spy1b = testUtils.sinon.spy();
-			const spy1c = testUtils.sinon.spy();
-			const spy1d = testUtils.sinon.spy();
-
-			domEmitter.listenTo( node, 'test1', spy1a );
-			domEmitter.listenTo( node, 'test2', spy1b );
-
-			const proxyEmitter = domEmitter._getProxyEmitter( node );
-			const spy2 = testUtils.sinon.spy( proxyEmitter, 'fire' );
-
-			node.dispatchEvent( new Event( 'test1' ) );
-			node.dispatchEvent( new Event( 'test2' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledTwice( spy2 );
-
-			domEmitter.stopListening();
-
-			node.dispatchEvent( new Event( 'test1' ) );
-			node.dispatchEvent( new Event( 'test2' ) );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledTwice( spy2 );
-
-			// Attach same event again.
-			domEmitter.listenTo( node, 'test1', spy1c );
-			domEmitter.listenTo( node, 'test2', spy1d );
-
-			// Old proxy emitter died when stopped listening to the node.
-			const proxyEmitter2 = domEmitter._getProxyEmitter( node );
-			const spy3 = testUtils.sinon.spy( proxyEmitter2, 'fire' );
-
-			node.dispatchEvent( new Event( 'test1' ) );
-			node.dispatchEvent( new Event( 'test2' ) );
-
-			expect( proxyEmitter ).to.not.be.equal( proxyEmitter2 );
-
-			sinon.assert.calledOnce( spy1a );
-			sinon.assert.calledOnce( spy1b );
-			sinon.assert.calledOnce( spy1c );
-			sinon.assert.calledOnce( spy1d );
-			sinon.assert.calledTwice( spy2 );
-			sinon.assert.calledTwice( spy3 );
-		} );
-
-		// #187
-		it( 'should work for DOM Nodes belonging to another window', () => {
-			const spy = testUtils.sinon.spy();
-			const iframe = document.createElement( 'iframe' );
-
-			document.body.appendChild( iframe );
-			const iframeNode = iframe.contentWindow.document.createElement( 'div' );
-
-			domEmitter.listenTo( iframeNode, 'test', spy );
-
-			iframeNode.dispatchEvent( new Event( 'test' ) );
-			domEmitter.stopListening( iframeNode );
-			iframeNode.dispatchEvent( new Event( 'test' ) );
-
-			sinon.assert.calledOnce( spy );
-		} );
-	} );
-} );

+ 0 - 43
packages/ckeditor5-engine/tests/ui/model.js

@@ -1,43 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: ui */
-
-'use strict';
-
-import Model from '/ckeditor5/core/ui/model.js';
-
-let Car, car;
-
-describe( 'Model', () => {
-	beforeEach( () => {
-		Car = class extends Model {};
-
-		car = new Car( {
-			color: 'red',
-			year: 2015
-		} );
-	} );
-
-	it( 'should set attributes on creation', () => {
-		expect( car ).to.have.property( 'color', 'red' );
-		expect( car ).to.have.property( 'year', 2015 );
-
-		const spy = sinon.spy();
-
-		car.on( 'change:color', spy );
-		car.color = 'blue';
-
-		expect( spy.called ).to.be.true;
-	} );
-
-	it( 'should add properties on creation', () => {
-		let car = new Car( null, {
-			prop: 1
-		} );
-
-		expect( car ).to.have.property( 'prop', 1 );
-	} );
-} );

+ 0 - 128
packages/ckeditor5-engine/tests/ui/region.js

@@ -1,128 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* global document */
-/* bender-tags: ui */
-
-'use strict';
-
-import Region from '/ckeditor5/core/ui/region.js';
-import View from '/ckeditor5/core/ui/view.js';
-
-let TestViewA, TestViewB;
-let region, el;
-
-describe( 'View', () => {
-	beforeEach( createRegionInstance );
-
-	describe( 'constructor', () => {
-		it( 'accepts name', () => {
-			expect( region.name ).to.be.equal( 'foo' );
-			expect( region.element ).to.be.null;
-			expect( region.views.length ).to.be.equal( 0 );
-		} );
-	} );
-
-	describe( 'init', () => {
-		it( 'accepts region element', () => {
-			region.init( el );
-
-			expect( region.element ).to.be.equal( el );
-		} );
-	} );
-
-	describe( 'views collection', () => {
-		it( 'updates DOM when adding views', () => {
-			region.init( el );
-
-			expect( region.element.childNodes.length ).to.be.equal( 0 );
-
-			region.views.add( new TestViewA() );
-			expect( region.element.childNodes.length ).to.be.equal( 1 );
-
-			region.views.add( new TestViewA() );
-			expect( region.element.childNodes.length ).to.be.equal( 2 );
-		} );
-
-		it( 'does not update DOM when no region element', () => {
-			region.init();
-
-			expect( () => {
-				region.views.add( new TestViewA() );
-				region.views.add( new TestViewA() );
-			} ).to.not.throw();
-		} );
-
-		it( 'updates DOM when removing views', () => {
-			region.init( el );
-
-			let viewA = new TestViewA();
-			let viewB = new TestViewB();
-
-			region.views.add( viewA );
-			region.views.add( viewB );
-
-			expect( el.childNodes.length ).to.be.equal( 2 );
-			expect( el.firstChild.nodeName ).to.be.equal( 'A' );
-			expect( el.lastChild.nodeName ).to.be.equal( 'B' );
-
-			region.views.remove( viewA );
-			expect( el.childNodes.length ).to.be.equal( 1 );
-			expect( el.firstChild.nodeName ).to.be.equal( 'B' );
-
-			region.views.remove( viewB );
-			expect( el.childNodes.length ).to.be.equal( 0 );
-		} );
-	} );
-
-	describe( 'destroy', () => {
-		it( 'destroys the region', () => {
-			region.init( el );
-			region.views.add( new TestViewA() );
-
-			const elRef = region.element;
-			const viewsRef = region.views;
-
-			region.destroy();
-
-			expect( elRef.parentNode ).to.be.null;
-			expect( region.name ).to.be.equal( 'foo' );
-			expect( region.views ).to.be.null;
-			expect( viewsRef.length ).to.be.equal( 0 );
-			expect( region.element ).to.be.null;
-		} );
-
-		it( 'destroys an element–less region', () => {
-			region.init();
-
-			expect( () => {
-				region.destroy();
-			} ).to.not.throw();
-		} );
-	} );
-} );
-
-function createRegionInstance() {
-	class A extends View {
-		constructor() {
-			super();
-			this.template = { tag: 'a' };
-		}
-	}
-
-	class B extends View {
-		constructor() {
-			super();
-			this.template = { tag: 'b' };
-		}
-	}
-
-	TestViewA = A;
-	TestViewB = B;
-
-	el = document.createElement( 'div' );
-
-	region = new Region( 'foo' );
-}

+ 0 - 525
packages/ckeditor5-engine/tests/ui/template.js

@@ -1,525 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: ui */
-/* global HTMLElement */
-
-'use strict';
-
-import testUtils from '/tests/_utils/utils.js';
-import Template from '/ckeditor5/core/ui/template.js';
-import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
-
-testUtils.createSinonSandbox();
-
-let el, text;
-
-describe( 'Template', () => {
-	describe( 'constructor', () => {
-		it( 'accepts the definition', () => {
-			const def = {
-				tag: 'p'
-			};
-
-			expect( new Template( def ).definition ).to.equal( def );
-		} );
-	} );
-
-	describe( 'render', () => {
-		it( 'throws when wrong template definition', () => {
-			expect( () => {
-				new Template( {} ).render();
-			} ).to.throw( CKEditorError, /ui-template-wrong-syntax/ );
-
-			expect( () => {
-				new Template( {
-					tag: 'p',
-					text: 'foo'
-				} ).render();
-			} ).to.throw( CKEditorError, /ui-template-wrong-syntax/ );
-		} );
-
-		it( 'creates a HTMLElement', () => {
-			const el = new Template( {
-				tag: 'p',
-			} ).render();
-
-			expect( el ).to.be.instanceof( HTMLElement );
-			expect( el.parentNode ).to.be.null;
-			expect( el.outerHTML ).to.be.equal( '<p></p>' );
-		} );
-
-		it( 'renders HTMLElement attributes', () => {
-			const el = new Template( {
-				tag: 'p',
-				attributes: {
-					'class': [ 'a', 'b' ],
-					x: 'bar'
-				},
-				children: [ 'foo' ]
-			} ).render();
-
-			expect( el ).to.be.instanceof( HTMLElement );
-			expect( el.parentNode ).to.be.null;
-			expect( el.outerHTML ).to.be.equal( '<p class="a b" x="bar">foo</p>' );
-		} );
-
-		it( 'renders HTMLElement attributes – empty', () => {
-			const el = new Template( {
-				tag: 'p',
-				attributes: {
-					class: '',
-					x: [ '', '' ]
-				},
-				children: [ 'foo' ]
-			} ).render();
-
-			expect( el.outerHTML ).to.be.equal( '<p class="" x="">foo</p>' );
-		} );
-
-		it( 'renders HTMLElement attributes – falsy values', () => {
-			const el = new Template( {
-				tag: 'p',
-				attributes: {
-					class: false,
-					x: [ '', null ]
-				},
-				children: [ 'foo' ]
-			} ).render();
-
-			expect( el.outerHTML ).to.be.equal( '<p class="false" x="null">foo</p>' );
-		} );
-
-		it( 'creates HTMLElement children', () => {
-			const el = new Template( {
-				tag: 'p',
-				attributes: {
-					a: 'A'
-				},
-				children: [
-					{
-						tag: 'b',
-						children: [ 'B' ]
-					},
-					{
-						tag: 'i',
-						children: [
-							'C',
-							{
-								tag: 'b',
-								children: [ 'D' ]
-							}
-						]
-					}
-				]
-			} ).render();
-
-			expect( el.outerHTML ).to.be.equal( '<p a="A"><b>B</b><i>C<b>D</b></i></p>' );
-		} );
-
-		it( 'creates a Text node', () => {
-			const node = new Template( { text: 'foo' } ).render();
-
-			expect( node.nodeType ).to.be.equal( 3 );
-			expect( node.textContent ).to.be.equal( 'foo' );
-		} );
-
-		it( 'creates a child Text Node (different syntaxes)', () => {
-			const el = new Template( {
-				tag: 'p',
-				children: [
-					'foo',
-					{ text: 'bar' }
-				]
-			} ).render();
-
-			expect( el.outerHTML ).to.be.equal( '<p>foobar</p>' );
-		} );
-
-		it( 'creates multiple child Text Nodes', () => {
-			const el = new Template( {
-				tag: 'p',
-				children: [ 'a', 'b', { text: 'c' }, 'd' ]
-			} ).render();
-
-			expect( el.childNodes ).to.have.length( 4 );
-			expect( el.outerHTML ).to.be.equal( '<p>abcd</p>' );
-		} );
-
-		it( 'activates listener attachers – root', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-			const spy3 = testUtils.sinon.spy();
-
-			const el = new Template( {
-				tag: 'p',
-				on: {
-					_listenerAttachers: {
-						foo: spy1,
-						baz: [ spy2, spy3 ]
-					}
-				}
-			} ).render();
-
-			sinon.assert.calledWithExactly( spy1, el, 'foo', null );
-			sinon.assert.calledWithExactly( spy2, el, 'baz', null );
-			sinon.assert.calledWithExactly( spy3, el, 'baz', null );
-		} );
-
-		it( 'activates listener attachers – children', () => {
-			const spy = testUtils.sinon.spy();
-			const el = new Template( {
-				tag: 'p',
-				children: [
-					{
-						tag: 'span',
-						on: {
-							_listenerAttachers: {
-								bar: spy
-							}
-						}
-					}
-				],
-			} ).render();
-
-			sinon.assert.calledWithExactly( spy, el.firstChild, 'bar', null );
-		} );
-
-		it( 'activates listener attachers – DOM selectors', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-			const spy3 = testUtils.sinon.spy();
-			const spy4 = testUtils.sinon.spy();
-
-			const el = new Template( {
-				tag: 'p',
-				children: [
-					{
-						tag: 'span',
-						attributes: {
-							'id': 'x'
-						}
-					},
-					{
-						tag: 'span',
-						attributes: {
-							'class': 'y'
-						},
-						on: {
-							_listenerAttachers: {
-								'bar@p': spy2
-							}
-						}
-					},
-				],
-				on: {
-					_listenerAttachers: {
-						'foo@span': spy1,
-						'baz@.y': [ spy3, spy4 ]
-					}
-				}
-			} ).render();
-
-			sinon.assert.calledWithExactly( spy1, el, 'foo', 'span' );
-			sinon.assert.calledWithExactly( spy2, el.lastChild, 'bar', 'p' );
-			sinon.assert.calledWithExactly( spy3, el, 'baz', '.y' );
-			sinon.assert.calledWithExactly( spy4, el, 'baz', '.y' );
-		} );
-
-		it( 'activates model bindings – attributes', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-
-			const el = new Template( {
-				tag: 'p',
-				attributes: {
-					'class': {}
-				},
-				children: [
-					{
-						tag: 'span',
-						attributes: {
-							id: {}
-						},
-						_modelBinders: {
-							attributes: {
-								id: spy2
-							}
-						}
-					}
-				],
-				_modelBinders: {
-					attributes: {
-						class: spy1
-					}
-				}
-			} ).render();
-
-			sinon.assert.calledWithExactly( spy1, el, sinon.match.object );
-			sinon.assert.calledWithExactly( spy2, el.firstChild, sinon.match.object );
-		} );
-
-		it( 'activates model bindings – Text Node', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-
-			const el = new Template( {
-				tag: 'p',
-				children: [
-					{
-						text: {},
-						_modelBinders: {
-							text: spy1
-						}
-					},
-					{
-						tag: 'span',
-						children: [
-							{
-								text: {},
-								_modelBinders: {
-									text: spy2
-								}
-							}
-						]
-					}
-				]
-			} ).render();
-
-			sinon.assert.calledWithExactly( spy1, el.firstChild, sinon.match.object );
-			sinon.assert.calledWithExactly( spy2, el.lastChild.firstChild, sinon.match.object );
-		} );
-
-		it( 'uses DOM updater – attributes', () => {
-			const spy = testUtils.sinon.spy();
-			const el = new Template( {
-				tag: 'p',
-				attributes: {
-					'class': {}
-				},
-				_modelBinders: {
-					attributes: {
-						class: spy
-					}
-				}
-			} ).render();
-
-			// Check whether DOM updater is correct.
-			spy.firstCall.args[ 1 ].set( 'x' );
-			expect( el.outerHTML ).to.be.equal( '<p class="x"></p>' );
-
-			spy.firstCall.args[ 1 ].remove();
-			expect( el.outerHTML ).to.be.equal( '<p></p>' );
-		} );
-
-		it( 'uses DOM updater – text', () => {
-			const spy = testUtils.sinon.spy();
-			const el = new Template( {
-				tag: 'p',
-				children: [
-					{
-						text: {},
-						_modelBinders: {
-							text: spy
-						}
-					}
-				],
-			} ).render();
-
-			// Check whether DOM updater is correct.
-			spy.firstCall.args[ 1 ].set( 'x' );
-			expect( el.outerHTML ).to.be.equal( '<p>x</p>' );
-
-			spy.firstCall.args[ 1 ].remove();
-			expect( el.outerHTML ).to.be.equal( '<p></p>' );
-		} );
-	} );
-
-	describe( 'apply', () => {
-		beforeEach( () => {
-			el = document.createElement( 'div' );
-			text = document.createTextNode( '' );
-		} );
-
-		it( 'throws when wrong template definition', () => {
-			expect( () => {
-				new Template( {
-					tag: 'p',
-					text: 'foo'
-				} ).apply( el );
-			} ).to.throw( CKEditorError, /ui-template-wrong-syntax/ );
-		} );
-
-		it( 'throws when no HTMLElement passed', () => {
-			expect( () => {
-				new Template( {
-					tag: 'p'
-				} ).apply();
-			} ).to.throw( CKEditorError, /ui-template-wrong-node/ );
-		} );
-
-		it( 'accepts empty template definition', () => {
-			new Template( {} ).apply( el );
-			new Template( {} ).apply( text );
-
-			expect( el.outerHTML ).to.be.equal( '<div></div>' );
-			expect( text.textContent ).to.be.equal( '' );
-		} );
-
-		it( 'applies textContent to a Text Node', () => {
-			new Template( {
-				text: 'abc'
-			} ).apply( text );
-
-			expect( text.textContent ).to.be.equal( 'abc' );
-		} );
-
-		it( 'applies attributes to an HTMLElement', () => {
-			new Template( {
-				tag: 'div',
-				attributes: {
-					'class': [ 'a', 'b' ],
-					x: 'bar'
-				}
-			} ).apply( el );
-
-			expect( el.outerHTML ).to.be.equal( '<div class="a b" x="bar"></div>' );
-		} );
-
-		it( 'applies doesn\'t apply new child to an HTMLElement – Text Node', () => {
-			new Template( {
-				tag: 'div',
-				children: [ 'foo' ]
-			} ).apply( el );
-
-			expect( el.outerHTML ).to.be.equal( '<div></div>' );
-		} );
-
-		it( 'applies doesn\'t apply new child to an HTMLElement – HTMLElement', () => {
-			new Template( {
-				tag: 'div',
-				children: [
-					{
-						tag: 'span'
-					}
-				]
-			} ).apply( el );
-
-			expect( el.outerHTML ).to.be.equal( '<div></div>' );
-		} );
-
-		it( 'applies new textContent to an existing Text Node of an HTMLElement', () => {
-			el.textContent = 'bar';
-
-			new Template( {
-				tag: 'div',
-				children: [ 'foo' ]
-			} ).apply( el );
-
-			expect( el.outerHTML ).to.be.equal( '<div>foo</div>' );
-		} );
-
-		it( 'applies attributes and TextContent to a DOM tree', () => {
-			el.textContent = 'abc';
-			el.appendChild( document.createElement( 'span' ) );
-
-			new Template( {
-				tag: 'div',
-				attributes: {
-					'class': 'parent'
-				},
-				children: [
-					'Children: ',
-					{
-						tag: 'span',
-						attributes: {
-							class: 'child'
-						}
-					}
-				]
-			} ).apply( el );
-
-			expect( el.outerHTML ).to.be.equal( '<div class="parent">Children: <span class="child"></span></div>' );
-		} );
-
-		it( 'activates listener attachers – root', () => {
-			const spy = testUtils.sinon.spy();
-
-			new Template( {
-				tag: 'div',
-				on: {
-					_listenerAttachers: {
-						click: spy
-					}
-				}
-			} ).apply( el );
-
-			sinon.assert.calledWithExactly( spy, el, 'click', null );
-		} );
-
-		it( 'activates listener attachers – children', () => {
-			const spy = testUtils.sinon.spy();
-			el.appendChild( document.createElement( 'span' ) );
-
-			new Template( {
-				tag: 'div',
-				children: [
-					{
-						tag: 'span',
-						on: {
-							_listenerAttachers: {
-								click: spy
-							}
-						}
-					}
-				]
-			} ).apply( el );
-
-			sinon.assert.calledWithExactly( spy, el.firstChild, 'click', null );
-		} );
-
-		it( 'activates model bindings – root', () => {
-			const spy = testUtils.sinon.spy();
-
-			new Template( {
-				tag: 'div',
-				attributes: {
-					class: {}
-				},
-				_modelBinders: {
-					attributes: {
-						class: spy
-					}
-				}
-			} ).apply( el );
-
-			sinon.assert.calledWithExactly( spy, el, sinon.match.object );
-		} );
-
-		it( 'activates model bindings – children', () => {
-			const spy = testUtils.sinon.spy();
-			el.appendChild( document.createElement( 'span' ) );
-
-			new Template( {
-				tag: 'div',
-				children: [
-					{
-						tag: 'span',
-						attributes: {
-							class: {}
-						},
-						_modelBinders: {
-							attributes: {
-								class: spy
-							}
-						}
-					}
-				]
-			} ).apply( el );
-
-			sinon.assert.calledWithExactly( spy, el.firstChild, sinon.match.object );
-		} );
-	} );
-} );

+ 0 - 1187
packages/ckeditor5-engine/tests/ui/view.js

@@ -1,1187 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* global document, HTMLElement */
-/* bender-tags: ui */
-
-'use strict';
-
-import testUtils from '/tests/_utils/utils.js';
-import View from '/ckeditor5/core/ui/view.js';
-import Region from '/ckeditor5/core/ui/region.js';
-import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
-import Model from '/ckeditor5/core/ui/model.js';
-
-let TestView, view;
-
-testUtils.createSinonSandbox();
-
-describe( 'View', () => {
-	describe( 'constructor', () => {
-		beforeEach( () => {
-			setTestViewClass();
-			setTestViewInstance();
-		} );
-
-		it( 'accepts the model', () => {
-			setTestViewInstance( { a: 'foo', b: 42 } );
-
-			expect( view.model ).to.be.an.instanceof( Model );
-			expect( view ).to.have.deep.property( 'model.a', 'foo' );
-			expect( view ).to.have.deep.property( 'model.b', 42 );
-		} );
-
-		it( 'defines basic view properties', () => {
-			const model = new Model();
-
-			view = new View( model );
-
-			expect( view.model ).to.equal( model );
-			expect( view.t ).to.be.undefined;
-			expect( view.regions.length ).to.equal( 0 );
-		} );
-
-		it( 'defines the locale property and the t function', () => {
-			const model = new Model();
-			const locale = { t() {} };
-
-			view = new View( model, locale );
-
-			expect( view.locale ).to.equal( locale );
-			expect( view.t ).to.equal( locale.t );
-		} );
-	} );
-
-	describe( 'init', () => {
-		beforeEach( () => {
-			setTestViewClass( () => ( {
-				tag: 'p',
-				children: [
-					{ tag: 'span' },
-					{ tag: 'strong' }
-				]
-			} ) );
-		} );
-
-		it( 'calls child regions #init', () => {
-			setTestViewInstance();
-
-			const region1 = new Region( 'x' );
-			const region2 = new Region( 'y' );
-
-			view.register( region1, el => el );
-			view.register( region2, el => el );
-
-			const spy1 = testUtils.sinon.spy( region1, 'init' );
-			const spy2 = testUtils.sinon.spy( region2, 'init' );
-
-			view.init();
-
-			sinon.assert.calledOnce( spy1 );
-			sinon.assert.calledOnce( spy2 );
-		} );
-
-		it( 'initializes view regions with string selector', () => {
-			setTestViewInstance();
-
-			const region1 = new Region( 'x' );
-			const region2 = new Region( 'y' );
-
-			view.register( region1, 'span' );
-			view.register( region2, 'strong' );
-
-			view.init();
-
-			expect( region1.element ).to.equal( view.element.firstChild );
-			expect( region2.element ).to.equal( view.element.lastChild );
-		} );
-
-		it( 'initializes view regions with function selector', () => {
-			setTestViewInstance();
-
-			const region1 = new Region( 'x' );
-			const region2 = new Region( 'y' );
-
-			view.register( region1, el => el.firstChild );
-			view.register( region2, el => el.lastChild );
-
-			view.init();
-
-			expect( region1.element ).to.equal( view.element.firstChild );
-			expect( region2.element ).to.equal( view.element.lastChild );
-		} );
-
-		it( 'initializes view regions with boolean selector', () => {
-			setTestViewInstance();
-
-			const region1 = new Region( 'x' );
-			const region2 = new Region( 'y' );
-
-			view.register( region1, true );
-			view.register( region2, true );
-
-			view.init();
-
-			expect( region1.element ).to.be.null;
-			expect( region2.element ).to.be.null;
-		} );
-	} );
-
-	describe( 'register', () => {
-		beforeEach( () => {
-			setTestViewClass();
-			setTestViewInstance();
-		} );
-
-		it( 'should throw when first argument is neither Region instance nor string', () => {
-			expect( () => {
-				view.register( new Date() );
-			} ).to.throw( CKEditorError, /ui-view-register-wrongtype/ );
-		} );
-
-		it( 'should throw when missing the selector argument', () => {
-			expect( () => {
-				view.register( 'x' );
-			} ).to.throw( CKEditorError, /ui-view-register-badselector/ );
-		} );
-
-		it( 'should throw when selector argument is of a wrong type', () => {
-			expect( () => {
-				view.register( 'x', new Date() );
-			} ).to.throw( CKEditorError, /ui-view-register-badselector/ );
-
-			expect( () => {
-				view.register( 'x', false );
-			} ).to.throw( CKEditorError, /ui-view-register-badselector/ );
-		} );
-
-		it( 'should throw when overriding an existing region but without override flag set', () => {
-			expect( () => {
-				view.register( 'x', true );
-				view.register( new Region( 'x' ), true );
-			} ).to.throw( CKEditorError, /ui-view-register-override/ );
-		} );
-
-		it( 'should register a new region with region name as a first argument', () => {
-			view.register( 'x', true );
-
-			expect( view.regions.get( 'x' ) ).to.be.an.instanceof( Region );
-		} );
-
-		it( 'should register a new region with Region instance as a first argument', () => {
-			view.register( new Region( 'y' ), true );
-
-			expect( view.regions.get( 'y' ) ).to.be.an.instanceof( Region );
-		} );
-
-		it( 'should override an existing region with override flag', () => {
-			view.template = {
-				tag: 'div',
-				children: [
-					{ tag: 'span' }
-				]
-			};
-
-			const region1 = new Region( 'x' );
-			const region2 = new Region( 'x' );
-
-			view.register( region1, true );
-			view.register( region2, true, true );
-			view.register( 'x', 'span', true );
-
-			view.init();
-
-			expect( view.regions.get( 'x' ) ).to.equal( region2 );
-			expect( view.regions.get( 'x' ).element ).to.equal( view.element.firstChild );
-		} );
-
-		it( 'should not override an existing region with the same region with override flag', () => {
-			const region = new Region( 'x' );
-			const spy = testUtils.sinon.spy( view.regions, 'remove' );
-
-			view.register( region, true );
-			view.register( region, true, true );
-
-			sinon.assert.notCalled( spy );
-		} );
-	} );
-
-	describe( 'el', () => {
-		beforeEach( createViewInstanceWithTemplate );
-
-		it( 'invokes out of #template', () => {
-			setTestViewInstance( { a: 1 } );
-
-			expect( view.element ).to.be.an.instanceof( HTMLElement );
-			expect( view.element.nodeName ).to.equal( 'A' );
-		} );
-
-		it( 'can be explicitly declared', () => {
-			class CustomView extends View {
-				constructor() {
-					super();
-
-					this.element = document.createElement( 'span' );
-				}
-			}
-
-			view = new CustomView();
-
-			expect( view.element ).to.be.an.instanceof( HTMLElement );
-		} );
-	} );
-
-	describe( 'attributeBinder', () => {
-		it( 'provides "to" and "if" interface', () => {
-			const bind = view.attributeBinder;
-
-			expect( bind ).to.have.keys( 'to', 'if' );
-			expect( typeof bind.to ).to.equal( 'function' );
-			expect( typeof bind.if ).to.equal( 'function' );
-		} );
-
-		describe( 'to', () => {
-			it( 'returns an object which describes the binding', () => {
-				setTestViewInstance( { a: 1 } );
-
-				const spy = testUtils.sinon.spy();
-				const bind = view.attributeBinder;
-				const binding = bind.to( 'a', spy );
-
-				expect( spy.called ).to.be.false;
-				expect( binding ).to.have.keys( [ 'type', 'model', 'attribute', 'callback' ] );
-				expect( binding.model ).to.equal( view.model );
-				expect( binding.callback ).to.equal( spy );
-				expect( binding.attribute ).to.equal( 'a' );
-			} );
-
-			it( 'allows binding attribute to the model – simple (HTMLElement attribute)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.to( 'foo' )
-						},
-						children: [ 'abc' ]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.equal( '<p class="bar">abc</p>' );
-
-				view.model.foo = 'baz';
-				expect( view.element.outerHTML ).to.equal( '<p class="baz">abc</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – simple (Text Node)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						children: [
-							{
-								text: bind.to( 'foo' )
-							}
-						]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.equal( '<p>bar</p>' );
-
-				view.model.foo = 'baz';
-				expect( view.element.outerHTML ).to.equal( '<p>baz</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – value processing', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-					const callback = value => value > 0 ? 'positive' : 'negative';
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.to( 'foo', callback )
-						},
-						children: [
-							{
-								text: bind.to( 'foo', callback )
-							}
-						]
-					};
-				} );
-
-				setTestViewInstance( { foo: 3 } );
-				expect( view.element.outerHTML ).to.equal( '<p class="positive">positive</p>' );
-
-				view.model.foo = -7;
-				expect( view.element.outerHTML ).to.equal( '<p class="negative">negative</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – value processing (use Node)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-					const callback = ( value, node ) => {
-						return ( !!node.tagName && value > 0 ) ? 'HTMLElement positive' : '';
-					};
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.to( 'foo', callback )
-						},
-						children: [
-							{
-								text: bind.to( 'foo', callback )
-							}
-						]
-					};
-				} );
-
-				setTestViewInstance( { foo: 3 } );
-				expect( view.element.outerHTML ).to.equal( '<p class="HTMLElement positive"></p>' );
-
-				view.model.foo = -7;
-				expect( view.element.outerHTML ).to.equal( '<p></p>' );
-			} );
-
-			it( 'allows binding attribute to the model – custom callback', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.to( 'foo', ( value, el ) => {
-								el.innerHTML = value;
-
-								if ( value == 'changed' ) {
-									return value;
-								}
-							} )
-						}
-					};
-				} );
-
-				setTestViewInstance( { foo: 'moo' } );
-				expect( view.element.outerHTML ).to.equal( '<p class="undefined">moo</p>' );
-
-				view.model.foo = 'changed';
-				expect( view.element.outerHTML ).to.equal( '<p class="changed">changed</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – array of bindings (HTMLElement attribute)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': [
-								'ck-class',
-								bind.to( 'foo' ),
-								bind.to( 'bar' ),
-								bind.to( 'foo', value => `foo-is-${value}` ),
-								'ck-end'
-							]
-						},
-						children: [ 'abc' ]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'a', bar: 'b' } );
-				expect( view.element.outerHTML ).to.equal( '<p class="ck-class a b foo-is-a ck-end">abc</p>' );
-
-				view.model.foo = 'c';
-				view.model.bar = 'd';
-				expect( view.element.outerHTML ).to.equal( '<p class="ck-class c d foo-is-c ck-end">abc</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – array of bindings (Text Node)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-						},
-						children: [
-							{
-								text: [
-									'ck-class',
-									bind.to( 'foo' ),
-									bind.to( 'bar' ),
-									bind.to( 'foo', value => `foo-is-${value}` ),
-									'ck-end'
-								]
-							}
-						]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'a', bar: 'b' } );
-				expect( view.element.outerHTML ).to.equal( '<p>ck-class a b foo-is-a ck-end</p>' );
-
-				view.model.foo = 'c';
-				view.model.bar = 'd';
-				expect( view.element.outerHTML ).to.equal( '<p>ck-class c d foo-is-c ck-end</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – falsy values', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.to( 'foo' )
-						},
-						children: [ 'abc' ]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.equal( '<p class="bar">abc</p>' );
-
-				view.model.foo = false;
-				expect( view.element.outerHTML ).to.equal( '<p class="false">abc</p>' );
-
-				view.model.foo = null;
-				expect( view.element.outerHTML ).to.equal( '<p class="null">abc</p>' );
-
-				view.model.foo = undefined;
-				expect( view.element.outerHTML ).to.equal( '<p class="undefined">abc</p>' );
-
-				view.model.foo = 0;
-				expect( view.element.outerHTML ).to.equal( '<p class="0">abc</p>' );
-
-				view.model.foo = '';
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-			} );
-		} );
-
-		describe( 'if', () => {
-			it( 'returns an object which describes the binding', () => {
-				setTestViewInstance( { a: 1 } );
-
-				const spy = testUtils.sinon.spy();
-				const bind = view.attributeBinder;
-				const binding = bind.if( 'a', 'foo', spy );
-
-				expect( spy.called ).to.be.false;
-				expect( binding ).to.have.keys( [ 'type', 'model', 'attribute', 'callback', 'valueIfTrue' ] );
-				expect( binding.model ).to.equal( view.model );
-				expect( binding.callback ).to.equal( spy );
-				expect( binding.attribute ).to.equal( 'a' );
-				expect( binding.valueIfTrue ).to.equal( 'foo' );
-			} );
-
-			it( 'allows binding attribute to the model – presence of an attribute (HTMLElement attribute)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.if( 'foo' )
-						},
-						children: [ 'abc' ]
-					};
-				} );
-
-				setTestViewInstance( { foo: true } );
-				expect( view.element.outerHTML ).to.equal( '<p class="">abc</p>' );
-
-				view.model.foo = false;
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-
-				view.model.foo = 'bar';
-				expect( view.element.outerHTML ).to.equal( '<p class="">abc</p>' );
-			} );
-
-			// TODO: Is this alright? It makes sense but it's pretty useless. Text Node cannot be
-			// removed just like an attribute of some HTMLElement.
-			it( 'allows binding attribute to the model – presence of an attribute (Text Node)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						children: [
-							{
-								text: bind.if( 'foo' )
-							}
-						]
-					};
-				} );
-
-				setTestViewInstance( { foo: true } );
-				expect( view.element.outerHTML ).to.equal( '<p></p>' );
-
-				view.model.foo = false;
-				expect( view.element.outerHTML ).to.equal( '<p></p>' );
-
-				view.model.foo = 'bar';
-				expect( view.element.outerHTML ).to.equal( '<p></p>' );
-			} );
-
-			it( 'allows binding attribute to the model – value of an attribute (HTMLElement attribute)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.if( 'foo', 'bar' )
-						},
-						children: [ 'abc' ]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.equal( '<p class="bar">abc</p>' );
-
-				view.model.foo = false;
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-
-				view.model.foo = 64;
-				expect( view.element.outerHTML ).to.equal( '<p class="bar">abc</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – value of an attribute (Text Node)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						children: [
-							{
-								text: bind.if( 'foo', 'bar' )
-							}
-						]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.equal( '<p>bar</p>' );
-
-				view.model.foo = false;
-				expect( view.element.outerHTML ).to.equal( '<p></p>' );
-
-				view.model.foo = 64;
-				expect( view.element.outerHTML ).to.equal( '<p>bar</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – value of an attribute processed by a callback', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.if( 'foo', 'there–is–no–foo', value => !value )
-						},
-						children: [ 'abc' ]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-
-				view.model.foo = false;
-				expect( view.element.outerHTML ).to.equal( '<p class="there–is–no–foo">abc</p>' );
-
-				view.model.foo = 64;
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – value of an attribute processed by a callback (use Node)', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.if( 'foo', 'eqls-tag-name', ( value, el ) => el.tagName === value )
-						},
-						children: [ 'abc' ]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-
-				view.model.foo = 'P';
-				expect( view.element.outerHTML ).to.equal( '<p class="eqls-tag-name">abc</p>' );
-
-				view.model.foo = 64;
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-			} );
-
-			it( 'allows binding attribute to the model – falsy values', () => {
-				setTestViewClass( function() {
-					const bind = this.attributeBinder;
-
-					return {
-						tag: 'p',
-						attributes: {
-							'class': bind.if( 'foo', 'foo-is-set' )
-						},
-						children: [ 'abc' ]
-					};
-				} );
-
-				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.equal( '<p class="foo-is-set">abc</p>' );
-
-				view.model.foo = false;
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-
-				view.model.foo = null;
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-
-				view.model.foo = undefined;
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-
-				view.model.foo = '';
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-
-				view.model.foo = 0;
-				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
-			} );
-		} );
-	} );
-
-	describe( 'on', () => {
-		it( 'accepts plain binding', () => {
-			const spy = testUtils.sinon.spy();
-
-			setTestViewClass( function() {
-				return {
-					tag: 'p',
-					on: {
-						x: 'a',
-					}
-				};
-			} );
-
-			setTestViewInstance();
-
-			view.on( 'a', spy );
-
-			dispatchEvent( view.element, 'x' );
-			sinon.assert.calledWithExactly( spy,
-				sinon.match.has( 'name', 'a' ),
-				sinon.match.has( 'target', view.element )
-			);
-		} );
-
-		it( 'accepts an array of event bindings', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-
-			setTestViewClass( function() {
-				return {
-					tag: 'p',
-					on: {
-						x: [ 'a', 'b' ]
-					}
-				};
-			} );
-
-			setTestViewInstance();
-
-			view.on( 'a', spy1 );
-			view.on( 'b', spy2 );
-
-			dispatchEvent( view.element, 'x' );
-			sinon.assert.calledWithExactly( spy1,
-				sinon.match.has( 'name', 'a' ),
-				sinon.match.has( 'target', view.element )
-			);
-			sinon.assert.calledWithExactly( spy2,
-				sinon.match.has( 'name', 'b' ),
-				sinon.match.has( 'target', view.element )
-			);
-		} );
-
-		it( 'accepts DOM selectors', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-			const spy3 = testUtils.sinon.spy();
-
-			setTestViewClass( function() {
-				return {
-					tag: 'p',
-					children: [
-						{
-							tag: 'span',
-							attributes: {
-								'class': 'y',
-							},
-							on: {
-								'test@p': 'c'
-							}
-						},
-						{
-							tag: 'div',
-							children: [
-								{
-									tag: 'span',
-									attributes: {
-										'class': 'y',
-									}
-								}
-							],
-						}
-					],
-					on: {
-						'test@.y': 'a',
-						'test@div': 'b'
-					}
-				};
-			} );
-
-			setTestViewInstance();
-
-			view.on( 'a', spy1 );
-			view.on( 'b', spy2 );
-			view.on( 'c', spy3 );
-
-			// Test "test@p".
-			dispatchEvent( view.element, 'test' );
-
-			sinon.assert.callCount( spy1, 0 );
-			sinon.assert.callCount( spy2, 0 );
-			sinon.assert.callCount( spy3, 0 );
-
-			// Test "test@.y".
-			dispatchEvent( view.element.firstChild, 'test' );
-
-			expect( spy1.firstCall.calledWithExactly(
-				sinon.match.has( 'name', 'a' ),
-				sinon.match.has( 'target', view.element.firstChild )
-			) ).to.be.true;
-
-			sinon.assert.callCount( spy2, 0 );
-			sinon.assert.callCount( spy3, 0 );
-
-			// Test "test@div".
-			dispatchEvent( view.element.lastChild, 'test' );
-
-			sinon.assert.callCount( spy1, 1 );
-
-			expect( spy2.firstCall.calledWithExactly(
-				sinon.match.has( 'name', 'b' ),
-				sinon.match.has( 'target', view.element.lastChild )
-			) ).to.be.true;
-
-			sinon.assert.callCount( spy3, 0 );
-
-			// Test "test@.y".
-			dispatchEvent( view.element.lastChild.firstChild, 'test' );
-
-			expect( spy1.secondCall.calledWithExactly(
-				sinon.match.has( 'name', 'a' ),
-				sinon.match.has( 'target', view.element.lastChild.firstChild )
-			) ).to.be.true;
-
-			sinon.assert.callCount( spy2, 1 );
-			sinon.assert.callCount( spy3, 0 );
-		} );
-
-		it( 'accepts function callbacks', () => {
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-
-			setTestViewClass( function() {
-				return {
-					tag: 'p',
-					children: [
-						{
-							tag: 'span'
-						}
-					],
-					on: {
-						x: spy1,
-						'y@span': [ spy2, 'c' ],
-					}
-				};
-			} );
-
-			setTestViewInstance();
-
-			dispatchEvent( view.element, 'x' );
-			dispatchEvent( view.element.firstChild, 'y' );
-
-			sinon.assert.calledWithExactly( spy1,
-				sinon.match.has( 'target', view.element )
-			);
-
-			sinon.assert.calledWithExactly( spy2,
-				sinon.match.has( 'target', view.element.firstChild )
-			);
-		} );
-
-		it( 'supports event delegation', () => {
-			const spy = testUtils.sinon.spy();
-
-			setTestViewClass( function() {
-				return {
-					tag: 'p',
-					children: [
-						{
-							tag: 'span'
-						}
-					],
-					on: {
-						x: 'a',
-					}
-				};
-			} );
-
-			setTestViewInstance();
-
-			view.on( 'a', spy );
-
-			dispatchEvent( view.element.firstChild, 'x' );
-			sinon.assert.calledWithExactly( spy,
-				sinon.match.has( 'name', 'a' ),
-				sinon.match.has( 'target', view.element.firstChild )
-			);
-		} );
-
-		it( 'works for future elements', () => {
-			const spy = testUtils.sinon.spy();
-
-			setTestViewClass( function() {
-				return {
-					tag: 'p',
-					on: {
-						'test@div': 'a'
-					}
-				};
-			} );
-
-			setTestViewInstance();
-
-			view.on( 'a', spy );
-
-			const div = document.createElement( 'div' );
-			view.element.appendChild( div );
-
-			dispatchEvent( div, 'test' );
-			sinon.assert.calledWithExactly( spy, sinon.match.has( 'name', 'a' ), sinon.match.has( 'target', div ) );
-		} );
-	} );
-
-	describe( 'destroy', () => {
-		beforeEach( createViewInstanceWithTemplate );
-
-		it( 'should destroy the view', () => {
-			view.destroy();
-
-			expect( view.model ).to.be.null;
-			expect( view.regions ).to.be.null;
-			expect( view.template ).to.be.null;
-			expect( view.locale ).to.be.null;
-			expect( view.t ).to.be.null;
-		} );
-
-		it( 'detaches the element from DOM', () => {
-			const elRef = view.element;
-
-			document.createElement( 'div' ).appendChild( view.element );
-
-			view.destroy();
-
-			expect( elRef.parentNode ).to.be.null;
-		} );
-
-		it( 'destroys child regions', () => {
-			const region = new Region( 'x' );
-			const spy = testUtils.sinon.spy( region, 'destroy' );
-			const regionsRef = view.regions;
-			const regionViewsRef = region.views;
-
-			view.register( region, true );
-			view.regions.get( 'x' ).views.add( new View() );
-			view.destroy();
-
-			expect( regionsRef.length ).to.equal( 0 );
-			expect( regionViewsRef.length ).to.equal( 0 );
-			expect( spy.calledOnce ).to.be.true;
-		} );
-
-		it( 'detaches bound model listeners', () => {
-			setTestViewClass( function() {
-				const bind = this.attributeBinder;
-
-				return {
-					tag: 'p',
-					children: [
-						{ text: bind.to( 'foo' ) }
-					]
-				};
-			} );
-
-			setTestViewInstance( { foo: 'bar' } );
-
-			const modelRef = view.model;
-			const elRef = view.element;
-
-			expect( view.element.outerHTML ).to.equal( '<p>bar</p>' );
-
-			modelRef.foo = 'baz';
-			expect( view.element.outerHTML ).to.equal( '<p>baz</p>' );
-
-			view.destroy();
-
-			modelRef.foo = 'abc';
-			expect( elRef.outerHTML ).to.equal( '<p>baz</p>' );
-		} );
-
-		it( 'destroy a template–less view', () => {
-			view = new View();
-
-			expect( () => {
-				view.destroy();
-			} ).to.not.throw();
-		} );
-	} );
-
-	describe( 'applyTemplateToElement', () => {
-		beforeEach( () => {
-			setTestViewClass();
-			setTestViewInstance( { a: 'foo', b: 42 } );
-		} );
-
-		it( 'should initialize attribute bindings', () => {
-			const el = document.createElement( 'div' );
-			const bind = view.attributeBinder;
-
-			view.applyTemplateToElement( el, {
-				tag: 'div',
-				attributes: {
-					class: bind.to( 'b' ),
-					id: 'foo',
-					checked: 'checked'
-				}
-			} );
-
-			expect( el.outerHTML ).to.equal( '<div class="42" id="foo" checked="checked"></div>' );
-
-			view.model.b = 64;
-
-			expect( el.outerHTML ).to.equal( '<div class="64" id="foo" checked="checked"></div>' );
-		} );
-
-		it( 'should initialize DOM listeners', () => {
-			const el = document.createElement( 'div' );
-			const spy = testUtils.sinon.spy();
-
-			view.applyTemplateToElement( el, {
-				tag: 'div',
-				on: {
-					click: spy
-				}
-			} );
-
-			document.body.appendChild( el );
-
-			dispatchEvent( el, 'click' );
-
-			sinon.assert.calledOnce( spy );
-
-			view.stopListening( el, 'click' );
-			dispatchEvent( el, 'click' );
-
-			sinon.assert.calledOnce( spy );
-		} );
-
-		it( 'should work for deep DOM structure', () => {
-			const el = document.createElement( 'div' );
-			const childA = document.createElement( 'a' );
-			const childB = document.createElement( 'b' );
-
-			childA.textContent = 'anchor';
-			childB.textContent = 'bold';
-
-			el.appendChild( childA );
-			el.appendChild( childB );
-
-			expect( el.outerHTML ).to.equal( '<div><a>anchor</a><b>bold</b></div>' );
-
-			const spy1 = testUtils.sinon.spy();
-			const spy2 = testUtils.sinon.spy();
-			const spy3 = testUtils.sinon.spy();
-			const bind = view.attributeBinder;
-
-			view.applyTemplateToElement( el, {
-				tag: 'div',
-				children: [
-					{
-						tag: 'a',
-						on: {
-							keyup: spy2
-						},
-						attributes: {
-							class: bind.to( 'b', b => 'applied-A-' + b ),
-							id: 'applied-A'
-						},
-						children: [ 'Text applied to childA.' ]
-					},
-					{
-						tag: 'b',
-						on: {
-							keydown: spy3
-						},
-						attributes: {
-							class: bind.to( 'b', b => 'applied-B-' + b ),
-							id: 'applied-B'
-						},
-						children: [ 'Text applied to childB.' ]
-					},
-					'Text which is not to be applied because it does NOT exist in original element.'
-				],
-				on: {
-					'mouseover@a': spy1
-				},
-				attributes: {
-					id: bind.to( 'a', a => a.toUpperCase() ),
-					class: bind.to( 'b', b => 'applied-parent-' + b )
-				}
-			} );
-
-			expect( el.outerHTML ).to.equal( '<div id="FOO" class="applied-parent-42">' +
-				'<a class="applied-A-42" id="applied-A">Text applied to childA.</a>' +
-				'<b class="applied-B-42" id="applied-B">Text applied to childB.</b>' +
-			'</div>' );
-
-			view.model.b = 16;
-
-			expect( el.outerHTML ).to.equal( '<div id="FOO" class="applied-parent-16">' +
-				'<a class="applied-A-16" id="applied-A">Text applied to childA.</a>' +
-				'<b class="applied-B-16" id="applied-B">Text applied to childB.</b>' +
-			'</div>' );
-
-			document.body.appendChild( el );
-
-			// Test "mouseover@a".
-			dispatchEvent( el, 'mouseover' );
-			dispatchEvent( childA, 'mouseover' );
-
-			// Test "keyup".
-			dispatchEvent( childA, 'keyup' );
-
-			// Test "keydown".
-			dispatchEvent( childB, 'keydown' );
-
-			sinon.assert.calledOnce( spy1 );
-			sinon.assert.calledOnce( spy2 );
-			sinon.assert.calledOnce( spy3 );
-		} );
-
-		it( 're–uses a template definition object without redundant re–definition of "on" listener attachers', () => {
-			const el1 = document.createElement( 'div' );
-			const el2 = document.createElement( 'div' );
-			const spy = testUtils.sinon.spy();
-			const def = {
-				tag: 'div',
-				on: {
-					click: spy
-				}
-			};
-
-			view.applyTemplateToElement( el1, def );
-			const attacherFn = def.on.click;
-			view.applyTemplateToElement( el2, def );
-
-			// Attacher function didn't change because it's still the same View instance.
-			expect( attacherFn ).to.equal( def.on.click );
-			expect( def.on._listenerView ).to.equal( view );
-
-			document.body.appendChild( el1 );
-			document.body.appendChild( el2 );
-
-			dispatchEvent( el1, 'click' );
-			sinon.assert.calledOnce( spy );
-
-			dispatchEvent( el2, 'click' );
-			sinon.assert.calledTwice( spy );
-		} );
-
-		it( 'shares a template definition between View instances – event listeners', () => {
-			const el = document.createElement( 'div' );
-			const spy = testUtils.sinon.spy();
-			const view1 = new View();
-			const view2 = new View();
-			const def = {
-				tag: 'div',
-				on: {
-					click: spy
-				}
-			};
-
-			document.body.appendChild( el );
-
-			view1.applyTemplateToElement( el, def );
-			expect( def.on._listenerView ).to.equal( view1 );
-
-			dispatchEvent( el, 'click' );
-			sinon.assert.calledOnce( spy );
-
-			// Use another view1 to see if attachers are re–defined.
-			view2.applyTemplateToElement( el, def );
-			expect( def.on._listenerView ).to.equal( view2 );
-
-			dispatchEvent( el, 'click' );
-			// Spy was called for view1 (1st dispatch), then for view1 and view2 (2nd dispatch).
-			sinon.assert.calledThrice( spy );
-		} );
-	} );
-} );
-
-function createViewInstanceWithTemplate() {
-	setTestViewClass( () => ( { tag: 'a' } ) );
-	setTestViewInstance();
-}
-
-function setTestViewClass( templateFn, regionsFn ) {
-	TestView = class V extends View {
-		constructor( model ) {
-			super( model );
-
-			if ( templateFn ) {
-				this.template = templateFn.call( this );
-			}
-
-			if ( templateFn && regionsFn ) {
-				regionsFn.call( this );
-			}
-		}
-	};
-}
-
-function setTestViewInstance( model ) {
-	view = new TestView( new Model( model ) );
-
-	if ( view.template ) {
-		document.body.appendChild( view.element );
-	}
-}
-
-function dispatchEvent( el, domEvtName ) {
-	if ( !el.parentNode ) {
-		throw new Error( 'To dispatch an event, element must be in DOM. Otherwise #target is null.' );
-	}
-
-	el.dispatchEvent( new Event( domEvtName, {
-		bubbles: true
-	} ) );
-}