浏览代码

Merge branch 'master' into t/55

Piotrek Koszuliński 10 年之前
父节点
当前提交
cc19d31144
共有 96 个文件被更改,包括 3393 次插入1964 次删除
  1. 1 0
      packages/ckeditor5-engine/.jshintrc
  2. 2 1
      packages/ckeditor5-engine/dev/.jshintrc
  3. 4 4
      packages/ckeditor5-engine/dev/tasks/jscs.js
  4. 4 4
      packages/ckeditor5-engine/dev/tasks/jshint.js
  5. 19 19
      packages/ckeditor5-engine/dev/tasks/utils/tools.js
  6. 3 3
      packages/ckeditor5-engine/gruntfile.js
  7. 11 13
      packages/ckeditor5-engine/src/ckeditor.js
  8. 1 1
      packages/ckeditor5-engine/src/ckeditorerror.js
  9. 170 55
      packages/ckeditor5-engine/src/collection.js
  10. 7 7
      packages/ckeditor5-engine/src/config.js
  11. 1 1
      packages/ckeditor5-engine/src/creator.js
  12. 9 9
      packages/ckeditor5-engine/src/document/attribute.js
  13. 1 1
      packages/ckeditor5-engine/src/document/character.js
  14. 155 0
      packages/ckeditor5-engine/src/document/delta/changedelta.js
  15. 58 0
      packages/ckeditor5-engine/src/document/delta/delta.js
  16. 44 0
      packages/ckeditor5-engine/src/document/delta/insertdelta.js
  17. 78 0
      packages/ckeditor5-engine/src/document/delta/mergedelta.js
  18. 14 0
      packages/ckeditor5-engine/src/document/delta/register.js
  19. 47 0
      packages/ckeditor5-engine/src/document/delta/removedelta.js
  20. 71 0
      packages/ckeditor5-engine/src/document/delta/splitdelta.js
  21. 124 0
      packages/ckeditor5-engine/src/document/delta/transaction-base.js
  22. 26 6
      packages/ckeditor5-engine/src/document/document.js
  23. 8 3
      packages/ckeditor5-engine/src/document/element.js
  24. 19 24
      packages/ckeditor5-engine/src/document/node.js
  25. 9 9
      packages/ckeditor5-engine/src/document/nodelist.js
  26. 4 4
      packages/ckeditor5-engine/src/document/operation/changeoperation.js
  27. 2 2
      packages/ckeditor5-engine/src/document/operation/insertoperation.js
  28. 9 9
      packages/ckeditor5-engine/src/document/operation/moveoperation.js
  29. 9 1
      packages/ckeditor5-engine/src/document/operation/operation.js
  30. 2 2
      packages/ckeditor5-engine/src/document/operation/reinsertoperation.js
  31. 4 4
      packages/ckeditor5-engine/src/document/operation/removeoperation.js
  32. 4 4
      packages/ckeditor5-engine/src/document/position.js
  33. 13 10
      packages/ckeditor5-engine/src/document/positioniterator.js
  34. 27 1
      packages/ckeditor5-engine/src/document/range.js
  35. 1 1
      packages/ckeditor5-engine/src/document/rootelement.js
  36. 1 1
      packages/ckeditor5-engine/src/document/text.js
  37. 21 0
      packages/ckeditor5-engine/src/document/transaction.js
  38. 10 10
      packages/ckeditor5-engine/src/editor.js
  39. 2 2
      packages/ckeditor5-engine/src/editorconfig.js
  40. 17 17
      packages/ckeditor5-engine/src/emittermixin.js
  41. 1 1
      packages/ckeditor5-engine/src/eventinfo.js
  42. 2 2
      packages/ckeditor5-engine/src/log.js
  43. 28 5
      packages/ckeditor5-engine/src/model.js
  44. 0 195
      packages/ckeditor5-engine/src/namedcollection.js
  45. 1 1
      packages/ckeditor5-engine/src/plugin.js
  46. 22 29
      packages/ckeditor5-engine/src/plugincollection.js
  47. 12 12
      packages/ckeditor5-engine/src/ui/domemittermixin.js
  48. 4 2
      packages/ckeditor5-engine/src/ui/region.js
  49. 8 8
      packages/ckeditor5-engine/src/ui/template.js
  50. 7 5
      packages/ckeditor5-engine/src/ui/view.js
  51. 3 3
      packages/ckeditor5-engine/src/utils-lodash.js
  52. 12 11
      packages/ckeditor5-engine/src/utils.js
  53. 1 0
      packages/ckeditor5-engine/tests/.jshintrc
  54. 22 4
      packages/ckeditor5-engine/tests/_tools/tools.js
  55. 22 15
      packages/ckeditor5-engine/tests/bender/tools.js
  56. 36 36
      packages/ckeditor5-engine/tests/ckeditor/ckeditor.js
  57. 19 19
      packages/ckeditor5-engine/tests/ckeditorerror/ckeditorerror.js
  58. 390 0
      packages/ckeditor5-engine/tests/collection/collection.js
  59. 38 38
      packages/ckeditor5-engine/tests/config/config.js
  60. 26 26
      packages/ckeditor5-engine/tests/document/attribute.js
  61. 18 14
      packages/ckeditor5-engine/tests/document/character.js
  62. 275 0
      packages/ckeditor5-engine/tests/document/deltas/changedelta.js
  63. 67 0
      packages/ckeditor5-engine/tests/document/deltas/delta.js
  64. 46 0
      packages/ckeditor5-engine/tests/document/deltas/insertdelta.js
  65. 80 0
      packages/ckeditor5-engine/tests/document/deltas/mergedelta.js
  66. 61 0
      packages/ckeditor5-engine/tests/document/deltas/removedelta.js
  67. 101 0
      packages/ckeditor5-engine/tests/document/deltas/splitdelta.js
  68. 38 27
      packages/ckeditor5-engine/tests/document/document.js
  69. 39 35
      packages/ckeditor5-engine/tests/document/element.js
  70. 92 69
      packages/ckeditor5-engine/tests/document/node.js
  71. 27 27
      packages/ckeditor5-engine/tests/document/nodelist.js
  72. 74 70
      packages/ckeditor5-engine/tests/document/operation/changeoperation.js
  73. 20 20
      packages/ckeditor5-engine/tests/document/operation/insertoperation.js
  74. 38 38
      packages/ckeditor5-engine/tests/document/operation/moveoperation.js
  75. 11 11
      packages/ckeditor5-engine/tests/document/operation/reinsertoperation.js
  76. 20 20
      packages/ckeditor5-engine/tests/document/operation/removeoperation.js
  77. 26 26
      packages/ckeditor5-engine/tests/document/position.js
  78. 28 28
      packages/ckeditor5-engine/tests/document/positioniterator.js
  79. 72 23
      packages/ckeditor5-engine/tests/document/range.js
  80. 13 9
      packages/ckeditor5-engine/tests/document/rootelement.js
  81. 8 8
      packages/ckeditor5-engine/tests/document/text.js
  82. 82 0
      packages/ckeditor5-engine/tests/document/transaction.js
  83. 41 41
      packages/ckeditor5-engine/tests/editor/editor.js
  84. 11 11
      packages/ckeditor5-engine/tests/editorconfig/editorconfig.js
  85. 94 94
      packages/ckeditor5-engine/tests/emittermixin/emittermixin.js
  86. 11 11
      packages/ckeditor5-engine/tests/eventinfo/eventinfo.js
  87. 13 13
      packages/ckeditor5-engine/tests/log/log.js
  88. 0 183
      packages/ckeditor5-engine/tests/mvc/collection/collection.js
  89. 0 254
      packages/ckeditor5-engine/tests/mvc/collection/namedcollection.js
  90. 55 26
      packages/ckeditor5-engine/tests/mvc/model/model.js
  91. 8 8
      packages/ckeditor5-engine/tests/plugin/plugin.js
  92. 91 91
      packages/ckeditor5-engine/tests/plugincollection/plugincollection.js
  93. 64 64
      packages/ckeditor5-engine/tests/ui/domemittermixin.js
  94. 20 20
      packages/ckeditor5-engine/tests/ui/region.js
  95. 32 32
      packages/ckeditor5-engine/tests/ui/template.js
  96. 51 51
      packages/ckeditor5-engine/tests/ui/view.js

+ 1 - 0
packages/ckeditor5-engine/.jshintrc

@@ -9,6 +9,7 @@
 	"undef": true,
 	"undef": true,
 	"unused": true,
 	"unused": true,
 	"strict": true,
 	"strict": true,
+	"varstmt": true,
 
 
 	"globalstrict": true,
 	"globalstrict": true,
 
 

+ 2 - 1
packages/ckeditor5-engine/dev/.jshintrc

@@ -8,5 +8,6 @@
 	"nonbsp": true,
 	"nonbsp": true,
 	"undef": true,
 	"undef": true,
 	"unused": true,
 	"unused": true,
-	"strict": true
+	"strict": true,
+	"varstmt": true
 }
 }

+ 4 - 4
packages/ckeditor5-engine/dev/tasks/jscs.js

@@ -1,8 +1,8 @@
 'use strict';
 'use strict';
 
 
-var tools = require( './utils/tools' );
+const tools = require( './utils/tools' );
 
 
-module.exports = function( grunt ) {
+module.exports = ( grunt ) => {
 	tools.setupMultitaskConfig( grunt, {
 	tools.setupMultitaskConfig( grunt, {
 		task: 'jscs',
 		task: 'jscs',
 		defaultOptions: {
 		defaultOptions: {
@@ -10,11 +10,11 @@ module.exports = function( grunt ) {
 			},
 			},
 		addGitIgnore: 'excludeFiles',
 		addGitIgnore: 'excludeFiles',
 		targets: {
 		targets: {
-			all: function() {
+			all() {
 				return [ '**/*.js' ];
 				return [ '**/*.js' ];
 			},
 			},
 
 
-			git: function() {
+			git() {
 				return tools.getGitDirtyFiles().filter( function( file ) {
 				return tools.getGitDirtyFiles().filter( function( file ) {
 					return ( /\.js$/ ).test( file );
 					return ( /\.js$/ ).test( file );
 				} );
 				} );

+ 4 - 4
packages/ckeditor5-engine/dev/tasks/jshint.js

@@ -1,8 +1,8 @@
 'use strict';
 'use strict';
 
 
-var tools = require( './utils/tools' );
+const tools = require( './utils/tools' );
 
 
-module.exports = function( grunt ) {
+module.exports = ( grunt ) => {
 	tools.setupMultitaskConfig( grunt, {
 	tools.setupMultitaskConfig( grunt, {
 		task: 'jshint',
 		task: 'jshint',
 		defaultOptions: {
 		defaultOptions: {
@@ -10,11 +10,11 @@ module.exports = function( grunt ) {
 			},
 			},
 		addGitIgnore: 'ignores',
 		addGitIgnore: 'ignores',
 		targets: {
 		targets: {
-			all: function() {
+			all() {
 				return [ '**/*.js' ];
 				return [ '**/*.js' ];
 			},
 			},
 
 
-			git: function() {
+			git() {
 				return tools.getGitDirtyFiles().filter( function( file ) {
 				return tools.getGitDirtyFiles().filter( function( file ) {
 					return ( /\.js$/ ).test( file );
 					return ( /\.js$/ ).test( file );
 				} );
 				} );

+ 19 - 19
packages/ckeditor5-engine/dev/tasks/utils/tools.js

@@ -1,6 +1,6 @@
 'use strict';
 'use strict';
 
 
-var dirtyFiles,
+let dirtyFiles,
 	ignoreList;
 	ignoreList;
 
 
 module.exports = {
 module.exports = {
@@ -11,15 +11,15 @@ module.exports = {
 	 * @param task {String} The task name. May optionally include the target (e.g. 'task:target').
 	 * @param task {String} The task name. May optionally include the target (e.g. 'task:target').
 	 * @returns {Boolean} "true" if the task is in the queue.
 	 * @returns {Boolean} "true" if the task is in the queue.
 	 */
 	 */
-	checkTaskInQueue: function( grunt, task ) {
-		var cliTasks = grunt.cli.tasks;
+	checkTaskInQueue( grunt, task ) {
+		const cliTasks = grunt.cli.tasks;
 
 
 		// Check if the task has been called directly.
 		// Check if the task has been called directly.
-		var isDirectCall = ( cliTasks.indexOf( task ) > -1 );
+		const isDirectCall = ( cliTasks.indexOf( task ) > -1 );
 		// Check if this is a "default" call and that the task is inside "default".
 		// Check if this is a "default" call and that the task is inside "default".
-		var isDefaultTask = ( cliTasks.indexOf( 'default' ) > -1 ) || !cliTasks.length;
+		const isDefaultTask = ( cliTasks.indexOf( 'default' ) > -1 ) || !cliTasks.length;
 		// Hacking Grunt hard.
 		// Hacking Grunt hard.
-		var isTaskInDefault = isDefaultTask && ( grunt.task._tasks.default.info.indexOf( '"' + task + '"' ) > -1 );
+		const isTaskInDefault = isDefaultTask && ( grunt.task._tasks.default.info.indexOf( '"' + task + '"' ) > -1 );
 
 
 		return isDirectCall || isTaskInDefault;
 		return isDirectCall || isTaskInDefault;
 	},
 	},
@@ -30,16 +30,16 @@ module.exports = {
 	 * @param grunt {Object} The Grunt object.
 	 * @param grunt {Object} The Grunt object.
 	 * @param options {Object} A list of options for the method. See the jscs and jshint tasks for example.
 	 * @param options {Object} A list of options for the method. See the jscs and jshint tasks for example.
 	 */
 	 */
-	setupMultitaskConfig: function( grunt, options ) {
-		var task = options.task;
-		var taskConfig = {};
-		var config = taskConfig[ task ] = {
+	setupMultitaskConfig( grunt, options ) {
+		const task = options.task;
+		const taskConfig = {};
+		const config = taskConfig[ task ] = {
 			options: options.defaultOptions
 			options: options.defaultOptions
 		};
 		};
 
 
 		// "all" is the default target to be used if others are not to be run.
 		// "all" is the default target to be used if others are not to be run.
-		var all = options.targets.all;
-		var isAll = true;
+		const all = options.targets.all;
+		let isAll = true;
 
 
 		delete options.targets.all;
 		delete options.targets.all;
 
 
@@ -56,8 +56,8 @@ module.exports = {
 
 
 		// Append .gitignore entries to the ignore list.
 		// Append .gitignore entries to the ignore list.
 		if ( options.addGitIgnore ) {
 		if ( options.addGitIgnore ) {
-			var ignoreProp = task + '.options.' + options.addGitIgnore;
-			var ignores = grunt.config.get( ignoreProp ) || [];
+			let ignoreProp = task + '.options.' + options.addGitIgnore;
+			let ignores = grunt.config.get( ignoreProp ) || [];
 
 
 			ignores = ignores.concat( this.getGitIgnore( grunt ) );
 			ignores = ignores.concat( this.getGitIgnore( grunt ) );
 			grunt.config.set( ignoreProp, ignores );
 			grunt.config.set( ignoreProp, ignores );
@@ -73,7 +73,7 @@ module.exports = {
 	 * @param grunt {Object} The Grunt object.
 	 * @param grunt {Object} The Grunt object.
 	 * @returns {String[]} The list of ignores.
 	 * @returns {String[]} The list of ignores.
 	 */
 	 */
-	getGitIgnore: function( grunt ) {
+	getGitIgnore( grunt ) {
 		if ( !ignoreList ) {
 		if ( !ignoreList ) {
 			ignoreList = grunt.file.read( '.gitignore' );
 			ignoreList = grunt.file.read( '.gitignore' );
 
 
@@ -96,7 +96,7 @@ module.exports = {
 	 *
 	 *
 	 * @returns {String[]} A list of file paths.
 	 * @returns {String[]} A list of file paths.
 	 */
 	 */
-	getGitDirtyFiles: function() {
+	getGitDirtyFiles() {
 		// Cache it, so it is executed only once when running multiple tasks.
 		// Cache it, so it is executed only once when running multiple tasks.
 		if ( !dirtyFiles ) {
 		if ( !dirtyFiles ) {
 			dirtyFiles = this
 			dirtyFiles = this
@@ -122,11 +122,11 @@ module.exports = {
 	 * @param command {String} The command to be executed.
 	 * @param command {String} The command to be executed.
 	 * @returns {String} The command output.
 	 * @returns {String} The command output.
 	 */
 	 */
-	shExec: function( command ) {
-		var sh = require( 'shelljs' );
+	shExec( command ) {
+		const sh = require( 'shelljs' );
 		sh.config.silent = true;
 		sh.config.silent = true;
 
 
-		var ret = sh.exec( command );
+		const ret = sh.exec( command );
 
 
 		if ( ret.code ) {
 		if ( ret.code ) {
 			throw new Error(
 			throw new Error(

+ 3 - 3
packages/ckeditor5-engine/gruntfile.js

@@ -1,13 +1,13 @@
-/* jshint node: true */
+/* jshint node: true, esnext: true, varstmt: true */
 
 
 'use strict';
 'use strict';
 
 
-module.exports = function( grunt ) {
+module.exports = ( grunt ) => {
 	// First register the "default" task, so it can be analyzed by other tasks.
 	// First register the "default" task, so it can be analyzed by other tasks.
 	grunt.registerTask( 'default', [ 'jshint:git', 'jscs:git' ] );
 	grunt.registerTask( 'default', [ 'jshint:git', 'jscs:git' ] );
 
 
 	// Files that will be ignored by the "jscs" and "jshint" tasks.
 	// Files that will be ignored by the "jscs" and "jshint" tasks.
-	var ignoreFiles = [
+	const ignoreFiles = [
 		'src/lib/**',
 		'src/lib/**',
 		// Automatically loaded from .gitignore. Add more if necessary.
 		// Automatically loaded from .gitignore. Add more if necessary.
 	];
 	];

+ 11 - 13
packages/ckeditor5-engine/src/ckeditor.js

@@ -12,8 +12,8 @@
  * @singleton
  * @singleton
  */
  */
 
 
-CKEDITOR.define( [ 'editor', 'collection', 'config' ], function( Editor, Collection, Config ) {
-	var CKEDITOR = {
+CKEDITOR.define( [ 'editor', 'collection', 'config' ], ( Editor, Collection, Config ) => {
+	const CKEDITOR = {
 		/**
 		/**
 		 * A collection containing all editor instances created.
 		 * A collection containing all editor instances created.
 		 *
 		 *
@@ -30,7 +30,7 @@ CKEDITOR.define( [ 'editor', 'collection', 'config' ], function( Editor, Collect
 		 *
 		 *
 		 *		CKEDITOR.create( '#content' );
 		 *		CKEDITOR.create( '#content' );
 		 *
 		 *
-		 *		CKEDITOR.create( '#content' ).then( function( editor ) {
+		 *		CKEDITOR.create( '#content' ).then( ( editor ) => {
 		 *			// Manipulate "editor" here.
 		 *			// Manipulate "editor" here.
 		 *		} );
 		 *		} );
 		 *
 		 *
@@ -38,10 +38,8 @@ CKEDITOR.define( [ 'editor', 'collection', 'config' ], function( Editor, Collect
 		 * created instance.
 		 * created instance.
 		 * @returns {Promise} A promise, which will be fulfilled with the created editor.
 		 * @returns {Promise} A promise, which will be fulfilled with the created editor.
 		 */
 		 */
-		create: function( element, config ) {
-			var that = this;
-
-			return new Promise( function( resolve, reject ) {
+		create( element, config ) {
+			return new Promise( ( resolve, reject ) => {
 				// If a query selector has been passed, transform it into a real element.
 				// If a query selector has been passed, transform it into a real element.
 				if ( typeof element == 'string' ) {
 				if ( typeof element == 'string' ) {
 					element = document.querySelector( element );
 					element = document.querySelector( element );
@@ -51,19 +49,19 @@ CKEDITOR.define( [ 'editor', 'collection', 'config' ], function( Editor, Collect
 					}
 					}
 				}
 				}
 
 
-				var editor = new Editor( element, config );
+				const editor = new Editor( element, config );
 
 
-				that.instances.add( editor );
+				this.instances.add( editor );
 
 
 				// Remove the editor from `instances` when destroyed.
 				// Remove the editor from `instances` when destroyed.
-				editor.once( 'destroy', function() {
-					that.instances.remove( editor );
+				editor.once( 'destroy', () => {
+					this.instances.remove( editor );
 				} );
 				} );
 
 
 				resolve(
 				resolve(
 					// Initializes the editor, which returns a promise.
 					// Initializes the editor, which returns a promise.
 					editor.init()
 					editor.init()
-						.then( function() {
+						.then( () => {
 							// After initialization, return the created editor.
 							// After initialization, return the created editor.
 							return editor;
 							return editor;
 						} )
 						} )
@@ -85,7 +83,7 @@ CKEDITOR.define( [ 'editor', 'collection', 'config' ], function( Editor, Collect
 		 * @param {String} name The plugin name.
 		 * @param {String} name The plugin name.
 		 * @returns {String} The full URL path of the plugin.
 		 * @returns {String} The full URL path of the plugin.
 		 */
 		 */
-		getPluginPath: function( name ) {
+		getPluginPath( name ) {
 			return this.basePath + 'plugins/' + name + '/';
 			return this.basePath + 'plugins/' + name + '/';
 		}
 		}
 	};
 	};

+ 1 - 1
packages/ckeditor5-engine/src/ckeditorerror.js

@@ -17,7 +17,7 @@
  * @extends Error
  * @extends Error
  */
  */
 
 
-CKEDITOR.define( function() {
+CKEDITOR.define( () => {
 	class CKEditorError extends Error {
 	class CKEditorError extends Error {
 		/**
 		/**
 		 * Creates an instance of the CKEditorError class.
 		 * Creates an instance of the CKEditorError class.

+ 170 - 55
packages/ckeditor5-engine/src/collection.js

@@ -6,29 +6,61 @@
 'use strict';
 'use strict';
 
 
 /**
 /**
- * Collections are ordered sets of models.
+ * Collections are ordered sets of objects. Items in the collection can be retrieved by their indexes
+ * in the collection (like in an array) or by their ids.
  *
  *
- * See also {@link core/NamedCollection}.
+ * If an object without an `id` property is being added to the collection, the `id` property will be generated
+ * automatically. Note that the automatically generated id is unique only within this single collection instance.
+ *
+ * By default an item in the collection is identified by its `id` property. The name of the identifier can be
+ * configured through the constructor of the collection.
  *
  *
  * @class Collection
  * @class Collection
  * @mixins EventEmitter
  * @mixins EventEmitter
  */
  */
 
 
-CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], function( EmitterMixin, CKEditorError, utils ) {
+CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], ( EmitterMixin, CKEditorError, utils ) => {
 	class Collection {
 	class Collection {
 		/**
 		/**
 		 * Creates a new Collection instance.
 		 * Creates a new Collection instance.
 		 *
 		 *
 		 * @constructor
 		 * @constructor
+		 * @param {Iterale} [items] Items to be added to the collection.
+		 * @param {Object} options The options object.
+		 * @param {String} [options.idProperty='id'] The name of the property which is considered to identify an item.
 		 */
 		 */
-		constructor() {
+		constructor( options ) {
+			/**
+			 * The internal list of items in the collection.
+			 *
+			 * @private
+			 * @property {Object[]}
+			 */
+			this._items = [];
+
+			/**
+			 * The internal map of items in the collection.
+			 *
+			 * @private
+			 * @property {Map}
+			 */
+			this._itemMap = new Map();
+
+			/**
+			 * Next id which will be assigned to unidentified item while adding it to the collection.
+			 *
+			 * @private
+			 * @property
+			 */
+			this._nextId = 0;
+
 			/**
 			/**
-			 * The internal list of models in the collection.
+			 * The name of the property which is considered to identify an item.
 			 *
 			 *
-			 * @property _models
 			 * @private
 			 * @private
+			 * @property {String}
 			 */
 			 */
-			this._models = [];
+			this._idProperty = options && options.idProperty || 'id';
 		}
 		}
 
 
 		/**
 		/**
@@ -37,106 +69,189 @@ CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], function( Emitter
 		 * @property length
 		 * @property length
 		 */
 		 */
 		get length() {
 		get length() {
-			return this._models.length;
+			return this._items.length;
 		}
 		}
 
 
 		/**
 		/**
 		 * Adds an item into the collection.
 		 * Adds an item into the collection.
 		 *
 		 *
-		 * Note that this is an array-like collection, so the same item can be present more than once. This behavior is
-		 * for performance purposes only and is not guaranteed to be kept in the same way in the future.
+		 * If the item does not have an id, then it will be automatically generated and set on the item.
 		 *
 		 *
-		 * @param {Model} model The item to be added.
+		 * @chainable
+		 * @param {Object} item
 		 */
 		 */
-		add( model ) {
-			this._models.push( model );
+		add( item ) {
+			let itemId;
+			const idProperty = this._idProperty;
+
+			if ( ( idProperty in item ) ) {
+				itemId = item[ idProperty ];
+
+				if ( typeof itemId != 'string' ) {
+					/**
+					 * This item's id should be a string.
+					 *
+					 * @error collection-add-invalid-id
+					 */
+					throw new CKEditorError( 'collection-add-invalid-id' );
+				}
+
+				if ( this.get( itemId ) ) {
+					/**
+					 * This item already exists in the collection.
+					 *
+					 * @error collection-add-item-already-exists
+					 */
+					throw new CKEditorError( 'collection-add-item-already-exists' );
+				}
+			} else {
+				itemId = this._getNextId();
+				item[ idProperty ] = itemId;
+			}
 
 
-			this.fire( 'add', model );
+			this._items.push( item );
+			this._itemMap.set( itemId, item );
+
+			this.fire( 'add', item );
+
+			return this;
 		}
 		}
 
 
 		/**
 		/**
-		 * Gets one item from the collection.
+		 * Gets item by its id or index.
 		 *
 		 *
-		 * @param {Number} index The index to take the item from.
-		 * @returns {Model} The requested item or `null` if such item does not exist.
+		 * @param {String|Number} idOrIndex The item id or index in the collection.
+		 * @returns {Object} The requested item or `null` if such item does not exist.
 		 */
 		 */
-		get( index ) {
-			return this._models[ index ] || null;
+		get( idOrIndex ) {
+			let item;
+
+			if ( typeof idOrIndex == 'string' ) {
+				item = this._itemMap.get( idOrIndex );
+			} else if ( typeof idOrIndex == 'number' ) {
+				item = this._items[ idOrIndex ];
+			} else {
+				/**
+				 * Index or id must be given.
+				 *
+				 * @error collection-get-invalid-arg
+				 */
+				throw new CKEditorError( 'collection-get-invalid-arg: Index or id must be given.' );
+			}
+
+			return item || null;
 		}
 		}
 
 
 		/**
 		/**
 		 * Removes an item from the collection.
 		 * Removes an item from the collection.
 		 *
 		 *
-		 * @param {Model|Number} modelOrIndex Either the item itself or its index inside the collection.
-		 * @returns {Model} The removed item.
+		 * @param {Object|Number|String} subject The item to remove, its id or index in the collection.
+		 * @returns {Object} The removed item.
 		 */
 		 */
-		remove( modelOrIndex ) {
-			// If a model has been passed, convert it to its index.
-			if ( typeof modelOrIndex != 'number' ) {
-				modelOrIndex = this._models.indexOf( modelOrIndex );
+		remove( subject ) {
+			let index, id, item;
+			let itemDoesNotExist = false;
+			const idProperty = this._idProperty;
 
 
-				if ( modelOrIndex == -1 ) {
-					/**
-					 * Model not found.
-					 *
-					 * @error collection-model-404
-					 */
-					throw new CKEditorError( 'collection-model-404: Model not found.' );
+			if ( typeof subject == 'string' ) {
+				id = subject;
+				item = this._itemMap.get( id );
+				itemDoesNotExist = !item;
+
+				if ( item ) {
+					index = this._items.indexOf( item );
 				}
 				}
-			}
+			} else if ( typeof subject == 'number' ) {
+				index = subject;
+				item = this._items[ index ];
+				itemDoesNotExist = !item;
 
 
-			var removedModel = this._models.splice( modelOrIndex, 1 )[ 0 ];
+				if ( item ) {
+					id = item[ idProperty ];
+				}
+			} else {
+				item = subject;
+				id = item[ idProperty ];
+				index = this._items.indexOf( item );
+				itemDoesNotExist = ( index == -1 || !this._itemMap.get( id ) );
+			}
 
 
-			if ( !removedModel ) {
+			if ( itemDoesNotExist ) {
 				/**
 				/**
-				 * Index not found.
+				 * Item not found.
 				 *
 				 *
-				 * @error collection-index-404
+				 * @error collection-remove-404
 				 */
 				 */
-				throw new CKEditorError( 'collection-index-404: Index not found.' );
+				throw new CKEditorError( 'collection-remove-404: Item not found.' );
 			}
 			}
 
 
-			this.fire( 'remove', removedModel );
+			this._items.splice( index, 1 );
+			this._itemMap.delete( id );
+
+			this.fire( 'remove', item );
 
 
-			return removedModel;
+			return item;
 		}
 		}
 
 
 		/**
 		/**
-		 * Executes the callback for each model in the collection.
+		 * Executes the callback for each item in the collection and composes an array or values returned by this callback.
 		 *
 		 *
 		 * @param {Function} callback
 		 * @param {Function} callback
-		 * @param {Model} callback.item
-		 * @param {String} callback.index
+		 * @param {Item} callback.item
+		 * @param {Number} callback.index
 		 * @params {Object} ctx Context in which the `callback` will be called.
 		 * @params {Object} ctx Context in which the `callback` will be called.
+		 * @returns {Array} The result of mapping.
 		 */
 		 */
-		forEach( callback, ctx ) {
-			this._models.forEach( callback, ctx );
+		map( callback, ctx ) {
+			return this._items.map( callback, ctx );
 		}
 		}
 
 
 		/**
 		/**
 		 * Finds the first item in the collection for which the `callback` returns a true value.
 		 * Finds the first item in the collection for which the `callback` returns a true value.
 		 *
 		 *
 		 * @param {Function} callback
 		 * @param {Function} callback
-		 * @param {Model} callback.item
-		 * @param {String} callback.name
-		 * @returns {Model} The item for which `callback` returned a true value.
+		 * @param {Object} callback.item
+		 * @param {Number} callback.index
+		 * @returns {Object} The item for which `callback` returned a true value.
 		 * @params {Object} ctx Context in which the `callback` will be called.
 		 * @params {Object} ctx Context in which the `callback` will be called.
 		 */
 		 */
 		find( callback, ctx ) {
 		find( callback, ctx ) {
-			return this._models.find( callback, ctx );
+			return this._items.find( callback, ctx );
 		}
 		}
 
 
 		/**
 		/**
 		 * Returns an array with items for which the `callback` returned a true value.
 		 * Returns an array with items for which the `callback` returned a true value.
 		 *
 		 *
 		 * @param {Function} callback
 		 * @param {Function} callback
-		 * @param {Model} callback.item
-		 * @param {String} callback.name
+		 * @param {Object} callback.item
+		 * @param {Number} callback.index
 		 * @params {Object} ctx Context in which the `callback` will be called.
 		 * @params {Object} ctx Context in which the `callback` will be called.
-		 * @returns {Model[]} The array with matching items.
+		 * @returns {Object[]} The array with matching items.
 		 */
 		 */
 		filter( callback, ctx ) {
 		filter( callback, ctx ) {
-			return this._models.filter( callback, ctx );
+			return this._items.filter( callback, ctx );
+		}
+
+		/**
+		 * Collection iterator.
+		 */
+		[ Symbol.iterator ]() {
+			return this._items[ Symbol.iterator ]();
+		}
+
+		/**
+		 * Generates next (not yet used) id for unidentified item being add to the collection.
+		 *
+		 * @returns {String} The next id.
+		 */
+		_getNextId() {
+			let id;
+
+			do {
+				id = String( this._nextId++ );
+			} while ( this._itemMap.has( id ) );
+
+			return id;
 		}
 		}
 	}
 	}
 
 
@@ -149,12 +264,12 @@ CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], function( Emitter
  * Fired when an item is added to the collection.
  * Fired when an item is added to the collection.
  *
  *
  * @event add
  * @event add
- * @param {Model} model The added item.
+ * @param {Object} item The added item.
  */
  */
 
 
 /**
 /**
  * Fired when an item is removed from the collection.
  * Fired when an item is removed from the collection.
  *
  *
  * @event remove
  * @event remove
- * @param {Model} model The removed item.
+ * @param {Object} item The removed item.
  */
  */

+ 7 - 7
packages/ckeditor5-engine/src/config.js

@@ -12,7 +12,7 @@
  * @extends Model
  * @extends Model
  */
  */
 
 
-CKEDITOR.define( [ 'model', 'utils' ], function( Model, utils ) {
+CKEDITOR.define( [ 'model', 'utils' ], ( Model, utils ) => {
 	class Config extends Model {
 	class Config extends Model {
 		/**
 		/**
 		 * Creates an instance of the {@link Config} class.
 		 * Creates an instance of the {@link Config} class.
@@ -76,17 +76,17 @@ CKEDITOR.define( [ 'model', 'utils' ], function( Model, utils ) {
 
 
 			// The target for this configuration is, for now, this object.
 			// The target for this configuration is, for now, this object.
 			//jscs:disable safeContextKeyword
 			//jscs:disable safeContextKeyword
-			var target = this;
+			let target = this;
 			//jscs:enable
 			//jscs:enable
 
 
 			// The configuration name should be split into parts if it has dots. E.g: `resize.width`.
 			// The configuration name should be split into parts if it has dots. E.g: `resize.width`.
-			var parts = name.toLowerCase().split( '.' );
+			const parts = name.toLowerCase().split( '.' );
 
 
 			// Take the name of the configuration out of the parts. E.g. `resize.width` -> `width`
 			// Take the name of the configuration out of the parts. E.g. `resize.width` -> `width`
 			name = parts.pop();
 			name = parts.pop();
 
 
 			// Retrieves the final target for this configuration recursively.
 			// Retrieves the final target for this configuration recursively.
-			for ( var i = 0; i < parts.length; i++ ) {
+			for ( let i = 0; i < parts.length; i++ ) {
 				// The target will always be an instance of Config.
 				// The target will always be an instance of Config.
 				if ( !( target[ parts[ i ] ] instanceof Config ) ) {
 				if ( !( target[ parts[ i ] ] instanceof Config ) ) {
 					target.set( parts[ i ], new Config() );
 					target.set( parts[ i ], new Config() );
@@ -132,17 +132,17 @@ CKEDITOR.define( [ 'model', 'utils' ], function( Model, utils ) {
 		get( name ) {
 		get( name ) {
 			// The target for this configuration is, for now, this object.
 			// The target for this configuration is, for now, this object.
 			//jscs:disable safeContextKeyword
 			//jscs:disable safeContextKeyword
-			var source = this;
+			let source = this;
 			//jscs:enable
 			//jscs:enable
 
 
 			// The configuration name should be split into parts if it has dots. E.g. `resize.width` -> [`resize`, `width`]
 			// The configuration name should be split into parts if it has dots. E.g. `resize.width` -> [`resize`, `width`]
-			var parts = name.toLowerCase().split( '.' );
+			const parts = name.toLowerCase().split( '.' );
 
 
 			// Take the name of the configuration from the parts. E.g. `resize.width` -> `width`
 			// Take the name of the configuration from the parts. E.g. `resize.width` -> `width`
 			name = parts.pop();
 			name = parts.pop();
 
 
 			// Retrieves the source for this configuration recursively.
 			// Retrieves the source for this configuration recursively.
-			for ( var i = 0; i < parts.length; i++ ) {
+			for ( let i = 0; i < parts.length; i++ ) {
 				// The target will always be an instance of Config.
 				// The target will always be an instance of Config.
 				if ( !( source[ parts[ i ] ] instanceof Config ) ) {
 				if ( !( source[ parts[ i ] ] instanceof Config ) ) {
 					source = null;
 					source = null;

+ 1 - 1
packages/ckeditor5-engine/src/creator.js

@@ -12,7 +12,7 @@
  * @extends Plugin
  * @extends Plugin
  */
  */
 
 
-CKEDITOR.define( [ 'plugin' ], function( Plugin ) {
+CKEDITOR.define( [ 'plugin' ], ( Plugin ) => {
 	class Creator extends Plugin {
 	class Creator extends Plugin {
 	}
 	}
 
 

+ 9 - 9
packages/ckeditor5-engine/src/document/attribute.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [ 'utils' ], function( utils ) {
+CKEDITOR.define( [ 'utils' ], ( utils ) => {
 	/**
 	/**
 	 * Attributes can store any additional information for nodes in the data model.
 	 * Attributes can store any additional information for nodes in the data model.
 	 *
 	 *
@@ -53,10 +53,10 @@ CKEDITOR.define( [ 'utils' ], function( utils ) {
 			// We do not care about the order, so collections with the same elements should return the same hash.
 			// We do not care about the order, so collections with the same elements should return the same hash.
 			function sort( key, value ) {
 			function sort( key, value ) {
 				if ( !utils.isArray( value ) && utils.isObject( value ) ) {
 				if ( !utils.isArray( value ) && utils.isObject( value ) ) {
-					var sorted = {};
+					const sorted = {};
 
 
 					// Sort keys and fill up the sorted object.
 					// Sort keys and fill up the sorted object.
-					Object.keys( value ).sort().forEach( function( key ) {
+					Object.keys( value ).sort().forEach( ( key ) => {
 						sorted[ key ] = value[ key ];
 						sorted[ key ] = value[ key ];
 					} );
 					} );
 
 
@@ -71,8 +71,8 @@ CKEDITOR.define( [ 'utils' ], function( utils ) {
 		 * Compares two attributes. Returns `true` if two attributes have the same key and value even if the order of keys
 		 * Compares two attributes. Returns `true` if two attributes have the same key and value even if the order of keys
 		 * in the value object is different.
 		 * in the value object is different.
 		 *
 		 *
-		 *		var attr1 = new Attribute( 'foo', { a: 1, b: 2 } );
-		 *		var attr2 = new Attribute( 'foo', { b: 2, a: 1 } );
+		 *		let attr1 = new Attribute( 'foo', { a: 1, b: 2 } );
+		 *		let attr2 = new Attribute( 'foo', { b: 2, a: 1 } );
 		 *		attr1.isEqual( attr2 ); // true
 		 *		attr1.isEqual( attr2 ); // true
 		 *
 		 *
 		 * @param {document.Attribute} otherAttr Attribute to compare with.
 		 * @param {document.Attribute} otherAttr Attribute to compare with.
@@ -88,9 +88,9 @@ CKEDITOR.define( [ 'utils' ], function( utils ) {
 		 *
 		 *
 		 * Note that attributes are registered globally.
 		 * Note that attributes are registered globally.
 		 *
 		 *
-		 *		var attr1 = Attribute.register( 'bold', true );
-		 *		var attr2 = Attribute.register( 'bold', true );
-		 *		var attr3 = new Attribute( 'bold', true );
+		 *		let attr1 = Attribute.register( 'bold', true );
+		 *		let attr2 = Attribute.register( 'bold', true );
+		 *		let attr3 = new Attribute( 'bold', true );
 		 *		attr1 === attr2 // true
 		 *		attr1 === attr2 // true
 		 *		attr1 === attr3 // true
 		 *		attr1 === attr3 // true
 		 *
 		 *
@@ -100,7 +100,7 @@ CKEDITOR.define( [ 'utils' ], function( utils ) {
 		 * @returns {document.Attribute} Registered attribute.
 		 * @returns {document.Attribute} Registered attribute.
 		 */
 		 */
 		static register( key, value ) {
 		static register( key, value ) {
-			var attr = new Attribute( key, value );
+			const attr = new Attribute( key, value );
 
 
 			if ( this._register[ attr._hash ] ) {
 			if ( this._register[ attr._hash ] ) {
 				return this._register[ attr._hash ];
 				return this._register[ attr._hash ];

+ 1 - 1
packages/ckeditor5-engine/src/document/character.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [ 'document/node' ], function( Node ) {
+CKEDITOR.define( [ 'document/node' ], ( Node ) => {
 	/**
 	/**
 	 * Data structure for character stored in the tree data model.
 	 * Data structure for character stored in the tree data model.
 	 *
 	 *

+ 155 - 0
packages/ckeditor5-engine/src/document/delta/changedelta.js

@@ -0,0 +1,155 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [
+	'document/delta/delta',
+	'document/delta/register',
+	'document/operation/changeoperation',
+	'document/position',
+	'document/range',
+	'document/attribute',
+	'document/element'
+], ( Delta, register, ChangeOperation, Position, Range, Attribute, Element ) => {
+	/**
+	 * To provide specific OT behavior and better collisions solving, change methods ({@link document.Transaction#setAttr}
+	 * and {@link document.Transaction#removeAttr}) use `ChangeDelta` class which inherits from the `Delta` class and may
+	 * overwrite some methods.
+	 *
+	 * @class document.delta.ChangeDelta
+	 */
+	class ChangeDelta extends Delta {}
+
+	/**
+	 * Sets the value of the attribute of the node or on the range.
+	 *
+	 * @chainable
+	 * @method setAttr
+	 * @memberOf document.Transaction
+	 * @param {String} key Attribute key.
+	 * @param {Mixed} value Attribute new value.
+	 * @param {document.Node|document.Range} nodeOrRange Node or range on which the attribute will be set.
+	 */
+	register( 'setAttr', function( key, value, nodeOrRange ) {
+		change( this, key, value, nodeOrRange );
+
+		return this;
+	} );
+
+	/**
+	 * Removes an attribute from the range.
+	 *
+	 * @chainable
+	 * @method removeAttr
+	 * @memberOf document.Transaction
+	 * @param {String} key Attribute key.
+	 * @param {document.Node|document.Range} nodeOrRange Node or range on which the attribute will be removed.
+	 */
+	register( 'removeAttr', function( key, nodeOrRange ) {
+		change( this, key, null, nodeOrRange );
+
+		return this;
+	} );
+
+	function change( transaction, key, value, nodeOrRange ) {
+		const delta = new ChangeDelta();
+
+		if ( nodeOrRange instanceof Range ) {
+			changeRange( transaction.doc, delta, key, value, nodeOrRange );
+		} else {
+			changeNode( transaction.doc, delta, key, value, nodeOrRange );
+		}
+
+		transaction.addDelta( delta );
+	}
+
+	function changeNode( doc, delta, key, value, node ) {
+		const previousValue = node.getAttr( key );
+		let range;
+
+		if ( previousValue != value ) {
+			if ( node instanceof Element ) {
+				// If we change the attribute of the element, we do not want to change attributes of its children, so
+				// the end on the range can not be put after the closing tag, it should be inside that element with the
+				// offset 0, so the range will contains only the opening tag...
+				range = new Range( Position.createBefore( node ), Position.createFromParentAndOffset( node, 0 ) );
+			} else {
+				// ...but for characters we can not put the range inside it, so we end the range after that character.
+				range = new Range( Position.createBefore( node ), Position.createAfter( node ) );
+			}
+
+			const operation = new ChangeOperation(
+					range,
+					previousValue ? new Attribute( key, previousValue ) : null,
+					value ? new Attribute( key, value ) : null,
+					doc.version
+				);
+
+			doc.applyOperation( operation );
+			delta.addOperation( operation );
+		}
+	}
+
+	// Because change operation needs to have the same attribute value on the whole range, this function split the range
+	// into smaller parts.
+	function changeRange( doc, delta, key, value, range ) {
+		// Position of the last split, the beginning of the new range.
+		let lastSplitPosition = range.start;
+
+		// Currently position in the scanning range. Because we need value after the position, it is not a current
+		// position of the iterator but the previous one (we need to iterate one more time to get the value after).
+		let position;
+		// Value before the currently position.
+		let valueBefore;
+		// Value after the currently position.
+		let valueAfter;
+
+		// Because we need not only a node, but also a position, we can not use ( value of range ).
+		const iterator = range[ Symbol.iterator ]();
+		// Iterator state.
+		let next = iterator.next();
+
+		while ( !next.done ) {
+			valueAfter = next.value.node.getAttr( key );
+
+			// At the first run of the iterator the position in undefined. We also do not have a valueBefore, but
+			// because valueAfter may be null, valueBefore may be equal valueAfter ( undefined == null ).
+			if ( position && valueBefore != valueAfter ) {
+				// if valueBefore == value there is nothing to change, so we add operation only if these values are different.
+				if ( valueBefore != value ) {
+					addOperation();
+				}
+
+				lastSplitPosition = position;
+			}
+
+			position = iterator.position;
+			valueBefore = valueAfter;
+
+			next = iterator.next();
+		}
+
+		// Because position in the loop is not the iterator position (see let position comment), the last position in
+		// the while loop will be last but one position in the range. We need to check the last position manually.
+		if ( position != lastSplitPosition && valueBefore != value ) {
+			addOperation();
+		}
+
+		function addOperation() {
+			const operation = new ChangeOperation(
+					new Range( lastSplitPosition, position ),
+					valueBefore ? new Attribute( key, valueBefore ) : null,
+					value ? new Attribute( key, value ) : null,
+					doc.version
+				);
+
+			doc.applyOperation( operation );
+			delta.addOperation( operation );
+		}
+	}
+
+	return ChangeDelta;
+} );

+ 58 - 0
packages/ckeditor5-engine/src/document/delta/delta.js

@@ -0,0 +1,58 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [], () => {
+	/**
+	 * Base class for all deltas.
+	 *
+	 * Delta is a single, from the user action point of view, change in the editable document, like insert, split or
+	 * rename element. Delta is composed of operations, which are unit changes needed to be done to execute user action.
+	 *
+	 * Multiple deltas are grouped into a single {@link document.Transaction}.
+	 *
+	 * @class document.delta.Delta
+	 */
+	class Delta {
+		/**
+		 * Creates a delta instance.
+		 *
+		 * @constructor
+		 */
+		constructor() {
+			/**
+			 * {@link document.Transaction} which delta is a part of. This property is null by default and set by the
+			 * {@link Document.Transaction#addDelta} method.
+			 *
+			 * @readonly
+			 * @type {document.Transaction}
+			 */
+			this.transaction = null;
+
+			/**
+			 * Array of operations which compose delta.
+			 *
+			 * @readonly
+			 * @type {document.operation.Operation[]}
+			 */
+			this.operations = [];
+		}
+
+		/**
+		 * Add operation to the delta.
+		 *
+		 * @param {document.operation.Operation} operation Operation instance.
+		 */
+		addOperation( operation ) {
+			operation.delta = this;
+			this.operations.push( operation );
+
+			return operation;
+		}
+	}
+
+	return Delta;
+} );

+ 44 - 0
packages/ckeditor5-engine/src/document/delta/insertdelta.js

@@ -0,0 +1,44 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [
+	'document/delta/delta',
+	'document/delta/register',
+	'document/operation/insertoperation'
+], ( Delta, register, InsertOperation ) => {
+	/**
+	 * To provide specific OT behavior and better collisions solving, the {@link document.Transaction#insert} method
+	 * uses the `InsertDelta` class which inherits from the `Delta` class and may overwrite some methods.
+	 *
+	 * @class document.delta.InsertDelta
+	 */
+	class InsertDelta extends Delta {}
+
+	/**
+	 * Inserts a node or nodes at the given position.
+	 *
+	 * @chainable
+	 * @memberOf document.Transaction
+	 * @method insert
+	 * @param {document.Position} position Position of insertion.
+	 * @param {document.Node|document.Text|document.NodeList|String|Iterable} nodes The list of nodes to be inserted.
+	 * List of nodes can be of any type accepted by the {@link document.NodeList} constructor.
+	 */
+	register( 'insert', function( position, nodes ) {
+		const delta = new InsertDelta();
+
+		const operation = new InsertOperation( position, nodes, this.doc.version );
+		this.doc.applyOperation( operation );
+		delta.addOperation( operation );
+
+		this.addDelta( delta );
+
+		return this;
+	} );
+
+	return InsertDelta;
+} );

+ 78 - 0
packages/ckeditor5-engine/src/document/delta/mergedelta.js

@@ -0,0 +1,78 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [
+	'document/delta/delta',
+	'document/delta/register',
+	'document/position',
+	'document/element',
+	'document/operation/removeoperation',
+	'document/operation/moveoperation',
+	'ckeditorerror'
+], ( Delta, register, Position, Element, RemoveOperation, MoveOperation, CKEditorError ) => {
+	/**
+	 * To provide specific OT behavior and better collisions solving, {@link document.Transaction#merge} method
+	 * uses the `MergeDelta` class which inherits from the `Delta` class and may overwrite some methods.
+	 *
+	 * @class document.delta.MergeDelta
+	 */
+	class MergeDelta extends Delta {}
+
+	/**
+	 * Merges two siblings at the given position.
+	 *
+	 * Node before and after the position have to be an element. Otherwise `transaction-merge-no-element-before` or
+	 * `transaction-merge-no-element-after` error will be thrown.
+	 *
+	 * @chainable
+	 * @method merge
+	 * @memberOf document.Transaction
+	 * @param {document.Position} position Position of merge.
+	 */
+	register( 'merge', function( position ) {
+		const delta = new MergeDelta();
+		const nodeBefore = position.nodeBefore;
+		const nodeAfter = position.nodeAfter;
+
+		if ( !( nodeBefore instanceof Element ) ) {
+			/**
+			 * Node before merge position must be an element.
+			 *
+			 * @error transaction-merge-no-element-before
+			 */
+			throw new CKEditorError(
+				'transaction-merge-no-element-before: Node before merge position must be an element.' );
+		}
+
+		if ( !( nodeAfter instanceof Element ) ) {
+			/**
+			 * Node after merge position must be an element.
+			 *
+			 * @error transaction-merge-no-element-after
+			 */
+			throw new CKEditorError(
+				'transaction-merge-no-element-after: Node after merge position must be an element.' );
+		}
+
+		const positionAfter = Position.createFromParentAndOffset( nodeAfter, 0 );
+		const positionBefore = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );
+
+		const move = new MoveOperation( positionAfter, positionBefore, nodeAfter.getChildCount(), this.doc.version );
+		this.doc.applyOperation( move );
+		delta.addOperation( move );
+
+		const remove = new RemoveOperation( position, 1, this.doc.version );
+		this.doc.applyOperation( remove );
+		delta.addOperation( remove );
+
+		this.addDelta( delta );
+
+		return this;
+	} );
+
+	return MergeDelta;
+} );

+ 14 - 0
packages/ckeditor5-engine/src/document/delta/register.js

@@ -0,0 +1,14 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+// Register method exposed for deltas, which needs only this method, to make code simpler, more beautiful and, first of
+// all, to solve circular dependencies.
+CKEDITOR.define( [
+	'document/delta/transaction-base'
+], ( Transaction ) => {
+	return Transaction.register;
+} );

+ 47 - 0
packages/ckeditor5-engine/src/document/delta/removedelta.js

@@ -0,0 +1,47 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [
+	'document/delta/delta',
+	'document/delta/register',
+	'document/operation/removeoperation'
+], ( Delta, register, RemoveOperation ) => {
+	/**
+	 * To provide specific OT behavior and better collisions solving, {@link document.Transaction#remove} method
+	 * uses the `RemoveDelta` class which inherits from the `Delta` class and may overwrite some methods.
+	 *
+	 * @class document.delta.RemoveDelta
+	 */
+	class RemoveDelta extends Delta {}
+
+	/**
+	 * Removes nodes starting from the given position.
+	 *
+	 * @chainable
+	 * @method remove
+	 * @memberOf document.Transaction
+	 * @param {document.Position} position Position before the first node to remove.
+	 * @param {Number} howMany How many nodes to remove.
+	 */
+	register( 'remove', function( position, howMany ) {
+		if ( typeof howMany !== 'number' ) {
+			howMany = 1;
+		}
+
+		const delta = new RemoveDelta();
+
+		const operation = new RemoveOperation( position, howMany, this.doc.version );
+		this.doc.applyOperation( operation );
+		delta.addOperation( operation );
+
+		this.addDelta( delta );
+
+		return this;
+	} );
+
+	return RemoveDelta;
+} );

+ 71 - 0
packages/ckeditor5-engine/src/document/delta/splitdelta.js

@@ -0,0 +1,71 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [
+	'document/delta/delta',
+	'document/delta/register',
+	'document/position',
+	'document/element',
+	'document/operation/insertoperation',
+	'document/operation/moveoperation',
+	'ckeditorerror'
+], ( Delta, register, Position, Element, InsertOperation, MoveOperation, CKEditorError ) => {
+	/**
+	 * To provide specific OT behavior and better collisions solving, the {@link document.Transaction#split} method
+	 * uses `SplitDelta` class which inherits from the `Delta` class and may overwrite some methods.
+	 *
+	 * @class document.delta.SplitDelta
+	 */
+	class SplitDelta extends Delta {}
+
+	/**
+	 * Splits a node at the given position.
+	 *
+	 * This cannot be a position inside the root element. The `transaction-split-root` error will be thrown if
+	 * you try to split the root element.
+	 *
+	 * @chainable
+	 * @method split
+	 * @memberOf document.Transaction
+	 * @param {document.Position} position Position of split.
+	 */
+	register( 'split', function( position ) {
+		const delta = new SplitDelta();
+		const splitElement = position.parent;
+
+		if ( !splitElement.parent ) {
+			/**
+			 * Root element can not be split.
+			 *
+			 * @error transaction-split-root
+			 */
+			throw new CKEditorError( 'transaction-split-root: Root element can not be split.' );
+		}
+
+		const copy = new Element( splitElement.name, splitElement.getAttrs() );
+		const insert = new InsertOperation( Position.createAfter( splitElement ), copy, this.doc.version );
+
+		this.doc.applyOperation( insert );
+		delta.addOperation( insert );
+
+		const move = new MoveOperation(
+			position,
+			Position.createFromParentAndOffset( copy, 0 ),
+			splitElement.getChildCount() - position.offset,
+			this.doc.version
+		);
+
+		this.doc.applyOperation( move );
+		delta.addOperation( move );
+
+		this.addDelta( delta );
+
+		return this;
+	} );
+
+	return SplitDelta;
+} );

+ 124 - 0
packages/ckeditor5-engine/src/document/delta/transaction-base.js

@@ -0,0 +1,124 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [ 'ckeditorerror' ], ( CKEditorError ) => {
+	/**
+	 * The transaction class groups document changes (deltas). All deltas grouped in a single transactions can be
+	 * reverted together, so you can think about the transaction as a single undo step. If you want to extend one
+	 * undo step you can call another method on the same transaction object. If you want to create a separate undo step
+	 * you can create a new transaction.
+	 *
+	 * For example to create two separate undo steps you can call:
+	 *
+	 *		doc.createTransaction().insert( firstPosition, 'foo' );
+	 *		doc.createTransaction().insert( secondPosition, 'bar' );
+	 *
+	 * To create a single undo step:
+	 *
+	 *		const transaction = doc.createTransaction()
+	 *		transaction.insert( firstPosition, 'foo' );
+	 *		transaction.insert( secontPosition, 'bar' );
+	 *
+	 * Note that all document modification methods (insert, remove, split, etc.) are chainable so you can shorten code to:
+	 *
+	 *		doc.createTransaction().insert( firstPosition, 'foo' ).insert( secontPosition, 'bar' );
+	 *
+	 * @class document.Transaction
+	 */
+	class Transaction {
+		/**
+		 * Creates transaction instance. Not recommended to use directly, use {@link document.Document#createTransaction}
+		 * instead.
+		 *
+		 * @constructor
+		 * @param {document.Document} doc Document which this transaction changes.
+		 */
+		constructor( doc ) {
+			/**
+			 * Document which this transaction changes.
+			 *
+			 * @readonly
+			 * @type {document.Document}
+			 */
+			this.doc = doc;
+
+			/**
+			 * Array of deltas which compose transaction.
+			 *
+			 * @readonly
+			 * @type {document.delta.Delta[]}
+			 */
+			this.deltas = [];
+		}
+
+		/**
+		 * Adds delta to the transaction instance. All modification methods (insert, remove, split, etc.) use this method
+		 * to add created deltas.
+		 *
+		 * @param {document.delta.Delta} delta Delta to add.
+		 * @return {document.delta.Delta} Added delta.
+		 */
+		addDelta( delta ) {
+			delta.transaction = this;
+			this.deltas.push( delta );
+
+			return delta;
+		}
+
+		/**
+		 * Static method to register transaction methods. To make code scalable transaction do not have modification
+		 * methods built in. They can be registered using this method.
+		 *
+		 * This method checks if there is no naming collision and throw `transaction-register-taken` if the method name
+		 * is already taken.
+		 *
+		 * Beside that no magic happens here, the method is added to the `Transaction` class prototype.
+		 *
+		 * For example:
+		 *
+		 *		Transaction.register( 'insert', function( position, nodes ) {
+		 *			// You can use a class inherit from Delta if that class should handle OT in the special way.
+		 *			const delta = new Delta();
+		 *
+		 * 			// Create operations which should be components of this delta.
+		 *			const operation = new InsertOperation( position, nodes, this.doc.version );
+		 *
+		 *			// Remember to apply every operation, no magic, you need to do it manually.
+		 *			this.doc.applyOperation( operation );
+		 *
+		 *			// Add operation to the delta.
+		 *			delta.addOperation( operation );
+		 *
+		 *			// Add delta to the transaction instance.
+		 *			this.addDelta( delta );
+		 *
+		 * 			// Make this method chainable.
+		 * 			return this;
+		 *		} );
+		 *
+		 * @param {String} name Method name.
+		 * @param {Fuction} creator Method body.
+		 */
+		static register( name, creator ) {
+			if ( Transaction.prototype[ name ] ) {
+				/**
+				 * This transaction method is already taken.
+				 *
+				 * @error transaction-register-taken
+				 * @param {String} name
+				 */
+				throw new CKEditorError(
+					'transaction-register-taken: This transaction method is already taken.',
+					{ name: name } );
+			}
+
+			Transaction.prototype[ name ] = creator;
+		}
+	}
+
+	return Transaction;
+} );

+ 26 - 6
packages/ckeditor5-engine/src/document/document.js

@@ -8,20 +8,29 @@
 CKEDITOR.define( [
 CKEDITOR.define( [
 	'document/element',
 	'document/element',
 	'document/rootelement',
 	'document/rootelement',
+	'document/transaction',
 	'emittermixin',
 	'emittermixin',
 	'utils',
 	'utils',
 	'ckeditorerror'
 	'ckeditorerror'
-], function( Element, RootElement, EmitterMixin, utils, CKEditorError ) {
-	var graveyardSymbol = Symbol( 'graveyard' );
+], ( Element, RootElement, Tranaction, EmitterMixin, utils, CKEditorError ) => {
+	const graveyardSymbol = Symbol( 'graveyard' );
 
 
 	/**
 	/**
-	 * Document model.
+	 * Document tree model describes all editable data in the editor. It may contain multiple {@link #roots root elements},
+	 * for example if the editor have multiple editable areas, each area will be represented by the separate root.
+	 *
+	 * All changes in the document are done by {@link document.operation.Operation operations}. To create operations in
+	 * the simple way use use the {@link document.Transaction transaction} API, for example:
+	 *
+	 *		document.createTransaction().insert( position, nodes ).split( otherPosition );
+	 *
+	 * @see #createTransaction
 	 *
 	 *
 	 * @class document.Document
 	 * @class document.Document
 	 */
 	 */
 	class Document {
 	class Document {
 		/**
 		/**
-		 * Creates an empty document instance.
+		 * Creates an empty document instance with no {@link #roots}.
 		 *
 		 *
 		 * @constructor
 		 * @constructor
 		 */
 		 */
@@ -49,7 +58,9 @@ CKEDITOR.define( [
 		}
 		}
 
 
 		/**
 		/**
-		 * This is the only entry point for all document changes.
+		 * This is the entry point for all document changes. All changes on the document are done using
+		 * {@link document.operation.Operation operations}. To create operations in the simple way use the
+		 * {@link document.Transaction} API available via {@link #createTransaction} method.
 		 *
 		 *
 		 * @param {document.operation.Operation} operation Operation to be applied.
 		 * @param {document.operation.Operation} operation Operation to be applied.
 		 */
 		 */
@@ -92,7 +103,7 @@ CKEDITOR.define( [
 				);
 				);
 			}
 			}
 
 
-			var root = new RootElement( this );
+			const root = new RootElement( this );
 			this.roots.set( name, root );
 			this.roots.set( name, root );
 
 
 			return root;
 			return root;
@@ -131,6 +142,15 @@ CKEDITOR.define( [
 		get _graveyard() {
 		get _graveyard() {
 			return this.getRoot( graveyardSymbol );
 			return this.getRoot( graveyardSymbol );
 		}
 		}
+
+		/**
+		 * Creates a {@link document.Transaction} instance which allows to change the document.
+		 *
+		 * @returns {document.Transaction} Transaction instance.
+		 */
+		createTransaction() {
+			return new Tranaction( this );
+		}
 	}
 	}
 
 
 	utils.extend( Document.prototype, EmitterMixin );
 	utils.extend( Document.prototype, EmitterMixin );

+ 8 - 3
packages/ckeditor5-engine/src/document/element.js

@@ -5,7 +5,12 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [ 'document/node', 'document/nodelist' ], function( Node, NodeList ) {
+CKEDITOR.define( [
+	'document/node',
+	'document/nodelist',
+	'document/range',
+	'document/position'
+], ( Node, NodeList ) => {
 	/**
 	/**
 	 * Tree data model element.
 	 * Tree data model element.
 	 *
 	 *
@@ -60,7 +65,7 @@ CKEDITOR.define( [ 'document/node', 'document/nodelist' ], function( Node, NodeL
 		insertChildren( index, nodes ) {
 		insertChildren( index, nodes ) {
 			this._children.insert( index, new NodeList( nodes ) );
 			this._children.insert( index, new NodeList( nodes ) );
 
 
-			for ( var node of this._children ) {
+			for ( let node of this._children ) {
 				node.parent = this;
 				node.parent = this;
 			}
 			}
 		}
 		}
@@ -77,7 +82,7 @@ CKEDITOR.define( [ 'document/node', 'document/nodelist' ], function( Node, NodeL
 		 */
 		 */
 
 
 		removeChildren( index, number ) {
 		removeChildren( index, number ) {
-			for ( var i = index; i < index + number; i++ ) {
+			for ( let i = index; i < index + number; i++ ) {
 				this._children.get( i ).parent = null;
 				this._children.get( i ).parent = null;
 			}
 			}
 
 

+ 19 - 24
packages/ckeditor5-engine/src/document/node.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( Attribute, utils, CKEditorError ) {
+CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute, utils, CKEditorError ) => {
 	/**
 	/**
 	 * Abstract document tree node class.
 	 * Abstract document tree node class.
 	 *
 	 *
@@ -49,7 +49,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @returns {Number|Null} Index of the node in the parent element or null if the node has not parent.
 		 * @returns {Number|Null} Index of the node in the parent element or null if the node has not parent.
 		 */
 		 */
 		getIndex() {
 		getIndex() {
-			var pos;
+			let pos;
 
 
 			if ( !this.parent ) {
 			if ( !this.parent ) {
 				return null;
 				return null;
@@ -75,8 +75,8 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @property {Number} depth
 		 * @property {Number} depth
 		 */
 		 */
 		get depth() {
 		get depth() {
-			var depth = 0;
-			var parent = this.parent;
+			let depth = 0;
+			let parent = this.parent;
 
 
 			while ( parent ) {
 			while ( parent ) {
 				depth++;
 				depth++;
@@ -94,7 +94,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @property {Number} depth
 		 * @property {Number} depth
 		 */
 		 */
 		get root() {
 		get root() {
-			var root = this; // jscs:ignore safeContextKeyword
+			let root = this; // jscs:ignore safeContextKeyword
 
 
 			while ( root.parent ) {
 			while ( root.parent ) {
 				root = root.parent;
 				root = root.parent;
@@ -110,7 +110,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @property {document.Node|null} nextSibling
 		 * @property {document.Node|null} nextSibling
 		 */
 		 */
 		get nextSibling() {
 		get nextSibling() {
-			var index = this.getIndex();
+			const index = this.getIndex();
 
 
 			return ( index !== null && this.parent.getChild( index + 1 ) ) || null;
 			return ( index !== null && this.parent.getChild( index + 1 ) ) || null;
 		}
 		}
@@ -122,7 +122,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @property {document.Node|null} previousSibling
 		 * @property {document.Node|null} previousSibling
 		 */
 		 */
 		get previousSibling() {
 		get previousSibling() {
-			var index = this.getIndex();
+			const index = this.getIndex();
 
 
 			return ( index !== null && this.parent.getChild( index - 1 ) ) || null;
 			return ( index !== null && this.parent.getChild( index - 1 ) ) || null;
 		}
 		}
@@ -135,7 +135,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @returns {Boolean} True if node contains given attribute or an attribute with the given key.
 		 * @returns {Boolean} True if node contains given attribute or an attribute with the given key.
 		 */
 		 */
 		hasAttr( key ) {
 		hasAttr( key ) {
-			var attr;
+			let attr;
 
 
 			// Attribute.
 			// Attribute.
 			if ( key instanceof Attribute ) {
 			if ( key instanceof Attribute ) {
@@ -164,7 +164,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @returns {document.Attribute} The found attribute.
 		 * @returns {document.Attribute} The found attribute.
 		 */
 		 */
 		getAttr( key ) {
 		getAttr( key ) {
-			for ( var attr of this._attrs ) {
+			for ( let attr of this._attrs ) {
 				if ( attr.key == key ) {
 				if ( attr.key == key ) {
 					return attr.value;
 					return attr.value;
 				}
 				}
@@ -179,7 +179,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @param {String} key The attribute key.
 		 * @param {String} key The attribute key.
 		 */
 		 */
 		removeAttr( key ) {
 		removeAttr( key ) {
-			for ( var attr of this._attrs ) {
+			for ( let attr of this._attrs ) {
 				if ( attr.key == key ) {
 				if ( attr.key == key ) {
 					this._attrs.delete( attr );
 					this._attrs.delete( attr );
 
 
@@ -206,8 +206,8 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @returns {Number[]} The path.
 		 * @returns {Number[]} The path.
 		 */
 		 */
 		getPath() {
 		getPath() {
-			var path = [];
-			var node = this; // jscs:ignore safeContextKeyword
+			const path = [];
+			let node = this; // jscs:ignore safeContextKeyword
 
 
 			while ( node.parent ) {
 			while ( node.parent ) {
 				path.unshift( node.getIndex() );
 				path.unshift( node.getIndex() );
@@ -223,7 +223,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		 * @returns {Object} Clone of this object with the parent property replaced with its name.
 		 * @returns {Object} Clone of this object with the parent property replaced with its name.
 		 */
 		 */
 		toJSON() {
 		toJSON() {
-			var json = utils.clone( this );
+			const json = utils.clone( this );
 
 
 			// Due to circular references we need to remove parent reference.
 			// Due to circular references we need to remove parent reference.
 			json.parent = this.parent ? this.parent.name : null;
 			json.parent = this.parent ? this.parent.name : null;
@@ -232,19 +232,14 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], function( A
 		}
 		}
 
 
 		/**
 		/**
-		 * Gets the number of attributes.
+		 * Returns attribute iterator. It can be use to create a new element with the same attributes:
 		 *
 		 *
-		 * @protected
-		 * @returns {Number} Number of attributes.
+		 *		const copy = new Element( element.name, element.getAttrs() );
+		 *
+		 * @returns {Iterable.<document.Attribute>} Attribute iterator.
 		 */
 		 */
-		_getAttrCount() {
-			var count = 0;
-
-			for ( var attr of this._attrs ) { // jshint ignore:line
-				count++;
-			}
-
-			return count;
+		getAttrs() {
+			return this._attrs[ Symbol.iterator ]();
 		}
 		}
 	}
 	}
 
 

+ 9 - 9
packages/ckeditor5-engine/src/document/nodelist.js

@@ -10,7 +10,7 @@ CKEDITOR.define( [
 	'document/text',
 	'document/text',
 	'document/node',
 	'document/node',
 	'utils'
 	'utils'
-], function( Character, Text, Node, utils ) {
+], ( Character, Text, Node, utils ) => {
 	/**
 	/**
 	 * List of nodes. It is used to represent multiple nodes with a given order, for example children of
 	 * List of nodes. It is used to represent multiple nodes with a given order, for example children of
 	 * {@link document.Element} object or nodes inserted using {@link document.operation.InsertOperation}.
 	 * {@link document.Element} object or nodes inserted using {@link document.operation.InsertOperation}.
@@ -25,26 +25,26 @@ CKEDITOR.define( [
 		/**
 		/**
 		 * Constructor let you create a list of nodes in many ways. See examples:
 		 * Constructor let you create a list of nodes in many ways. See examples:
 		 *
 		 *
-		 *		var nodeList = new NodeList( [ new Element( p1 ), new Element( p1 ) ] );
+		 *		let nodeList = new NodeList( [ new Element( p1 ), new Element( p1 ) ] );
 		 *		nodeList.length; // 2
 		 *		nodeList.length; // 2
 		 *
 		 *
-		 *		var nodeList = new NodeList( new Element( p ) );
+		 *		let nodeList = new NodeList( new Element( p ) );
 		 *		nodeList.length; // 1
 		 *		nodeList.length; // 1
 		 *
 		 *
-		 *		var nodeList = new NodeList( [ 'foo', new Element( p ), 'bar' ] );
+		 *		let nodeList = new NodeList( [ 'foo', new Element( p ), 'bar' ] );
 		 *		nodeList.length; // 7
 		 *		nodeList.length; // 7
 		 *
 		 *
-		 *		var nodeList = new NodeList( 'foo' );
+		 *		let nodeList = new NodeList( 'foo' );
 		 *		nodeList.length; // 3
 		 *		nodeList.length; // 3
 		 *
 		 *
-		 *		var nodeList = new NodeList( new Text( 'foo', [ new Attribute( 'bar', 'bom' ) ] ) );
+		 *		let nodeList = new NodeList( new Text( 'foo', [ new Attribute( 'bar', 'bom' ) ] ) );
 		 *		nodeList.length; // 3
 		 *		nodeList.length; // 3
 		 *		nodeList.get( 0 ).getAttr( 'bar' ); // 'bom'
 		 *		nodeList.get( 0 ).getAttr( 'bar' ); // 'bom'
 		 *		nodeList.get( 1 ).getAttr( 'bar' ); // 'bom'
 		 *		nodeList.get( 1 ).getAttr( 'bar' ); // 'bom'
 		 *		nodeList.get( 2 ).getAttr( 'bar' ); // 'bom'
 		 *		nodeList.get( 2 ).getAttr( 'bar' ); // 'bom'
 		 *
 		 *
-		 *		var nodeListA = new NodeList( 'foo' );
-		 *		var nodeListB = new NodeList( nodeListA );
+		 *		let nodeListA = new NodeList( 'foo' );
+		 *		let nodeListB = new NodeList( nodeListA );
 		 *		nodeListA === nodeListB // true
 		 *		nodeListA === nodeListB // true
 		 *		nodeListB.length // 3
 		 *		nodeListB.length // 3
 		 *
 		 *
@@ -66,7 +66,7 @@ CKEDITOR.define( [
 			this._nodes = [];
 			this._nodes = [];
 
 
 			if ( nodes ) {
 			if ( nodes ) {
-				var node, character;
+				let node, character;
 
 
 				if ( !utils.isIterable( nodes ) ) {
 				if ( !utils.isIterable( nodes ) ) {
 					nodes = [ nodes ];
 					nodes = [ nodes ];

+ 4 - 4
packages/ckeditor5-engine/src/document/operation/changeoperation.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [ 'document/operation/operation', 'ckeditorerror' ], function( Operation, CKEditorError ) {
+CKEDITOR.define( [ 'document/operation/operation', 'ckeditorerror' ], ( Operation, CKEditorError ) => {
 	/**
 	/**
 	 * Operation to change nodes' attribute. Using this class you can add, remove or change value of the attribute.
 	 * Operation to change nodes' attribute. Using this class you can add, remove or change value of the attribute.
 	 *
 	 *
@@ -59,9 +59,9 @@ CKEDITOR.define( [ 'document/operation/operation', 'ckeditorerror' ], function(
 		}
 		}
 
 
 		_execute() {
 		_execute() {
-			var oldAttr = this.oldAttr;
-			var newAttr = this.newAttr;
-			var value;
+			const oldAttr = this.oldAttr;
+			const newAttr = this.newAttr;
+			let value;
 
 
 			if ( oldAttr !== null && newAttr !== null && oldAttr.key != newAttr.key ) {
 			if ( oldAttr !== null && newAttr !== null && oldAttr.key != newAttr.key ) {
 				/**
 				/**

+ 2 - 2
packages/ckeditor5-engine/src/document/operation/insertoperation.js

@@ -9,7 +9,7 @@ CKEDITOR.define( [
 	'document/operation/operation',
 	'document/operation/operation',
 	'document/nodelist',
 	'document/nodelist',
 	'document/operation/removeoperation'
 	'document/operation/removeoperation'
-], function( Operation, NodeList ) {
+], ( Operation, NodeList ) => {
 	/**
 	/**
 	 * Operation to insert list of nodes on the given position in the tree data model.
 	 * Operation to insert list of nodes on the given position in the tree data model.
 	 *
 	 *
@@ -51,7 +51,7 @@ CKEDITOR.define( [
 
 
 		getReversed() {
 		getReversed() {
 			// Because of circular dependencies we need to re-require remove operation here.
 			// Because of circular dependencies we need to re-require remove operation here.
-			var RemoveOperation = CKEDITOR.require( 'document/operation/removeoperation' );
+			const RemoveOperation = CKEDITOR.require( 'document/operation/removeoperation' );
 
 
 			return new RemoveOperation( this.position, this.nodeList.length, this.baseVersion + 1 );
 			return new RemoveOperation( this.position, this.nodeList.length, this.baseVersion + 1 );
 		}
 		}

+ 9 - 9
packages/ckeditor5-engine/src/document/operation/moveoperation.js

@@ -10,7 +10,7 @@ CKEDITOR.define( [
 	'document/nodelist',
 	'document/nodelist',
 	'ckeditorerror',
 	'ckeditorerror',
 	'utils'
 	'utils'
-], function( Operation, NodeList, CKEditorError, utils ) {
+], ( Operation, NodeList, CKEditorError, utils ) => {
 	/**
 	/**
 	 * Operation to move list of subsequent nodes from one position in the document to another.
 	 * Operation to move list of subsequent nodes from one position in the document to another.
 	 *
 	 *
@@ -52,10 +52,10 @@ CKEDITOR.define( [
 		}
 		}
 
 
 		_execute() {
 		_execute() {
-			var sourceElement = this.sourcePosition.parent;
-			var targetElement = this.targetPosition.parent;
-			var sourceOffset = this.sourcePosition.offset;
-			var targetOffset = this.targetPosition.offset;
+			let sourceElement = this.sourcePosition.parent;
+			let targetElement = this.targetPosition.parent;
+			let sourceOffset = this.sourcePosition.offset;
+			let targetOffset = this.targetPosition.offset;
 
 
 			// Validate whether move operation has correct parameters.
 			// Validate whether move operation has correct parameters.
 			// Validation is pretty complex but move operation is one of the core ways to manipulate the document state.
 			// Validation is pretty complex but move operation is one of the core ways to manipulate the document state.
@@ -88,11 +88,11 @@ CKEDITOR.define( [
 					'operation-move-range-into-itself: Trying to move a range of nodes to the inside of that range.'
 					'operation-move-range-into-itself: Trying to move a range of nodes to the inside of that range.'
 				);
 				);
 			} else {
 			} else {
-				var sourcePath = this.sourcePosition.getParentPath();
-				var targetPath = this.targetPosition.getParentPath();
+				const sourcePath = this.sourcePosition.getParentPath();
+				const targetPath = this.targetPosition.getParentPath();
 
 
 				if ( utils.compareArrays( sourcePath, targetPath ) == utils.compareArrays.PREFIX ) {
 				if ( utils.compareArrays( sourcePath, targetPath ) == utils.compareArrays.PREFIX ) {
-					var i = sourcePath.length;
+					let i = sourcePath.length;
 
 
 					if ( this.targetPosition.path[ i ] >= sourceOffset && this.targetPosition.path[ i ] < sourceOffset + this.howMany ) {
 					if ( this.targetPosition.path[ i ] >= sourceOffset && this.targetPosition.path[ i ] < sourceOffset + this.howMany ) {
 						/**
 						/**
@@ -114,7 +114,7 @@ CKEDITOR.define( [
 				targetOffset -= this.howMany;
 				targetOffset -= this.howMany;
 			}
 			}
 
 
-			var removedNodes = sourceElement.removeChildren( sourceOffset, this.howMany );
+			const removedNodes = sourceElement.removeChildren( sourceOffset, this.howMany );
 
 
 			targetElement.insertChildren( targetOffset, removedNodes );
 			targetElement.insertChildren( targetOffset, removedNodes );
 		}
 		}

+ 9 - 1
packages/ckeditor5-engine/src/document/operation/operation.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [], function() {
+CKEDITOR.define( [], () => {
 	/**
 	/**
 	 * Abstract base operation class.
 	 * Abstract base operation class.
 	 *
 	 *
@@ -30,6 +30,14 @@ CKEDITOR.define( [], function() {
 			 */
 			 */
 			this.baseVersion = baseVersion;
 			this.baseVersion = baseVersion;
 
 
+			/**
+			 * {@link Document.Delta Delta} which the operation is a part of. This property is set by the
+			 * {@link Document.Delta delta} when the operations is added to it by the
+			 * {@link Document.Delta#addOperation} method.
+			 *
+			 * @property {Document.Delta} delta
+			 */
+
 			/**
 			/**
 			 * Executes the operation - modifications described by the operation attributes
 			 * Executes the operation - modifications described by the operation attributes
 			 * will be applied to the tree model.
 			 * will be applied to the tree model.

+ 2 - 2
packages/ckeditor5-engine/src/document/operation/reinsertoperation.js

@@ -8,7 +8,7 @@
 CKEDITOR.define( [
 CKEDITOR.define( [
 	'document/operation/moveoperation',
 	'document/operation/moveoperation',
 	'document/operation/removeoperation'
 	'document/operation/removeoperation'
-], function( MoveOperation ) {
+], ( MoveOperation ) => {
 	/**
 	/**
 	 * Operation to reinsert previously removed nodes back to the non-graveyard root.
 	 * Operation to reinsert previously removed nodes back to the non-graveyard root.
 	 * This is basically {@link document.operation.MoveOperation} but it returns
 	 * This is basically {@link document.operation.MoveOperation} but it returns
@@ -23,7 +23,7 @@ CKEDITOR.define( [
 	class ReinsertOperation extends MoveOperation {
 	class ReinsertOperation extends MoveOperation {
 		getReversed() {
 		getReversed() {
 			// Because of circular dependencies we need to re-require reinsert operation here.
 			// Because of circular dependencies we need to re-require reinsert operation here.
-			var RemoveOperation = CKEDITOR.require( 'document/operation/removeoperation' );
+			const RemoveOperation = CKEDITOR.require( 'document/operation/removeoperation' );
 
 
 			return new RemoveOperation( this.targetPosition, this.howMany, this.baseVersion + 1 );
 			return new RemoveOperation( this.targetPosition, this.howMany, this.baseVersion + 1 );
 		}
 		}

+ 4 - 4
packages/ckeditor5-engine/src/document/operation/removeoperation.js

@@ -9,7 +9,7 @@ CKEDITOR.define( [
 	'document/operation/moveoperation',
 	'document/operation/moveoperation',
 	'document/position',
 	'document/position',
 	'document/operation/reinsertoperation'
 	'document/operation/reinsertoperation'
-], function( MoveOperation, Position ) {
+], ( MoveOperation, Position ) => {
 	/**
 	/**
 	 * Operation to remove a range of nodes.
 	 * Operation to remove a range of nodes.
 	 *
 	 *
@@ -25,17 +25,17 @@ CKEDITOR.define( [
 		 * @constructor
 		 * @constructor
 		 */
 		 */
 		constructor( position, howMany, baseVersion ) {
 		constructor( position, howMany, baseVersion ) {
-			var graveyard = position.root.document._graveyard;
+			const graveyard = position.root.document._graveyard;
 
 
 			// Position in a graveyard where nodes were moved.
 			// Position in a graveyard where nodes were moved.
-			var graveyardPosition = Position.createFromParentAndOffset( graveyard, 0 );
+			const graveyardPosition = Position.createFromParentAndOffset( graveyard, 0 );
 
 
 			super( position, graveyardPosition, howMany, baseVersion );
 			super( position, graveyardPosition, howMany, baseVersion );
 		}
 		}
 
 
 		getReversed() {
 		getReversed() {
 			// Because of circular dependencies we need to re-require reinsert operation here.
 			// Because of circular dependencies we need to re-require reinsert operation here.
-			var ReinsertOperation = CKEDITOR.require( 'document/operation/reinsertoperation' );
+			const ReinsertOperation = CKEDITOR.require( 'document/operation/reinsertoperation' );
 
 
 			return new ReinsertOperation( this.targetPosition, this.sourcePosition, this.howMany, this.baseVersion + 1 );
 			return new ReinsertOperation( this.targetPosition, this.sourcePosition, this.howMany, this.baseVersion + 1 );
 		}
 		}

+ 4 - 4
packages/ckeditor5-engine/src/document/position.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [ 'utils', 'ckeditorerror' ], function( utils, CKEditorError ) {
+CKEDITOR.define( [ 'utils', 'ckeditorerror' ], ( utils, CKEditorError ) => {
 	/**
 	/**
 	 * Position in the tree. Position is always located before or after a node.
 	 * Position in the tree. Position is always located before or after a node.
 	 * See {@link #path} property for more information.
 	 * See {@link #path} property for more information.
@@ -55,9 +55,9 @@ CKEDITOR.define( [ 'utils', 'ckeditorerror' ], function( utils, CKEditorError )
 		 * @property {document.Element} parent
 		 * @property {document.Element} parent
 		 */
 		 */
 		get parent() {
 		get parent() {
-			var parent = this.root;
+			let parent = this.root;
 
 
-			var i, len;
+			let i, len;
 
 
 			for ( i = 0, len = this.path.length - 1; i < len; i++ ) {
 			for ( i = 0, len = this.path.length - 1; i < len; i++ ) {
 				parent = parent.getChild( this.path[ i ] );
 				parent = parent.getChild( this.path[ i ] );
@@ -125,7 +125,7 @@ CKEDITOR.define( [ 'utils', 'ckeditorerror' ], function( utils, CKEditorError )
 		 * @returns {document.Position}
 		 * @returns {document.Position}
 		 */
 		 */
 		static createFromParentAndOffset( parent, offset ) {
 		static createFromParentAndOffset( parent, offset ) {
-			var path = parent.getPath();
+			const path = parent.getPath();
 
 
 			path.push( offset );
 			path.push( offset );
 
 

+ 13 - 10
packages/ckeditor5-engine/src/document/positioniterator.js

@@ -9,10 +9,10 @@ CKEDITOR.define( [
 	'document/character',
 	'document/character',
 	'document/element',
 	'document/element',
 	'document/position'
 	'document/position'
-], function( Character, Element, Position ) {
-	var ELEMENT_ENTER = 0;
-	var ELEMENT_LEAVE = 1;
-	var CHARACTER = 2;
+], ( Character, Element, Position ) => {
+	const ELEMENT_ENTER = 0;
+	const ELEMENT_LEAVE = 1;
+	const CHARACTER = 2;
 
 
 	/**
 	/**
 	 * Position iterator class. It allows to iterate forward and backward over the tree document.
 	 * Position iterator class. It allows to iterate forward and backward over the tree document.
@@ -64,8 +64,11 @@ CKEDITOR.define( [
 		 * @returns {document.Node} return.value.node Encountered node.
 		 * @returns {document.Node} return.value.node Encountered node.
 		 */
 		 */
 		next() {
 		next() {
-			var position = this.position;
-			var parent = position.parent;
+			const position = this.position;
+			const parent = position.parent;
+
+			// Ugh... added here because of circular deps in AMD ;<.
+			Element = CKEDITOR.require( 'document/element' );
 
 
 			// We are at the end of the root.
 			// We are at the end of the root.
 			if ( parent.parent === null && position.offset === parent.getChildCount() ) {
 			if ( parent.parent === null && position.offset === parent.getChildCount() ) {
@@ -76,7 +79,7 @@ CKEDITOR.define( [
 				return { done: true };
 				return { done: true };
 			}
 			}
 
 
-			var nodeAfter = position.nodeAfter;
+			const nodeAfter = position.nodeAfter;
 
 
 			if ( nodeAfter instanceof Element ) {
 			if ( nodeAfter instanceof Element ) {
 				this.position = Position.createFromParentAndOffset( nodeAfter, 0 );
 				this.position = Position.createFromParentAndOffset( nodeAfter, 0 );
@@ -104,8 +107,8 @@ CKEDITOR.define( [
 		 * @returns {document.Node} return.value.node Scanned node.
 		 * @returns {document.Node} return.value.node Scanned node.
 		 */
 		 */
 		previous() {
 		previous() {
-			var position = this.position;
-			var parent = position.parent;
+			const position = this.position;
+			const parent = position.parent;
 
 
 			// We are at the beginning of the root.
 			// We are at the beginning of the root.
 			if ( parent.parent === null && position.offset === 0 ) {
 			if ( parent.parent === null && position.offset === 0 ) {
@@ -116,7 +119,7 @@ CKEDITOR.define( [
 				return { done: true };
 				return { done: true };
 			}
 			}
 
 
-			var nodeBefore = position.nodeBefore;
+			const nodeBefore = position.nodeBefore;
 
 
 			if ( nodeBefore instanceof Element ) {
 			if ( nodeBefore instanceof Element ) {
 				this.position = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );
 				this.position = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );

+ 27 - 1
packages/ckeditor5-engine/src/document/range.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [ 'document/positioniterator' ], function( PositionIterator ) {
+CKEDITOR.define( [ 'document/positioniterator', 'document/position' ], ( PositionIterator, Position ) => {
 	/**
 	/**
 	 * Range class. Range is iterable.
 	 * Range class. Range is iterable.
 	 *
 	 *
@@ -35,6 +35,32 @@ CKEDITOR.define( [ 'document/positioniterator' ], function( PositionIterator ) {
 			this.end = end;
 			this.end = end;
 		}
 		}
 
 
+		/**
+		 * Creates a range inside an element which starts before the first child and ends after the last child.
+		 *
+		 * @param {document.Element} element Element which is a parent for the range.
+		 * @returns {document.Range} Created range.
+		 */
+		static createFromElement( element ) {
+			return Range.createFromParentsAndOffsets( element, 0, element, element.getChildCount() );
+		}
+
+		/**
+		 * Creates a range from given parents and offsets.
+		 *
+		 * @param {document.Element} startElement Start position parent element.
+		 * @param {Number} startOffset Start position offset.
+		 * @param {document.Element} endElement End position parent element.
+		 * @param {Number} endOffset End position offset.
+		 * @returns {document.Range} Created range.
+		 */
+		static createFromParentsAndOffsets( startElement, startOffset, endElement, endOffset ) {
+			return new Range(
+					Position.createFromParentAndOffset( startElement, startOffset ),
+					Position.createFromParentAndOffset( endElement, endOffset )
+				);
+		}
+
 		/**
 		/**
 		 * Two ranges equal if their start and end positions equal.
 		 * Two ranges equal if their start and end positions equal.
 		 *
 		 *

+ 1 - 1
packages/ckeditor5-engine/src/document/rootelement.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [ 'document/element' ], function( Element ) {
+CKEDITOR.define( [ 'document/element' ], ( Element ) => {
 	/**
 	/**
 	 * Class for nodes that are roots of trees in tree data model.
 	 * Class for nodes that are roots of trees in tree data model.
 	 *
 	 *

+ 1 - 1
packages/ckeditor5-engine/src/document/text.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-CKEDITOR.define( [], function() {
+CKEDITOR.define( [], () => {
 	/**
 	/**
 	 * Data structure for text with attributes. Note that the `Text` is not a {@link document.Node},
 	 * Data structure for text with attributes. Note that the `Text` is not a {@link document.Node},
 	 * because it will never be part of the document tree. {@link document.Character is a node}.
 	 * because it will never be part of the document tree. {@link document.Character is a node}.

+ 21 - 0
packages/ckeditor5-engine/src/document/transaction.js

@@ -0,0 +1,21 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+// All deltas need to be loaded so they can register themselves as transaction methods.
+//
+// To solve circular dependencies (deltas need to require transaction class), transaction class body is moved
+// to document/delta/transaction-base.
+CKEDITOR.define( [
+	'document/delta/transaction-base',
+	'document/delta/insertdelta',
+	'document/delta/removedelta',
+	'document/delta/changedelta',
+	'document/delta/splitdelta',
+	'document/delta/mergedelta'
+], ( Transaction ) => {
+	return Transaction;
+} );

+ 10 - 10
packages/ckeditor5-engine/src/editor.js

@@ -19,7 +19,7 @@ CKEDITOR.define( [
 	'creator',
 	'creator',
 	'ckeditorerror',
 	'ckeditorerror',
 	'utils'
 	'utils'
-], function( Model, EditorConfig, PluginCollection, Creator, CKEditorError, utils ) {
+], ( Model, EditorConfig, PluginCollection, Creator, CKEditorError, utils ) => {
 	class Editor extends Model {
 	class Editor extends Model {
 		/**
 		/**
 		 * Creates a new instance of the Editor class.
 		 * Creates a new instance of the Editor class.
@@ -92,8 +92,8 @@ CKEDITOR.define( [
 		 * @returns {Promise} A promise which resolves once the initialization is completed.
 		 * @returns {Promise} A promise which resolves once the initialization is completed.
 		 */
 		 */
 		init() {
 		init() {
-			var that = this;
-			var config = this.config;
+			const that = this;
+			const config = this.config;
 
 
 			return loadPlugins()
 			return loadPlugins()
 				.then( initPlugins )
 				.then( initPlugins )
@@ -106,7 +106,7 @@ CKEDITOR.define( [
 
 
 			function initPlugins( loadedPlugins ) {
 			function initPlugins( loadedPlugins ) {
 				// Start with a resolved promise.
 				// Start with a resolved promise.
-				var promise = Promise.resolve();
+				let promise = Promise.resolve();
 
 
 				// Chain it with promises that resolve with the init() call of every plugin.
 				// Chain it with promises that resolve with the init() call of every plugin.
 				for ( let i = 0; i < loadedPlugins.length; i++ ) {
 				for ( let i = 0; i < loadedPlugins.length; i++ ) {
@@ -118,17 +118,17 @@ CKEDITOR.define( [
 			}
 			}
 
 
 			function findCreators() {
 			function findCreators() {
-				that.plugins.forEach( ( plugin, name ) => {
+				for ( let plugin of that.plugins ) {
 					if ( plugin instanceof Creator ) {
 					if ( plugin instanceof Creator ) {
-						that._creators.set( name, plugin );
+						that._creators.set( plugin.name, plugin );
 					}
 					}
-				} );
+				}
 			}
 			}
 
 
 			function fireCreator() {
 			function fireCreator() {
 				// Take the name of the creator to use (config or any of the registered ones).
 				// Take the name of the creator to use (config or any of the registered ones).
-				var creatorName = config.creator && ( 'creator-' + config.creator );
-				var creator;
+				const creatorName = config.creator && ( 'creator-' + config.creator );
+				let creator;
 
 
 				if ( creatorName ) {
 				if ( creatorName ) {
 					// Take the registered class for the given creator name.
 					// Take the registered class for the given creator name.
@@ -180,7 +180,7 @@ CKEDITOR.define( [
 		 * @returns {Promise} A promise that resolves once the editor instance is fully destroyed.
 		 * @returns {Promise} A promise that resolves once the editor instance is fully destroyed.
 		 */
 		 */
 		destroy() {
 		destroy() {
-			var that = this;
+			const that = this;
 
 
 			this.fire( 'destroy' );
 			this.fire( 'destroy' );
 
 

+ 2 - 2
packages/ckeditor5-engine/src/editorconfig.js

@@ -15,14 +15,14 @@
  * @extends Config
  * @extends Config
  */
  */
 
 
-CKEDITOR.define( [ 'ckeditor', 'config' ], function( CKE, Config ) {
+CKEDITOR.define( [ 'ckeditor', 'config' ], ( CKE, Config ) => {
 	class EditorConfig extends Config {
 	class EditorConfig extends Config {
 		/**
 		/**
 		 * @inheritdoc Config#get
 		 * @inheritdoc Config#get
 		 */
 		 */
 		get() {
 		get() {
 			// Try to take it from this editor instance.
 			// Try to take it from this editor instance.
-			var value = super.get.apply( this, arguments );
+			let value = super.get.apply( this, arguments );
 
 
 			// If the configuration is not defined in the instance, try to take it from CKEDITOR.config.
 			// If the configuration is not defined in the instance, try to take it from CKEDITOR.config.
 			if ( typeof value == 'undefined' ) {
 			if ( typeof value == 'undefined' ) {

+ 17 - 17
packages/ckeditor5-engine/src/emittermixin.js

@@ -12,8 +12,8 @@
  * @singleton
  * @singleton
  */
  */
 
 
-CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
-	var EmitterMixin = {
+CKEDITOR.define( [ 'eventinfo', 'utils' ], ( EventInfo, utils ) => {
+	const EmitterMixin = {
 		/**
 		/**
 		 * Registers a callback function to be executed when an event is fired.
 		 * Registers a callback function to be executed when an event is fired.
 		 *
 		 *
@@ -25,7 +25,7 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 		 * Lower values are called first.
 		 * Lower values are called first.
 		 */
 		 */
 		on( event, callback, ctx, priority ) {
 		on( event, callback, ctx, priority ) {
-			var callbacks = getCallbacks( this, event );
+			const callbacks = getCallbacks( this, event );
 
 
 			// Set the priority defaults.
 			// Set the priority defaults.
 			if ( typeof priority != 'number' ) {
 			if ( typeof priority != 'number' ) {
@@ -39,7 +39,7 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 			};
 			};
 
 
 			// Add the callback to the list in the right priority position.
 			// Add the callback to the list in the right priority position.
-			for ( var i = callbacks.length - 1; i >= 0; i-- ) {
+			for ( let i = callbacks.length - 1; i >= 0; i-- ) {
 				if ( callbacks[ i ].priority <= priority ) {
 				if ( callbacks[ i ].priority <= priority ) {
 					callbacks.splice( i + 1, 0, callback );
 					callbacks.splice( i + 1, 0, callback );
 
 
@@ -62,7 +62,7 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 		 * Lower values are called first.
 		 * Lower values are called first.
 		 */
 		 */
 		once( event, callback, ctx, priority ) {
 		once( event, callback, ctx, priority ) {
-			var onceCallback = function( event ) {
+			const onceCallback = function( event ) {
 				// Go off() at the first call.
 				// Go off() at the first call.
 				event.off();
 				event.off();
 
 
@@ -83,13 +83,13 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 		 * the same callback is used several times with different contexts.
 		 * the same callback is used several times with different contexts.
 		 */
 		 */
 		off( event, callback, ctx ) {
 		off( event, callback, ctx ) {
-			var callbacks = getCallbacksIfAny( this, event );
+			const callbacks = getCallbacksIfAny( this, event );
 
 
 			if ( !callbacks ) {
 			if ( !callbacks ) {
 				return;
 				return;
 			}
 			}
 
 
-			for ( var i = 0; i < callbacks.length; i++ ) {
+			for ( let i = 0; i < callbacks.length; i++ ) {
 				if ( callbacks[ i ].callback == callback ) {
 				if ( callbacks[ i ].callback == callback ) {
 					if ( !ctx || ctx == callbacks[ i ].ctx ) {
 					if ( !ctx || ctx == callbacks[ i ].ctx ) {
 						// Remove the callback from the list (fixing the next index).
 						// Remove the callback from the list (fixing the next index).
@@ -111,7 +111,7 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 		 * Lower values are called first.
 		 * Lower values are called first.
 		 */
 		 */
 		listenTo( emitter, event, callback, ctx, priority ) {
 		listenTo( emitter, event, callback, ctx, priority ) {
-			var emitters, emitterId, emitterInfo, eventCallbacks;
+			let emitters, emitterId, emitterInfo, eventCallbacks;
 
 
 			// _listeningTo contains a list of emitters that this object is listening to.
 			// _listeningTo contains a list of emitters that this object is listening to.
 			// This list has the following format:
 			// This list has the following format:
@@ -167,10 +167,10 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 		 * `event`.
 		 * `event`.
 		 */
 		 */
 		stopListening( emitter, event, callback ) {
 		stopListening( emitter, event, callback ) {
-			var emitters = this._listeningTo;
-			var emitterId = emitter && emitter._emitterId;
-			var emitterInfo = emitters && emitterId && emitters[ emitterId ];
-			var eventCallbacks = emitterInfo && event && emitterInfo.callbacks[ event ];
+			let emitters = this._listeningTo;
+			let emitterId = emitter && emitter._emitterId;
+			let emitterInfo = emitters && emitterId && emitters[ emitterId ];
+			let eventCallbacks = emitterInfo && event && emitterInfo.callbacks[ event ];
 
 
 			// Stop if nothing has been listened.
 			// Stop if nothing has been listened.
 			if ( !emitters || ( emitter && !emitterInfo ) || ( event && !eventCallbacks ) ) {
 			if ( !emitters || ( emitter && !emitterInfo ) || ( event && !eventCallbacks ) ) {
@@ -214,19 +214,19 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 		 * @param {...*} [args] Additional arguments to be passed to the callbacks.
 		 * @param {...*} [args] Additional arguments to be passed to the callbacks.
 		 */
 		 */
 		fire( event, args ) {
 		fire( event, args ) {
-			var callbacks = getCallbacksIfAny( this, event );
+			const callbacks = getCallbacksIfAny( this, event );
 
 
 			if ( !callbacks ) {
 			if ( !callbacks ) {
 				return;
 				return;
 			}
 			}
 
 
-			var eventInfo = new EventInfo( this, event );
+			let eventInfo = new EventInfo( this, event );
 
 
 			// Take the list of arguments to pass to the callbacks.
 			// Take the list of arguments to pass to the callbacks.
 			args = Array.prototype.slice.call( arguments, 1 );
 			args = Array.prototype.slice.call( arguments, 1 );
 			args.unshift( eventInfo );
 			args.unshift( eventInfo );
 
 
-			for ( var i = 0; i < callbacks.length; i++ ) {
+			for ( let i = 0; i < callbacks.length; i++ ) {
 				callbacks[ i ].callback.apply( callbacks[ i ].ctx, args );
 				callbacks[ i ].callback.apply( callbacks[ i ].ctx, args );
 
 
 				// Remove the callback from future requests if off() has been called.
 				// Remove the callback from future requests if off() has been called.
@@ -262,7 +262,7 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 
 
 	// Gets the list of callbacks for a given event.
 	// Gets the list of callbacks for a given event.
 	function getCallbacks( source, eventName ) {
 	function getCallbacks( source, eventName ) {
-		var events = getEvents( source );
+		const events = getEvents( source );
 
 
 		if ( !events[ eventName ] ) {
 		if ( !events[ eventName ] ) {
 			events[ eventName ] = [];
 			events[ eventName ] = [];
@@ -273,7 +273,7 @@ CKEDITOR.define( [ 'eventinfo', 'utils' ], function( EventInfo, utils ) {
 
 
 	// Get the list of callbacks for a given event only if there is any available.
 	// Get the list of callbacks for a given event only if there is any available.
 	function getCallbacksIfAny( source, event ) {
 	function getCallbacksIfAny( source, event ) {
-		var callbacks;
+		let callbacks;
 
 
 		if ( !source._events || !( callbacks = source._events[ event ] ) || !callbacks.length ) {
 		if ( !source._events || !( callbacks = source._events[ event ] ) || !callbacks.length ) {
 			return null;
 			return null;

+ 1 - 1
packages/ckeditor5-engine/src/eventinfo.js

@@ -12,7 +12,7 @@
  * @class EventInfo
  * @class EventInfo
  */
  */
 
 
-CKEDITOR.define( [ 'utils' ], function( utils ) {
+CKEDITOR.define( [ 'utils' ], ( utils ) => {
 	class EventInfo {
 	class EventInfo {
 		constructor( source, name ) {
 		constructor( source, name ) {
 			/**
 			/**

+ 2 - 2
packages/ckeditor5-engine/src/log.js

@@ -39,8 +39,8 @@
  * @singleton
  * @singleton
  */
  */
 
 
-CKEDITOR.define( function() {
-	var log = {
+CKEDITOR.define( () => {
+	const log = {
 		/**
 		/**
 		 * Logs an error to the console.
 		 * Logs an error to the console.
 		 *
 		 *

+ 28 - 5
packages/ckeditor5-engine/src/model.js

@@ -12,7 +12,7 @@
  * @mixins EventEmitter
  * @mixins EventEmitter
  */
  */
 
 
-CKEDITOR.define( [ 'emittermixin', 'utils' ], function( EmitterMixin, utils ) {
+CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], ( EmitterMixin, CKEditorError, utils ) => {
 	class Model {
 	class Model {
 		/**
 		/**
 		 * Creates a new Model instance.
 		 * Creates a new Model instance.
@@ -47,29 +47,52 @@ CKEDITOR.define( [ 'emittermixin', 'utils' ], function( EmitterMixin, utils ) {
 		 *
 		 *
 		 * It accepts also a single object literal containing key/value pairs with attributes to be set.
 		 * It accepts also a single object literal containing key/value pairs with attributes to be set.
 		 *
 		 *
+		 * This method throws the {@link model-set-cannot-override} error if the model instance already
+		 * have a property with a given attribute name. This prevents from mistakenly overriding existing
+		 * properties and methods, but means that `foo.set( 'bar', 1 )` may be slightly slower than `foo.bar = 1`.
+		 *
 		 * @param {String} name The attributes name.
 		 * @param {String} name The attributes name.
 		 * @param {*} value The attributes value.
 		 * @param {*} value The attributes value.
 		 */
 		 */
 		set( name, value ) {
 		set( name, value ) {
 			// If the first parameter is an Object, we gonna interact through its properties.
 			// If the first parameter is an Object, we gonna interact through its properties.
 			if ( utils.isObject( name ) ) {
 			if ( utils.isObject( name ) ) {
-				Object.keys( name ).forEach( function( attr ) {
+				Object.keys( name ).forEach( ( attr ) => {
 					this.set( attr, name[ attr ] );
 					this.set( attr, name[ attr ] );
 				}, this );
 				}, this );
 
 
 				return;
 				return;
 			}
 			}
 
 
+			if ( ( name in this ) && !( name in this._attributes ) ) {
+				/**
+				 * Cannot override an existing property.
+				 *
+				 * This error is thrown when trying to {@link Model#set set} an attribute with
+				 * a name of an already existing property. For example:
+				 *
+				 *		let model = new Model();
+				 *		model.property = 1;
+				 *		model.set( 'property', 2 );		// throws
+				 *
+				 *		model.set( 'attr', 1 );
+				 *		model.set( 'attr', 2 );			// ok, because this is an existing attribute.
+				 *
+				 * @error model-set-cannot-override
+				 */
+				throw new CKEditorError( 'model-set-cannot-override: Cannot override an existing property.' );
+			}
+
 			Object.defineProperty( this, name, {
 			Object.defineProperty( this, name, {
 				enumerable: true,
 				enumerable: true,
 				configurable: true,
 				configurable: true,
 
 
-				get: function() {
+				get: () => {
 					return this._attributes[ name ];
 					return this._attributes[ name ];
 				},
 				},
 
 
-				set: function( value ) {
-					var oldValue = this._attributes[ name ];
+				set: ( value ) => {
+					const oldValue = this._attributes[ name ];
 
 
 					if ( oldValue !== value ) {
 					if ( oldValue !== value ) {
 						this._attributes[ name ] = value;
 						this._attributes[ name ] = value;

+ 0 - 195
packages/ckeditor5-engine/src/namedcollection.js

@@ -1,195 +0,0 @@
-/**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-/**
- * Named collections are key => model maps.
- *
- * See also {@link core/Collection}.
- *
- * @class NamedCollection
- * @mixins EventEmitter
- */
-
-CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], function( EmitterMixin, CKEditorError, utils ) {
-	class NamedCollection {
-		/**
-		 * Creates a new NamedCollection instance.
-		 *
-		 * @constructor
-		 */
-		constructor() {
-			/**
-			 * The internal map of models in the collection.
-			 *
-			 * @property _models
-			 * @private
-			 */
-			this._models = new Map();
-		}
-
-		/**
-		 * The number of items available in the collection.
-		 *
-		 * @property length
-		 */
-		get length() {
-			return this._models.size;
-		}
-
-		/**
-		 * Adds an item into the collection.
-		 *
-		 * Throws exception if an item with this name already exists or if the item does not have a name.
-		 *
-		 * @param {Model} model The item to be added.
-		 */
-		add( model ) {
-			var name = model.name;
-
-			if ( !name || this._models.has( name ) ) {
-				/**
-				 * Model isn't named or such model already exists in this collection
-				 *
-				 * Thrown when:
-				 *
-				 * * Model without a name was given.
-				 * * Model with this name already exists in the collection.
-				 *
-				 * @error namedcollection-add
-				 * @param {String} name Name of the model.
-				 */
-				throw new CKEditorError(
-					'namedcollection-add: Model isn\'t named or such model already exists in this collection',
-					{ name: name }
-				);
-			}
-
-			this._models.set( name, model );
-
-			this.fire( 'add', model );
-		}
-
-		/**
-		 * Gets one item from the collection.
-		 *
-		 * @param {String} name The name of the item to take.
-		 * @returns {Model} The requested item or `null` if such item does not exist.
-		 */
-		get( name ) {
-			return this._models.get( name ) || null;
-		}
-
-		/**
-		 * Removes an item from the collection.
-		 *
-		 * @param {Model|String} modelOrName Either the item itself (it must have a `name` property)
-		 * or its name inside the collection.
-		 * @returns {Model} The removed item.
-		 */
-		remove( modelOrName ) {
-			var nameGiven = typeof modelOrName == 'string';
-			var name = nameGiven ? modelOrName : modelOrName.name;
-			var model = this._models.get( name );
-
-			if ( nameGiven ? !model : ( model !== modelOrName ) ) {
-				/**
-				 * Model not found or other model exists under its name.
-				 *
-				 * Thrown when:
-				 *
-				 * * a model without a name was given,
-				 * * no model found when a name was given,
-				 * * a model was given and it does not exist in the collection or some other model was found under its name.
-				 *
-				 * @error namedcollection-remove
-				 * @param {String} name Name of the model to remove.
-				 * @param {Model} model The model which was found under the given name.
-				 */
-				throw new CKEditorError(
-					'namedcollection-remove: Model not found or other model exists under its name.',
-					{ name: name, model: model }
-				);
-			}
-
-			this._models.delete( name );
-
-			this.fire( 'remove', model );
-
-			return model;
-		}
-
-		/**
-		 * Executes the callback for each model in the collection.
-		 *
-		 * @param {Function} callback
-		 * @param {Model} callback.item
-		 * @param {String} callback.index
-		 * @params {Object} ctx Context in which the `callback` will be called.
-		 */
-		forEach( callback, ctx ) {
-			this._models.forEach( callback, ctx );
-		}
-
-		/**
-		 * Finds the first item in the collection for which the `callback` returns a true value.
-		 *
-		 * @param {Function} callback
-		 * @param {Model} callback.item
-		 * @param {String} callback.name
-		 * @params {Object} ctx Context in which the `callback` will be called.
-		 * @returns {Model} The first item for which `callback` returned a true value.
-		 */
-		find( callback, ctx ) {
-			// TODO: Use ES6 destructuring.
-			for ( let pair of this._models ) {
-				if ( callback.call( ctx, pair[ 1 ], pair[ 0 ] ) ) {
-					return pair[ 1 ];
-				}
-			}
-		}
-
-		/**
-		 * Returns an object (`name => item`) with items for which the `callback` returned a true value.
-		 *
-		 * @param {Function} callback
-		 * @param {Model} callback.item
-		 * @param {String} callback.name
-		 * @params {Object} ctx Context in which the `callback` will be called.
-		 * @returns {Object} The object with matching items.
-		 */
-		filter( callback, ctx ) {
-			var ret = {};
-
-			// TODO: Use ES6 destructuring.
-			for ( let pair of this._models ) {
-				if ( callback.call( ctx, pair[ 1 ], pair[ 0 ] ) ) {
-					ret[ pair[ 0 ] ] = pair[ 1 ];
-				}
-			}
-
-			return ret;
-		}
-	}
-
-	utils.extend( NamedCollection.prototype, EmitterMixin );
-
-	return NamedCollection;
-} );
-
-/**
- * Fired when an item is added to the collection.
- *
- * @event add
- * @param {Model} model The added item.
- */
-
-/**
- * Fired when an item is removed from the collection.
- *
- * @event remove
- * @param {Model} model The removed item.
- */

+ 1 - 1
packages/ckeditor5-engine/src/plugin.js

@@ -12,7 +12,7 @@
  * @extends Model
  * @extends Model
  */
  */
 
 
-CKEDITOR.define( [ 'model' ], function( Model ) {
+CKEDITOR.define( [ 'model' ], ( Model ) => {
 	class Plugin extends Model {
 	class Plugin extends Model {
 		constructor( editor ) {
 		constructor( editor ) {
 			super();
 			super();

+ 22 - 29
packages/ckeditor5-engine/src/plugincollection.js

@@ -13,19 +13,19 @@
  */
  */
 
 
 CKEDITOR.define( [
 CKEDITOR.define( [
-	'namedcollection',
+	'collection',
 	'plugin',
 	'plugin',
 	'ckeditorerror',
 	'ckeditorerror',
 	'log'
 	'log'
-], function( NamedCollection, Plugin, CKEditorError, log ) {
-	class PluginCollection extends NamedCollection {
+], ( Collection, Plugin, CKEditorError, log ) => {
+	class PluginCollection extends Collection {
 		/**
 		/**
 		 * Creates an instance of the PluginCollection class, initializing it with a set of plugins.
 		 * Creates an instance of the PluginCollection class, initializing it with a set of plugins.
 		 *
 		 *
 		 * @constructor
 		 * @constructor
 		 */
 		 */
 		constructor( editor ) {
 		constructor( editor ) {
-			super();
+			super( { idProperty: 'name' } );
 
 
 			this._editor = editor;
 			this._editor = editor;
 		}
 		}
@@ -39,39 +39,36 @@ CKEDITOR.define( [
 		 * @param {core/Plugin[]} returns.loadedPlugins The array of loaded plugins.
 		 * @param {core/Plugin[]} returns.loadedPlugins The array of loaded plugins.
 		 */
 		 */
 		load( plugins ) {
 		load( plugins ) {
-			var that = this;
-
 			// The list of plugins which are being loaded (to avoid circular references issues).
 			// The list of plugins which are being loaded (to avoid circular references issues).
-			var loading = {};
+			const loading = {};
 			// Plugins added to the collection (for the purpose of returning an array of loaded plugins).
 			// Plugins added to the collection (for the purpose of returning an array of loaded plugins).
-			var loaded = [];
+			const loaded = [];
 
 
 			// It may happen that an empty list was passed – don't fail.
 			// It may happen that an empty list was passed – don't fail.
 			plugins = plugins ? plugins.split( ',' ) : [];
 			plugins = plugins ? plugins.split( ',' ) : [];
 
 
 			// Creates a promise for the loading of each plugin and returns a main promise that resolves when all are
 			// Creates a promise for the loading of each plugin and returns a main promise that resolves when all are
 			// done.
 			// done.
-			return Promise.all( plugins.map( pluginPromise ) )
-				.then( function() {
-					return loaded;
-				} );
+			return Promise.all( plugins.map( pluginPromise, this ) )
+				.then( () => loaded );
 
 
 			// Returns a promise that will load the plugin and add it to the collection before resolving.
 			// Returns a promise that will load the plugin and add it to the collection before resolving.
 			function pluginPromise( plugin ) {
 			function pluginPromise( plugin ) {
-				return new Promise( function( resolve, reject ) {
+				return new Promise( ( resolve, reject ) => {
 					// Do nothing if the plugin is already loaded (or if is being loaded right now).
 					// Do nothing if the plugin is already loaded (or if is being loaded right now).
-					if ( that._models.get( plugin ) || loading[ plugin ] ) {
+					if ( this.get( plugin ) || loading[ plugin ] ) {
 						return resolve();
 						return resolve();
 					}
 					}
 
 
 					CKEDITOR.require( [ 'plugin!' + plugin ],
 					CKEDITOR.require( [ 'plugin!' + plugin ],
 						// Success callback.
 						// Success callback.
-						function( LoadedPlugin ) {
-							var deps = getPluginDeps( plugin );
-							var isPluginDep = plugin.indexOf( '/' ) > 0;
+						( LoadedPlugin ) => {
+							const deps = getPluginDeps( plugin );
+							const isPluginDep = plugin.indexOf( '/' ) > 0;
+							let loadedPlugin;
 
 
 							if ( !isPluginDep ) {
 							if ( !isPluginDep ) {
-								var loadedPlugin = new LoadedPlugin( that._editor );
+								loadedPlugin = new LoadedPlugin( this._editor );
 
 
 								if ( !( loadedPlugin instanceof Plugin ) ) {
 								if ( !( loadedPlugin instanceof Plugin ) ) {
 									/**
 									/**
@@ -97,19 +94,19 @@ CKEDITOR.define( [
 
 
 							// Resolve with a promise that resolves once all dependencies are loaded.
 							// Resolve with a promise that resolves once all dependencies are loaded.
 							resolve(
 							resolve(
-								Promise.all( deps.map( pluginPromise ) )
-									.then( function() {
+								Promise.all( deps.map( pluginPromise, this ) )
+									.then( () => {
 										// Once dependencies are loaded, add the new instance of the loaded plugin to
 										// Once dependencies are loaded, add the new instance of the loaded plugin to
 										// the collection. This guarantees that dependecies come first in the collection.
 										// the collection. This guarantees that dependecies come first in the collection.
 										if ( !isPluginDep ) {
 										if ( !isPluginDep ) {
-											that.add( loadedPlugin );
+											this.add( loadedPlugin );
 											loaded.push( loadedPlugin );
 											loaded.push( loadedPlugin );
 										}
 										}
 									} )
 									} )
 							);
 							);
 						},
 						},
 						// Error callback.
 						// Error callback.
-						function( err ) {
+						( err ) => {
 							/**
 							/**
 							 * It was not possible to load the plugin.
 							 * It was not possible to load the plugin.
 							 *
 							 *
@@ -125,17 +122,13 @@ CKEDITOR.define( [
 
 
 			function getPluginDeps( name ) {
 			function getPluginDeps( name ) {
 				// Get the list of AMD modules that the plugin depends on.
 				// Get the list of AMD modules that the plugin depends on.
-				var deps = CKEDITOR._dependencies[ 'plugin!' + name ] || [];
+				let deps = CKEDITOR._dependencies[ 'plugin!' + name ] || [];
 
 
 				deps = deps
 				deps = deps
 					// Pick only dependencies that are other plugins.
 					// Pick only dependencies that are other plugins.
-					.filter( function( dep ) {
-						return dep.indexOf( 'plugin!' ) === 0;
-					} )
+					.filter( dep => dep.indexOf( 'plugin!' ) === 0 )
 					// Remove the 'plugin!' prefix.
 					// Remove the 'plugin!' prefix.
-					.map( function( dep ) {
-						return dep.substr( 7 );
-					} );
+					.map( dep => dep.substr( 7 ) );
 
 
 				return deps;
 				return deps;
 			}
 			}

+ 12 - 12
packages/ckeditor5-engine/src/ui/domemittermixin.js

@@ -36,8 +36,8 @@
  * @singleton
  * @singleton
  */
  */
 
 
-CKEDITOR.define( [ 'emittermixin', 'utils', 'log' ], function( EmitterMixin, utils, log ) {
-	var DOMEmitterMixin = {
+CKEDITOR.define( [ 'emittermixin', 'utils', 'log' ], ( EmitterMixin, utils, log ) => {
+	const DOMEmitterMixin = {
 		/**
 		/**
 		 * Registers a callback function to be executed when an event is fired in a specific Emitter or DOM Node.
 		 * 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 EmitterMixin#listenTo}.
 		 * It is backwards compatible with {@link EmitterMixin#listenTo}.
@@ -50,8 +50,8 @@ CKEDITOR.define( [ 'emittermixin', 'utils', 'log' ], function( EmitterMixin, uti
 		 * Lower values are called first.
 		 * Lower values are called first.
 		 */
 		 */
 		listenTo() {
 		listenTo() {
-			var args = Array.prototype.slice.call( arguments );
-			var emitter = args[ 0 ];
+			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
 			// Check if emitter is an instance of DOM Node. If so, replace the argument with
 			// corresponding ProxyEmitter (or create one if not existing).
 			// corresponding ProxyEmitter (or create one if not existing).
@@ -79,12 +79,12 @@ CKEDITOR.define( [ 'emittermixin', 'utils', 'log' ], function( EmitterMixin, uti
 		 * `event`.
 		 * `event`.
 		 */
 		 */
 		stopListening() {
 		stopListening() {
-			var args = Array.prototype.slice.call( arguments );
-			var emitter = args[ 0 ];
+			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.
 			// Check if emitter is an instance of DOM Node. If so, replace the argument with corresponding ProxyEmitter.
 			if ( emitter instanceof Node ) {
 			if ( emitter instanceof Node ) {
-				var proxy = this._getProxyEmitter( emitter );
+				let proxy = this._getProxyEmitter( emitter );
 
 
 				if ( proxy ) {
 				if ( proxy ) {
 					args[ 0 ] = proxy;
 					args[ 0 ] = proxy;
@@ -107,10 +107,10 @@ CKEDITOR.define( [ 'emittermixin', 'utils', 'log' ], function( EmitterMixin, uti
 		 * @return {ProxyEmitter} ProxyEmitter instance or null.
 		 * @return {ProxyEmitter} ProxyEmitter instance or null.
 		 */
 		 */
 		_getProxyEmitter( node ) {
 		_getProxyEmitter( node ) {
-			var proxy, emitters, emitterInfo;
+			let proxy, emitters, emitterInfo;
 
 
 			// Get node UID. It allows finding Proxy Emitter for this DOM Node.
 			// Get node UID. It allows finding Proxy Emitter for this DOM Node.
-			var uid = getNodeUID( node );
+			const uid = getNodeUID( node );
 
 
 			// Find existing Proxy Emitter for this DOM Node among emitters.
 			// Find existing Proxy Emitter for this DOM Node among emitters.
 			if ( ( emitters = this._listeningTo ) ) {
 			if ( ( emitters = this._listeningTo ) ) {
@@ -172,7 +172,7 @@ CKEDITOR.define( [ 'emittermixin', 'utils', 'log' ], function( EmitterMixin, uti
 				return;
 				return;
 			}
 			}
 
 
-			var domListener = this._createDomListener( event );
+			const domListener = this._createDomListener( event );
 
 
 			// Attach the native DOM listener to DOM Node.
 			// Attach the native DOM listener to DOM Node.
 			this._domNode.addEventListener( event, domListener );
 			this._domNode.addEventListener( event, domListener );
@@ -198,7 +198,7 @@ CKEDITOR.define( [ 'emittermixin', 'utils', 'log' ], function( EmitterMixin, uti
 			// Execute parent class method first.
 			// Execute parent class method first.
 			EmitterMixin.off.apply( this, arguments );
 			EmitterMixin.off.apply( this, arguments );
 
 
-			var callbacks;
+			let callbacks;
 
 
 			// Remove native DOM listeners which are orphans. If no callbacks
 			// Remove native DOM listeners which are orphans. If no callbacks
 			// are awaiting given event, detach native DOM listener from DOM Node.
 			// are awaiting given event, detach native DOM listener from DOM Node.
@@ -217,7 +217,7 @@ CKEDITOR.define( [ 'emittermixin', 'utils', 'log' ], function( EmitterMixin, uti
 		 * @returns {Function} The DOM listener callback.
 		 * @returns {Function} The DOM listener callback.
 		 */
 		 */
 		_createDomListener( event ) {
 		_createDomListener( event ) {
-			var domListener = domEvt => {
+			const domListener = domEvt => {
 				this.fire( event, domEvt );
 				this.fire( event, domEvt );
 			};
 			};
 
 

+ 4 - 2
packages/ckeditor5-engine/src/ui/region.js

@@ -12,7 +12,7 @@
  * @extends Model
  * @extends Model
  */
  */
 
 
-CKEDITOR.define( [ 'collection', 'model' ], function( Collection, Model ) {
+CKEDITOR.define( [ 'collection', 'model' ], ( Collection, Model ) => {
 	class Region extends Model {
 	class Region extends Model {
 		/**
 		/**
 		 * Creates an instance of the {@link Region} class.
 		 * Creates an instance of the {@link Region} class.
@@ -53,7 +53,9 @@ CKEDITOR.define( [ 'collection', 'model' ], function( Collection, Model ) {
 			this.el = null;
 			this.el = null;
 
 
 			// Remove and destroy views.
 			// Remove and destroy views.
-			this.views.forEach( v => this.views.remove( v ).destroy() );
+			for ( let view of this.views ) {
+				this.views.remove( view ).destroy();
+			}
 		}
 		}
 	}
 	}
 
 

+ 8 - 8
packages/ckeditor5-engine/src/ui/template.js

@@ -13,7 +13,7 @@
  * @class Template
  * @class Template
  */
  */
 
 
-CKEDITOR.define( function() {
+CKEDITOR.define( () => {
 	class Template {
 	class Template {
 		/**
 		/**
 		 * Creates an instance of the {@link Template} class.
 		 * Creates an instance of the {@link Template} class.
@@ -53,7 +53,7 @@ CKEDITOR.define( function() {
 			return null;
 			return null;
 		}
 		}
 
 
-		var el = document.createElement( def.tag );
+		const el = document.createElement( def.tag );
 
 
 		// Set the text first.
 		// Set the text first.
 		renderElementText( def, el );
 		renderElementText( def, el );
@@ -81,8 +81,8 @@ CKEDITOR.define( function() {
 	}
 	}
 
 
 	function renderElementAttributes( def, el ) {
 	function renderElementAttributes( def, el ) {
-		var value;
-		var attr;
+		let value;
+		let attr;
 
 
 		for ( attr in def.attrs ) {
 		for ( attr in def.attrs ) {
 			value = def.attrs[ attr ];
 			value = def.attrs[ attr ];
@@ -105,7 +105,7 @@ CKEDITOR.define( function() {
 	}
 	}
 
 
 	function renderElementChildren( def, el ) {
 	function renderElementChildren( def, el ) {
-		var child;
+		let child;
 
 
 		if ( def.children ) {
 		if ( def.children ) {
 			for ( child of def.children ) {
 			for ( child of def.children ) {
@@ -116,9 +116,9 @@ CKEDITOR.define( function() {
 
 
 	function activateElementListeners( def, el ) {
 	function activateElementListeners( def, el ) {
 		if ( def.on ) {
 		if ( def.on ) {
-			for ( var l in def.on ) {
-				var domEvtDef = l.split( '@' );
-				var name, selector;
+			for ( let l in def.on ) {
+				let domEvtDef = l.split( '@' );
+				let name, selector;
 
 
 				if ( domEvtDef.length == 2 ) {
 				if ( domEvtDef.length == 2 ) {
 					name = domEvtDef[ 0 ];
 					name = domEvtDef[ 0 ];

+ 7 - 5
packages/ckeditor5-engine/src/ui/view.js

@@ -14,13 +14,13 @@
  */
  */
 
 
 CKEDITOR.define( [
 CKEDITOR.define( [
-	'namedcollection',
+	'collection',
 	'model',
 	'model',
 	'ui/template',
 	'ui/template',
 	'ckeditorerror',
 	'ckeditorerror',
 	'ui/domemittermixin',
 	'ui/domemittermixin',
 	'utils'
 	'utils'
-], function( NamedCollection, Model, Template, CKEditorError, DOMEmitterMixin, utils ) {
+], ( Collection, Model, Template, CKEditorError, DOMEmitterMixin, utils ) => {
 	class View extends Model {
 	class View extends Model {
 		/**
 		/**
 		 * Creates an instance of the {@link View} class.
 		 * Creates an instance of the {@link View} class.
@@ -39,7 +39,7 @@ CKEDITOR.define( [
 			/**
 			/**
 			 * Regions which belong to this view.
 			 * Regions which belong to this view.
 			 */
 			 */
-			this.regions = new NamedCollection();
+			this.regions = new Collection( null, 'name' );
 
 
 			/**
 			/**
 			 * @property {HTMLElement} _el
 			 * @property {HTMLElement} _el
@@ -95,7 +95,7 @@ CKEDITOR.define( [
 				onModelChange( null, this.model[ property ] );
 				onModelChange( null, this.model[ property ] );
 
 
 				function onModelChange( evt, value ) {
 				function onModelChange( evt, value ) {
-					var processedValue = callback( el, value );
+					let processedValue = callback( el, value );
 
 
 					if ( typeof processedValue != 'undefined' ) {
 					if ( typeof processedValue != 'undefined' ) {
 						domUpdater( el, processedValue );
 						domUpdater( el, processedValue );
@@ -144,7 +144,9 @@ CKEDITOR.define( [
 			}
 			}
 
 
 			// Remove and destroy regions.
 			// Remove and destroy regions.
-			this.regions.forEach( r => this.regions.remove( r ).destroy() );
+			for ( let region of this.regions ) {
+				this.regions.remove( region ).destroy();
+			}
 
 
 			// Remove all listeners related to this view.
 			// Remove all listeners related to this view.
 			this.stopListening();
 			this.stopListening();

+ 3 - 3
packages/ckeditor5-engine/src/utils-lodash.js

@@ -13,10 +13,10 @@
 //
 //
 // https://lodash.com/docs
 // https://lodash.com/docs
 
 
-( function() {
+( () => {
 	// The list of Lo-Dash methods to include in "utils".
 	// The list of Lo-Dash methods to include in "utils".
 	// It is mandatory to execute `grunt lodash` after changes to this list.
 	// It is mandatory to execute `grunt lodash` after changes to this list.
-	var lodashInclude = [
+	const lodashInclude = [
 		/**
 		/**
 		 * See Lo-Dash: https://lodash.com/docs#clone
 		 * See Lo-Dash: https://lodash.com/docs#clone
 		 *
 		 *
@@ -79,7 +79,7 @@
 	if ( typeof module == 'object' && module.exports ) {
 	if ( typeof module == 'object' && module.exports ) {
 		module.exports = lodashInclude;
 		module.exports = lodashInclude;
 	} else {
 	} else {
-		CKEDITOR.define( function() {
+		CKEDITOR.define( () => {
 			return lodashInclude;
 			return lodashInclude;
 		} );
 		} );
 	}
 	}

+ 12 - 11
packages/ckeditor5-engine/src/utils.js

@@ -12,8 +12,8 @@
  * @singleton
  * @singleton
  */
  */
 
 
-CKEDITOR.define( [ 'utils-lodash', 'lib/lodash/lodash-ckeditor' ], function( lodashIncludes, lodash ) {
-	var utils = {
+CKEDITOR.define( [ 'utils-lodash', 'lib/lodash/lodash-ckeditor' ], ( lodashIncludes, lodash ) => {
+	const utils = {
 		/**
 		/**
 		 * Creates a spy function (ala Sinon.js) that can be used to inspect call to it.
 		 * Creates a spy function (ala Sinon.js) that can be used to inspect call to it.
 		 *
 		 *
@@ -24,11 +24,9 @@ CKEDITOR.define( [ 'utils-lodash', 'lib/lodash/lodash-ckeditor' ], function( lod
 		 * @returns {Function} The spy function.
 		 * @returns {Function} The spy function.
 		 */
 		 */
 		spy() {
 		spy() {
-			var spy = function() {
+			return function spy() {
 				spy.called = true;
 				spy.called = true;
 			};
 			};
-
-			return spy;
 		},
 		},
 
 
 		/**
 		/**
@@ -37,10 +35,10 @@ CKEDITOR.define( [ 'utils-lodash', 'lib/lodash/lodash-ckeditor' ], function( lod
 		 *
 		 *
 		 * @returns {Number} A number representing the id.
 		 * @returns {Number} A number representing the id.
 		 */
 		 */
-		uid: ( function() {
-			var next = 1;
+		uid: ( () => {
+			let next = 1;
 
 
-			return function() {
+			return () => {
 				return next++;
 				return next++;
 			};
 			};
 		} )(),
 		} )(),
@@ -71,9 +69,9 @@ CKEDITOR.define( [ 'utils-lodash', 'lib/lodash/lodash-ckeditor' ], function( lod
 		 * `a` is a {@link utils.compareArrays#EXTENSION extension}, or `a` is {@link utils.compareArrays#DIFFERENT different}.
 		 * `a` is a {@link utils.compareArrays#EXTENSION extension}, or `a` is {@link utils.compareArrays#DIFFERENT different}.
 		 */
 		 */
 		compareArrays( a, b ) {
 		compareArrays( a, b ) {
-			var minLen = Math.min( a.length, b.length );
+			const minLen = Math.min( a.length, b.length );
 
 
-			for ( var i = 0; i < minLen; i++ ) {
+			for ( let i = 0; i < minLen; i++ ) {
 				if ( a[ i ] != b[ i ] ) {
 				if ( a[ i ] != b[ i ] ) {
 					// The arrays are different.
 					// The arrays are different.
 					return utils.compareArrays.DIFFERENT;
 					return utils.compareArrays.DIFFERENT;
@@ -118,18 +116,21 @@ CKEDITOR.define( [ 'utils-lodash', 'lib/lodash/lodash-ckeditor' ], function( lod
 	 * @type {Number}
 	 * @type {Number}
 	 */
 	 */
 	utils.compareArrays.SAME = 0;
 	utils.compareArrays.SAME = 0;
+
 	/**
 	/**
 	 * Flag for "is a prefix of" relation between arrays.
 	 * Flag for "is a prefix of" relation between arrays.
 	 *
 	 *
 	 * @type {Number}
 	 * @type {Number}
 	 */
 	 */
 	utils.compareArrays.PREFIX = 1;
 	utils.compareArrays.PREFIX = 1;
+
 	/**
 	/**
 	 * Flag for "is a suffix of" relation between arrays.
 	 * Flag for "is a suffix of" relation between arrays.
 	 *
 	 *
 	 * @type {number}
 	 * @type {number}
 	 */
 	 */
 	utils.compareArrays.EXTENSION = 2;
 	utils.compareArrays.EXTENSION = 2;
+
 	/**
 	/**
 	 * Flag for "is different than" relation between arrays.
 	 * Flag for "is different than" relation between arrays.
 	 *
 	 *
@@ -138,7 +139,7 @@ CKEDITOR.define( [ 'utils-lodash', 'lib/lodash/lodash-ckeditor' ], function( lod
 	utils.compareArrays.DIFFERENT = 3;
 	utils.compareArrays.DIFFERENT = 3;
 
 
 	// Extend "utils" with Lo-Dash methods.
 	// Extend "utils" with Lo-Dash methods.
-	for ( var i = 0; i < lodashIncludes.length; i++ ) {
+	for ( let i = 0; i < lodashIncludes.length; i++ ) {
 		utils[ lodashIncludes[ i ] ] = lodash[ lodashIncludes[ i ] ];
 		utils[ lodashIncludes[ i ] ] = lodash[ lodashIncludes[ i ] ];
 	}
 	}
 
 

+ 1 - 0
packages/ckeditor5-engine/tests/.jshintrc

@@ -10,6 +10,7 @@
 	"unused": true,
 	"unused": true,
 	"strict": true,
 	"strict": true,
 	"expr": true,
 	"expr": true,
+	"varstmt": true,
 
 
 	"globalstrict": true,
 	"globalstrict": true,
 
 

+ 22 - 4
packages/ckeditor5-engine/tests/_tools/tools.js

@@ -5,7 +5,7 @@
 
 
 'use strict';
 'use strict';
 
 
-( function() {
+( () => {
 	bender.tools.core = {
 	bender.tools.core = {
 		/**
 		/**
 		 * Defines CKEditor plugin which is a mock of an editor creator.
 		 * Defines CKEditor plugin which is a mock of an editor creator.
@@ -23,8 +23,8 @@
 		 * @param {Object} [proto] Prototype of the creator. Properties from the proto param will
 		 * @param {Object} [proto] Prototype of the creator. Properties from the proto param will
 		 * be copied to the prototype of the creator.
 		 * be copied to the prototype of the creator.
 		 */
 		 */
-		defineEditorCreatorMock: function( creatorName, proto ) {
-			CKEDITOR.define( 'plugin!creator-' + creatorName, [ 'creator' ], function( Creator ) {
+		defineEditorCreatorMock: ( creatorName, proto ) => {
+			CKEDITOR.define( 'plugin!creator-' + creatorName, [ 'creator' ], ( Creator ) => {
 				return mockCreator( Creator );
 				return mockCreator( Creator );
 			} );
 			} );
 
 
@@ -32,7 +32,7 @@
 				class TestCreator extends Creator {}
 				class TestCreator extends Creator {}
 
 
 				if ( proto ) {
 				if ( proto ) {
-					for ( var propName in proto ) {
+					for ( let propName in proto ) {
 						TestCreator.prototype[ propName ] = proto[ propName ];
 						TestCreator.prototype[ propName ] = proto[ propName ];
 					}
 					}
 				}
 				}
@@ -47,6 +47,24 @@
 
 
 				return TestCreator;
 				return TestCreator;
 			}
 			}
+		},
+
+		/**
+		 * Returns the number of elements return by the iterator.
+		 *
+		 *	  bender.tools.core.getIteratorCount( [ 1, 2, 3, 4, 5 ] ); // 5;
+		 *
+		 * @param {Iterable.<*>} iterator Any iterator.
+		 * @returns {Number} Number of elements returned by that iterator.
+		 */
+		getIteratorCount: ( iterator ) => {
+			let count = 0;
+
+			for ( let _ of iterator ) { // jshint ignore:line
+				count++;
+			}
+
+			return count;
 		}
 		}
 	};
 	};
 } )();
 } )();

+ 22 - 15
packages/ckeditor5-engine/tests/bender/tools.js

@@ -7,8 +7,8 @@
 
 
 'use strict';
 'use strict';
 
 
-var createFn3 = function() {};
-var destroyFn3 = function() {};
+let createFn3 = () => {};
+let destroyFn3 = () => {};
 
 
 bender.tools.core.defineEditorCreatorMock( 'test1' );
 bender.tools.core.defineEditorCreatorMock( 'test1' );
 bender.tools.core.defineEditorCreatorMock( 'test2', {
 bender.tools.core.defineEditorCreatorMock( 'test2', {
@@ -20,33 +20,33 @@ bender.tools.core.defineEditorCreatorMock( 'test3', {
 	destroy: destroyFn3
 	destroy: destroyFn3
 } );
 } );
 
 
-var modules = bender.amd.require( 'creator', 'plugin!creator-test1', 'plugin!creator-test2', 'plugin!creator-test3' );
+const modules = bender.amd.require( 'creator', 'plugin!creator-test1', 'plugin!creator-test2', 'plugin!creator-test3' );
 
 
 ///////////////////
 ///////////////////
 
 
-describe( 'bender.tools.core.defineEditorCreatorMock()', function() {
-	it( 'should register all creators', function() {
-		var Creator = modules.creator;
-		var TestCreator1 = modules[ 'plugin!creator-test1' ];
-		var TestCreator2 = modules[ 'plugin!creator-test2' ];
-		var TestCreator3 = modules[ 'plugin!creator-test3' ];
+describe( 'bender.tools.core.defineEditorCreatorMock()', () => {
+	it( 'should register all creators', () => {
+		const Creator = modules.creator;
+		const TestCreator1 = modules[ 'plugin!creator-test1' ];
+		const TestCreator2 = modules[ 'plugin!creator-test2' ];
+		const TestCreator3 = modules[ 'plugin!creator-test3' ];
 
 
 		expect( TestCreator1.prototype ).to.be.instanceof( Creator );
 		expect( TestCreator1.prototype ).to.be.instanceof( Creator );
 		expect( TestCreator2.prototype ).to.be.instanceof( Creator );
 		expect( TestCreator2.prototype ).to.be.instanceof( Creator );
 		expect( TestCreator3.prototype ).to.be.instanceof( Creator );
 		expect( TestCreator3.prototype ).to.be.instanceof( Creator );
 	} );
 	} );
 
 
-	it( 'should copy properties from the second argument', function() {
-		var TestCreator = modules[ 'plugin!creator-test2' ];
+	it( 'should copy properties from the second argument', () => {
+		const TestCreator = modules[ 'plugin!creator-test2' ];
 
 
 		expect( TestCreator.prototype ).to.have.property( 'foo', 1 );
 		expect( TestCreator.prototype ).to.have.property( 'foo', 1 );
 		expect( TestCreator.prototype ).to.have.property( 'bar', 2 );
 		expect( TestCreator.prototype ).to.have.property( 'bar', 2 );
 	} );
 	} );
 
 
-	it( 'should create spies for create() and destroy() if not defined', function() {
-		var TestCreator1 = modules[ 'plugin!creator-test1' ];
-		var TestCreator2 = modules[ 'plugin!creator-test2' ];
-		var TestCreator3 = modules[ 'plugin!creator-test3' ];
+	it( 'should create spies for create() and destroy() if not defined', () => {
+		const TestCreator1 = modules[ 'plugin!creator-test1' ];
+		const TestCreator2 = modules[ 'plugin!creator-test2' ];
+		const TestCreator3 = modules[ 'plugin!creator-test3' ];
 
 
 		expect( TestCreator1.prototype.create ).to.have.property( 'called', false, 'test1.create' );
 		expect( TestCreator1.prototype.create ).to.have.property( 'called', false, 'test1.create' );
 		expect( TestCreator1.prototype.destroy ).to.have.property( 'called', false, 'test1.destroy' );
 		expect( TestCreator1.prototype.destroy ).to.have.property( 'called', false, 'test1.destroy' );
@@ -57,4 +57,11 @@ describe( 'bender.tools.core.defineEditorCreatorMock()', function() {
 		expect( TestCreator3.prototype ).to.have.property( 'create', createFn3 );
 		expect( TestCreator3.prototype ).to.have.property( 'create', createFn3 );
 		expect( TestCreator3.prototype ).to.have.property( 'destroy', destroyFn3 );
 		expect( TestCreator3.prototype ).to.have.property( 'destroy', destroyFn3 );
 	} );
 	} );
+} );
+
+describe( 'bender.tools.core.getIteratorCount()', () => {
+	it( 'should returns number of editable items ', () => {
+		const count = bender.tools.core.getIteratorCount( [ 1, 2, 3, 4, 5 ] );
+		expect( count ).to.equal( 5 );
+	} );
 } );
 } );

+ 36 - 36
packages/ckeditor5-engine/tests/ckeditor/ckeditor.js

@@ -7,16 +7,16 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'ckeditor', 'editor', 'config' );
+const modules = bender.amd.require( 'ckeditor', 'editor', 'config' );
 
 
-var content = document.getElementById( 'content' );
-var editorConfig = { plugins: 'creator-test' };
+let content = document.getElementById( 'content' );
+let editorConfig = { plugins: 'creator-test' };
 
 
 bender.tools.createSinonSandbox();
 bender.tools.createSinonSandbox();
 bender.tools.core.defineEditorCreatorMock( 'test' );
 bender.tools.core.defineEditorCreatorMock( 'test' );
 
 
-beforeEach( function() {
-	var CKEDITOR = modules.ckeditor;
+beforeEach( () => {
+	const CKEDITOR = modules.ckeditor;
 
 
 	// Destroy all editor instances.
 	// Destroy all editor instances.
 	while ( CKEDITOR.instances.length ) {
 	while ( CKEDITOR.instances.length ) {
@@ -24,60 +24,60 @@ beforeEach( function() {
 	}
 	}
 } );
 } );
 
 
-describe( 'create', function() {
-	it( 'should return a promise', function() {
-		var CKEDITOR = modules.ckeditor;
+describe( 'create', () => {
+	it( 'should return a promise', () => {
+		const CKEDITOR = modules.ckeditor;
 
 
 		expect( CKEDITOR.create( content, editorConfig ) ).to.be.instanceof( Promise );
 		expect( CKEDITOR.create( content, editorConfig ) ).to.be.instanceof( Promise );
 	} );
 	} );
 
 
-	it( 'should create a new editor instance', function() {
-		var CKEDITOR = modules.ckeditor;
-		var Editor = modules.editor;
+	it( 'should create a new editor instance', () => {
+		const CKEDITOR = modules.ckeditor;
+		const Editor = modules.editor;
 
 
-		return CKEDITOR.create( content, editorConfig ).then( function( editor ) {
+		return CKEDITOR.create( content, editorConfig ).then( ( editor ) => {
 			expect( editor ).to.be.instanceof( Editor );
 			expect( editor ).to.be.instanceof( Editor );
 			expect( editor.element ).to.equal( content );
 			expect( editor.element ).to.equal( content );
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should create a new editor instance (using a selector)', function() {
-		var CKEDITOR = modules.ckeditor;
-		var Editor = modules.editor;
+	it( 'should create a new editor instance (using a selector)', () => {
+		const CKEDITOR = modules.ckeditor;
+		const Editor = modules.editor;
 
 
-		return CKEDITOR.create( '.editor', editorConfig ).then( function( editor ) {
+		return CKEDITOR.create( '.editor', editorConfig ).then( ( editor ) => {
 			expect( editor ).to.be.instanceof( Editor );
 			expect( editor ).to.be.instanceof( Editor );
 			expect( editor.element ).to.equal( document.querySelector( '.editor' ) );
 			expect( editor.element ).to.equal( document.querySelector( '.editor' ) );
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should set configurations on the new editor', function() {
-		var CKEDITOR = modules.ckeditor;
+	it( 'should set configurations on the new editor', () => {
+		const CKEDITOR = modules.ckeditor;
 
 
-		return CKEDITOR.create( content, { test: 1, plugins: 'creator-test' } ).then( function( editor ) {
+		return CKEDITOR.create( content, { test: 1, plugins: 'creator-test' } ).then( ( editor ) => {
 			expect( editor.config.test ).to.equal( 1 );
 			expect( editor.config.test ).to.equal( 1 );
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should add the editor to the `instances` collection', function() {
-		var CKEDITOR = modules.ckeditor;
+	it( 'should add the editor to the `instances` collection', () => {
+		const CKEDITOR = modules.ckeditor;
 
 
-		return CKEDITOR.create( content, editorConfig ).then( function( editor ) {
+		return CKEDITOR.create( content, editorConfig ).then( ( editor ) => {
 			expect( CKEDITOR.instances ).to.have.length( 1 );
 			expect( CKEDITOR.instances ).to.have.length( 1 );
 			expect( CKEDITOR.instances.get( 0 ) ).to.equal( editor );
 			expect( CKEDITOR.instances.get( 0 ) ).to.equal( editor );
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should remove the editor from the `instances` collection on `destroy` event', function() {
-		var CKEDITOR = modules.ckeditor;
-		var editor1, editor2;
+	it( 'should remove the editor from the `instances` collection on `destroy` event', () => {
+		const CKEDITOR = modules.ckeditor;
+		let editor1, editor2;
 
 
 		// Create the first editor.
 		// Create the first editor.
-		return CKEDITOR.create( content, editorConfig ).then( function( editor ) {
+		return CKEDITOR.create( content, editorConfig ).then( ( editor ) => {
 			editor1 = editor;
 			editor1 = editor;
 
 
 			// Create the second editor.
 			// Create the second editor.
-			return CKEDITOR.create( '.editor', editorConfig ).then( function( editor ) {
+			return CKEDITOR.create( '.editor', editorConfig ).then( ( editor ) => {
 				editor2 = editor;
 				editor2 = editor;
 
 
 				// It should have 2 editors.
 				// It should have 2 editors.
@@ -95,14 +95,14 @@ describe( 'create', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should be rejected on element not found', function() {
-		var CKEDITOR = modules.ckeditor;
+	it( 'should be rejected on element not found', () => {
+		const CKEDITOR = modules.ckeditor;
 
 
-		var addSpy = bender.sinon.spy( CKEDITOR.instances, 'add' );
+		let addSpy = bender.sinon.spy( CKEDITOR.instances, 'add' );
 
 
-		return CKEDITOR.create( '.undefined' ).then( function() {
+		return CKEDITOR.create( '.undefined' ).then( () => {
 			throw new Error( 'It should not enter this function' );
 			throw new Error( 'It should not enter this function' );
-		} ).catch( function( error ) {
+		} ).catch( ( error ) => {
 			expect( error ).to.be.instanceof( Error );
 			expect( error ).to.be.instanceof( Error );
 			expect( error.message ).to.equal( 'Element not found' );
 			expect( error.message ).to.equal( 'Element not found' );
 			// We need to make sure that create()'s execution is stopped.
 			// We need to make sure that create()'s execution is stopped.
@@ -112,10 +112,10 @@ describe( 'create', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'config', function() {
-	it( 'should be an instance of Config', function() {
-		var CKEDITOR = modules.ckeditor;
-		var Config = modules.config;
+describe( 'config', () => {
+	it( 'should be an instance of Config', () => {
+		const CKEDITOR = modules.ckeditor;
+		const Config = modules.config;
 
 
 		expect( CKEDITOR.config ).to.be.an.instanceof( Config );
 		expect( CKEDITOR.config ).to.be.an.instanceof( Config );
 	} );
 	} );

+ 19 - 19
packages/ckeditor5-engine/tests/ckeditorerror/ckeditorerror.js

@@ -5,55 +5,55 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'ckeditorerror' );
+const modules = bender.amd.require( 'ckeditorerror' );
 
 
-describe( 'CKEditorError', function() {
-	it( 'inherits from Error', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var error = new CKEditorError( 'foo' );
+describe( 'CKEditorError', () => {
+	it( 'inherits from Error', () => {
+		const CKEditorError = modules.ckeditorerror;
+		let error = new CKEditorError( 'foo' );
 
 
 		expect( error ).to.be.an.instanceOf( Error );
 		expect( error ).to.be.an.instanceOf( Error );
 		expect( error ).to.be.an.instanceOf( CKEditorError );
 		expect( error ).to.be.an.instanceOf( CKEditorError );
 	} );
 	} );
 
 
-	it( 'sets the name', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var error = new CKEditorError( 'foo' );
+	it( 'sets the name', () => {
+		const CKEditorError = modules.ckeditorerror;
+		let error = new CKEditorError( 'foo' );
 
 
 		expect( error ).to.have.property( 'name', 'CKEditorError' );
 		expect( error ).to.have.property( 'name', 'CKEditorError' );
 	} );
 	} );
 
 
-	it( 'sets the message', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var error = new CKEditorError( 'foo' );
+	it( 'sets the message', () => {
+		const CKEditorError = modules.ckeditorerror;
+		let error = new CKEditorError( 'foo' );
 
 
 		expect( error ).to.have.property( 'message', 'foo' );
 		expect( error ).to.have.property( 'message', 'foo' );
 		expect( error.data ).to.be.undefined;
 		expect( error.data ).to.be.undefined;
 	} );
 	} );
 
 
-	it( 'sets the message and data', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var data = { bar: 1 };
-		var error = new CKEditorError( 'foo', data );
+	it( 'sets the message and data', () => {
+		const CKEditorError = modules.ckeditorerror;
+		let data = { bar: 1 };
+		let error = new CKEditorError( 'foo', data );
 
 
 		expect( error ).to.have.property( 'message', 'foo {"bar":1}' );
 		expect( error ).to.have.property( 'message', 'foo {"bar":1}' );
 		expect( error ).to.have.property( 'data', data );
 		expect( error ).to.have.property( 'data', data );
 	} );
 	} );
 
 
-	it( 'appends stringified data to the message', function() {
+	it( 'appends stringified data to the message', () => {
 		class Foo {
 		class Foo {
 			constructor() {
 			constructor() {
 				this.x = 1;
 				this.x = 1;
 			}
 			}
 		}
 		}
 
 
-		var CKEditorError = modules.ckeditorerror;
-		var data = {
+		const CKEditorError = modules.ckeditorerror;
+		let data = {
 			bar: 'a',
 			bar: 'a',
 			bom: new Foo(),
 			bom: new Foo(),
 			bim: document.body
 			bim: document.body
 		};
 		};
-		var error = new CKEditorError( 'foo', data );
+		let error = new CKEditorError( 'foo', data );
 
 
 		expect( error ).to.have.property( 'message', 'foo {"bar":"a","bom":{"x":1},"bim":{}}' );
 		expect( error ).to.have.property( 'message', 'foo {"bar":"a","bom":{"x":1},"bim":{}}' );
 		expect( error ).to.have.property( 'data', data );
 		expect( error ).to.have.property( 'data', data );

+ 390 - 0
packages/ckeditor5-engine/tests/collection/collection.js

@@ -0,0 +1,390 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+const modules = bender.amd.require( 'collection', 'ckeditorerror' );
+
+bender.tools.createSinonSandbox();
+
+function getItem( id, idProperty ) {
+	return {
+		[ idProperty || 'id' ]: id
+	};
+}
+
+describe( 'Collection', () => {
+	let Collection, CKEditorError;
+
+	before( () => {
+		Collection = modules.collection;
+		CKEditorError = modules.CKEditorError;
+	} );
+
+	let collection;
+
+	beforeEach( () => {
+		collection = new Collection();
+	} );
+
+	describe( 'constructor', () => {
+		it( 'allows to change the id property used by the collection', () => {
+			let item1 = { id: 'foo', name: 'xx' };
+			let item2 = { id: 'foo', name: 'yy' };
+			let collection = new Collection( { idProperty: 'name' } );
+
+			collection.add( item1 );
+			collection.add( item2 );
+
+			expect( collection ).to.have.length( 2 );
+
+			expect( collection.get( 'xx' ) ).to.equal( item1 );
+			expect( collection.remove( 'yy' ) ).to.equal( item2 );
+		} );
+	} );
+
+	describe( 'add', () => {
+		it( 'should be chainable', () => {
+			expect( collection.add( {} ) ).to.equal( collection );
+		} );
+
+		it( 'should change the length', () => {
+			expect( collection ).to.have.length( 0 );
+
+			collection.add( {} );
+			expect( collection ).to.have.length( 1 );
+
+			collection.add( {} );
+			expect( collection ).to.have.length( 2 );
+		} );
+
+		it( 'should enable get( index )', () => {
+			let item1 = {};
+			let item2 = {};
+
+			collection.add( item1 );
+			expect( collection.get( 0 ) ).to.equal( item1 );
+
+			collection.add( item2 );
+			expect( collection.get( 0 ) ).to.equal( item1 );
+			expect( collection.get( 1 ) ).to.equal( item2 );
+		} );
+
+		it( 'should enable get( id )', () => {
+			let item1 = getItem( 'foo' );
+			let item2 = getItem( 'bar' );
+
+			collection.add( item1 );
+			collection.add( item2 );
+
+			expect( collection.get( 'foo' ) ).to.equal( item1 );
+			expect( collection.get( 'bar' ) ).to.equal( item2 );
+		} );
+
+		it( 'should enable get( id ) - custom id property', () => {
+			let collection = new Collection( { idProperty: 'name' } );
+			let item1 = getItem( 'foo', 'name' );
+			let item2 = getItem( 'bar', 'name' );
+
+			collection.add( item1 );
+			collection.add( item2 );
+
+			expect( collection.get( 'foo' ) ).to.equal( item1 );
+			expect( collection.get( 'bar' ) ).to.equal( item2 );
+		} );
+
+		it( 'should generate an id when not defined', () => {
+			let item = {};
+
+			collection.add( item );
+
+			expect( item.id ).to.be.a( 'string' );
+			expect( collection.get( item.id ) ).to.equal( item );
+		} );
+
+		it( 'should generate an id when not defined - custom id property', () => {
+			let collection = new Collection( { idProperty: 'name' } );
+			let item = {};
+
+			collection.add( item );
+
+			expect( item.name ).to.be.a( 'string' );
+			expect( collection.get( item.name ) ).to.equal( item );
+		} );
+
+		it( 'should not change an existing id of an item', () => {
+			let item = getItem( 'foo' );
+
+			collection.add( item );
+
+			expect( item.id ).to.equal( 'foo' );
+		} );
+
+		it( 'should throw when item with this id already exists', () => {
+			let item1 = getItem( 'foo' );
+			let item2 = getItem( 'foo' );
+
+			collection.add( item1 );
+
+			expect( () => {
+				collection.add( item2 );
+			} ).to.throw( CKEditorError, /^collection-add-item-already-exists/ );
+		} );
+
+		it( 'should throw when item\'s id is not a string', () => {
+			let item = { id: 1 };
+
+			expect( () => {
+				collection.add( item );
+			} ).to.throw( CKEditorError, /^collection-add-invalid-id/ );
+		} );
+
+		it(
+			'should not override item under an existing id in case of a collision ' +
+			'between existing items and one with an automatically generated id',
+			() => {
+				collection.add( getItem( '0' ) );
+				collection.add( getItem( '1' ) );
+				collection.add( getItem( '2' ) );
+
+				let item = {};
+
+				collection.add( item );
+
+				expect( item ).to.have.property( 'id', '3' );
+			}
+		);
+
+		it( 'should fire the "add" event', () => {
+			let spy = sinon.spy();
+			let item = {};
+
+			collection.on( 'add', spy );
+
+			collection.add( item );
+
+			sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', collection ), item );
+		} );
+	} );
+
+	describe( 'get', () => {
+		it( 'should return an item', () => {
+			let item = getItem( 'foo' );
+			collection.add( item );
+
+			expect( collection.get( 'foo' ) ).to.equal( item );
+		} );
+
+		it( 'should return null if id does not exist', () => {
+			collection.add( getItem( 'foo' ) );
+
+			expect( collection.get( 'bar' ) ).to.be.null;
+		} );
+
+		it( 'should throw if neither string or number given', () => {
+			expect( () => {
+				collection.get( true );
+			} ).to.throw( CKEditorError, /^collection-get-invalid-arg/ );
+		} );
+	} );
+
+	describe( 'remove', () => {
+		it( 'should remove the model by index', () => {
+			collection.add( getItem( 'bom' ) );
+			collection.add( getItem( 'foo' ) );
+			collection.add( getItem( 'bar' ) );
+
+			expect( collection ).to.have.length( 3 );
+
+			let removedItem = collection.remove( 1 );
+
+			expect( collection ).to.have.length( 2 );
+			expect( collection.get( 'foo' ) ).to.be.null;
+			expect( collection.get( 1 ) ).to.have.property( 'id', 'bar' );
+			expect( removedItem ).to.have.property( 'id', 'foo' );
+		} );
+
+		it( 'should remove the model by index - custom id property', () => {
+			let collection = new Collection( { idProperty: 'name' } );
+
+			collection.add( getItem( 'foo', 'name' ) );
+
+			let removedItem = collection.remove( 0 );
+
+			expect( collection ).to.have.length( 0 );
+			expect( collection.get( 'foo' ) ).to.be.null;
+			expect( removedItem ).to.have.property( 'name', 'foo' );
+		} );
+
+		it( 'should remove the model by id', () => {
+			collection.add( getItem( 'bom' ) );
+			collection.add( getItem( 'foo' ) );
+			collection.add( getItem( 'bar' ) );
+
+			expect( collection ).to.have.length( 3 );
+
+			let removedItem = collection.remove( 'foo' );
+
+			expect( collection ).to.have.length( 2 );
+			expect( collection.get( 'foo' ) ).to.be.null;
+			expect( collection.get( 1 ) ).to.have.property( 'id', 'bar' );
+			expect( removedItem ).to.have.property( 'id', 'foo' );
+		} );
+
+		it( 'should remove the model by model', () => {
+			let item = getItem( 'foo' );
+
+			collection.add( getItem( 'bom' ) );
+			collection.add( item );
+			collection.add( getItem( 'bar' ) );
+
+			expect( collection ).to.have.length( 3 );
+
+			let removedItem = collection.remove( item );
+
+			expect( collection ).to.have.length( 2 );
+			expect( collection.get( 'foo' ) ).to.be.null;
+			expect( collection.get( 1 ) ).to.have.property( 'id', 'bar' );
+			expect( removedItem ).to.equal( item );
+		} );
+
+		it( 'should remove the model by model - custom id property', () => {
+			let collection = new Collection( null, 'name' );
+			let item = getItem( 'foo', 'name' );
+
+			collection.add( item );
+
+			let removedItem = collection.remove( item );
+
+			expect( collection ).to.have.length( 0 );
+			expect( collection.get( 'foo' ) ).to.be.null;
+			expect( removedItem ).to.have.property( 'name', 'foo' );
+		} );
+
+		it( 'should fire the "remove" event', () => {
+			let item1 = getItem( 'foo' );
+			let item2 = getItem( 'bar' );
+			let item3 = getItem( 'bom' );
+
+			collection.add( item1 );
+			collection.add( item2 );
+			collection.add( item3 );
+
+			let spy = sinon.spy();
+
+			collection.on( 'remove', spy );
+
+			collection.remove( 1 );		// by index
+			collection.remove( item1 );	// by model
+			collection.remove( 'bom' );	// by id
+
+			sinon.assert.calledThrice( spy );
+			sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', collection ), item1 );
+			sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', collection ), item2 );
+			sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', collection ), item3 );
+		} );
+
+		it( 'should throw an error on invalid index', () => {
+			collection.add( getItem( 'foo' ) );
+
+			expect( () => {
+				collection.remove( 1 );
+			} ).to.throw( CKEditorError, /^collection-remove-404/ );
+
+			expect( collection ).to.have.length( 1 );
+		} );
+
+		it( 'should throw an error on invalid id', () => {
+			collection.add( getItem( 'foo' ) );
+
+			expect( () => {
+				collection.remove( 'bar' );
+			} ).to.throw( CKEditorError, /^collection-remove-404/ );
+
+			expect( collection ).to.have.length( 1 );
+		} );
+
+		it( 'should throw an error on invalid model', () => {
+			collection.add( getItem( 'foo' ) );
+
+			expect( () => {
+				collection.remove( getItem( 'bar' ) );
+			} ).to.throw( CKEditorError, /^collection-remove-404/ );
+
+			expect( collection ).to.have.length( 1 );
+		} );
+	} );
+
+	describe( 'map', () => {
+		it( 'uses native map', () => {
+			let spy = bender.sinon.stub( Array.prototype, 'map', () => {
+				return [ 'foo' ];
+			} );
+			let ctx = {};
+
+			let ret = collection.map( callback, ctx );
+
+			sinon.assert.calledWithExactly( spy, callback, ctx );
+			expect( ret ).to.deep.equal( [ 'foo' ], 'ret value was forwarded' );
+
+			function callback() {}
+		} );
+	} );
+
+	describe( 'find', () => {
+		it( 'uses native find', () => {
+			let needl = getItem( 'foo' );
+
+			let spy = bender.sinon.stub( Array.prototype, 'find', () => {
+				return needl;
+			} );
+			let ctx = {};
+
+			let ret = collection.find( callback, ctx );
+
+			sinon.assert.calledWithExactly( spy, callback, ctx );
+			expect( ret ).to.equal( needl, 'ret value was forwarded' );
+
+			function callback() {}
+		} );
+	} );
+
+	describe( 'filter', () => {
+		it( 'uses native filter', () => {
+			let needl = getItem( 'foo' );
+
+			let spy = bender.sinon.stub( Array.prototype, 'filter', () => {
+				return [ needl ];
+			} );
+			let ctx = {};
+
+			let ret = collection.filter( callback, ctx );
+
+			sinon.assert.calledWithExactly( spy, callback, ctx );
+			expect( ret ).to.deep.equal( [ needl ], 'ret value was forwarded' );
+
+			function callback() {}
+		} );
+	} );
+
+	describe( 'iterator', () => {
+		it( 'covers the whole collection', () => {
+			let item1 = getItem( 'foo' );
+			let item2 = getItem( 'bar' );
+			let item3 = getItem( 'bom' );
+			let items = [];
+
+			collection.add( item1 );
+			collection.add( item2 );
+			collection.add( item3 );
+
+			for ( let item of collection ) {
+				items.push( item.id );
+			}
+
+			expect( items ).to.deep.equal( [ 'foo', 'bar', 'bom' ] );
+		} );
+	} );
+} );

+ 38 - 38
packages/ckeditor5-engine/tests/config/config.js

@@ -5,12 +5,12 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'config' );
+const modules = bender.amd.require( 'config' );
 
 
-var config;
+let config;
 
 
-beforeEach( function() {
-	var Config = modules.config;
+beforeEach( () => {
+	const Config = modules.config;
 
 
 	config = new Config( {
 	config = new Config( {
 		creator: 'inline',
 		creator: 'inline',
@@ -26,8 +26,8 @@ beforeEach( function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'constructor', function() {
-	it( 'should set configurations', function() {
+describe( 'constructor', () => {
+	it( 'should set configurations', () => {
 		expect( config ).to.have.property( 'creator' ).to.equal( 'inline' );
 		expect( config ).to.have.property( 'creator' ).to.equal( 'inline' );
 		expect( config ).to.have.property( 'language' ).to.equal( 'pl' );
 		expect( config ).to.have.property( 'language' ).to.equal( 'pl' );
 		expect( config ).to.have.property( 'resize' ).to.have.property( 'minheight' ).to.equal( 300 );
 		expect( config ).to.have.property( 'resize' ).to.have.property( 'minheight' ).to.equal( 300 );
@@ -37,23 +37,23 @@ describe( 'constructor', function() {
 		expect( config ).to.have.property( 'toolbar' ).to.equal( 'top' );
 		expect( config ).to.have.property( 'toolbar' ).to.equal( 'top' );
 	} );
 	} );
 
 
-	it( 'should work with no parameters', function() {
-		var Config = modules.config;
+	it( 'should work with no parameters', () => {
+		const Config = modules.config;
 
 
 		// No error should be thrown.
 		// No error should be thrown.
 		config = new Config();
 		config = new Config();
 	} );
 	} );
 } );
 } );
 
 
-describe( 'set', function() {
-	it( 'should create Config instances for objects', function() {
-		var Config = modules.config;
+describe( 'set', () => {
+	it( 'should create Config instances for objects', () => {
+		const Config = modules.config;
 
 
 		expect( config.resize ).to.be.an.instanceof( Config );
 		expect( config.resize ).to.be.an.instanceof( Config );
 		expect( config.resize.icon ).to.be.an.instanceof( Config );
 		expect( config.resize.icon ).to.be.an.instanceof( Config );
 	} );
 	} );
 
 
-	it( 'should set configurations when passing objects', function() {
+	it( 'should set configurations when passing objects', () => {
 		config.set( {
 		config.set( {
 			option1: 1,
 			option1: 1,
 			option2: {
 			option2: {
@@ -69,13 +69,13 @@ describe( 'set', function() {
 			.to.have.property( 'suboption21' ).to.equal( 21 );
 			.to.have.property( 'suboption21' ).to.equal( 21 );
 	} );
 	} );
 
 
-	it( 'should set configurations when passing name and value', function() {
+	it( 'should set configurations when passing name and value', () => {
 		config.set( 'something', 'anything' );
 		config.set( 'something', 'anything' );
 
 
 		expect( config ).to.have.property( 'something' ).to.equal( 'anything' );
 		expect( config ).to.have.property( 'something' ).to.equal( 'anything' );
 	} );
 	} );
 
 
-	it( 'should set configurations when passing name.with.deep and value', function() {
+	it( 'should set configurations when passing name.with.deep and value', () => {
 		config.set( 'color.red', 'f00' );
 		config.set( 'color.red', 'f00' );
 		config.set( 'background.color.blue', '00f' );
 		config.set( 'background.color.blue', '00f' );
 
 
@@ -89,7 +89,7 @@ describe( 'set', function() {
 			.to.have.property( 'blue' ).to.equal( '00f' );
 			.to.have.property( 'blue' ).to.equal( '00f' );
 	} );
 	} );
 
 
-	it( 'should override and expand deep configurations', function() {
+	it( 'should override and expand deep configurations', () => {
 		config.set( {
 		config.set( {
 			resize: {
 			resize: {
 				minHeight: 400,		// Override
 				minHeight: 400,		// Override
@@ -111,8 +111,8 @@ describe( 'set', function() {
 		expect( config.resize.icon ).to.have.property( 'url' ).to.equal( true );
 		expect( config.resize.icon ).to.have.property( 'url' ).to.equal( true );
 	} );
 	} );
 
 
-	it( 'should replace a simple entry with a Config instance', function() {
-		var Config = modules.config;
+	it( 'should replace a simple entry with a Config instance', () => {
+		const Config = modules.config;
 
 
 		config.set( 'test', 1 );
 		config.set( 'test', 1 );
 		config.set( 'test', {
 		config.set( 'test', {
@@ -123,8 +123,8 @@ describe( 'set', function() {
 		expect( config.test.prop ).to.equal( 1 );
 		expect( config.test.prop ).to.equal( 1 );
 	} );
 	} );
 
 
-	it( 'should replace a simple entry with a Config instance when passing an object', function() {
-		var Config = modules.config;
+	it( 'should replace a simple entry with a Config instance when passing an object', () => {
+		const Config = modules.config;
 
 
 		config.set( 'test', 1 );
 		config.set( 'test', 1 );
 		config.set( {
 		config.set( {
@@ -137,8 +137,8 @@ describe( 'set', function() {
 		expect( config.test.prop ).to.equal( 1 );
 		expect( config.test.prop ).to.equal( 1 );
 	} );
 	} );
 
 
-	it( 'should replace a simple entry with a Config instance when passing a name.with.deep', function() {
-		var Config = modules.config;
+	it( 'should replace a simple entry with a Config instance when passing a name.with.deep', () => {
+		const Config = modules.config;
 
 
 		config.set( 'test.prop', 1 );
 		config.set( 'test.prop', 1 );
 		config.set( 'test.prop.value', 1 );
 		config.set( 'test.prop.value', 1 );
@@ -148,7 +148,7 @@ describe( 'set', function() {
 		expect( config.test.prop.value ).to.equal( 1 );
 		expect( config.test.prop.value ).to.equal( 1 );
 	} );
 	} );
 
 
-	it( 'should not create Config instances for non-pure objects', function() {
+	it( 'should not create Config instances for non-pure objects', () => {
 		function SomeClass() {}
 		function SomeClass() {}
 
 
 		config.set( 'date', new Date() );
 		config.set( 'date', new Date() );
@@ -160,7 +160,7 @@ describe( 'set', function() {
 		expect( config.instance ).to.be.an.instanceof( SomeClass );
 		expect( config.instance ).to.be.an.instanceof( SomeClass );
 	} );
 	} );
 
 
-	it( 'should set `null` for undefined value', function() {
+	it( 'should set `null` for undefined value', () => {
 		config.set( 'test' );
 		config.set( 'test' );
 
 
 		expect( config.test ).to.be.null();
 		expect( config.test ).to.be.null();
@@ -168,46 +168,46 @@ describe( 'set', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'get', function() {
-	it( 'should retrieve a configuration', function() {
+describe( 'get', () => {
+	it( 'should retrieve a configuration', () => {
 		expect( config.get( 'creator' ) ).to.equal( 'inline' );
 		expect( config.get( 'creator' ) ).to.equal( 'inline' );
 	} );
 	} );
 
 
-	it( 'should retrieve a deep configuration', function() {
+	it( 'should retrieve a deep configuration', () => {
 		expect( config.get( 'resize.minheight' ) ).to.equal( 300 );
 		expect( config.get( 'resize.minheight' ) ).to.equal( 300 );
 		expect( config.get( 'resize.icon.path' ) ).to.equal( 'xyz' );
 		expect( config.get( 'resize.icon.path' ) ).to.equal( 'xyz' );
 	} );
 	} );
 
 
-	it( 'should retrieve a subset of the configuration', function() {
-		var resizeConfig = config.get( 'resize' );
+	it( 'should retrieve a subset of the configuration', () => {
+		let resizeConfig = config.get( 'resize' );
 
 
 		expect( resizeConfig ).to.have.property( 'minheight' ).to.equal( 300 );
 		expect( resizeConfig ).to.have.property( 'minheight' ).to.equal( 300 );
 		expect( resizeConfig ).to.have.property( 'maxheight' ).to.equal( 800 );
 		expect( resizeConfig ).to.have.property( 'maxheight' ).to.equal( 800 );
 		expect( resizeConfig ).to.have.property( 'icon' ).to.have.property( 'path' ).to.equal( 'xyz' );
 		expect( resizeConfig ).to.have.property( 'icon' ).to.have.property( 'path' ).to.equal( 'xyz' );
 
 
-		var iconConfig = resizeConfig.get( 'icon' );
+		let iconConfig = resizeConfig.get( 'icon' );
 
 
 		expect( iconConfig ).to.have.property( 'path' ).to.equal( 'xyz' );
 		expect( iconConfig ).to.have.property( 'path' ).to.equal( 'xyz' );
 	} );
 	} );
 
 
-	it( 'should retrieve values case-insensitively', function() {
+	it( 'should retrieve values case-insensitively', () => {
 		expect( config.get( 'Creator' ) ).to.equal( 'inline' );
 		expect( config.get( 'Creator' ) ).to.equal( 'inline' );
 		expect( config.get( 'CREATOR' ) ).to.equal( 'inline' );
 		expect( config.get( 'CREATOR' ) ).to.equal( 'inline' );
 		expect( config.get( 'resize.minHeight' ) ).to.equal( 300 );
 		expect( config.get( 'resize.minHeight' ) ).to.equal( 300 );
 		expect( config.get( 'resize.MINHEIGHT' ) ).to.equal( 300 );
 		expect( config.get( 'resize.MINHEIGHT' ) ).to.equal( 300 );
 	} );
 	} );
 
 
-	it( 'should return undefined for non existing configuration', function() {
+	it( 'should return undefined for non existing configuration', () => {
 		expect( config.get( 'invalid' ) ).to.be.undefined();
 		expect( config.get( 'invalid' ) ).to.be.undefined();
 	} );
 	} );
 
 
-	it( 'should return undefined for non existing deep configuration', function() {
+	it( 'should return undefined for non existing deep configuration', () => {
 		expect( config.get( 'resize.invalid.value' ) ).to.be.undefined();
 		expect( config.get( 'resize.invalid.value' ) ).to.be.undefined();
 	} );
 	} );
 } );
 } );
 
 
-describe( 'define', function() {
-	it( 'should create the definition property', function() {
+describe( 'define', () => {
+	it( 'should create the definition property', () => {
 		expect( config ).to.not.have.property( 'definition' );
 		expect( config ).to.not.have.property( 'definition' );
 
 
 		config.define( 'test', 1 );
 		config.define( 'test', 1 );
@@ -215,7 +215,7 @@ describe( 'define', function() {
 		expect( config ).to.have.property( 'definition' );
 		expect( config ).to.have.property( 'definition' );
 	} );
 	} );
 
 
-	it( 'should set configurations in the definition property', function() {
+	it( 'should set configurations in the definition property', () => {
 		config.define( 'test1', 1 );
 		config.define( 'test1', 1 );
 
 
 		// This is for Code Coverage to ensure that it works when `definition` is already defined.
 		// This is for Code Coverage to ensure that it works when `definition` is already defined.
@@ -225,7 +225,7 @@ describe( 'define', function() {
 		expect( config.definition ).to.have.property( 'test2' ).to.equal( 2 );
 		expect( config.definition ).to.have.property( 'test2' ).to.equal( 2 );
 	} );
 	} );
 
 
-	it( 'should set configurations passed as object in the definition property', function() {
+	it( 'should set configurations passed as object in the definition property', () => {
 		config.define( {
 		config.define( {
 			test: 1
 			test: 1
 		} );
 		} );
@@ -233,14 +233,14 @@ describe( 'define', function() {
 		expect( config.definition ).to.have.property( 'test' ).to.equal( 1 );
 		expect( config.definition ).to.have.property( 'test' ).to.equal( 1 );
 	} );
 	} );
 
 
-	it( 'should not define main config properties but still be retrieved with get()', function() {
+	it( 'should not define main config properties but still be retrieved with get()', () => {
 		config.define( 'test', 1 );
 		config.define( 'test', 1 );
 
 
 		expect( config ).to.not.have.property( 'test' );
 		expect( config ).to.not.have.property( 'test' );
 		expect( config.get( 'test' ) ).to.equal( 1 );
 		expect( config.get( 'test' ) ).to.equal( 1 );
 	} );
 	} );
 
 
-	it( 'should be overridden by set()', function() {
+	it( 'should be overridden by set()', () => {
 		config.define( 'test', 1 );
 		config.define( 'test', 1 );
 		config.set( 'test', 2 );
 		config.set( 'test', 2 );
 
 

+ 26 - 26
packages/ckeditor5-engine/tests/document/attribute.js

@@ -7,71 +7,71 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'document/attribute' );
+const modules = bender.amd.require( 'document/attribute' );
 
 
-describe( 'Attribute', function() {
-	var Attribute;
+describe( 'Attribute', () => {
+	let Attribute;
 
 
-	before( function() {
+	before( () => {
 		Attribute = modules[ 'document/attribute' ];
 		Attribute = modules[ 'document/attribute' ];
 	} );
 	} );
 
 
-	beforeEach( function() {
+	beforeEach( () => {
 		Attribute._register = {};
 		Attribute._register = {};
 	} );
 	} );
 
 
-	describe( 'constructor', function() {
-		it( 'should create attribute', function() {
-			var attr = new Attribute( 'foo', 'bar' );
+	describe( 'constructor', () => {
+		it( 'should create attribute', () => {
+			let attr = new Attribute( 'foo', 'bar' );
 
 
 			expect( attr ).to.have.property( 'key' ).that.equals( 'foo' );
 			expect( attr ).to.have.property( 'key' ).that.equals( 'foo' );
 			expect( attr ).to.have.property( 'value' ).that.equals( 'bar' );
 			expect( attr ).to.have.property( 'value' ).that.equals( 'bar' );
 		} );
 		} );
 
 
-		it( 'should create equal instance even if object has different order', function() {
-			var attr1 = new Attribute( 'foo', { a: 1, b: 2 } );
-			var attr2 = new Attribute( 'foo', { b: 2, a: 1 } );
+		it( 'should create equal instance even if object has different order', () => {
+			let attr1 = new Attribute( 'foo', { a: 1, b: 2 } );
+			let attr2 = new Attribute( 'foo', { b: 2, a: 1 } );
 
 
 			expect( attr1.isEqual( attr2 ) ).to.be.true;
 			expect( attr1.isEqual( attr2 ) ).to.be.true;
 		} );
 		} );
 
 
-		it( 'should return the same object for registered objects', function() {
+		it( 'should return the same object for registered objects', () => {
 			Attribute.register( 'register', true );
 			Attribute.register( 'register', true );
 
 
-			var attr1 = new Attribute( 'register', true );
-			var attr2 = new Attribute( 'register', true );
+			let attr1 = new Attribute( 'register', true );
+			let attr2 = new Attribute( 'register', true );
 
 
 			expect( attr1 ).to.equal( attr2 );
 			expect( attr1 ).to.equal( attr2 );
 			expect( attr1.isEqual( attr2 ) ).to.be.true;
 			expect( attr1.isEqual( attr2 ) ).to.be.true;
 		} );
 		} );
 
 
-		it( 'should return different objects for different values', function() {
+		it( 'should return different objects for different values', () => {
 			Attribute.register( 'register', true );
 			Attribute.register( 'register', true );
 
 
-			var attr1 = new Attribute( 'register', true );
-			var attr2 = new Attribute( 'register', false );
+			let attr1 = new Attribute( 'register', true );
+			let attr2 = new Attribute( 'register', false );
 
 
 			expect( attr1 ).to.not.be.equals( attr2 );
 			expect( attr1 ).to.not.be.equals( attr2 );
 			expect( attr1.isEqual( attr2 ) ).to.not.be.true;
 			expect( attr1.isEqual( attr2 ) ).to.not.be.true;
 		} );
 		} );
 
 
-		it( 'should return different objects for not registered objects', function() {
+		it( 'should return different objects for not registered objects', () => {
 			Attribute.register( 'register', true );
 			Attribute.register( 'register', true );
 
 
-			var attr1 = new Attribute( 'register', false );
-			var attr2 = new Attribute( 'register', false );
+			let attr1 = new Attribute( 'register', false );
+			let attr2 = new Attribute( 'register', false );
 
 
 			expect( attr1 ).to.not.be.equals( attr2 );
 			expect( attr1 ).to.not.be.equals( attr2 );
 			expect( attr1.isEqual( attr2 ) ).to.be.true;
 			expect( attr1.isEqual( attr2 ) ).to.be.true;
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'register', function() {
-		it( 'Attribute.register should return registered attribute', function() {
-			var attr1 = new Attribute( 'register', true );
-			var attr2 = Attribute.register( 'register', true );
-			var attr3 = Attribute.register( 'register', true );
-			var attr4 = new Attribute( 'register', true );
+	describe( 'register', () => {
+		it( 'Attribute.register should return registered attribute', () => {
+			let attr1 = new Attribute( 'register', true );
+			let attr2 = Attribute.register( 'register', true );
+			let attr3 = Attribute.register( 'register', true );
+			let attr4 = new Attribute( 'register', true );
 
 
 			expect( attr1 ).to.not.be.equals( attr2 );
 			expect( attr1 ).to.not.be.equals( attr2 );
 			expect( attr2 ).to.equal( attr3 );
 			expect( attr2 ).to.equal( attr3 );

+ 18 - 14
packages/ckeditor5-engine/tests/document/character.js

@@ -7,45 +7,49 @@
 
 
 /* bender-tags: document */
 /* bender-tags: document */
 
 
+/* bender-include: ../_tools/tools.js */
+
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
 	'document/character',
 	'document/character',
 	'document/node',
 	'document/node',
 	'document/element',
 	'document/element',
 	'document/attribute'
 	'document/attribute'
 );
 );
 
 
-describe( 'Character', function() {
-	var Element, Character, Node, Attribute;
+describe( 'Character', () => {
+	let Element, Character, Node, Attribute;
 
 
-	before( function() {
+	before( () => {
 		Element = modules[ 'document/element' ];
 		Element = modules[ 'document/element' ];
 		Character = modules[ 'document/character' ];
 		Character = modules[ 'document/character' ];
 		Node = modules[ 'document/node' ];
 		Node = modules[ 'document/node' ];
 		Attribute = modules[ 'document/attribute' ];
 		Attribute = modules[ 'document/attribute' ];
 	} );
 	} );
 
 
-	describe( 'constructor', function() {
-		it( 'should create character without attributes', function() {
-			var character = new Character( 'f' );
-			var parent = new Element( 'parent', [], character );
+	describe( 'constructor', () => {
+		it( 'should create character without attributes', () => {
+			let character = new Character( 'f' );
+			let parent = new Element( 'parent', [], character );
 
 
 			expect( character ).to.be.an.instanceof( Node );
 			expect( character ).to.be.an.instanceof( Node );
 			expect( character ).to.have.property( 'character' ).that.equals( 'f' );
 			expect( character ).to.have.property( 'character' ).that.equals( 'f' );
 			expect( character ).to.have.property( 'parent' ).that.equals( parent );
 			expect( character ).to.have.property( 'parent' ).that.equals( parent );
-			expect( character._getAttrCount() ).to.equal( 0 );
+			expect( getIteratorCount( character.getAttrs() ) ).to.equal( 0 );
 		} );
 		} );
 
 
-		it( 'should create character with attributes', function() {
-			var attr = new Attribute( 'foo', 'bar' );
-			var character = new Character( 'f', [ attr ] );
-			var parent = new Element( 'parent', [], character );
+		it( 'should create character with attributes', () => {
+			let attr = new Attribute( 'foo', 'bar' );
+			let character = new Character( 'f', [ attr ] );
+			let parent = new Element( 'parent', [], character );
 
 
 			expect( character ).to.be.an.instanceof( Node );
 			expect( character ).to.be.an.instanceof( Node );
 			expect( character ).to.have.property( 'character' ).that.equals( 'f' );
 			expect( character ).to.have.property( 'character' ).that.equals( 'f' );
 			expect( character ).to.have.property( 'parent' ).that.equals( parent );
 			expect( character ).to.have.property( 'parent' ).that.equals( parent );
-			expect( character._getAttrCount() ).to.equal( 1 );
+			expect( getIteratorCount( character.getAttrs() ) ).to.equal( 1 );
 			expect( character.getAttr( attr.key ) ).to.equal( attr.value );
 			expect( character.getAttr( attr.key ) ).to.equal( attr.value );
 		} );
 		} );
 	} );
 	} );

+ 275 - 0
packages/ckeditor5-engine/tests/document/deltas/changedelta.js

@@ -0,0 +1,275 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+/* bender-include: ../../_tools/tools.js */
+
+'use strict';
+
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
+	'document/transaction',
+	'document/document',
+	'document/text',
+	'document/attribute',
+	'document/range',
+	'document/position',
+	'document/element',
+	'document/character' );
+
+describe( 'Transaction', () => {
+	let Transaction, Document, Text, Attribute, Range, Position, Element, Character;
+
+	let doc, root, transaction;
+
+	before( () => {
+		Transaction = modules[ 'document/transaction' ];
+		Document = modules[ 'document/document' ];
+		Text = modules[ 'document/text' ];
+		Attribute = modules[ 'document/attribute' ];
+		Range = modules[ 'document/range' ];
+		Position = modules[ 'document/position' ];
+		Element = modules[ 'document/element' ];
+		Character = modules[ 'document/character' ];
+	} );
+
+	beforeEach( () => {
+		doc = new Document();
+		root = doc.createRoot( 'root' );
+		transaction = doc.createTransaction();
+	} );
+
+	function getOperationsCount() {
+		let count = 0;
+
+		for ( let delta of transaction.deltas ) {
+			count += getIteratorCount( delta.operations );
+		}
+
+		return count;
+	}
+
+	describe( 'change attribute on node', () => {
+		let node, character;
+
+		beforeEach( () => {
+			node = new Element( 'p', [ new Attribute( 'a', 1 ) ] );
+			character = new Character( 'c', [ new Attribute( 'a', 1 ) ] );
+			root.insertChildren( 0, [ node, character ] );
+		} );
+
+		describe( 'setAttr', () => {
+			it( 'should create the attribute on element', () => {
+				transaction.setAttr( 'b', 2, node );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( node.getAttr( 'b' ) ).to.equal( 2 );
+			} );
+
+			it( 'should change the attribute of element', () => {
+				transaction.setAttr( 'a', 2, node );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( node.getAttr( 'a' ) ).to.equal( 2 );
+			} );
+
+			it( 'should create the attribute on character', () => {
+				transaction.setAttr( 'b', 2, character );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( character.getAttr( 'b' ) ).to.equal( 2 );
+			} );
+
+			it( 'should change the attribute of character', () => {
+				transaction.setAttr( 'a', 2, character );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( character.getAttr( 'a' ) ).to.equal( 2 );
+			} );
+
+			it( 'should do nothing if the attribute value is the same', () => {
+				transaction.setAttr( 'a', 1, node );
+				expect( getOperationsCount() ).to.equal( 0 );
+				expect( node.getAttr( 'a' ) ).to.equal( 1 );
+			} );
+
+			it( 'should be chainable', () => {
+				const chain = transaction.setAttr( 'b', 2, node );
+				expect( chain ).to.equal( transaction );
+			} );
+		} );
+
+		describe( 'removeAttr', () => {
+			it( 'should remove the attribute from element', () => {
+				transaction.removeAttr( 'a', node );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( node.getAttr( 'a' ) ).to.be.null;
+			} );
+
+			it( 'should remove the attribute from character', () => {
+				transaction.removeAttr( 'a', character );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( character.getAttr( 'a' ) ).to.be.null;
+			} );
+
+			it( 'should do nothing if the attribute is not set', () => {
+				transaction.removeAttr( 'b', node );
+				expect( getOperationsCount() ).to.equal( 0 );
+			} );
+
+			it( 'should be chainable', () => {
+				const chain = transaction.removeAttr( 'a', node );
+				expect( chain ).to.equal( transaction );
+			} );
+		} );
+	} );
+
+	describe( 'change attribute on range', () => {
+		beforeEach( () => {
+			root.insertChildren( 0, [
+				new Text( 'xxx', [ new Attribute( 'a', 1 ) ] ),
+				'xxx',
+				new Text( 'xxx', [ new Attribute( 'a', 1 ) ] ),
+				new Text( 'xxx', [ new Attribute( 'a', 2 ) ] ),
+				'xxx',
+				new Text( 'xxx', [ new Attribute( 'a', 1 ) ] )
+			] );
+		} );
+
+		function getRange( startIndex, endIndex ) {
+			return new Range(
+					Position.createFromParentAndOffset( root, startIndex ),
+					Position.createFromParentAndOffset( root, endIndex )
+				);
+		}
+
+		function getChangesAttrsCount() {
+			let count = 0;
+
+			for ( let delta of transaction.deltas ) {
+				for ( let operation of delta.operations ) {
+					count += getIteratorCount( operation.range );
+				}
+			}
+
+			return count;
+		}
+
+		function getCompressedAttrs() {
+			// default: 111---111222---111
+			const range = Range.createFromElement( root );
+
+			return Array.from( range ).map( value => value.node.getAttr( 'a' ) || '-' ).join( '' );
+		}
+
+		describe( 'setAttr', () => {
+			it( 'should set the attribute on the range', () => {
+				transaction.setAttr( 'a', 3, getRange( 3, 6 ) );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( getChangesAttrsCount() ).to.equal( 3 );
+				expect( getCompressedAttrs() ).to.equal( '111333111222---111' );
+			} );
+
+			it( 'should split the operations if parts of the range have different attributes', () => {
+				transaction.setAttr( 'a', 3, getRange( 4, 14 ) );
+				expect( getOperationsCount() ).to.equal( 4 );
+				expect( getChangesAttrsCount() ).to.equal( 10 );
+				expect( getCompressedAttrs() ).to.equal( '111-3333333333-111' );
+			} );
+
+			it( 'should split the operations if parts of the part of the range have the attribute', () => {
+				transaction.setAttr( 'a', 2, getRange( 4, 14 ) );
+				expect( getOperationsCount() ).to.equal( 3 );
+				expect( getChangesAttrsCount() ).to.equal( 7 );
+				expect( getCompressedAttrs() ).to.equal( '111-2222222222-111' );
+			} );
+
+			it( 'should strip the range if the beginning have the attribute', () => {
+				transaction.setAttr( 'a', 1, getRange( 1, 5 ) );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( getChangesAttrsCount() ).to.equal( 2 );
+				expect( getCompressedAttrs() ).to.equal( '11111-111222---111' );
+			} );
+
+			it( 'should strip the range if the ending have the attribute', () => {
+				transaction.setAttr( 'a', 1, getRange( 13, 17 ) );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( getChangesAttrsCount() ).to.equal( 2 );
+				expect( getCompressedAttrs() ).to.equal( '111---111222-11111' );
+			} );
+
+			it( 'should do nothing if the range has attribute', () => {
+				transaction.setAttr( 'a', 1, getRange( 0, 3 ) );
+				expect( getOperationsCount() ).to.equal( 0 );
+				expect( getCompressedAttrs() ).to.equal( '111---111222---111' );
+			} );
+
+			it( 'should create a proper operations for the mixed range', () => {
+				transaction.setAttr( 'a', 1, getRange( 0, 18 ) );
+				expect( getOperationsCount() ).to.equal( 3 );
+				expect( getChangesAttrsCount() ).to.equal( 9 );
+				expect( getCompressedAttrs() ).to.equal( '111111111111111111' );
+			} );
+
+			it( 'should be chainable', () => {
+				const chain = transaction.setAttr( 'a', 3, getRange( 3, 6 ) );
+				expect( chain ).to.equal( transaction );
+			} );
+		} );
+
+		describe( 'removeAttr', () => {
+			it( 'should remove the attribute on the range', () => {
+				transaction.removeAttr( 'a', getRange( 0, 2 ) );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( getChangesAttrsCount() ).to.equal( 2 );
+				expect( getCompressedAttrs() ).to.equal( '--1---111222---111' );
+			} );
+
+			it( 'should split the operations if parts of the range have different attributes', () => {
+				transaction.removeAttr( 'a', getRange( 7, 11 ) );
+				expect( getOperationsCount() ).to.equal( 2 );
+				expect( getChangesAttrsCount() ).to.equal( 4 );
+				expect( getCompressedAttrs() ).to.equal( '111---1----2---111' );
+			} );
+
+			it( 'should split the operations if parts of the part of the range have no attribute', () => {
+				transaction.removeAttr( 'a', getRange( 1, 7 ) );
+				expect( getOperationsCount() ).to.equal( 2 );
+				expect( getChangesAttrsCount() ).to.equal( 3 );
+				expect( getCompressedAttrs() ).to.equal( '1------11222---111' );
+			} );
+
+			it( 'should strip the range if the beginning have no attribute', () => {
+				transaction.removeAttr( 'a', getRange( 4, 12 ) );
+				expect( getOperationsCount() ).to.equal( 2 );
+				expect( getChangesAttrsCount() ).to.equal( 6 );
+				expect( getCompressedAttrs() ).to.equal( '111------------111' );
+			} );
+
+			it( 'should strip the range if the ending have no attribute', () => {
+				transaction.removeAttr( 'a', getRange( 7, 15 ) );
+				expect( getOperationsCount() ).to.equal( 2 );
+				expect( getChangesAttrsCount() ).to.equal( 5 );
+				expect( getCompressedAttrs() ).to.equal( '111---1--------111' );
+			} );
+
+			it( 'should do nothing if the range has no attribute', () => {
+				transaction.removeAttr( 'a', getRange( 4, 5 ) );
+				expect( getOperationsCount() ).to.equal( 0 );
+				expect( getCompressedAttrs() ).to.equal( '111---111222---111' );
+			} );
+
+			it( 'should create a proper operations for the mixed range', () => {
+				transaction.removeAttr( 'a', getRange( 3, 15 ) );
+				expect( getOperationsCount() ).to.equal( 2 );
+				expect( getChangesAttrsCount() ).to.equal( 6 );
+				expect( getCompressedAttrs() ).to.equal( '111------------111' );
+			} );
+
+			it( 'should be chainable', () => {
+				const chain = transaction.removeAttr( 'a', getRange( 0, 2 ) );
+				expect( chain ).to.equal( transaction );
+			} );
+		} );
+	} );
+} );

+ 67 - 0
packages/ckeditor5-engine/tests/document/deltas/delta.js

@@ -0,0 +1,67 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+/* bender-include: ../../_tools/tools.js */
+
+'use strict';
+
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
+	'document/delta/delta' );
+
+describe( 'Delta', () => {
+	let Delta;
+
+	before( () => {
+		Delta = modules[ 'document/delta/delta' ];
+	} );
+
+	describe( 'constructor', () => {
+		it( 'should create an delta with empty properties', () => {
+			const delta = new Delta();
+
+			expect( delta ).to.have.property( 'transaction' ).that.is.null;
+			expect( delta ).to.have.property( 'operations' ).that.a( 'array' ).and.have.length( 0 );
+		} );
+	} );
+
+	describe( 'addOperation', () => {
+		it( 'should add operation to the delta', () => {
+			const delta = new Delta();
+			const operation = {};
+
+			delta.addOperation( operation );
+
+			expect( delta.operations ).to.have.length( 1 );
+			expect( delta.operations[ 0 ] ).to.equal( operation );
+		} );
+
+		it( 'should add delta property to the operation', () => {
+			const delta = new Delta();
+			const operation = {};
+
+			delta.addOperation( operation );
+
+			expect( operation.delta ).to.equal( delta );
+		} );
+	} );
+
+	describe( 'iterator', () => {
+		it( 'should iterate over delta operations', () => {
+			const delta = new Delta();
+
+			delta.addOperation( {} );
+			delta.addOperation( {} );
+			delta.addOperation( {} );
+
+			const count = getIteratorCount( delta.operations );
+
+			expect( count ).to.equal( 3 );
+		} );
+	} );
+} );

+ 46 - 0
packages/ckeditor5-engine/tests/document/deltas/insertdelta.js

@@ -0,0 +1,46 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'document/document',
+	'document/position' );
+
+describe( 'Transaction', () => {
+	let Document, Position;
+
+	let doc, root;
+
+	before( () => {
+		Document = modules[ 'document/document' ];
+		Position = modules[ 'document/position' ];
+	} );
+
+	beforeEach( () => {
+		doc = new Document();
+		root = doc.createRoot( 'root' );
+	} );
+
+	it( 'should insert text', () => {
+		const position = new Position( [ 0 ], root );
+		doc.createTransaction().insert( position, 'foo' );
+
+		expect( root.getChildCount() ).to.equal( 3 );
+		expect( root.getChild( 0 ).character ).to.equal( 'f' );
+		expect( root.getChild( 1 ).character ).to.equal( 'o' );
+		expect( root.getChild( 2 ).character ).to.equal( 'o' );
+	} );
+
+	it( 'should be chainable', () => {
+		const position = new Position( [ 0 ], root );
+		const transaction = doc.createTransaction();
+
+		const chain = transaction.insert( position, 'foo' );
+		expect( chain ).to.equal( transaction );
+	} );
+} );

+ 80 - 0
packages/ckeditor5-engine/tests/document/deltas/mergedelta.js

@@ -0,0 +1,80 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+/* bender-include: ../../_tools/tools.js */
+
+'use strict';
+
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
+	'document/document',
+	'document/position',
+	'document/element',
+	'document/attribute',
+	'ckeditorerror' );
+
+describe( 'Transaction', () => {
+	let Document, Position, Element, Attribute, CKEditorError;
+
+	let doc, root, p1, p2;
+
+	before( () => {
+		Document = modules[ 'document/document' ];
+		Position = modules[ 'document/position' ];
+		Element = modules[ 'document/element' ];
+		Attribute = modules[ 'document/attribute' ];
+		CKEditorError = modules.ckeditorerror;
+	} );
+
+	beforeEach( () => {
+		doc = new Document();
+		root = doc.createRoot( 'root' );
+
+		p1 = new Element( 'p', [ new Attribute( 'key1', 'value1' ) ], 'foo' );
+		p2 = new Element( 'p', [ new Attribute( 'key2', 'value2' ) ], 'bar' );
+
+		root.insertChildren( 0, [ p1, p2 ] );
+	} );
+
+	describe( 'merge', () => {
+		it( 'should merge foo and bar into foobar', () => {
+			doc.createTransaction().merge( new Position( [ 1 ], root ) );
+
+			expect( root.getChildCount() ).to.equal( 1 );
+			expect( root.getChild( 0 ).name ).to.equal( 'p' );
+			expect( root.getChild( 0 ).getChildCount() ).to.equal( 6 );
+			expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 1 );
+			expect( root.getChild( 0 ).getAttr( 'key1' ) ).to.equal( 'value1' );
+			expect( root.getChild( 0 ).getChild( 0 ).character ).to.equal( 'f' );
+			expect( root.getChild( 0 ).getChild( 1 ).character ).to.equal( 'o' );
+			expect( root.getChild( 0 ).getChild( 2 ).character ).to.equal( 'o' );
+			expect( root.getChild( 0 ).getChild( 3 ).character ).to.equal( 'b' );
+			expect( root.getChild( 0 ).getChild( 4 ).character ).to.equal( 'a' );
+			expect( root.getChild( 0 ).getChild( 5 ).character ).to.equal( 'r' );
+		} );
+
+		it( 'should throw if there is no element after', () => {
+			expect( () => {
+				doc.createTransaction().merge( new Position( [ 2 ], root ) );
+			} ).to.throw( CKEditorError, /^transaction-merge-no-element-after/ );
+		} );
+
+		it( 'should throw if there is no element before', () => {
+			expect( () => {
+				doc.createTransaction().merge( new Position( [ 0, 2 ], root ) );
+			} ).to.throw( CKEditorError, /^transaction-merge-no-element-before/ );
+		} );
+
+		it( 'should be chainable', () => {
+			const transaction = doc.createTransaction();
+
+			const chain = transaction.merge( new Position( [ 1 ], root ) );
+			expect( chain ).to.equal( transaction );
+		} );
+	} );
+} );

+ 61 - 0
packages/ckeditor5-engine/tests/document/deltas/removedelta.js

@@ -0,0 +1,61 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'document/document',
+	'document/position' );
+
+describe( 'Transaction', () => {
+	let Document, Position;
+
+	let doc, root;
+
+	before( () => {
+		Document = modules[ 'document/document' ];
+		Position = modules[ 'document/position' ];
+	} );
+
+	beforeEach( () => {
+		doc = new Document();
+		root = doc.createRoot( 'root' );
+		root.insertChildren( 0, 'foobar' );
+	} );
+
+	describe( 'remove', () => {
+		it( 'should remove one element', () => {
+			const position = new Position( [ 1 ], root );
+			doc.createTransaction().remove( position );
+
+			expect( root.getChildCount() ).to.equal( 5 );
+			expect( root.getChild( 0 ).character ).to.equal( 'f' );
+			expect( root.getChild( 1 ).character ).to.equal( 'o' );
+			expect( root.getChild( 2 ).character ).to.equal( 'b' );
+			expect( root.getChild( 3 ).character ).to.equal( 'a' );
+			expect( root.getChild( 4 ).character ).to.equal( 'r' );
+		} );
+
+		it( 'should remove 3 elements', () => {
+			const position = new Position( [ 1 ], root );
+			doc.createTransaction().remove( position, 3 );
+
+			expect( root.getChildCount() ).to.equal( 3 );
+			expect( root.getChild( 0 ).character ).to.equal( 'f' );
+			expect( root.getChild( 1 ).character ).to.equal( 'a' );
+			expect( root.getChild( 2 ).character ).to.equal( 'r' );
+		} );
+
+		it( 'should be chainable', () => {
+			const position = new Position( [ 1 ], root );
+			const transaction = doc.createTransaction();
+
+			const chain = transaction.remove( position );
+			expect( chain ).to.equal( transaction );
+		} );
+	} );
+} );

+ 101 - 0
packages/ckeditor5-engine/tests/document/deltas/splitdelta.js

@@ -0,0 +1,101 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+/* bender-include: ../../_tools/tools.js */
+
+'use strict';
+
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
+	'document/document',
+	'document/position',
+	'document/element',
+	'document/attribute',
+	'ckeditorerror' );
+
+describe( 'Transaction', () => {
+	let Document, Position, Element, Attribute, CKEditorError;
+
+	let doc, root, p;
+
+	before( () => {
+		Document = modules[ 'document/document' ];
+		Position = modules[ 'document/position' ];
+		Element = modules[ 'document/element' ];
+		Attribute = modules[ 'document/attribute' ];
+		CKEditorError = modules.ckeditorerror;
+	} );
+
+	beforeEach( () => {
+		doc = new Document();
+		root = doc.createRoot( 'root' );
+
+		p = new Element( 'p', [ new Attribute( 'key', 'value' ) ], 'foobar' );
+
+		root.insertChildren( 0, p );
+	} );
+
+	describe( 'split', () => {
+		it( 'should split foobar to foo and bar', () => {
+			doc.createTransaction().split( new Position( [ 0, 3 ], root ) );
+
+			expect( root.getChildCount() ).to.equal( 2 );
+
+			expect( root.getChild( 0 ).name ).to.equal( 'p' );
+			expect( root.getChild( 0 ).getChildCount() ).to.equal( 3 );
+			expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 1 );
+			expect( root.getChild( 0 ).getAttr( 'key' ) ).to.equal( 'value' );
+			expect( root.getChild( 0 ).getChild( 0 ).character ).to.equal( 'f' );
+			expect( root.getChild( 0 ).getChild( 1 ).character ).to.equal( 'o' );
+			expect( root.getChild( 0 ).getChild( 2 ).character ).to.equal( 'o' );
+
+			expect( root.getChild( 1 ).name ).to.equal( 'p' );
+			expect( root.getChild( 1 ).getChildCount() ).to.equal( 3 );
+			expect( getIteratorCount( root.getChild( 1 ).getAttrs() ) ).to.equal( 1 );
+			expect( root.getChild( 1 ).getAttr( 'key' ) ).to.equal( 'value' );
+			expect( root.getChild( 1 ).getChild( 0 ).character ).to.equal( 'b' );
+			expect( root.getChild( 1 ).getChild( 1 ).character ).to.equal( 'a' );
+			expect( root.getChild( 1 ).getChild( 2 ).character ).to.equal( 'r' );
+		} );
+
+		it( 'should create an empty paragraph if we split at the end', () => {
+			doc.createTransaction().split( new Position( [ 0, 6 ], root ) );
+
+			expect( root.getChildCount() ).to.equal( 2 );
+
+			expect( root.getChild( 0 ).name ).to.equal( 'p' );
+			expect( root.getChild( 0 ).getChildCount() ).to.equal( 6 );
+			expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 1 );
+			expect( root.getChild( 0 ).getAttr( 'key' ) ).to.equal( 'value' );
+			expect( root.getChild( 0 ).getChild( 0 ).character ).to.equal( 'f' );
+			expect( root.getChild( 0 ).getChild( 1 ).character ).to.equal( 'o' );
+			expect( root.getChild( 0 ).getChild( 2 ).character ).to.equal( 'o' );
+			expect( root.getChild( 0 ).getChild( 3 ).character ).to.equal( 'b' );
+			expect( root.getChild( 0 ).getChild( 4 ).character ).to.equal( 'a' );
+			expect( root.getChild( 0 ).getChild( 5 ).character ).to.equal( 'r' );
+
+			expect( root.getChild( 1 ).name ).to.equal( 'p' );
+			expect( root.getChild( 1 ).getChildCount() ).to.equal( 0 );
+			expect( getIteratorCount( root.getChild( 1 ).getAttrs() ) ).to.equal( 1 );
+			expect( root.getChild( 1 ).getAttr( 'key' ) ).to.equal( 'value' );
+		} );
+
+		it( 'should throw if we try to split a root', () => {
+			expect( () => {
+				doc.createTransaction().split( new Position( [ 0 ], root ) );
+			} ).to.throw( CKEditorError, /^transaction-split-root/ );
+		} );
+
+		it( 'should be chainable', () => {
+			const transaction = doc.createTransaction();
+
+			const chain = transaction.split( new Position( [ 0, 3 ], root ) );
+			expect( chain ).to.equal( transaction );
+		} );
+	} );
+} );

+ 38 - 27
packages/ckeditor5-engine/tests/document/document.js

@@ -7,29 +7,31 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/document',
 	'document/document',
 	'document/rootelement',
 	'document/rootelement',
+	'document/transaction',
 	'ckeditorerror'
 	'ckeditorerror'
 );
 );
 
 
-describe( 'Document', function() {
-	var Document, RootElement, CKEditorError;
+describe( 'Document', () => {
+	let Document, RootElement, Transaction, CKEditorError;
 
 
-	before( function() {
+	before( () => {
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
 		RootElement = modules[ 'document/rootelement' ];
 		RootElement = modules[ 'document/rootelement' ];
+		Transaction = modules[ 'document/transaction' ];
 		CKEditorError = modules.ckeditorerror;
 		CKEditorError = modules.ckeditorerror;
 	} );
 	} );
 
 
-	var document;
+	let document;
 
 
-	beforeEach( function() {
+	beforeEach( () => {
 		document = new Document();
 		document = new Document();
 	} );
 	} );
 
 
-	describe( 'constructor', function() {
-		it( 'should create Document with no data', function() {
+	describe( 'constructor', () => {
+		it( 'should create Document with no data', () => {
 			expect( document ).to.have.property( 'roots' ).that.is.instanceof( Map );
 			expect( document ).to.have.property( 'roots' ).that.is.instanceof( Map );
 			expect( document.roots.size ).to.equal( 1 );
 			expect( document.roots.size ).to.equal( 1 );
 			expect( document._graveyard ).to.be.instanceof( RootElement );
 			expect( document._graveyard ).to.be.instanceof( RootElement );
@@ -37,47 +39,47 @@ describe( 'Document', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'createRoot', function() {
-		it( 'should create a new RootElement, add it to roots map and return it', function() {
-			var root = document.createRoot( 'root' );
+	describe( 'createRoot', () => {
+		it( 'should create a new RootElement, add it to roots map and return it', () => {
+			let root = document.createRoot( 'root' );
 
 
 			expect( document.roots.size ).to.equal( 2 );
 			expect( document.roots.size ).to.equal( 2 );
 			expect( root ).to.be.instanceof( RootElement );
 			expect( root ).to.be.instanceof( RootElement );
 			expect( root.getChildCount() ).to.equal( 0 );
 			expect( root.getChildCount() ).to.equal( 0 );
 		} );
 		} );
 
 
-		it( 'should throw an error when trying to create a second root with the same name', function() {
+		it( 'should throw an error when trying to create a second root with the same name', () => {
 			document.createRoot( 'root' );
 			document.createRoot( 'root' );
 
 
 			expect(
 			expect(
-				function() {
+				() => {
 					document.createRoot( 'root' );
 					document.createRoot( 'root' );
 				}
 				}
 			).to.throw( CKEditorError, /document-createRoot-name-exists/ );
 			).to.throw( CKEditorError, /document-createRoot-name-exists/ );
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'getRoot', function() {
-		it( 'should return a RootElement previously created with given name', function() {
-			var newRoot = document.createRoot( 'root' );
-			var getRoot = document.getRoot( 'root' );
+	describe( 'getRoot', () => {
+		it( 'should return a RootElement previously created with given name', () => {
+			let newRoot = document.createRoot( 'root' );
+			let getRoot = document.getRoot( 'root' );
 
 
 			expect( getRoot ).to.equal( newRoot );
 			expect( getRoot ).to.equal( newRoot );
 		} );
 		} );
 
 
-		it( 'should throw an error when trying to get non-existent root', function() {
+		it( 'should throw an error when trying to get non-existent root', () => {
 			expect(
 			expect(
-				function() {
+				() => {
 					document.getRoot( 'root' );
 					document.getRoot( 'root' );
 				}
 				}
 			).to.throw( CKEditorError, /document-createRoot-root-not-exist/ );
 			).to.throw( CKEditorError, /document-createRoot-root-not-exist/ );
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'applyOperation', function() {
-		it( 'should increase document version, execute operation and fire operationApplied', function() {
-			var operationApplied = sinon.spy();
-			var operation = {
+	describe( 'applyOperation', () => {
+		it( 'should increase document version, execute operation and fire operationApplied', () => {
+			let operationApplied = sinon.spy();
+			let operation = {
 				baseVersion: 0,
 				baseVersion: 0,
 				_execute: sinon.spy()
 				_execute: sinon.spy()
 			};
 			};
@@ -91,19 +93,28 @@ describe( 'Document', function() {
 			sinon.assert.calledOnce( operation._execute );
 			sinon.assert.calledOnce( operation._execute );
 		} );
 		} );
 
 
-		it( 'should throw an error on the operation base version and the document version is different', function() {
-			var operationApplied = sinon.spy();
-			var operation = {
+		it( 'should throw an error on the operation base version and the document version is different', () => {
+			let operationApplied = sinon.spy();
+			let operation = {
 				baseVersion: 1
 				baseVersion: 1
 			};
 			};
 
 
 			document.on( 'operationApplied', operationApplied );
 			document.on( 'operationApplied', operationApplied );
 
 
 			expect(
 			expect(
-				function() {
+				() => {
 					document.applyOperation( operation );
 					document.applyOperation( operation );
 				}
 				}
 			).to.throw( CKEditorError, /document-applyOperation-wrong-version/ );
 			).to.throw( CKEditorError, /document-applyOperation-wrong-version/ );
 		} );
 		} );
 	} );
 	} );
+
+	describe( 'createTransaction', () => {
+		it( 'should create a new transaction with the document property', () => {
+			const transaction = document.createTransaction();
+
+			expect( transaction ).to.be.instanceof( Transaction );
+			expect( transaction ).to.have.property( 'doc' ).that.equals( document );
+		} );
+	} );
 } );
 } );

+ 39 - 35
packages/ckeditor5-engine/tests/document/element.js

@@ -7,51 +7,55 @@
 
 
 /* bender-tags: document */
 /* bender-tags: document */
 
 
+/* bender-include: ../_tools/tools.js */
+
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
 	'document/node',
 	'document/node',
 	'document/nodelist',
 	'document/nodelist',
 	'document/element',
 	'document/element',
 	'document/attribute'
 	'document/attribute'
 );
 );
 
 
-describe( 'Element', function() {
-	var Element, Node, NodeList, Attribute;
+describe( 'Element', () => {
+	let Element, Node, NodeList, Attribute;
 
 
-	before( function() {
+	before( () => {
 		Element = modules[ 'document/element' ];
 		Element = modules[ 'document/element' ];
 		Node = modules[ 'document/node' ];
 		Node = modules[ 'document/node' ];
 		NodeList = modules[ 'document/nodelist' ];
 		NodeList = modules[ 'document/nodelist' ];
 		Attribute = modules[ 'document/attribute' ];
 		Attribute = modules[ 'document/attribute' ];
 	} );
 	} );
 
 
-	describe( 'constructor', function() {
-		it( 'should create element without attributes', function() {
-			var element = new Element( 'elem' );
-			var parent = new Element( 'parent', [], [ element ] );
+	describe( 'constructor', () => {
+		it( 'should create element without attributes', () => {
+			let element = new Element( 'elem' );
+			let parent = new Element( 'parent', [], [ element ] );
 
 
 			expect( element ).to.be.an.instanceof( Node );
 			expect( element ).to.be.an.instanceof( Node );
 			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
 			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
 			expect( element ).to.have.property( 'parent' ).that.equals( parent );
 			expect( element ).to.have.property( 'parent' ).that.equals( parent );
-			expect( element._getAttrCount() ).to.equal( 0 );
+			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 0 );
 		} );
 		} );
 
 
-		it( 'should create element with attributes', function() {
-			var attr = new Attribute( 'foo', 'bar' );
+		it( 'should create element with attributes', () => {
+			let attr = new Attribute( 'foo', 'bar' );
 
 
-			var element = new Element( 'elem', [ attr ] );
-			var parent = new Element( 'parent', [], [ element ] );
+			let element = new Element( 'elem', [ attr ] );
+			let parent = new Element( 'parent', [], [ element ] );
 
 
 			expect( element ).to.be.an.instanceof( Node );
 			expect( element ).to.be.an.instanceof( Node );
 			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
 			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
 			expect( element ).to.have.property( 'parent' ).that.equals( parent );
 			expect( element ).to.have.property( 'parent' ).that.equals( parent );
-			expect( element._getAttrCount() ).to.equal( 1 );
+			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 1 );
 			expect( element.getAttr( attr.key ) ).to.equal( attr.value );
 			expect( element.getAttr( attr.key ) ).to.equal( attr.value );
 		} );
 		} );
 
 
-		it( 'should create element with children', function() {
-			var element = new Element( 'elem', [], 'foo' );
+		it( 'should create element with children', () => {
+			let element = new Element( 'elem', [], 'foo' );
 
 
 			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
 			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
 			expect( element.getChildCount() ).to.equal( 3 );
 			expect( element.getChildCount() ).to.equal( 3 );
@@ -61,9 +65,9 @@ describe( 'Element', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'insertChildren', function() {
-		it( 'should add children to the element', function() {
-			var element = new Element( 'elem', [], [ 'xy' ] );
+	describe( 'insertChildren', () => {
+		it( 'should add children to the element', () => {
+			let element = new Element( 'elem', [], [ 'xy' ] );
 			element.insertChildren( 1, 'foo' );
 			element.insertChildren( 1, 'foo' );
 
 
 			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
 			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
@@ -76,13 +80,13 @@ describe( 'Element', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'removeChildren', function() {
-		it( 'should remove children from the element and return them as a NodeList', function() {
-			var element = new Element( 'elem', [], [ 'foobar' ] );
-			var o = element.getChild( 2 );
-			var b = element.getChild( 3 );
-			var a = element.getChild( 4 );
-			var removed = element.removeChildren( 2, 3 );
+	describe( 'removeChildren', () => {
+		it( 'should remove children from the element and return them as a NodeList', () => {
+			let element = new Element( 'elem', [], [ 'foobar' ] );
+			let o = element.getChild( 2 );
+			let b = element.getChild( 3 );
+			let a = element.getChild( 4 );
+			let removed = element.removeChildren( 2, 3 );
 
 
 			expect( element.getChildCount() ).to.equal( 3 );
 			expect( element.getChildCount() ).to.equal( 3 );
 			expect( element.getChild( 0 ) ).to.have.property( 'character' ).that.equals( 'f' );
 			expect( element.getChild( 0 ) ).to.have.property( 'character' ).that.equals( 'f' );
@@ -101,12 +105,12 @@ describe( 'Element', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'getChildIndex', function() {
-		it( 'should return child index', function() {
-			var element = new Element( 'elem', [], [ 'bar' ] );
-			var b = element.getChild( 0 );
-			var a = element.getChild( 1 );
-			var r = element.getChild( 2 );
+	describe( 'getChildIndex', () => {
+		it( 'should return child index', () => {
+			let element = new Element( 'elem', [], [ 'bar' ] );
+			let b = element.getChild( 0 );
+			let a = element.getChild( 1 );
+			let r = element.getChild( 2 );
 
 
 			expect( element.getChildIndex( b ) ).to.equal( 0 );
 			expect( element.getChildIndex( b ) ).to.equal( 0 );
 			expect( element.getChildIndex( a ) ).to.equal( 1 );
 			expect( element.getChildIndex( a ) ).to.equal( 1 );
@@ -114,9 +118,9 @@ describe( 'Element', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'getChildCount', function() {
-		it( 'should return number of children', function() {
-			var element = new Element( 'elem', [], [ 'bar' ] );
+	describe( 'getChildCount', () => {
+		it( 'should return number of children', () => {
+			let element = new Element( 'elem', [], [ 'bar' ] );
 
 
 			expect( element.getChildCount() ).to.equal( 3 );
 			expect( element.getChildCount() ).to.equal( 3 );
 		} );
 		} );

+ 92 - 69
packages/ckeditor5-engine/tests/document/node.js

@@ -5,9 +5,13 @@
 
 
 /* bender-tags: document */
 /* bender-tags: document */
 
 
+/* bender-include: ../_tools/tools.js */
+
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
 	'document/element',
 	'document/element',
 	'document/character',
 	'document/character',
 	'document/attribute',
 	'document/attribute',
@@ -15,14 +19,14 @@ var modules = bender.amd.require(
 	'ckeditorerror'
 	'ckeditorerror'
 );
 );
 
 
-describe( 'Node', function() {
-	var Element, Character, Attribute, NodeList, CKEditorError;
+describe( 'Node', () => {
+	let Element, Character, Attribute, NodeList, CKEditorError;
 
 
-	var root;
-	var one, two, three;
-	var charB, charA, charR, img;
+	let root;
+	let one, two, three;
+	let charB, charA, charR, img;
 
 
-	before( function() {
+	before( () => {
 		Element = modules[ 'document/element' ];
 		Element = modules[ 'document/element' ];
 		Character = modules[ 'document/character' ];
 		Character = modules[ 'document/character' ];
 		Attribute = modules[ 'document/attribute' ];
 		Attribute = modules[ 'document/attribute' ];
@@ -41,8 +45,8 @@ describe( 'Node', function() {
 		root = new Element( null, null, [ one, two, three ] );
 		root = new Element( null, null, [ one, two, three ] );
 	} );
 	} );
 
 
-	describe( 'should have a correct property', function() {
-		it( 'depth', function() {
+	describe( 'should have a correct property', () => {
+		it( 'depth', () => {
 			expect( root ).to.have.property( 'depth' ).that.equals( 0 );
 			expect( root ).to.have.property( 'depth' ).that.equals( 0 );
 
 
 			expect( one ).to.have.property( 'depth' ).that.equals( 1 );
 			expect( one ).to.have.property( 'depth' ).that.equals( 1 );
@@ -55,7 +59,7 @@ describe( 'Node', function() {
 			expect( charR ).to.have.property( 'depth' ).that.equals( 2 );
 			expect( charR ).to.have.property( 'depth' ).that.equals( 2 );
 		} );
 		} );
 
 
-		it( 'root', function() {
+		it( 'root', () => {
 			expect( root ).to.have.property( 'root' ).that.equals( root );
 			expect( root ).to.have.property( 'root' ).that.equals( root );
 
 
 			expect( one ).to.have.property( 'root' ).that.equals( root );
 			expect( one ).to.have.property( 'root' ).that.equals( root );
@@ -68,7 +72,7 @@ describe( 'Node', function() {
 			expect( charR ).to.have.property( 'root' ).that.equals( root );
 			expect( charR ).to.have.property( 'root' ).that.equals( root );
 		} );
 		} );
 
 
-		it( 'nextSibling', function() {
+		it( 'nextSibling', () => {
 			expect( root ).to.have.property( 'nextSibling' ).that.is.null;
 			expect( root ).to.have.property( 'nextSibling' ).that.is.null;
 
 
 			expect( one ).to.have.property( 'nextSibling' ).that.equals( two );
 			expect( one ).to.have.property( 'nextSibling' ).that.equals( two );
@@ -81,7 +85,7 @@ describe( 'Node', function() {
 			expect( charR ).to.have.property( 'nextSibling' ).that.is.null;
 			expect( charR ).to.have.property( 'nextSibling' ).that.is.null;
 		} );
 		} );
 
 
-		it( 'previousSibling', function() {
+		it( 'previousSibling', () => {
 			expect( root ).to.have.property( 'previousSibling' ).that.is.expect;
 			expect( root ).to.have.property( 'previousSibling' ).that.is.expect;
 
 
 			expect( one ).to.have.property( 'previousSibling' ).that.is.null;
 			expect( one ).to.have.property( 'previousSibling' ).that.is.null;
@@ -95,123 +99,142 @@ describe( 'Node', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'constructor', function() {
-		it( 'should copy attributes, not pass by reference', function() {
-			var attrs = [ new Attribute( 'attr', true ) ];
-			var foo = new Element( 'foo', attrs );
-			var bar = new Element( 'bar', attrs );
+	describe( 'constructor', () => {
+		it( 'should copy attributes, not pass by reference', () => {
+			let attrs = [ new Attribute( 'attr', true ) ];
+			let foo = new Element( 'foo', attrs );
+			let bar = new Element( 'bar', attrs );
 
 
 			foo.removeAttr( 'attr' );
 			foo.removeAttr( 'attr' );
 
 
-			expect( foo._getAttrCount() ).to.equal( 0 );
-			expect( bar._getAttrCount() ).to.equal( 1 );
+			expect( getIteratorCount( foo.getAttrs() ) ).to.equal( 0 );
+			expect( getIteratorCount( bar.getAttrs() ) ).to.equal( 1 );
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'getAttr', function() {
-		var fooAttr, element;
+	describe( 'getAttr', () => {
+		let fooAttr, element;
 
 
-		beforeEach( function() {
+		beforeEach( () => {
 			fooAttr = new Attribute( 'foo', true );
 			fooAttr = new Attribute( 'foo', true );
 			element = new Element( 'foo', [ fooAttr ] );
 			element = new Element( 'foo', [ fooAttr ] );
 		} );
 		} );
 
 
-		it( 'should be possible to get attribute by key', function() {
+		it( 'should be possible to get attribute by key', () => {
 			expect( element.getAttr( 'foo' ) ).to.equal( fooAttr.value );
 			expect( element.getAttr( 'foo' ) ).to.equal( fooAttr.value );
 		} );
 		} );
 
 
-		it( 'should return null if attribute was not found by key', function() {
+		it( 'should return null if attribute was not found by key', () => {
 			expect( element.getAttr( 'bar' ) ).to.be.null;
 			expect( element.getAttr( 'bar' ) ).to.be.null;
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'setAttr', function() {
-		it( 'should insert an attribute', function() {
-			var element = new Element( 'elem' );
-			var attr = new Attribute( 'foo', 'bar' );
+	describe( 'setAttr', () => {
+		it( 'should insert an attribute', () => {
+			let element = new Element( 'elem' );
+			let attr = new Attribute( 'foo', 'bar' );
 
 
 			element.setAttr( attr );
 			element.setAttr( attr );
 
 
-			expect( element._getAttrCount() ).to.equal( 1 );
+			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 1 );
 			expect( element.getAttr( attr.key ) ).to.equal( attr.value );
 			expect( element.getAttr( attr.key ) ).to.equal( attr.value );
 		} );
 		} );
 
 
-		it( 'should overwrite attribute with the same key', function() {
-			var oldAttr = new Attribute( 'foo', 'bar' );
-			var newAttr = new Attribute( 'foo', 'bar' );
-			var element = new Element( 'elem', [ oldAttr ] );
+		it( 'should overwrite attribute with the same key', () => {
+			let oldAttr = new Attribute( 'foo', 'bar' );
+			let newAttr = new Attribute( 'foo', 'bar' );
+			let element = new Element( 'elem', [ oldAttr ] );
 
 
 			element.setAttr( newAttr );
 			element.setAttr( newAttr );
 
 
-			expect( element._getAttrCount() ).to.equal( 1 );
+			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 1 );
 			expect( element.getAttr( newAttr.key ) ).to.equal( newAttr.value );
 			expect( element.getAttr( newAttr.key ) ).to.equal( newAttr.value );
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'removeAttr', function() {
-		it( 'should remove an attribute', function() {
-			var attrA = new Attribute( 'a', 'A' );
-			var attrB = new Attribute( 'b', 'b' );
-			var attrC = new Attribute( 'c', 'C' );
-			var element = new Element( 'elem', [ attrA, attrB, attrC ] );
+	describe( 'removeAttr', () => {
+		it( 'should remove an attribute', () => {
+			let attrA = new Attribute( 'a', 'A' );
+			let attrB = new Attribute( 'b', 'b' );
+			let attrC = new Attribute( 'c', 'C' );
+			let element = new Element( 'elem', [ attrA, attrB, attrC ] );
 
 
 			element.removeAttr( attrB.key );
 			element.removeAttr( attrB.key );
 
 
-			expect( element._getAttrCount() ).to.equal( 2 );
+			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 2 );
 			expect( element.getAttr( attrA.key ) ).to.equal( attrA.value );
 			expect( element.getAttr( attrA.key ) ).to.equal( attrA.value );
 			expect( element.getAttr( attrC.key ) ).to.equal( attrC.value );
 			expect( element.getAttr( attrC.key ) ).to.equal( attrC.value );
 			expect( element.getAttr( attrB.key ) ).to.be.null;
 			expect( element.getAttr( attrB.key ) ).to.be.null;
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'hasAttr', function() {
-		it( 'should check attribute by key', function() {
-			var fooAttr = new Attribute( 'foo', true );
-			var element = new Element( 'foo', [ fooAttr ] );
+	describe( 'hasAttr', () => {
+		it( 'should check attribute by key', () => {
+			let fooAttr = new Attribute( 'foo', true );
+			let element = new Element( 'foo', [ fooAttr ] );
 
 
 			expect( element.hasAttr( 'foo' ) ).to.be.true;
 			expect( element.hasAttr( 'foo' ) ).to.be.true;
 		} );
 		} );
 
 
-		it( 'should return false if attribute was not found by key', function() {
-			var fooAttr = new Attribute( 'foo', true );
-			var element = new Element( 'foo', [ fooAttr ] );
+		it( 'should return false if attribute was not found by key', () => {
+			let fooAttr = new Attribute( 'foo', true );
+			let element = new Element( 'foo', [ fooAttr ] );
 
 
 			expect( element.hasAttr( 'bar' ) ).to.be.false;
 			expect( element.hasAttr( 'bar' ) ).to.be.false;
 		} );
 		} );
 
 
-		it( 'should check attribute by object', function() {
-			var fooAttr = new Attribute( 'foo', true );
-			var foo2Attr = new Attribute( 'foo', true );
-			var element = new Element( 'foo', [ fooAttr ] );
+		it( 'should check attribute by object', () => {
+			let fooAttr = new Attribute( 'foo', true );
+			let foo2Attr = new Attribute( 'foo', true );
+			let element = new Element( 'foo', [ fooAttr ] );
 
 
 			expect( element.hasAttr( foo2Attr ) ).to.be.true;
 			expect( element.hasAttr( foo2Attr ) ).to.be.true;
 		} );
 		} );
 
 
-		it( 'should return false if attribute was not found by object', function() {
-			var fooAttr = new Attribute( 'foo', true );
-			var element = new Element( 'foo' );
+		it( 'should return false if attribute was not found by object', () => {
+			let fooAttr = new Attribute( 'foo', true );
+			let element = new Element( 'foo' );
 
 
 			expect( element.hasAttr( fooAttr ) ).to.be.false;
 			expect( element.hasAttr( fooAttr ) ).to.be.false;
 		} );
 		} );
 
 
-		it( 'should create proper JSON string using toJSON method', function() {
-			var b = new Character( 'b' );
-			var foo = new Element( 'foo', [], [ b ] );
+		it( 'should create proper JSON string using toJSON method', () => {
+			let b = new Character( 'b' );
+			let foo = new Element( 'foo', [], [ b ] );
 
 
-			var parsedFoo = JSON.parse( JSON.stringify( foo ) );
-			var parsedBar = JSON.parse( JSON.stringify( b ) );
+			let parsedFoo = JSON.parse( JSON.stringify( foo ) );
+			let parsedBar = JSON.parse( JSON.stringify( b ) );
 
 
 			expect( parsedFoo.parent ).to.equal( null );
 			expect( parsedFoo.parent ).to.equal( null );
 			expect( parsedBar.parent ).to.equal( 'foo' );
 			expect( parsedBar.parent ).to.equal( 'foo' );
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'getIndex', function() {
-		it( 'should return null if the parent is null', function() {
+	describe( 'getAttrs', () => {
+		it( 'should allows to get attribute count', () => {
+			let element = new Element( 'foo', [
+				new Attribute( 1, true ),
+				new Attribute( 2, true ),
+				new Attribute( 3, true )
+			] );
+
+			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 3 );
+		} );
+
+		it( 'should allows to copy attributes', () => {
+			let element = new Element( 'foo', [ new Attribute( 'x', true ) ] );
+			let copy = new Element( 'bar', element.getAttrs() );
+
+			expect( copy.getAttr( 'x' ) ).to.be.true;
+		} );
+	} );
+
+	describe( 'getIndex', () => {
+		it( 'should return null if the parent is null', () => {
 			expect( root.getIndex() ).to.be.null;
 			expect( root.getIndex() ).to.be.null;
 		} );
 		} );
 
 
-		it( 'should return index in the parent', function() {
+		it( 'should return index in the parent', () => {
 			expect( one.getIndex() ).to.equal( 0 );
 			expect( one.getIndex() ).to.equal( 0 );
 			expect( two.getIndex() ).to.equal( 1 );
 			expect( two.getIndex() ).to.equal( 1 );
 			expect( three.getIndex() ).to.equal( 2 );
 			expect( three.getIndex() ).to.equal( 2 );
@@ -222,22 +245,22 @@ describe( 'Node', function() {
 			expect( charR.getIndex() ).to.equal( 3 );
 			expect( charR.getIndex() ).to.equal( 3 );
 		} );
 		} );
 
 
-		it( 'should throw an error if parent does not contains element', function() {
-			var f = new Character( 'f' );
-			var bar = new Element( 'bar', [], [] );
+		it( 'should throw an error if parent does not contains element', () => {
+			let f = new Character( 'f' );
+			let bar = new Element( 'bar', [], [] );
 
 
 			f.parent = bar;
 			f.parent = bar;
 
 
 			expect(
 			expect(
-				function() {
+				() => {
 					f.getIndex();
 					f.getIndex();
 				}
 				}
 			).to.throw( CKEditorError, /node-not-found-in-parent/ );
 			).to.throw( CKEditorError, /node-not-found-in-parent/ );
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'getPath', function() {
-		it( 'should return proper path', function() {
+	describe( 'getPath', () => {
+		it( 'should return proper path', () => {
 			expect( root.getPath() ).to.deep.equal( [] );
 			expect( root.getPath() ).to.deep.equal( [] );
 
 
 			expect( one.getPath() ).to.deep.equal( [ 0 ] );
 			expect( one.getPath() ).to.deep.equal( [ 0 ] );

+ 27 - 27
packages/ckeditor5-engine/tests/document/nodelist.js

@@ -7,26 +7,26 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/nodelist',
 	'document/nodelist',
 	'document/character',
 	'document/character',
 	'document/text',
 	'document/text',
 	'document/attribute'
 	'document/attribute'
 );
 );
 
 
-describe( 'NodeList', function() {
-	var NodeList, Character, Text, Attribute;
+describe( 'NodeList', () => {
+	let NodeList, Character, Text, Attribute;
 
 
-	before( function() {
+	before( () => {
 		NodeList = modules[ 'document/nodelist' ];
 		NodeList = modules[ 'document/nodelist' ];
 		Character = modules[ 'document/character' ];
 		Character = modules[ 'document/character' ];
 		Text = modules[ 'document/text' ];
 		Text = modules[ 'document/text' ];
 		Attribute = modules[ 'document/attribute' ];
 		Attribute = modules[ 'document/attribute' ];
 	} );
 	} );
 
 
-	describe( 'constructor', function() {
-		it( 'should change array of strings into a set of nodes', function() {
-			var nodeList = new NodeList( [ 'foo', new Character( 'x' ), 'bar' ] );
+	describe( 'constructor', () => {
+		it( 'should change array of strings into a set of nodes', () => {
+			let nodeList = new NodeList( [ 'foo', new Character( 'x' ), 'bar' ] );
 
 
 			expect( nodeList.length ).to.equal( 7 );
 			expect( nodeList.length ).to.equal( 7 );
 			expect( nodeList.get( 0 ).character ).to.equal( 'f' );
 			expect( nodeList.get( 0 ).character ).to.equal( 'f' );
@@ -38,8 +38,8 @@ describe( 'NodeList', function() {
 			expect( nodeList.get( 6 ).character ).to.equal( 'r' );
 			expect( nodeList.get( 6 ).character ).to.equal( 'r' );
 		} );
 		} );
 
 
-		it( 'should change string into a set of nodes', function() {
-			var nodeList = new NodeList( 'foo' );
+		it( 'should change string into a set of nodes', () => {
+			let nodeList = new NodeList( 'foo' );
 
 
 			expect( nodeList.length ).to.equal( 3 );
 			expect( nodeList.length ).to.equal( 3 );
 			expect( nodeList.get( 0 ).character ).to.equal( 'f' );
 			expect( nodeList.get( 0 ).character ).to.equal( 'f' );
@@ -47,16 +47,16 @@ describe( 'NodeList', function() {
 			expect( nodeList.get( 2 ).character ).to.equal( 'o' );
 			expect( nodeList.get( 2 ).character ).to.equal( 'o' );
 		} );
 		} );
 
 
-		it( 'should change node into a set of nodes', function() {
-			var nodeList = new NodeList( new Character( 'x' ) );
+		it( 'should change node into a set of nodes', () => {
+			let nodeList = new NodeList( new Character( 'x' ) );
 
 
 			expect( nodeList.length ).to.equal( 1 );
 			expect( nodeList.length ).to.equal( 1 );
 			expect( nodeList.get( 0 ).character ).to.equal( 'x' );
 			expect( nodeList.get( 0 ).character ).to.equal( 'x' );
 		} );
 		} );
 
 
-		it( 'should change text with attribute into a set of nodes', function() {
-			var attr = new Attribute( 'bold', true );
-			var nodeList = new NodeList( new Text( 'foo', [ attr ] ) );
+		it( 'should change text with attribute into a set of nodes', () => {
+			let attr = new Attribute( 'bold', true );
+			let nodeList = new NodeList( new Text( 'foo', [ attr ] ) );
 
 
 			expect( nodeList.length ).to.equal( 3 );
 			expect( nodeList.length ).to.equal( 3 );
 			expect( nodeList.get( 0 ).character ).to.equal( 'f' );
 			expect( nodeList.get( 0 ).character ).to.equal( 'f' );
@@ -68,10 +68,10 @@ describe( 'NodeList', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'insert', function() {
-		it( 'should insert one nodelist into another', function() {
-			var outerList = new NodeList( 'foo' );
-			var innerList = new NodeList( 'xxx' );
+	describe( 'insert', () => {
+		it( 'should insert one nodelist into another', () => {
+			let outerList = new NodeList( 'foo' );
+			let innerList = new NodeList( 'xxx' );
 
 
 			outerList.insert( 2, innerList );
 			outerList.insert( 2, innerList );
 
 
@@ -85,9 +85,9 @@ describe( 'NodeList', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'remove', function() {
-		it( 'should remove part of the nodelist', function() {
-			var nodeList = new NodeList( 'foobar' );
+	describe( 'remove', () => {
+		it( 'should remove part of the nodelist', () => {
+			let nodeList = new NodeList( 'foobar' );
 
 
 			nodeList.remove( 2, 3 );
 			nodeList.remove( 2, 3 );
 
 
@@ -98,13 +98,13 @@ describe( 'NodeList', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'iterator', function() {
-		it( 'should iterate over all elements in the collection', function() {
-			var characters = 'foo';
-			var nodeList = new NodeList( characters );
-			var i = 0;
+	describe( 'iterator', () => {
+		it( 'should iterate over all elements in the collection', () => {
+			let characters = 'foo';
+			let nodeList = new NodeList( characters );
+			let i = 0;
 
 
-			for ( var node of nodeList ) {
+			for ( let node of nodeList ) {
 				expect( node.character ).to.equal( characters[ i ] );
 				expect( node.character ).to.equal( characters[ i ] );
 				i++;
 				i++;
 			}
 			}

+ 74 - 70
packages/ckeditor5-engine/tests/document/operation/changeoperation.js

@@ -5,9 +5,13 @@
 
 
 /* bender-tags: document */
 /* bender-tags: document */
 
 
+/* bender-include: ../../_tools/tools.js */
+
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
 	'document/document',
 	'document/document',
 	'document/operation/changeoperation',
 	'document/operation/changeoperation',
 	'document/position',
 	'document/position',
@@ -19,10 +23,10 @@ var modules = bender.amd.require(
 	'ckeditorerror'
 	'ckeditorerror'
 );
 );
 
 
-describe( 'ChangeOperation', function() {
-	var Document, ChangeOperation, Position, Range, Character, Attribute, NodeList, Text, CKEditorError;
+describe( 'ChangeOperation', () => {
+	let Document, ChangeOperation, Position, Range, Character, Attribute, NodeList, Text, CKEditorError;
 
 
-	before( function() {
+	before( () => {
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
 		ChangeOperation = modules[ 'document/operation/changeoperation' ];
 		ChangeOperation = modules[ 'document/operation/changeoperation' ];
 		Position = modules[ 'document/position' ];
 		Position = modules[ 'document/position' ];
@@ -34,15 +38,15 @@ describe( 'ChangeOperation', function() {
 		CKEditorError = modules.ckeditorerror;
 		CKEditorError = modules.ckeditorerror;
 	} );
 	} );
 
 
-	var doc, root;
+	let doc, root;
 
 
-	beforeEach( function() {
+	beforeEach( () => {
 		doc = new Document();
 		doc = new Document();
 		root = doc.createRoot( 'root' );
 		root = doc.createRoot( 'root' );
 	} );
 	} );
 
 
-	it( 'should insert attribute to the set of nodes', function() {
-		var newAttr = new Attribute( 'isNew', true );
+	it( 'should insert attribute to the set of nodes', () => {
+		let newAttr = new Attribute( 'isNew', true );
 
 
 		root.insertChildren( 0, 'bar' );
 		root.insertChildren( 0, 'bar' );
 
 
@@ -59,13 +63,13 @@ describe( 'ChangeOperation', function() {
 		expect( root.getChildCount() ).to.equal( 3 );
 		expect( root.getChildCount() ).to.equal( 3 );
 		expect( root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
 		expect( root.getChild( 1 ).hasAttr( newAttr ) ).to.be.true;
 		expect( root.getChild( 1 ).hasAttr( newAttr ) ).to.be.true;
-		expect( root.getChild( 2 )._getAttrCount() ).to.equal( 0 );
+		expect( getIteratorCount( root.getChild( 2 ).getAttrs() ) ).to.equal( 0 );
 	} );
 	} );
 
 
-	it( 'should add attribute to the existing attributes', function() {
-		var newAttr = new Attribute( 'isNew', true );
-		var fooAttr = new Attribute( 'foo', true );
-		var barAttr = new Attribute( 'bar', true );
+	it( 'should add attribute to the existing attributes', () => {
+		let newAttr = new Attribute( 'isNew', true );
+		let fooAttr = new Attribute( 'foo', true );
+		let barAttr = new Attribute( 'bar', true );
 
 
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, barAttr ] ) );
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, barAttr ] ) );
 
 
@@ -80,15 +84,15 @@ describe( 'ChangeOperation', function() {
 
 
 		expect( doc.version ).to.equal( 1 );
 		expect( doc.version ).to.equal( 1 );
 		expect( root.getChildCount() ).to.equal( 1 );
 		expect( root.getChildCount() ).to.equal( 1 );
-		expect( root.getChild( 0 )._getAttrCount() ).to.equal( 3 );
+		expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 3 );
 		expect( root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'should change attribute to the set of nodes', function() {
-		var oldAttr = new Attribute( 'isNew', false );
-		var newAttr = new Attribute( 'isNew', true );
+	it( 'should change attribute to the set of nodes', () => {
+		let oldAttr = new Attribute( 'isNew', false );
+		let newAttr = new Attribute( 'isNew', true );
 
 
 		root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
 		root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
 
 
@@ -103,19 +107,19 @@ describe( 'ChangeOperation', function() {
 
 
 		expect( doc.version ).to.equal( 1 );
 		expect( doc.version ).to.equal( 1 );
 		expect( root.getChildCount() ).to.equal( 3 );
 		expect( root.getChildCount() ).to.equal( 3 );
-		expect( root.getChild( 0 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
-		expect( root.getChild( 1 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 1 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 1 ).hasAttr( newAttr ) ).to.be.true;
 		expect( root.getChild( 1 ).hasAttr( newAttr ) ).to.be.true;
-		expect( root.getChild( 2 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 2 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 2 ).hasAttr( oldAttr ) ).to.be.true;
 		expect( root.getChild( 2 ).hasAttr( oldAttr ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'should change attribute in the middle of existing attributes', function() {
-		var fooAttr = new Attribute( 'foo', true );
-		var x1Attr = new Attribute( 'x', 1 );
-		var x2Attr = new Attribute( 'x', 2 );
-		var barAttr = new Attribute( 'bar', true );
+	it( 'should change attribute in the middle of existing attributes', () => {
+		let fooAttr = new Attribute( 'foo', true );
+		let x1Attr = new Attribute( 'x', 1 );
+		let x2Attr = new Attribute( 'x', 2 );
+		let barAttr = new Attribute( 'bar', true );
 
 
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, x1Attr, barAttr ] ) );
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, x1Attr, barAttr ] ) );
 
 
@@ -130,16 +134,16 @@ describe( 'ChangeOperation', function() {
 
 
 		expect( doc.version ).to.equal( 1 );
 		expect( doc.version ).to.equal( 1 );
 		expect( root.getChildCount() ).to.equal( 1 );
 		expect( root.getChildCount() ).to.equal( 1 );
-		expect( root.getChild( 0 )._getAttrCount() ).to.equal( 3 );
+		expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 3 );
 		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( x2Attr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( x2Attr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'should remove attribute', function() {
-		var fooAttr = new Attribute( 'foo', true );
-		var xAttr = new Attribute( 'x', true );
-		var barAttr = new Attribute( 'bar', true );
+	it( 'should remove attribute', () => {
+		let fooAttr = new Attribute( 'foo', true );
+		let xAttr = new Attribute( 'x', true );
+		let barAttr = new Attribute( 'bar', true );
 
 
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, xAttr, barAttr ] ) );
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, xAttr, barAttr ] ) );
 
 
@@ -154,17 +158,17 @@ describe( 'ChangeOperation', function() {
 
 
 		expect( doc.version ).to.equal( 1 );
 		expect( doc.version ).to.equal( 1 );
 		expect( root.getChildCount() ).to.equal( 1 );
 		expect( root.getChildCount() ).to.equal( 1 );
-		expect( root.getChild( 0 )._getAttrCount() ).to.equal( 2 );
+		expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 2 );
 		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'should create a change operation as a reverse', function() {
-		var oldAttr = new Attribute( 'x', 'old' );
-		var newAttr = new Attribute( 'x', 'new' );
-		var range = new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) );
-		var operation = new ChangeOperation( range, oldAttr, newAttr, doc.version );
-		var reverse = operation.getReversed();
+	it( 'should create a change operation as a reverse', () => {
+		let oldAttr = new Attribute( 'x', 'old' );
+		let newAttr = new Attribute( 'x', 'new' );
+		let range = new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) );
+		let operation = new ChangeOperation( range, oldAttr, newAttr, doc.version );
+		let reverse = operation.getReversed();
 
 
 		expect( reverse ).to.be.an.instanceof( ChangeOperation );
 		expect( reverse ).to.be.an.instanceof( ChangeOperation );
 		expect( reverse.baseVersion ).to.equal( 1 );
 		expect( reverse.baseVersion ).to.equal( 1 );
@@ -173,44 +177,44 @@ describe( 'ChangeOperation', function() {
 		expect( reverse.newAttr ).to.equal( oldAttr );
 		expect( reverse.newAttr ).to.equal( oldAttr );
 	} );
 	} );
 
 
-	it( 'should undo adding attribute by applying reverse operation', function() {
-		var newAttr = new Attribute( 'isNew', true );
+	it( 'should undo adding attribute by applying reverse operation', () => {
+		let newAttr = new Attribute( 'isNew', true );
 
 
 		root.insertChildren( 0, 'bar' );
 		root.insertChildren( 0, 'bar' );
 
 
-		var operation = new ChangeOperation(
+		let operation = new ChangeOperation(
 			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
 			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
 			null,
 			null,
 			newAttr,
 			newAttr,
 			doc.version
 			doc.version
 		);
 		);
 
 
-		var reverse = operation.getReversed();
+		let reverse = operation.getReversed();
 
 
 		doc.applyOperation( operation );
 		doc.applyOperation( operation );
 		doc.applyOperation( reverse );
 		doc.applyOperation( reverse );
 
 
 		expect( doc.version ).to.equal( 2 );
 		expect( doc.version ).to.equal( 2 );
 		expect( root.getChildCount() ).to.equal( 3 );
 		expect( root.getChildCount() ).to.equal( 3 );
-		expect( root.getChild( 0 )._getAttrCount() ).to.equal( 0 );
-		expect( root.getChild( 1 )._getAttrCount() ).to.equal( 0 );
-		expect( root.getChild( 2 )._getAttrCount() ).to.equal( 0 );
+		expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 0 );
+		expect( getIteratorCount( root.getChild( 1 ).getAttrs() ) ).to.equal( 0 );
+		expect( getIteratorCount( root.getChild( 2 ).getAttrs() ) ).to.equal( 0 );
 	} );
 	} );
 
 
-	it( 'should undo changing attribute by applying reverse operation', function() {
-		var oldAttr = new Attribute( 'isNew', false );
-		var newAttr = new Attribute( 'isNew', true );
+	it( 'should undo changing attribute by applying reverse operation', () => {
+		let oldAttr = new Attribute( 'isNew', false );
+		let newAttr = new Attribute( 'isNew', true );
 
 
 		root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
 		root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
 
 
-		var operation = new ChangeOperation(
+		let operation = new ChangeOperation(
 			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
 			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
 			oldAttr,
 			oldAttr,
 			newAttr,
 			newAttr,
 			doc.version
 			doc.version
 		);
 		);
 
 
-		var reverse = operation.getReversed();
+		let reverse = operation.getReversed();
 
 
 		doc.applyOperation( operation );
 		doc.applyOperation( operation );
 
 
@@ -218,27 +222,27 @@ describe( 'ChangeOperation', function() {
 
 
 		expect( doc.version ).to.equal( 2 );
 		expect( doc.version ).to.equal( 2 );
 		expect( root.getChildCount() ).to.equal( 3 );
 		expect( root.getChildCount() ).to.equal( 3 );
-		expect( root.getChild( 0 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 0 ).hasAttr( oldAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( oldAttr ) ).to.be.true;
-		expect( root.getChild( 1 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 1 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 1 ).hasAttr( oldAttr ) ).to.be.true;
 		expect( root.getChild( 1 ).hasAttr( oldAttr ) ).to.be.true;
-		expect( root.getChild( 2 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 2 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 2 ).hasAttr( oldAttr ) ).to.be.true;
 		expect( root.getChild( 2 ).hasAttr( oldAttr ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'should undo remove attribute by applying reverse operation', function() {
-		var fooAttr = new Attribute( 'foo', false );
+	it( 'should undo remove attribute by applying reverse operation', () => {
+		let fooAttr = new Attribute( 'foo', false );
 
 
 		root.insertChildren( 0, new Text( 'bar', [ fooAttr ] ) );
 		root.insertChildren( 0, new Text( 'bar', [ fooAttr ] ) );
 
 
-		var operation = new ChangeOperation(
+		let operation = new ChangeOperation(
 			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
 			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
 			fooAttr,
 			fooAttr,
 			null,
 			null,
 			doc.version
 			doc.version
 		);
 		);
 
 
-		var reverse = operation.getReversed();
+		let reverse = operation.getReversed();
 
 
 		doc.applyOperation( operation );
 		doc.applyOperation( operation );
 
 
@@ -246,21 +250,21 @@ describe( 'ChangeOperation', function() {
 
 
 		expect( doc.version ).to.equal( 2 );
 		expect( doc.version ).to.equal( 2 );
 		expect( root.getChildCount() ).to.equal( 3 );
 		expect( root.getChildCount() ).to.equal( 3 );
-		expect( root.getChild( 0 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 0 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
-		expect( root.getChild( 1 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 1 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 1 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 1 ).hasAttr( fooAttr ) ).to.be.true;
-		expect( root.getChild( 2 )._getAttrCount() ).to.equal( 1 );
+		expect( getIteratorCount( root.getChild( 2 ).getAttrs() ) ).to.equal( 1 );
 		expect( root.getChild( 2 ).hasAttr( fooAttr ) ).to.be.true;
 		expect( root.getChild( 2 ).hasAttr( fooAttr ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'should throw an error when one try to remove and the attribute does not exists', function() {
-		var fooAttr = new Attribute( 'foo', true );
+	it( 'should throw an error when one try to remove and the attribute does not exists', () => {
+		let fooAttr = new Attribute( 'foo', true );
 
 
 		root.insertChildren( 0, 'x' );
 		root.insertChildren( 0, 'x' );
 
 
 		expect(
 		expect(
-			function() {
+			() => {
 				doc.applyOperation(
 				doc.applyOperation(
 					new ChangeOperation(
 					new ChangeOperation(
 						new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
 						new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
@@ -273,14 +277,14 @@ describe( 'ChangeOperation', function() {
 		).to.throw( CKEditorError, /operation-change-no-attr-to-remove/ );
 		).to.throw( CKEditorError, /operation-change-no-attr-to-remove/ );
 	} );
 	} );
 
 
-	it( 'should throw an error when one try to insert and the attribute already exists', function() {
-		var x1Attr = new Attribute( 'x', 1 );
-		var x2Attr = new Attribute( 'x', 2 );
+	it( 'should throw an error when one try to insert and the attribute already exists', () => {
+		let x1Attr = new Attribute( 'x', 1 );
+		let x2Attr = new Attribute( 'x', 2 );
 
 
 		root.insertChildren( 0, new Character( 'x', [ x1Attr ] ) );
 		root.insertChildren( 0, new Character( 'x', [ x1Attr ] ) );
 
 
 		expect(
 		expect(
-			function() {
+			() => {
 				doc.applyOperation(
 				doc.applyOperation(
 					new ChangeOperation(
 					new ChangeOperation(
 						new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
 						new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
@@ -293,14 +297,14 @@ describe( 'ChangeOperation', function() {
 		).to.throw( CKEditorError, /operation-change-attr-exists/ );
 		).to.throw( CKEditorError, /operation-change-attr-exists/ );
 	} );
 	} );
 
 
-	it( 'should throw an error when one try to change and the new and old attributes have different keys', function() {
-		var fooAttr = new Attribute( 'foo', true );
-		var barAttr = new Attribute( 'bar', true );
+	it( 'should throw an error when one try to change and the new and old attributes have different keys', () => {
+		let fooAttr = new Attribute( 'foo', true );
+		let barAttr = new Attribute( 'bar', true );
 
 
 		root.insertChildren( 0, 'x' );
 		root.insertChildren( 0, 'x' );
 
 
 		expect(
 		expect(
-			function() {
+			() => {
 				doc.applyOperation(
 				doc.applyOperation(
 					new ChangeOperation(
 					new ChangeOperation(
 						new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
 						new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),

+ 20 - 20
packages/ckeditor5-engine/tests/document/operation/insertoperation.js

@@ -7,7 +7,7 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/document',
 	'document/document',
 	'document/operation/insertoperation',
 	'document/operation/insertoperation',
 	'document/operation/removeoperation',
 	'document/operation/removeoperation',
@@ -16,10 +16,10 @@ var modules = bender.amd.require(
 	'document/nodelist'
 	'document/nodelist'
 );
 );
 
 
-describe( 'InsertOperation', function() {
-	var Document, InsertOperation, RemoveOperation, Position, Character;
+describe( 'InsertOperation', () => {
+	let Document, InsertOperation, RemoveOperation, Position, Character;
 
 
-	before( function() {
+	before( () => {
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
 		InsertOperation = modules[ 'document/operation/insertoperation' ];
 		InsertOperation = modules[ 'document/operation/insertoperation' ];
 		RemoveOperation = modules[ 'document/operation/removeoperation' ];
 		RemoveOperation = modules[ 'document/operation/removeoperation' ];
@@ -27,14 +27,14 @@ describe( 'InsertOperation', function() {
 		Character = modules[ 'document/character' ];
 		Character = modules[ 'document/character' ];
 	} );
 	} );
 
 
-	var doc, root;
+	let doc, root;
 
 
-	beforeEach( function() {
+	beforeEach( () => {
 		doc = new Document();
 		doc = new Document();
 		root = doc.createRoot( 'root' );
 		root = doc.createRoot( 'root' );
 	} );
 	} );
 
 
-	it( 'should insert node', function() {
+	it( 'should insert node', () => {
 		doc.applyOperation(
 		doc.applyOperation(
 			new InsertOperation(
 			new InsertOperation(
 				new Position( [ 0 ], root ),
 				new Position( [ 0 ], root ),
@@ -48,7 +48,7 @@ describe( 'InsertOperation', function() {
 		expect( root.getChild( 0 ).character ).to.equal( 'x' );
 		expect( root.getChild( 0 ).character ).to.equal( 'x' );
 	} );
 	} );
 
 
-	it( 'should insert set of nodes', function() {
+	it( 'should insert set of nodes', () => {
 		doc.applyOperation(
 		doc.applyOperation(
 			new InsertOperation(
 			new InsertOperation(
 				new Position( [ 0 ], root ),
 				new Position( [ 0 ], root ),
@@ -64,7 +64,7 @@ describe( 'InsertOperation', function() {
 		expect( root.getChild( 2 ).character ).to.equal( 'r' );
 		expect( root.getChild( 2 ).character ).to.equal( 'r' );
 	} );
 	} );
 
 
-	it( 'should insert between existing nodes', function() {
+	it( 'should insert between existing nodes', () => {
 		root.insertChildren( 0, 'xy' );
 		root.insertChildren( 0, 'xy' );
 
 
 		doc.applyOperation(
 		doc.applyOperation(
@@ -84,7 +84,7 @@ describe( 'InsertOperation', function() {
 		expect( root.getChild( 4 ).character ).to.equal( 'y' );
 		expect( root.getChild( 4 ).character ).to.equal( 'y' );
 	} );
 	} );
 
 
-	it( 'should insert text', function() {
+	it( 'should insert text', () => {
 		doc.applyOperation(
 		doc.applyOperation(
 			new InsertOperation(
 			new InsertOperation(
 				new Position( [ 0 ], root ),
 				new Position( [ 0 ], root ),
@@ -104,15 +104,15 @@ describe( 'InsertOperation', function() {
 		expect( root.getChild( 6 ).character ).to.equal( 'r' );
 		expect( root.getChild( 6 ).character ).to.equal( 'r' );
 	} );
 	} );
 
 
-	it( 'should create a remove operation as a reverse', function() {
-		var position = new Position( [ 0 ], root );
-		var operation = new InsertOperation(
+	it( 'should create a remove operation as a reverse', () => {
+		let position = new Position( [ 0 ], root );
+		let operation = new InsertOperation(
 			position,
 			position,
 			[ 'foo', new Character( 'x' ), 'bar' ],
 			[ 'foo', new Character( 'x' ), 'bar' ],
 			0
 			0
 		);
 		);
 
 
-		var reverse = operation.getReversed();
+		let reverse = operation.getReversed();
 
 
 		expect( reverse ).to.be.an.instanceof( RemoveOperation );
 		expect( reverse ).to.be.an.instanceof( RemoveOperation );
 		expect( reverse.baseVersion ).to.equal( 1 );
 		expect( reverse.baseVersion ).to.equal( 1 );
@@ -120,14 +120,14 @@ describe( 'InsertOperation', function() {
 		expect( reverse.howMany ).to.equal( 7 );
 		expect( reverse.howMany ).to.equal( 7 );
 	} );
 	} );
 
 
-	it( 'should undo insert node by applying reverse operation', function() {
-		var operation = new InsertOperation(
+	it( 'should undo insert node by applying reverse operation', () => {
+		let operation = new InsertOperation(
 			new Position( [ 0 ], root ),
 			new Position( [ 0 ], root ),
 			new Character( 'x' ),
 			new Character( 'x' ),
 			doc.version
 			doc.version
 		);
 		);
 
 
-		var reverse = operation.getReversed();
+		let reverse = operation.getReversed();
 
 
 		doc.applyOperation( operation );
 		doc.applyOperation( operation );
 
 
@@ -139,14 +139,14 @@ describe( 'InsertOperation', function() {
 		expect( root.getChildCount() ).to.equal( 0 );
 		expect( root.getChildCount() ).to.equal( 0 );
 	} );
 	} );
 
 
-	it( 'should undo insert set of nodes by applying reverse operation', function() {
-		var operation = new InsertOperation(
+	it( 'should undo insert set of nodes by applying reverse operation', () => {
+		let operation = new InsertOperation(
 			new Position( [ 0 ], root ),
 			new Position( [ 0 ], root ),
 			'bar',
 			'bar',
 			doc.version
 			doc.version
 		);
 		);
 
 
-		var reverse = operation.getReversed();
+		let reverse = operation.getReversed();
 
 
 		doc.applyOperation( operation );
 		doc.applyOperation( operation );
 
 

+ 38 - 38
packages/ckeditor5-engine/tests/document/operation/moveoperation.js

@@ -7,7 +7,7 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/document',
 	'document/document',
 	'document/operation/moveoperation',
 	'document/operation/moveoperation',
 	'document/position',
 	'document/position',
@@ -16,10 +16,10 @@ var modules = bender.amd.require(
 	'ckeditorerror'
 	'ckeditorerror'
 );
 );
 
 
-describe( 'MoveOperation', function() {
-	var Document, MoveOperation, Position, Element, NodeList, CKEditorError;
+describe( 'MoveOperation', () => {
+	let Document, MoveOperation, Position, Element, NodeList, CKEditorError;
 
 
-	before( function() {
+	before( () => {
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
 		MoveOperation = modules[ 'document/operation/moveoperation' ];
 		MoveOperation = modules[ 'document/operation/moveoperation' ];
 		Position = modules[ 'document/position' ];
 		Position = modules[ 'document/position' ];
@@ -28,16 +28,16 @@ describe( 'MoveOperation', function() {
 		CKEditorError = modules.ckeditorerror;
 		CKEditorError = modules.ckeditorerror;
 	} );
 	} );
 
 
-	var doc, root;
+	let doc, root;
 
 
-	beforeEach( function() {
+	beforeEach( () => {
 		doc = new Document();
 		doc = new Document();
 		root = doc.createRoot( 'root' );
 		root = doc.createRoot( 'root' );
 	} );
 	} );
 
 
-	it( 'should move from one node to another', function() {
-		var p1 = new Element( 'p1', [], new Element( 'x' ) );
-		var p2 = new Element( 'p2' );
+	it( 'should move from one node to another', () => {
+		let p1 = new Element( 'p1', [], new Element( 'x' ) );
+		let p2 = new Element( 'p2' );
 
 
 		root.insertChildren( 0, [ p1, p2 ] );
 		root.insertChildren( 0, [ p1, p2 ] );
 
 
@@ -59,7 +59,7 @@ describe( 'MoveOperation', function() {
 		expect( p2.getChild( 0 ).name ).to.equal( 'x' );
 		expect( p2.getChild( 0 ).name ).to.equal( 'x' );
 	} );
 	} );
 
 
-	it( 'should move position of children in one node backward', function() {
+	it( 'should move position of children in one node backward', () => {
 		root.insertChildren( 0, 'xbarx' );
 		root.insertChildren( 0, 'xbarx' );
 
 
 		doc.applyOperation(
 		doc.applyOperation(
@@ -80,7 +80,7 @@ describe( 'MoveOperation', function() {
 		expect( root.getChild( 4 ).character ).to.equal( 'x' );
 		expect( root.getChild( 4 ).character ).to.equal( 'x' );
 	} );
 	} );
 
 
-	it( 'should move position of children in one node forward', function() {
+	it( 'should move position of children in one node forward', () => {
 		root.insertChildren( 0, 'xbarx' );
 		root.insertChildren( 0, 'xbarx' );
 
 
 		doc.applyOperation(
 		doc.applyOperation(
@@ -101,15 +101,15 @@ describe( 'MoveOperation', function() {
 		expect( root.getChild( 4 ).character ).to.equal( 'x' );
 		expect( root.getChild( 4 ).character ).to.equal( 'x' );
 	} );
 	} );
 
 
-	it( 'should create a move operation as a reverse', function() {
-		var nodeList = new NodeList( 'bar' );
+	it( 'should create a move operation as a reverse', () => {
+		let nodeList = new NodeList( 'bar' );
 
 
-		var sourcePosition = new Position( [ 0 ], root );
-		var targetPosition = new Position( [ 4 ], root );
+		let sourcePosition = new Position( [ 0 ], root );
+		let targetPosition = new Position( [ 4 ], root );
 
 
-		var operation = new MoveOperation( sourcePosition, targetPosition, nodeList.length, doc.version );
+		let operation = new MoveOperation( sourcePosition, targetPosition, nodeList.length, doc.version );
 
 
-		var reverse = operation.getReversed();
+		let reverse = operation.getReversed();
 
 
 		expect( reverse ).to.be.an.instanceof( MoveOperation );
 		expect( reverse ).to.be.an.instanceof( MoveOperation );
 		expect( reverse.baseVersion ).to.equal( 1 );
 		expect( reverse.baseVersion ).to.equal( 1 );
@@ -118,13 +118,13 @@ describe( 'MoveOperation', function() {
 		expect( reverse.targetPosition ).to.equal( sourcePosition );
 		expect( reverse.targetPosition ).to.equal( sourcePosition );
 	} );
 	} );
 
 
-	it( 'should undo move node by applying reverse operation', function() {
-		var p1 = new Element( 'p1', [], new Element( 'x' ) );
-		var p2 = new Element( 'p2' );
+	it( 'should undo move node by applying reverse operation', () => {
+		let p1 = new Element( 'p1', [], new Element( 'x' ) );
+		let p2 = new Element( 'p2' );
 
 
 		root.insertChildren( 0, [ p1, p2 ] );
 		root.insertChildren( 0, [ p1, p2 ] );
 
 
-		var operation = new MoveOperation(
+		let operation = new MoveOperation(
 			new Position( [ 0, 0 ], root ),
 			new Position( [ 0, 0 ], root ),
 			new Position( [ 1, 0 ], root ),
 			new Position( [ 1, 0 ], root ),
 			1,
 			1,
@@ -148,11 +148,11 @@ describe( 'MoveOperation', function() {
 		expect( p2.getChildCount() ).to.equal( 0 );
 		expect( p2.getChildCount() ).to.equal( 0 );
 	} );
 	} );
 
 
-	it( 'should throw an error if number of nodes to move exceeds the number of existing nodes in given element', function() {
+	it( 'should throw an error if number of nodes to move exceeds the number of existing nodes in given element', () => {
 		root.insertChildren( 0, 'xbarx' );
 		root.insertChildren( 0, 'xbarx' );
 
 
 		expect(
 		expect(
-			function() {
+			() => {
 				doc.applyOperation(
 				doc.applyOperation(
 					new MoveOperation(
 					new MoveOperation(
 						new Position( [ 3 ], root ),
 						new Position( [ 3 ], root ),
@@ -165,12 +165,12 @@ describe( 'MoveOperation', function() {
 		).to.throw( CKEditorError, /operation-move-nodes-do-not-exist/ );
 		).to.throw( CKEditorError, /operation-move-nodes-do-not-exist/ );
 	} );
 	} );
 
 
-	it( 'should throw an error if target or source parent-element specified by position does not exist', function() {
-		var p = new Element( 'p' );
+	it( 'should throw an error if target or source parent-element specified by position does not exist', () => {
+		let p = new Element( 'p' );
 		p.insertChildren( 0, 'foo' );
 		p.insertChildren( 0, 'foo' );
 		root.insertChildren( 0, [ 'ab', p ] );
 		root.insertChildren( 0, [ 'ab', p ] );
 
 
-		var operation = new MoveOperation(
+		let operation = new MoveOperation(
 			new Position( [ 2, 0 ], root ),
 			new Position( [ 2, 0 ], root ),
 			new Position( [ 1 ], root ),
 			new Position( [ 1 ], root ),
 			3,
 			3,
@@ -180,16 +180,16 @@ describe( 'MoveOperation', function() {
 		root.removeChildren( 2, 1 );
 		root.removeChildren( 2, 1 );
 
 
 		expect(
 		expect(
-			function() {
+			() => {
 				doc.applyOperation( operation );
 				doc.applyOperation( operation );
 			}
 			}
 		).to.throw( CKEditorError, /operation-move-position-invalid/ );
 		).to.throw( CKEditorError, /operation-move-position-invalid/ );
 	} );
 	} );
 
 
-	it( 'should throw an error if operation tries to move a range between the beginning and the end of that range', function() {
+	it( 'should throw an error if operation tries to move a range between the beginning and the end of that range', () => {
 		root.insertChildren( 0, 'xbarx' );
 		root.insertChildren( 0, 'xbarx' );
 
 
-		var operation = new MoveOperation(
+		let operation = new MoveOperation(
 			new Position( [ 1 ], root ),
 			new Position( [ 1 ], root ),
 			new Position( [ 2 ], root ),
 			new Position( [ 2 ], root ),
 			3,
 			3,
@@ -197,17 +197,17 @@ describe( 'MoveOperation', function() {
 		);
 		);
 
 
 		expect(
 		expect(
-			function() {
+			() => {
 				doc.applyOperation( operation );
 				doc.applyOperation( operation );
 			}
 			}
 		).to.throw( CKEditorError, /operation-move-range-into-itself/ );
 		).to.throw( CKEditorError, /operation-move-range-into-itself/ );
 	} );
 	} );
 
 
-	it( 'should throw an error if operation tries to move a range into a sub-tree of a node that is in that range', function() {
-		var p = new Element( 'p', [], [ new Element( 'p' ) ] );
+	it( 'should throw an error if operation tries to move a range into a sub-tree of a node that is in that range', () => {
+		let p = new Element( 'p', [], [ new Element( 'p' ) ] );
 		root.insertChildren( 0, [ 'ab', p, 'xy' ] );
 		root.insertChildren( 0, [ 'ab', p, 'xy' ] );
 
 
-		var operation = new MoveOperation(
+		let operation = new MoveOperation(
 			new Position( [ 1 ], root ),
 			new Position( [ 1 ], root ),
 			new Position( [ 2, 0, 0 ], root ),
 			new Position( [ 2, 0, 0 ], root ),
 			3,
 			3,
@@ -215,17 +215,17 @@ describe( 'MoveOperation', function() {
 		);
 		);
 
 
 		expect(
 		expect(
-			function() {
+			() => {
 				doc.applyOperation( operation );
 				doc.applyOperation( operation );
 			}
 			}
 		).to.throw( CKEditorError, /operation-move-node-into-itself/ );
 		).to.throw( CKEditorError, /operation-move-node-into-itself/ );
 	} );
 	} );
 
 
-	it( 'should not throw an error if operation move a range into a sibling', function() {
-		var p = new Element( 'p' );
+	it( 'should not throw an error if operation move a range into a sibling', () => {
+		let p = new Element( 'p' );
 		root.insertChildren( 0, [ 'ab', p, 'xy' ] );
 		root.insertChildren( 0, [ 'ab', p, 'xy' ] );
 
 
-		var operation = new MoveOperation(
+		let operation = new MoveOperation(
 			new Position( [ 1 ], root ),
 			new Position( [ 1 ], root ),
 			new Position( [ 2, 0 ], root ),
 			new Position( [ 2, 0 ], root ),
 			1,
 			1,
@@ -233,7 +233,7 @@ describe( 'MoveOperation', function() {
 		);
 		);
 
 
 		expect(
 		expect(
-			function() {
+			() => {
 				doc.applyOperation( operation );
 				doc.applyOperation( operation );
 			}
 			}
 		).not.to.throw();
 		).not.to.throw();

+ 11 - 11
packages/ckeditor5-engine/tests/document/operation/reinsertoperation.js

@@ -7,7 +7,7 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/document',
 	'document/document',
 	'document/operation/reinsertoperation',
 	'document/operation/reinsertoperation',
 	'document/operation/removeoperation',
 	'document/operation/removeoperation',
@@ -15,10 +15,10 @@ var modules = bender.amd.require(
 	'document/position'
 	'document/position'
 );
 );
 
 
-describe( 'ReinsertOperation', function() {
-	var Document, ReinsertOperation, RemoveOperation, MoveOperation, Position;
+describe( 'ReinsertOperation', () => {
+	let Document, ReinsertOperation, RemoveOperation, MoveOperation, Position;
 
 
-	before( function() {
+	before( () => {
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
 		ReinsertOperation = modules[ 'document/operation/reinsertoperation' ];
 		ReinsertOperation = modules[ 'document/operation/reinsertoperation' ];
 		RemoveOperation = modules[ 'document/operation/removeoperation' ];
 		RemoveOperation = modules[ 'document/operation/removeoperation' ];
@@ -26,9 +26,9 @@ describe( 'ReinsertOperation', function() {
 		Position = modules[ 'document/position' ];
 		Position = modules[ 'document/position' ];
 	} );
 	} );
 
 
-	var doc, root, graveyard, operation, graveyardPosition, rootPosition;
+	let doc, root, graveyard, operation, graveyardPosition, rootPosition;
 
 
-	beforeEach( function() {
+	beforeEach( () => {
 		doc = new Document();
 		doc = new Document();
 		root = doc.createRoot( 'root' );
 		root = doc.createRoot( 'root' );
 		graveyard = doc._graveyard;
 		graveyard = doc._graveyard;
@@ -44,12 +44,12 @@ describe( 'ReinsertOperation', function() {
 		);
 		);
 	} );
 	} );
 
 
-	it( 'should extend MoveOperation class', function() {
+	it( 'should extend MoveOperation class', () => {
 		expect( operation ).to.be.instanceof( MoveOperation );
 		expect( operation ).to.be.instanceof( MoveOperation );
 	} );
 	} );
 
 
-	it( 'should create a remove operation as a reverse', function() {
-		var reverse = operation.getReversed();
+	it( 'should create a remove operation as a reverse', () => {
+		let reverse = operation.getReversed();
 
 
 		expect( reverse ).to.be.an.instanceof( RemoveOperation );
 		expect( reverse ).to.be.an.instanceof( RemoveOperation );
 		expect( reverse.baseVersion ).to.equal( 1 );
 		expect( reverse.baseVersion ).to.equal( 1 );
@@ -58,8 +58,8 @@ describe( 'ReinsertOperation', function() {
 		expect( reverse.targetPosition.isEqual( graveyardPosition ) ).to.be.true;
 		expect( reverse.targetPosition.isEqual( graveyardPosition ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'should undo reinsert set of nodes by applying reverse operation', function() {
-		var reverse = operation.getReversed();
+	it( 'should undo reinsert set of nodes by applying reverse operation', () => {
+		let reverse = operation.getReversed();
 
 
 		graveyard.insertChildren( 0, 'bar' );
 		graveyard.insertChildren( 0, 'bar' );
 
 

+ 20 - 20
packages/ckeditor5-engine/tests/document/operation/removeoperation.js

@@ -7,7 +7,7 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/document',
 	'document/document',
 	'document/operation/reinsertoperation',
 	'document/operation/reinsertoperation',
 	'document/operation/removeoperation',
 	'document/operation/removeoperation',
@@ -15,10 +15,10 @@ var modules = bender.amd.require(
 	'document/position'
 	'document/position'
 );
 );
 
 
-describe( 'RemoveOperation', function() {
-	var Document, ReinsertOperation, RemoveOperation, MoveOperation, Position;
+describe( 'RemoveOperation', () => {
+	let Document, ReinsertOperation, RemoveOperation, MoveOperation, Position;
 
 
-	before( function() {
+	before( () => {
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
 		ReinsertOperation = modules[ 'document/operation/reinsertoperation' ];
 		ReinsertOperation = modules[ 'document/operation/reinsertoperation' ];
 		RemoveOperation = modules[ 'document/operation/removeoperation' ];
 		RemoveOperation = modules[ 'document/operation/removeoperation' ];
@@ -26,16 +26,16 @@ describe( 'RemoveOperation', function() {
 		Position = modules[ 'document/position' ];
 		Position = modules[ 'document/position' ];
 	} );
 	} );
 
 
-	var doc, root, graveyard;
+	let doc, root, graveyard;
 
 
-	beforeEach( function() {
+	beforeEach( () => {
 		doc = new Document();
 		doc = new Document();
 		root = doc.createRoot( 'root' );
 		root = doc.createRoot( 'root' );
 		graveyard = doc._graveyard;
 		graveyard = doc._graveyard;
 	} );
 	} );
 
 
-	it( 'should extend MoveOperation class', function() {
-		var operation = new RemoveOperation(
+	it( 'should extend MoveOperation class', () => {
+		let operation = new RemoveOperation(
 			new Position( [ 2 ], root ),
 			new Position( [ 2 ], root ),
 			2,
 			2,
 			doc.version
 			doc.version
@@ -44,12 +44,12 @@ describe( 'RemoveOperation', function() {
 		expect( operation ).to.be.instanceof( MoveOperation );
 		expect( operation ).to.be.instanceof( MoveOperation );
 	} );
 	} );
 
 
-	it( 'should remove set of nodes and append them to graveyard root', function() {
+	it( 'should remove set of nodes and append them to graveyard root', () => {
 		root.insertChildren( 0, 'fozbar' );
 		root.insertChildren( 0, 'fozbar' );
 
 
-		var z = root.getChild( 2 );
-		var b = root.getChild( 3 );
-		var a = root.getChild( 4 );
+		let z = root.getChild( 2 );
+		let b = root.getChild( 3 );
+		let a = root.getChild( 4 );
 
 
 		doc.applyOperation(
 		doc.applyOperation(
 			new RemoveOperation(
 			new RemoveOperation(
@@ -68,10 +68,10 @@ describe( 'RemoveOperation', function() {
 		expect( graveyard.getChild( 1 ) ).to.equal( b );
 		expect( graveyard.getChild( 1 ) ).to.equal( b );
 	} );
 	} );
 
 
-	it( 'should create a reinsert operation as a reverse', function() {
-		var position = new Position( [ 0 ], root );
-		var operation = new RemoveOperation( position, 2, 0 );
-		var reverse = operation.getReversed();
+	it( 'should create a reinsert operation as a reverse', () => {
+		let position = new Position( [ 0 ], root );
+		let operation = new RemoveOperation( position, 2, 0 );
+		let reverse = operation.getReversed();
 
 
 		expect( reverse ).to.be.an.instanceof( ReinsertOperation );
 		expect( reverse ).to.be.an.instanceof( ReinsertOperation );
 		expect( reverse.baseVersion ).to.equal( 1 );
 		expect( reverse.baseVersion ).to.equal( 1 );
@@ -80,10 +80,10 @@ describe( 'RemoveOperation', function() {
 		expect( reverse.targetPosition ).to.equal( position );
 		expect( reverse.targetPosition ).to.equal( position );
 	} );
 	} );
 
 
-	it( 'should undo remove set of nodes by applying reverse operation', function() {
-		var position = new Position( [ 0 ], root );
-		var operation = new RemoveOperation( position, 3, 0 );
-		var reverse = operation.getReversed();
+	it( 'should undo remove set of nodes by applying reverse operation', () => {
+		let position = new Position( [ 0 ], root );
+		let operation = new RemoveOperation( position, 3, 0 );
+		let reverse = operation.getReversed();
 
 
 		root.insertChildren( 0, 'bar' );
 		root.insertChildren( 0, 'bar' );
 
 

+ 26 - 26
packages/ckeditor5-engine/tests/document/position.js

@@ -7,7 +7,7 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/element',
 	'document/element',
 	'document/character',
 	'document/character',
 	'document/position',
 	'document/position',
@@ -16,10 +16,10 @@ var modules = bender.amd.require(
 	'document/nodelist'
 	'document/nodelist'
 );
 );
 
 
-describe( 'position', function() {
-	var Element, Character, Document, NodeList, Position, CKEditorError;
+describe( 'position', () => {
+	let Element, Character, Document, NodeList, Position, CKEditorError;
 
 
-	var doc, root, p, ul, li1, li2, f, o, z, b, a, r;
+	let doc, root, p, ul, li1, li2, f, o, z, b, a, r;
 
 
 	// root
 	// root
 	//  |- p         Before: [ 0 ]       After: [ 1 ]
 	//  |- p         Before: [ 0 ]       After: [ 1 ]
@@ -32,7 +32,7 @@ describe( 'position', function() {
 	//        |- b   Before: [ 1, 1, 0 ] After: [ 1, 1, 1 ]
 	//        |- b   Before: [ 1, 1, 0 ] After: [ 1, 1, 1 ]
 	//        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
 	//        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
 	//        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
 	//        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
-	before( function() {
+	before( () => {
 		Element = modules[ 'document/element' ];
 		Element = modules[ 'document/element' ];
 		Character = modules[ 'document/character' ];
 		Character = modules[ 'document/character' ];
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
@@ -63,14 +63,14 @@ describe( 'position', function() {
 		root.insertChildren( 0, [ p, ul ] );
 		root.insertChildren( 0, [ p, ul ] );
 	} );
 	} );
 
 
-	it( 'should create a position with path and document', function() {
-		var position = new Position( [ 0 ], root );
+	it( 'should create a position with path and document', () => {
+		let position = new Position( [ 0 ], root );
 
 
 		expect( position ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
 		expect( position ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
 		expect( position ).to.have.property( 'root' ).that.equals( root );
 		expect( position ).to.have.property( 'root' ).that.equals( root );
 	} );
 	} );
 
 
-	it( 'should make positions form node and offset', function() {
+	it( 'should make positions form node and offset', () => {
 		expect( Position.createFromParentAndOffset( root, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
 		expect( Position.createFromParentAndOffset( root, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
 		expect( Position.createFromParentAndOffset( root, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
 		expect( Position.createFromParentAndOffset( root, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
 		expect( Position.createFromParentAndOffset( root, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
 		expect( Position.createFromParentAndOffset( root, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
@@ -87,7 +87,7 @@ describe( 'position', function() {
 		expect( Position.createFromParentAndOffset( li1, 3 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
 		expect( Position.createFromParentAndOffset( li1, 3 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
 	} );
 	} );
 
 
-	it( 'should make positions before elements', function() {
+	it( 'should make positions before elements', () => {
 		expect( Position.createBefore( p ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
 		expect( Position.createBefore( p ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
 
 
 		expect( Position.createBefore( ul ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
 		expect( Position.createBefore( ul ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
@@ -105,15 +105,15 @@ describe( 'position', function() {
 		expect( Position.createBefore( r ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
 		expect( Position.createBefore( r ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
 	} );
 	} );
 
 
-	it( 'should throw error if one try to make positions before root', function() {
+	it( 'should throw error if one try to make positions before root', () => {
 		expect(
 		expect(
-			function() {
+			() => {
 				Position.createBefore( root );
 				Position.createBefore( root );
 			}
 			}
 		).to.throw( CKEditorError, /position-before-root/ );
 		).to.throw( CKEditorError, /position-before-root/ );
 	} );
 	} );
 
 
-	it( 'should make positions after elements', function() {
+	it( 'should make positions after elements', () => {
 		expect( Position.createAfter( p ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
 		expect( Position.createAfter( p ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
 
 
 		expect( Position.createAfter( ul ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
 		expect( Position.createAfter( ul ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
@@ -131,15 +131,15 @@ describe( 'position', function() {
 		expect( Position.createAfter( r ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 3 ] );
 		expect( Position.createAfter( r ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 3 ] );
 	} );
 	} );
 
 
-	it( 'should throw error if one try to make positions after root', function() {
+	it( 'should throw error if one try to make positions after root', () => {
 		expect(
 		expect(
-			function() {
+			() => {
 				Position.createAfter( root );
 				Position.createAfter( root );
 			}
 			}
 		).to.throw( CKEditorError, /position-after-root/ );
 		).to.throw( CKEditorError, /position-after-root/ );
 	} );
 	} );
 
 
-	it( 'should have parent', function() {
+	it( 'should have parent', () => {
 		expect( new Position( [ 0 ], root ) ).to.have.property( 'parent' ).that.equals( root );
 		expect( new Position( [ 0 ], root ) ).to.have.property( 'parent' ).that.equals( root );
 		expect( new Position( [ 1 ], root ) ).to.have.property( 'parent' ).that.equals( root );
 		expect( new Position( [ 1 ], root ) ).to.have.property( 'parent' ).that.equals( root );
 		expect( new Position( [ 2 ], root ) ).to.have.property( 'parent' ).that.equals( root );
 		expect( new Position( [ 2 ], root ) ).to.have.property( 'parent' ).that.equals( root );
@@ -156,7 +156,7 @@ describe( 'position', function() {
 		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'parent' ).that.equals( li1 );
 		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'parent' ).that.equals( li1 );
 	} );
 	} );
 
 
-	it( 'should have offset', function() {
+	it( 'should have offset', () => {
 		expect( new Position( [ 0 ], root ) ).to.have.property( 'offset' ).that.equals( 0 );
 		expect( new Position( [ 0 ], root ) ).to.have.property( 'offset' ).that.equals( 0 );
 		expect( new Position( [ 1 ], root ) ).to.have.property( 'offset' ).that.equals( 1 );
 		expect( new Position( [ 1 ], root ) ).to.have.property( 'offset' ).that.equals( 1 );
 		expect( new Position( [ 2 ], root ) ).to.have.property( 'offset' ).that.equals( 2 );
 		expect( new Position( [ 2 ], root ) ).to.have.property( 'offset' ).that.equals( 2 );
@@ -173,7 +173,7 @@ describe( 'position', function() {
 		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'offset' ).that.equals( 3 );
 		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'offset' ).that.equals( 3 );
 	} );
 	} );
 
 
-	it( 'should have nodeBefore', function() {
+	it( 'should have nodeBefore', () => {
 		expect( new Position( [ 0 ], root ) ).to.have.property( 'nodeBefore' ).that.is.null;
 		expect( new Position( [ 0 ], root ) ).to.have.property( 'nodeBefore' ).that.is.null;
 		expect( new Position( [ 1 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( p );
 		expect( new Position( [ 1 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( p );
 		expect( new Position( [ 2 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( ul );
 		expect( new Position( [ 2 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( ul );
@@ -190,7 +190,7 @@ describe( 'position', function() {
 		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( z );
 		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( z );
 	} );
 	} );
 
 
-	it( 'should have nodeAfter', function() {
+	it( 'should have nodeAfter', () => {
 		expect( new Position( [ 0 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( p );
 		expect( new Position( [ 0 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( p );
 		expect( new Position( [ 1 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( ul );
 		expect( new Position( [ 1 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( ul );
 		expect( new Position( [ 2 ], root ) ).to.have.property( 'nodeAfter' ).that.is.null;
 		expect( new Position( [ 2 ], root ) ).to.have.property( 'nodeAfter' ).that.is.null;
@@ -207,22 +207,22 @@ describe( 'position', function() {
 		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'nodeAfter' ).that.is.null;
 		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'nodeAfter' ).that.is.null;
 	} );
 	} );
 
 
-	it( 'should equals another position with the same path', function() {
-		var position = new Position( [ 1, 1, 2 ], root );
-		var samePosition = new Position( [ 1, 1, 2 ], root );
+	it( 'should equals another position with the same path', () => {
+		let position = new Position( [ 1, 1, 2 ], root );
+		let samePosition = new Position( [ 1, 1, 2 ], root );
 
 
 		expect( position.isEqual( samePosition ) ).to.be.true;
 		expect( position.isEqual( samePosition ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'should not equals another position with the different path', function() {
-		var position = new Position( [ 1, 1, 1 ], root );
-		var differentNode = new Position( [ 1, 2, 2 ], root );
+	it( 'should not equals another position with the different path', () => {
+		let position = new Position( [ 1, 1, 1 ], root );
+		let differentNode = new Position( [ 1, 2, 2 ], root );
 
 
 		expect( position.isEqual( differentNode ) ).to.be.false;
 		expect( position.isEqual( differentNode ) ).to.be.false;
 	} );
 	} );
 
 
-	it( 'should have proper parent path', function() {
-		var position = new Position( [ 1, 2, 3 ], root );
+	it( 'should have proper parent path', () => {
+		let position = new Position( [ 1, 2, 3 ], root );
 
 
 		expect( position.getParentPath() ).to.deep.equal( [ 1, 2 ] );
 		expect( position.getParentPath() ).to.deep.equal( [ 1, 2 ] );
 	} );
 	} );

+ 28 - 28
packages/ckeditor5-engine/tests/document/positioniterator.js

@@ -7,7 +7,7 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/document',
 	'document/document',
 	'document/element',
 	'document/element',
 	'document/character',
 	'document/character',
@@ -16,13 +16,13 @@ var modules = bender.amd.require(
 	'document/range'
 	'document/range'
 );
 );
 
 
-describe( 'range iterator', function() {
-	var Document, Element, Character, PositionIterator, Position, Range;
+describe( 'range iterator', () => {
+	let Document, Element, Character, PositionIterator, Position, Range;
+	let ELEMENT_ENTER, ELEMENT_LEAVE, CHARACTER;
 
 
-	var doc, expectedItems, root, img1, paragraph, b, a, r, img2, x;
-	var ELEMENT_ENTER, ELEMENT_LEAVE, CHARACTER;
+	let doc, expectedItems, root, img1, paragraph, b, a, r, img2, x;
 
 
-	before( function() {
+	before( () => {
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
 		Element = modules[ 'document/element' ];
 		Element = modules[ 'document/element' ];
 		Character = modules[ 'document/character' ];
 		Character = modules[ 'document/character' ];
@@ -73,9 +73,9 @@ describe( 'range iterator', function() {
 		];
 		];
 	} );
 	} );
 
 
-	it( 'should return next position', function() {
-		var iterator = new PositionIterator( new Position( [ 0 ], root ) ); // beginning of root
-		var i, len;
+	it( 'should return next position', () => {
+		let iterator = new PositionIterator( new Position( [ 0 ], root ) ); // beginning of root
+		let i, len;
 
 
 		for ( i = 0, len = expectedItems.length; i < len; i++ ) {
 		for ( i = 0, len = expectedItems.length; i < len; i++ ) {
 			expect( iterator.next() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
 			expect( iterator.next() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
@@ -83,22 +83,22 @@ describe( 'range iterator', function() {
 		expect( iterator.next() ).to.have.property( 'done' ).that.is.true;
 		expect( iterator.next() ).to.have.property( 'done' ).that.is.true;
 	} );
 	} );
 
 
-	it( 'should return previous position', function() {
-		var iterator = new PositionIterator( new Position( [ 2 ], root ) ); // ending of root
+	it( 'should return previous position', () => {
+		let iterator = new PositionIterator( new Position( [ 2 ], root ) ); // ending of root
 
 
-		for ( var i = expectedItems.length - 1; i >= 0; i-- ) {
+		for ( let i = expectedItems.length - 1; i >= 0; i-- ) {
 			expect( iterator.previous() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
 			expect( iterator.previous() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
 		}
 		}
 		expect( iterator.previous() ).to.have.property( 'done' ).that.is.true;
 		expect( iterator.previous() ).to.have.property( 'done' ).that.is.true;
 	} );
 	} );
 
 
-	it( 'should return next position in the boundaries', function() {
-		var start = new Position( [ 1, 0 ], root ); // p, 0
-		var end = new Position( [ 1, 3, 0 ], root ); // img, 0
+	it( 'should return next position in the boundaries', () => {
+		let start = new Position( [ 1, 0 ], root ); // p, 0
+		let end = new Position( [ 1, 3, 0 ], root ); // img, 0
 
 
-		var iterator = new PositionIterator( new Range( start, end ) );
+		let iterator = new PositionIterator( new Range( start, end ) );
 
 
-		var i, len;
+		let i, len;
 
 
 		for ( i = 3, len = expectedItems.length; i < 7; i++ ) {
 		for ( i = 3, len = expectedItems.length; i < 7; i++ ) {
 			expect( iterator.next() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
 			expect( iterator.next() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
@@ -106,13 +106,13 @@ describe( 'range iterator', function() {
 		expect( iterator.next() ).to.have.property( 'done' ).that.is.true;
 		expect( iterator.next() ).to.have.property( 'done' ).that.is.true;
 	} );
 	} );
 
 
-	it( 'should return previous position in the boundaries', function() {
-		var start = new Position( [ 1, 0 ], root ); // p, 0
-		var end = new Position( [ 1, 3, 0 ], root ); // img, 0
+	it( 'should return previous position in the boundaries', () => {
+		let start = new Position( [ 1, 0 ], root ); // p, 0
+		let end = new Position( [ 1, 3, 0 ], root ); // img, 0
 
 
-		var iterator = new PositionIterator( new Range( start, end ), end );
+		let iterator = new PositionIterator( new Range( start, end ), end );
 
 
-		var i, len;
+		let i, len;
 
 
 		for ( i = 6, len = expectedItems.length; i > 2; i-- ) {
 		for ( i = 6, len = expectedItems.length; i > 2; i-- ) {
 			expect( iterator.previous() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
 			expect( iterator.previous() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
@@ -120,13 +120,13 @@ describe( 'range iterator', function() {
 		expect( iterator.previous() ).to.have.property( 'done' ).that.is.true;
 		expect( iterator.previous() ).to.have.property( 'done' ).that.is.true;
 	} );
 	} );
 
 
-	it( 'should return iterate over the range', function() {
-		var start = new Position( [ 0 ], root ); // begging of root
-		var end = new Position( [ 2 ], root ); // ending of root
-		var range = new Range( start, end );
+	it( 'should return iterate over the range', () => {
+		let start = new Position( [ 0 ], root ); // begging of root
+		let end = new Position( [ 2 ], root ); // ending of root
+		let range = new Range( start, end );
 
 
-		var i = 0;
-		var value;
+		let i = 0;
+		let value;
 
 
 		for ( value of range ) {
 		for ( value of range ) {
 			expect( value ).to.deep.equal( expectedItems[ i ] );
 			expect( value ).to.deep.equal( expectedItems[ i ] );

+ 72 - 23
packages/ckeditor5-engine/tests/document/range.js

@@ -7,63 +7,112 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/range',
 	'document/range',
-	'document/position'
+	'document/position',
+	'document/element',
+	'document/character',
+	'document/document'
 );
 );
 
 
-describe( 'Range', function() {
-	var Range, Position, start, end;
+describe( 'Range', () => {
+	let Range, Position, Element, Character, Document;
 
 
-	before( function() {
+	let start, end;
+
+	before( () => {
 		Position = modules[ 'document/position' ];
 		Position = modules[ 'document/position' ];
 		Range = modules[ 'document/range' ];
 		Range = modules[ 'document/range' ];
+		Element = modules[ 'document/element' ];
+		Character = modules[ 'document/character' ];
+		Document = modules[ 'document/document' ];
 
 
 		start = new Position( [ 0 ] );
 		start = new Position( [ 0 ] );
 		end = new Position( [ 1 ] );
 		end = new Position( [ 1 ] );
 	} );
 	} );
 
 
-	var range;
+	let range;
 
 
-	beforeEach( function() {
+	beforeEach( () => {
 		range = new Range( start, end );
 		range = new Range( start, end );
 	} );
 	} );
 
 
-	describe( 'constructor', function() {
-		it( 'should create a range with given positions', function() {
+	describe( 'constructor', () => {
+		it( 'should create a range with given positions', () => {
 			expect( range ).to.have.property( 'start' ).that.equal( start );
 			expect( range ).to.have.property( 'start' ).that.equal( start );
 			expect( range ).to.have.property( 'end' ).that.equal( end );
 			expect( range ).to.have.property( 'end' ).that.equal( end );
 		} );
 		} );
 	} );
 	} );
 
 
-	describe( 'isEqual', function() {
-		it( 'should return true if the ranges are the same', function() {
-			var sameStart = new Position( [ 0 ] );
-			var sameEnd = new Position( [ 1 ] );
+	describe( 'isEqual', () => {
+		it( 'should return true if the ranges are the same', () => {
+			let sameStart = new Position( [ 0 ] );
+			let sameEnd = new Position( [ 1 ] );
 
 
-			var sameRange = new Range( sameStart, sameEnd );
+			let sameRange = new Range( sameStart, sameEnd );
 
 
 			expect( range.isEqual( sameRange ) ).to.be.true;
 			expect( range.isEqual( sameRange ) ).to.be.true;
 		} );
 		} );
 
 
-		it( 'should return false if the start position is different', function() {
-			var range = new Range( start, end );
+		it( 'should return false if the start position is different', () => {
+			let range = new Range( start, end );
 
 
-			var diffStart = new Position( [ 1 ] );
-			var sameEnd = new Position( [ 1 ] );
+			let diffStart = new Position( [ 1 ] );
+			let sameEnd = new Position( [ 1 ] );
 
 
-			var diffRange = new Range( diffStart, sameEnd );
+			let diffRange = new Range( diffStart, sameEnd );
 
 
 			expect( range.isEqual( diffRange ) ).to.not.be.true;
 			expect( range.isEqual( diffRange ) ).to.not.be.true;
 		} );
 		} );
 
 
-		it( 'should return false if the end position is different', function() {
-			var sameStart = new Position( [ 0 ] );
-			var diffEnd = new Position( [ 0 ] );
+		it( 'should return false if the end position is different', () => {
+			let sameStart = new Position( [ 0 ] );
+			let diffEnd = new Position( [ 0 ] );
 
 
-			var diffRange = new Range( sameStart, diffEnd );
+			let diffRange = new Range( sameStart, diffEnd );
 
 
 			expect( range.isEqual( diffRange ) ).to.not.be.true;
 			expect( range.isEqual( diffRange ) ).to.not.be.true;
 		} );
 		} );
 	} );
 	} );
+
+	describe( 'static constructors', () => {
+		let doc, root, p, f, o, z;
+
+		// root
+		//  |- p
+		//     |- f
+		//     |- o
+		//     |- z
+		before( () => {
+			doc = new Document();
+
+			root = doc.createRoot( 'root' );
+
+			f = new Character( 'f' );
+			o = new Character( 'o' );
+			z = new Character( 'z' );
+
+			p = new Element( 'p', [], [ f, o, z ] );
+
+			root.insertChildren( 0, [ p ] );
+		} );
+
+		describe( 'createFromElement', () => {
+			it( 'should return range', () => {
+				const range = Range.createFromElement( p );
+
+				expect( range.start.path ).to.deep.equal( [ 0, 0 ] );
+				expect( range.end.path ).to.deep.equal( [ 0, 3 ] );
+			} );
+		} );
+
+		describe( 'createFromParentsAndOffsets', () => {
+			it( 'should return range', () => {
+				const range = Range.createFromParentsAndOffsets( root, 0, p, 2 );
+
+				expect( range.start.path ).to.deep.equal( [ 0 ] );
+				expect( range.end.path ).to.deep.equal( [ 0, 2 ] );
+			} );
+		} );
+	} );
 } );
 } );

+ 13 - 9
packages/ckeditor5-engine/tests/document/rootelement.js

@@ -7,31 +7,35 @@
 
 
 /* bender-tags: document */
 /* bender-tags: document */
 
 
+/* bender-include: ../_tools/tools.js */
+
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const getIteratorCount = bender.tools.core.getIteratorCount;
+
+const modules = bender.amd.require(
 	'document/document',
 	'document/document',
 	'document/element',
 	'document/element',
 	'document/rootelement'
 	'document/rootelement'
 );
 );
 
 
-describe( 'Element', function() {
-	var Document, Element, RootElement;
+describe( 'Element', () => {
+	let Document, Element, RootElement;
 
 
-	before( function() {
+	before( () => {
 		Document = modules[ 'document/document' ];
 		Document = modules[ 'document/document' ];
 		Element = modules[ 'document/element' ];
 		Element = modules[ 'document/element' ];
 		RootElement = modules[ 'document/rootelement' ];
 		RootElement = modules[ 'document/rootelement' ];
 	} );
 	} );
 
 
-	describe( 'constructor', function() {
-		it( 'should create root element without attributes', function() {
-			var doc = new Document();
-			var root = new RootElement( doc );
+	describe( 'constructor', () => {
+		it( 'should create root element without attributes', () => {
+			let doc = new Document();
+			let root = new RootElement( doc );
 
 
 			expect( root ).to.be.an.instanceof( Element );
 			expect( root ).to.be.an.instanceof( Element );
 			expect( root ).to.have.property( 'document' ).that.equals( doc );
 			expect( root ).to.have.property( 'document' ).that.equals( doc );
-			expect( root._getAttrCount() ).to.equal( 0 );
+			expect( getIteratorCount( root.getAttrs() ) ).to.equal( 0 );
 			expect( root.getChildCount() ).to.equal( 0 );
 			expect( root.getChildCount() ).to.equal( 0 );
 		} );
 		} );
 	} );
 	} );

+ 8 - 8
packages/ckeditor5-engine/tests/document/text.js

@@ -9,19 +9,19 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require(
+const modules = bender.amd.require(
 	'document/text',
 	'document/text',
 	'document/attribute'
 	'document/attribute'
 );
 );
 
 
-describe( 'Text', function() {
-	describe( 'constructor', function() {
-		it( 'should create character without attributes', function() {
-			var Text = modules[ 'document/text' ];
-			var Attribute = modules[ 'document/attribute' ];
+describe( 'Text', () => {
+	describe( 'constructor', () => {
+		it( 'should create character without attributes', () => {
+			const Text = modules[ 'document/text' ];
+			const Attribute = modules[ 'document/attribute' ];
 
 
-			var attrs = [ new Attribute( 'bold', true ) ];
-			var text = new Text( 'bar', attrs );
+			let attrs = [ new Attribute( 'bold', true ) ];
+			let text = new Text( 'bar', attrs );
 
 
 			expect( text ).to.have.property( 'text' ).that.equals( 'bar' );
 			expect( text ).to.have.property( 'text' ).that.equals( 'bar' );
 			expect( text ).to.have.property( 'attrs' ).that.is.an( 'array' );
 			expect( text ).to.have.property( 'attrs' ).that.is.an( 'array' );

+ 82 - 0
packages/ckeditor5-engine/tests/document/transaction.js

@@ -0,0 +1,82 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'document/transaction',
+	'document/delta/delta',
+	'ckeditorerror' );
+
+describe( 'Transaction', () => {
+	let Transaction, Delta, CKEditorError;
+
+	before( () => {
+		Transaction = modules[ 'document/transaction' ];
+		Delta = modules[ 'document/delta/delta' ];
+		CKEditorError = modules.ckeditorerror;
+	} );
+
+	it( 'should have registered basic methods', () => {
+		const transaction = new Transaction();
+
+		expect( transaction.setAttr ).to.be.a( 'function' );
+		expect( transaction.removeAttr ).to.be.a( 'function' );
+	} );
+
+	describe( 'Transaction.register', () => {
+		let TestDelta;
+
+		before( () => {
+			TestDelta = class extends Delta {
+				constructor( transaction ) {
+					super( transaction, [] );
+				}
+			};
+		} );
+
+		afterEach( () => {
+			delete Transaction.prototype.foo;
+		} );
+
+		it( 'should register function which return an delta', () => {
+			Transaction.register( 'foo', function() {
+				this.addDelta( new TestDelta() );
+			} );
+
+			const transaction = new Transaction();
+
+			transaction.foo();
+
+			expect( transaction.deltas.length ).to.equal( 1 );
+			expect( transaction.deltas[ 0 ] ).to.be.instanceof( TestDelta );
+		} );
+
+		it( 'should register function which return an multiple deltas', () => {
+			Transaction.register( 'foo', function() {
+				this.addDelta( new TestDelta() );
+				this.addDelta( new TestDelta() );
+			} );
+
+			const transaction = new Transaction();
+
+			transaction.foo();
+
+			expect( transaction.deltas.length ).to.equal( 2 );
+			expect( transaction.deltas[ 0 ] ).to.be.instanceof( TestDelta );
+			expect( transaction.deltas[ 1 ] ).to.be.instanceof( TestDelta );
+		} );
+
+		it( 'should throw if one try to register the same transaction twice', () => {
+			Transaction.register( 'foo', () => {} );
+
+			expect( () => {
+				Transaction.register( 'foo', () => {} );
+			} ).to.throw( CKEditorError, /^transaction-register-taken/ );
+		} );
+	} );
+} );

+ 41 - 41
packages/ckeditor5-engine/tests/editor/editor.js

@@ -7,14 +7,14 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'editor', 'editorconfig', 'plugin' );
+const modules = bender.amd.require( 'editor', 'editorconfig', 'plugin' );
 
 
-var editor;
-var element;
-var asyncSpy;
+let editor;
+let element;
+let asyncSpy;
 
 
-beforeEach( function() {
-	var Editor = modules.editor;
+beforeEach( () => {
+	const Editor = modules.editor;
 
 
 	element = document.createElement( 'div' );
 	element = document.createElement( 'div' );
 	document.body.appendChild( element );
 	document.body.appendChild( element );
@@ -22,7 +22,7 @@ beforeEach( function() {
 	editor = new Editor( element );
 	editor = new Editor( element );
 } );
 } );
 
 
-before( function() {
+before( () => {
 	// Define fake plugins to be used in tests.
 	// Define fake plugins to be used in tests.
 	bender.tools.core.defineEditorCreatorMock( 'test', {
 	bender.tools.core.defineEditorCreatorMock( 'test', {
 		init: sinon.spy().named( 'creator-test' )
 		init: sinon.spy().named( 'creator-test' )
@@ -43,12 +43,12 @@ before( function() {
 
 
 	asyncSpy = sinon.spy().named( 'async-call-spy' );
 	asyncSpy = sinon.spy().named( 'async-call-spy' );
 
 
-	CKEDITOR.define( 'plugin!async', [ 'plugin' ], function( Plugin ) {
+	CKEDITOR.define( 'plugin!async', [ 'plugin' ], ( Plugin ) => {
 		class PluginAsync extends Plugin {}
 		class PluginAsync extends Plugin {}
 
 
-		PluginAsync.prototype.init = sinon.spy( function() {
-			return new Promise( function( resolve ) {
-				setTimeout( function() {
+		PluginAsync.prototype.init = sinon.spy( () => {
+			return new Promise( ( resolve ) => {
+				setTimeout( () => {
 					asyncSpy();
 					asyncSpy();
 					resolve();
 					resolve();
 				}, 0 );
 				}, 0 );
@@ -60,7 +60,7 @@ before( function() {
 } );
 } );
 
 
 function pluginDefinition( name ) {
 function pluginDefinition( name ) {
-	return function( Plugin ) {
+	return ( Plugin ) => {
 		class NewPlugin extends Plugin {}
 		class NewPlugin extends Plugin {}
 		NewPlugin.prototype.init = sinon.spy().named( name );
 		NewPlugin.prototype.init = sinon.spy().named( name );
 
 
@@ -70,38 +70,38 @@ function pluginDefinition( name ) {
 
 
 ///////////////////
 ///////////////////
 
 
-describe( 'constructor', function() {
-	it( 'should create a new editor instance', function() {
+describe( 'constructor', () => {
+	it( 'should create a new editor instance', () => {
 		expect( editor ).to.have.property( 'element' ).to.equal( element );
 		expect( editor ).to.have.property( 'element' ).to.equal( element );
 	} );
 	} );
 } );
 } );
 
 
-describe( 'config', function() {
-	it( 'should be an instance of EditorConfig', function() {
-		var EditorConfig = modules.editorconfig;
+describe( 'config', () => {
+	it( 'should be an instance of EditorConfig', () => {
+		const EditorConfig = modules.editorconfig;
 
 
 		expect( editor.config ).to.be.an.instanceof( EditorConfig );
 		expect( editor.config ).to.be.an.instanceof( EditorConfig );
 	} );
 	} );
 } );
 } );
 
 
-describe( 'init', function() {
-	it( 'should return a promise that resolves properly', function() {
-		var Editor = modules.editor;
+describe( 'init', () => {
+	it( 'should return a promise that resolves properly', () => {
+		const Editor = modules.editor;
 
 
 		editor = new Editor( element, {
 		editor = new Editor( element, {
 			plugins: 'creator-test'
 			plugins: 'creator-test'
 		} );
 		} );
 
 
-		var promise = editor.init();
+		let promise = editor.init();
 
 
 		expect( promise ).to.be.an.instanceof( Promise );
 		expect( promise ).to.be.an.instanceof( Promise );
 
 
 		return promise;
 		return promise;
 	} );
 	} );
 
 
-	it( 'should fill `plugins`', function() {
-		var Editor = modules.editor;
-		var Plugin = modules.plugin;
+	it( 'should fill `plugins`', () => {
+		const Editor = modules.editor;
+		const Plugin = modules.plugin;
 
 
 		editor = new Editor( element, {
 		editor = new Editor( element, {
 			plugins: 'A,B,creator-test'
 			plugins: 'A,B,creator-test'
@@ -109,7 +109,7 @@ describe( 'init', function() {
 
 
 		expect( editor.plugins.length ).to.equal( 0 );
 		expect( editor.plugins.length ).to.equal( 0 );
 
 
-		return editor.init().then( function() {
+		return editor.init().then( () => {
 			expect( editor.plugins.length ).to.equal( 3 );
 			expect( editor.plugins.length ).to.equal( 3 );
 
 
 			expect( editor.plugins.get( 'A' ) ).to.be.an.instanceof( Plugin );
 			expect( editor.plugins.get( 'A' ) ).to.be.an.instanceof( Plugin );
@@ -118,14 +118,14 @@ describe( 'init', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should initialize plugins in the right order', function() {
-		var Editor = modules.editor;
+	it( 'should initialize plugins in the right order', () => {
+		const Editor = modules.editor;
 
 
 		editor = new Editor( element, {
 		editor = new Editor( element, {
 			plugins: 'creator-test,A,D'
 			plugins: 'creator-test,A,D'
 		} );
 		} );
 
 
-		return editor.init().then( function() {
+		return editor.init().then( () => {
 			sinon.assert.callOrder(
 			sinon.assert.callOrder(
 				editor.plugins.get( 'creator-test' ).init,
 				editor.plugins.get( 'creator-test' ).init,
 				editor.plugins.get( 'A' ).init,
 				editor.plugins.get( 'A' ).init,
@@ -136,14 +136,14 @@ describe( 'init', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should initialize plugins in the right order, waiting for asynchronous ones', function() {
-		var Editor = modules.editor;
+	it( 'should initialize plugins in the right order, waiting for asynchronous ones', () => {
+		const Editor = modules.editor;
 
 
 		editor = new Editor( element, {
 		editor = new Editor( element, {
 			plugins: 'creator-test,A,F'
 			plugins: 'creator-test,A,F'
 		} );
 		} );
 
 
-		return editor.init().then( function() {
+		return editor.init().then( () => {
 			sinon.assert.callOrder(
 			sinon.assert.callOrder(
 				editor.plugins.get( 'creator-test' ).init,
 				editor.plugins.get( 'creator-test' ).init,
 				editor.plugins.get( 'A' ).init,
 				editor.plugins.get( 'A' ).init,
@@ -155,8 +155,8 @@ describe( 'init', function() {
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should not fail if loading a plugin that doesn\'t define init()', function() {
-		var Editor = modules.editor;
+	it( 'should not fail if loading a plugin that doesn\'t define init()', () => {
+		const Editor = modules.editor;
 
 
 		editor = new Editor( element, {
 		editor = new Editor( element, {
 			plugins: 'E,creator-test'
 			plugins: 'E,creator-test'
@@ -166,25 +166,25 @@ describe( 'init', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'plugins', function() {
-	it( 'should be empty on new editor', function() {
+describe( 'plugins', () => {
+	it( 'should be empty on new editor', () => {
 		expect( editor.plugins.length ).to.equal( 0 );
 		expect( editor.plugins.length ).to.equal( 0 );
 	} );
 	} );
 } );
 } );
 
 
-describe( 'destroy', function() {
-	it( 'should fire "destroy"', function() {
-		var spy = sinon.spy();
+describe( 'destroy', () => {
+	it( 'should fire "destroy"', () => {
+		let spy = sinon.spy();
 
 
 		editor.on( 'destroy', spy );
 		editor.on( 'destroy', spy );
 
 
-		return editor.destroy().then( function() {
+		return editor.destroy().then( () => {
 			sinon.assert.called( spy );
 			sinon.assert.called( spy );
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should delete the "element" property', function() {
-		return editor.destroy().then( function() {
+	it( 'should delete the "element" property', () => {
+		return editor.destroy().then( () => {
 			expect( editor ).to.not.have.property( 'element' );
 			expect( editor ).to.not.have.property( 'element' );
 		} );
 		} );
 	} );
 	} );

+ 11 - 11
packages/ckeditor5-engine/tests/editorconfig/editorconfig.js

@@ -5,31 +5,31 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'editorconfig', 'ckeditor' );
+const modules = bender.amd.require( 'editorconfig', 'ckeditor' );
 
 
-var config;
+let config;
 
 
-beforeEach( function() {
-	var EditorConfig = modules.editorconfig;
+beforeEach( () => {
+	const EditorConfig = modules.editorconfig;
 
 
 	config = new EditorConfig( {
 	config = new EditorConfig( {
 		test: 1
 		test: 1
 	} );
 	} );
 } );
 } );
 
 
-describe( 'constructor', function() {
-	it( 'should set configurations', function() {
+describe( 'constructor', () => {
+	it( 'should set configurations', () => {
 		expect( config ).to.have.property( 'test' ).to.equal( 1 );
 		expect( config ).to.have.property( 'test' ).to.equal( 1 );
 	} );
 	} );
 } );
 } );
 
 
-describe( 'get', function() {
-	it( 'should retrieve a configuration', function() {
+describe( 'get', () => {
+	it( 'should retrieve a configuration', () => {
 		expect( config.get( 'test' ) ).to.equal( 1 );
 		expect( config.get( 'test' ) ).to.equal( 1 );
 	} );
 	} );
 
 
-	it( 'should fallback to CKEDITOR.config', function() {
-		var CKEDITOR = modules.ckeditor;
+	it( 'should fallback to CKEDITOR.config', () => {
+		const CKEDITOR = modules.ckeditor;
 
 
 		CKEDITOR.config.set( {
 		CKEDITOR.config.set( {
 			globalConfig: 2
 			globalConfig: 2
@@ -38,7 +38,7 @@ describe( 'get', function() {
 		expect( config.get( 'globalConfig' ) ).to.equal( 2 );
 		expect( config.get( 'globalConfig' ) ).to.equal( 2 );
 	} );
 	} );
 
 
-	it( 'should return undefined for non existing configuration', function() {
+	it( 'should return undefined for non existing configuration', () => {
 		expect( config.get( 'invalid' ) ).to.be.undefined();
 		expect( config.get( 'invalid' ) ).to.be.undefined();
 	} );
 	} );
 } );
 } );

+ 94 - 94
packages/ckeditor5-engine/tests/emittermixin/emittermixin.js

@@ -5,17 +5,17 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'emittermixin', 'eventinfo', 'utils' );
+const modules = bender.amd.require( 'emittermixin', 'eventinfo', 'utils' );
 
 
-var emitter, listener;
+let emitter, listener;
 
 
 beforeEach( refreshEmitter );
 beforeEach( refreshEmitter );
 
 
-describe( 'fire', function() {
-	it( 'should execute callbacks in the right order without priority', function() {
-		var spy1 = sinon.spy().named( 1 );
-		var spy2 = sinon.spy().named( 2 );
-		var spy3 = sinon.spy().named( 3 );
+describe( 'fire', () => {
+	it( 'should execute callbacks in the right order without priority', () => {
+		let spy1 = sinon.spy().named( 1 );
+		let spy2 = sinon.spy().named( 2 );
+		let spy3 = sinon.spy().named( 3 );
 
 
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy2 );
 		emitter.on( 'test', spy2 );
@@ -26,12 +26,12 @@ describe( 'fire', function() {
 		sinon.assert.callOrder( spy1, spy2, spy3 );
 		sinon.assert.callOrder( spy1, spy2, spy3 );
 	} );
 	} );
 
 
-	it( 'should execute callbacks in the right order with priority defined', function() {
-		var spy1 = sinon.spy().named( 1 );
-		var spy2 = sinon.spy().named( 2 );
-		var spy3 = sinon.spy().named( 3 );
-		var spy4 = sinon.spy().named( 4 );
-		var spy5 = sinon.spy().named( 5 );
+	it( 'should execute callbacks in the right order with priority defined', () => {
+		let spy1 = sinon.spy().named( 1 );
+		let spy2 = sinon.spy().named( 2 );
+		let spy3 = sinon.spy().named( 3 );
+		let spy4 = sinon.spy().named( 4 );
+		let spy5 = sinon.spy().named( 5 );
 
 
 		emitter.on( 'test', spy2, null, 9 );
 		emitter.on( 'test', spy2, null, 9 );
 		emitter.on( 'test', spy3 );	// Defaults to 10.
 		emitter.on( 'test', spy3 );	// Defaults to 10.
@@ -44,11 +44,11 @@ describe( 'fire', function() {
 		sinon.assert.callOrder( spy1, spy2, spy3, spy4, spy5 );
 		sinon.assert.callOrder( spy1, spy2, spy3, spy4, spy5 );
 	} );
 	} );
 
 
-	it( 'should pass arguments to callbacks', function() {
-		var EventInfo = modules.eventinfo;
+	it( 'should pass arguments to callbacks', () => {
+		const EventInfo = modules.eventinfo;
 
 
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
 
 
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy2 );
 		emitter.on( 'test', spy2 );
@@ -59,13 +59,13 @@ describe( 'fire', function() {
 		sinon.assert.calledWithExactly( spy2, sinon.match.instanceOf( EventInfo ), 1, 'b', true );
 		sinon.assert.calledWithExactly( spy2, sinon.match.instanceOf( EventInfo ), 1, 'b', true );
 	} );
 	} );
 
 
-	it( 'should pass proper context to callbacks', function() {
-		var ctx1 = {};
-		var ctx2 = {};
+	it( 'should pass proper context to callbacks', () => {
+		let ctx1 = {};
+		let ctx2 = {};
 
 
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
-		var spy3 = sinon.spy();
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
+		let spy3 = sinon.spy();
 
 
 		emitter.on( 'test', spy1, ctx1 );
 		emitter.on( 'test', spy1, ctx1 );
 		emitter.on( 'test', spy2, ctx2 );
 		emitter.on( 'test', spy2, ctx2 );
@@ -78,9 +78,9 @@ describe( 'fire', function() {
 		sinon.assert.calledOn( spy3, emitter );
 		sinon.assert.calledOn( spy3, emitter );
 	} );
 	} );
 
 
-	it( 'should fire the right event', function() {
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
+	it( 'should fire the right event', () => {
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
 
 
 		emitter.on( '1', spy1 );
 		emitter.on( '1', spy1 );
 		emitter.on( '2', spy2 );
 		emitter.on( '2', spy2 );
@@ -91,8 +91,8 @@ describe( 'fire', function() {
 		sinon.assert.called( spy2 );
 		sinon.assert.called( spy2 );
 	} );
 	} );
 
 
-	it( 'should execute callbacks many times', function() {
-		var spy = sinon.spy();
+	it( 'should execute callbacks many times', () => {
+		let spy = sinon.spy();
 
 
 		emitter.on( 'test', spy );
 		emitter.on( 'test', spy );
 
 
@@ -103,12 +103,12 @@ describe( 'fire', function() {
 		sinon.assert.calledThrice( spy );
 		sinon.assert.calledThrice( spy );
 	} );
 	} );
 
 
-	it( 'should do nothing for a non listened event', function() {
+	it( 'should do nothing for a non listened event', () => {
 		emitter.fire( 'test' );
 		emitter.fire( 'test' );
 	} );
 	} );
 
 
-	it( 'should accept the same callback many times', function() {
-		var spy = sinon.spy();
+	it( 'should accept the same callback many times', () => {
+		let spy = sinon.spy();
 
 
 		emitter.on( 'test', spy );
 		emitter.on( 'test', spy );
 		emitter.on( 'test', spy );
 		emitter.on( 'test', spy );
@@ -120,11 +120,11 @@ describe( 'fire', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'on', function() {
-	it( 'should stop()', function() {
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
-		var spy3 = sinon.spy( function( event ) {
+describe( 'on', () => {
+	it( 'should stop()', () => {
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
+		let spy3 = sinon.spy( ( event ) => {
 			event.stop();
 			event.stop();
 		} );
 		} );
 
 
@@ -141,12 +141,12 @@ describe( 'on', function() {
 		sinon.assert.called( spy3 );
 		sinon.assert.called( spy3 );
 	} );
 	} );
 
 
-	it( 'should take a callback off()', function() {
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy( function( event ) {
+	it( 'should take a callback off()', () => {
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy( ( event ) => {
 			event.off();
 			event.off();
 		} );
 		} );
-		var spy3 = sinon.spy();
+		let spy3 = sinon.spy();
 
 
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy2 );
 		emitter.on( 'test', spy2 );
@@ -160,12 +160,12 @@ describe( 'on', function() {
 		sinon.assert.calledTwice( spy3 );
 		sinon.assert.calledTwice( spy3 );
 	} );
 	} );
 
 
-	it( 'should take the callback off() even after stop()', function() {
-		var spy1 = sinon.spy( function( event ) {
+	it( 'should take the callback off() even after stop()', () => {
+		let spy1 = sinon.spy( ( event ) => {
 			event.stop();
 			event.stop();
 			event.off();
 			event.off();
 		} );
 		} );
-		var spy2 = sinon.spy();
+		let spy2 = sinon.spy();
 
 
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy2 );
 		emitter.on( 'test', spy2 );
@@ -178,11 +178,11 @@ describe( 'on', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'once', function() {
-	it( 'should be called just once', function() {
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
-		var spy3 = sinon.spy();
+describe( 'once', () => {
+	it( 'should be called just once', () => {
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
+		let spy3 = sinon.spy();
 
 
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy1 );
 		emitter.once( 'test', spy2 );
 		emitter.once( 'test', spy2 );
@@ -196,11 +196,11 @@ describe( 'once', function() {
 		sinon.assert.calledTwice( spy3 );
 		sinon.assert.calledTwice( spy3 );
 	} );
 	} );
 
 
-	it( 'should have proper scope', function() {
-		var ctx = {};
+	it( 'should have proper scope', () => {
+		let ctx = {};
 
 
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
 
 
 		emitter.once( 'test', spy1, ctx );
 		emitter.once( 'test', spy1, ctx );
 		emitter.once( 'test', spy2 );
 		emitter.once( 'test', spy2 );
@@ -211,10 +211,10 @@ describe( 'once', function() {
 		sinon.assert.calledOn( spy2, emitter );
 		sinon.assert.calledOn( spy2, emitter );
 	} );
 	} );
 
 
-	it( 'should have proper arguments', function() {
-		var EventInfo = modules.eventinfo;
+	it( 'should have proper arguments', () => {
+		const EventInfo = modules.eventinfo;
 
 
-		var spy = sinon.spy();
+		let spy = sinon.spy();
 
 
 		emitter.once( 'test', spy );
 		emitter.once( 'test', spy );
 
 
@@ -224,11 +224,11 @@ describe( 'once', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'off', function() {
-	it( 'should get callbacks off()', function() {
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
-		var spy3 = sinon.spy();
+describe( 'off', () => {
+	it( 'should get callbacks off()', () => {
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
+		let spy3 = sinon.spy();
 
 
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy2 );
 		emitter.on( 'test', spy2 );
@@ -246,13 +246,13 @@ describe( 'off', function() {
 		sinon.assert.calledThrice( spy3 );
 		sinon.assert.calledThrice( spy3 );
 	} );
 	} );
 
 
-	it( 'should not fail with unknown events', function() {
-		emitter.off( 'test', function() {} );
+	it( 'should not fail with unknown events', () => {
+		emitter.off( 'test', () => {} );
 	} );
 	} );
 
 
-	it( 'should remove all entries for the same callback', function() {
-		var spy1 = sinon.spy().named( 1 );
-		var spy2 = sinon.spy().named( 2 );
+	it( 'should remove all entries for the same callback', () => {
+		let spy1 = sinon.spy().named( 1 );
+		let spy2 = sinon.spy().named( 2 );
 
 
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy1 );
 		emitter.on( 'test', spy2 );
 		emitter.on( 'test', spy2 );
@@ -269,11 +269,11 @@ describe( 'off', function() {
 		sinon.assert.callCount( spy2, 4 );
 		sinon.assert.callCount( spy2, 4 );
 	} );
 	} );
 
 
-	it( 'should remove the callback for a specific context only', function() {
-		var spy = sinon.spy().named( 1 );
+	it( 'should remove the callback for a specific context only', () => {
+		let spy = sinon.spy().named( 1 );
 
 
-		var ctx1 = { ctx: 1 };
-		var ctx2 = { ctx: 2 };
+		let ctx1 = { ctx: 1 };
+		let ctx2 = { ctx: 2 };
 
 
 		emitter.on( 'test', spy, ctx1 );
 		emitter.on( 'test', spy, ctx1 );
 		emitter.on( 'test', spy, ctx2 );
 		emitter.on( 'test', spy, ctx2 );
@@ -291,11 +291,11 @@ describe( 'off', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'listenTo', function() {
+describe( 'listenTo', () => {
 	beforeEach( refreshListener );
 	beforeEach( refreshListener );
 
 
-	it( 'should properly register callbacks', function() {
-		var spy = sinon.spy();
+	it( 'should properly register callbacks', () => {
+		let spy = sinon.spy();
 
 
 		listener.listenTo( emitter, 'test', spy );
 		listener.listenTo( emitter, 'test', spy );
 
 
@@ -305,12 +305,12 @@ describe( 'listenTo', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'stopListening', function() {
+describe( 'stopListening', () => {
 	beforeEach( refreshListener );
 	beforeEach( refreshListener );
 
 
-	it( 'should stop listening to a specific event callback', function() {
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
+	it( 'should stop listening to a specific event callback', () => {
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
 
 
 		listener.listenTo( emitter, 'event1', spy1 );
 		listener.listenTo( emitter, 'event1', spy1 );
 		listener.listenTo( emitter, 'event2', spy2 );
 		listener.listenTo( emitter, 'event2', spy2 );
@@ -327,10 +327,10 @@ describe( 'stopListening', function() {
 		sinon.assert.calledTwice( spy2 );
 		sinon.assert.calledTwice( spy2 );
 	} );
 	} );
 
 
-	it( 'should stop listening to an specific event', function() {
-		var spy1a = sinon.spy();
-		var spy1b = sinon.spy();
-		var spy2 = sinon.spy();
+	it( 'should stop listening to an specific event', () => {
+		let spy1a = sinon.spy();
+		let spy1b = sinon.spy();
+		let spy2 = sinon.spy();
 
 
 		listener.listenTo( emitter, 'event1', spy1a );
 		listener.listenTo( emitter, 'event1', spy1a );
 		listener.listenTo( emitter, 'event1', spy1b );
 		listener.listenTo( emitter, 'event1', spy1b );
@@ -349,9 +349,9 @@ describe( 'stopListening', function() {
 		sinon.assert.calledTwice( spy2 );
 		sinon.assert.calledTwice( spy2 );
 	} );
 	} );
 
 
-	it( 'should stop listening to all events from a specific emitter', function() {
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
+	it( 'should stop listening to all events from a specific emitter', () => {
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
 
 
 		listener.listenTo( emitter, 'event1', spy1 );
 		listener.listenTo( emitter, 'event1', spy1 );
 		listener.listenTo( emitter, 'event2', spy2 );
 		listener.listenTo( emitter, 'event2', spy2 );
@@ -368,12 +368,12 @@ describe( 'stopListening', function() {
 		sinon.assert.calledOnce( spy2 );
 		sinon.assert.calledOnce( spy2 );
 	} );
 	} );
 
 
-	it( 'should stop listening to everything', function() {
-		var spy1 = sinon.spy();
-		var spy2 = sinon.spy();
+	it( 'should stop listening to everything', () => {
+		let spy1 = sinon.spy();
+		let spy2 = sinon.spy();
 
 
-		var emitter1 = getEmitterInstance();
-		var emitter2 = getEmitterInstance();
+		let emitter1 = getEmitterInstance();
+		let emitter2 = getEmitterInstance();
 
 
 		listener.listenTo( emitter1, 'event1', spy1 );
 		listener.listenTo( emitter1, 'event1', spy1 );
 		listener.listenTo( emitter2, 'event2', spy2 );
 		listener.listenTo( emitter2, 'event2', spy2 );
@@ -394,11 +394,11 @@ describe( 'stopListening', function() {
 		expect( listener ).to.not.have.property( '_listeningTo' );
 		expect( listener ).to.not.have.property( '_listeningTo' );
 	} );
 	} );
 
 
-	it( 'should not stop other emitters when a non-listened emitter is provided', function() {
-		var spy = sinon.spy();
+	it( 'should not stop other emitters when a non-listened emitter is provided', () => {
+		let spy = sinon.spy();
 
 
-		var emitter1 = getEmitterInstance();
-		var emitter2 = getEmitterInstance();
+		let emitter1 = getEmitterInstance();
+		let emitter2 = getEmitterInstance();
 
 
 		listener.listenTo( emitter1, 'test', spy );
 		listener.listenTo( emitter1, 'test', spy );
 
 
@@ -419,8 +419,8 @@ function refreshListener() {
 }
 }
 
 
 function getEmitterInstance() {
 function getEmitterInstance() {
-	var EmitterMixin = modules.emittermixin;
-	var utils = modules.utils;
+	const EmitterMixin = modules.emittermixin;
+	let utils = modules.utils;
 
 
 	return utils.extend( {}, EmitterMixin );
 	return utils.extend( {}, EmitterMixin );
 }
 }

+ 11 - 11
packages/ckeditor5-engine/tests/eventinfo/eventinfo.js

@@ -5,13 +5,13 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'eventinfo' );
+const modules = bender.amd.require( 'eventinfo' );
 
 
-describe( 'EventInfo', function() {
-	it( 'should be created properly', function() {
-		var EventInfo = modules.eventinfo;
+describe( 'EventInfo', () => {
+	it( 'should be created properly', () => {
+		const EventInfo = modules.eventinfo;
 
 
-		var event = new EventInfo( this, 'test' );
+		let event = new EventInfo( this, 'test' );
 
 
 		expect( event.source ).to.equal( this );
 		expect( event.source ).to.equal( this );
 		expect( event.name ).to.equal( 'test' );
 		expect( event.name ).to.equal( 'test' );
@@ -19,10 +19,10 @@ describe( 'EventInfo', function() {
 		expect( event.off.called ).to.not.be.true();
 		expect( event.off.called ).to.not.be.true();
 	} );
 	} );
 
 
-	it( 'should have stop() and off() marked', function() {
-		var EventInfo = modules.eventinfo;
+	it( 'should have stop() and off() marked', () => {
+		const EventInfo = modules.eventinfo;
 
 
-		var event = new EventInfo( this, 'test' );
+		let event = new EventInfo( this, 'test' );
 
 
 		event.stop();
 		event.stop();
 		event.off();
 		event.off();
@@ -31,10 +31,10 @@ describe( 'EventInfo', function() {
 		expect( event.off.called ).to.be.true();
 		expect( event.off.called ).to.be.true();
 	} );
 	} );
 
 
-	it( 'should not mark "called" in future instances', function() {
-		var EventInfo = modules.eventinfo;
+	it( 'should not mark "called" in future instances', () => {
+		const EventInfo = modules.eventinfo;
 
 
-		var event = new EventInfo( this, 'test' );
+		let event = new EventInfo( this, 'test' );
 
 
 		event.stop();
 		event.stop();
 		event.off();
 		event.off();

+ 13 - 13
packages/ckeditor5-engine/tests/log/log.js

@@ -7,20 +7,20 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'log' );
-var spy;
+const modules = bender.amd.require( 'log' );
+let spy;
 
 
-beforeEach( function() {
+beforeEach( () => {
 	if ( spy ) {
 	if ( spy ) {
 		spy.restore();
 		spy.restore();
 	}
 	}
 } );
 } );
 
 
-describe( 'warn()', function() {
-	it( 'logs the message to the console using console.warn()', function() {
-		var log = modules.log;
-		var spy = sinon.stub( console, 'warn' );
-		var data = { bar: 1 };
+describe( 'warn()', () => {
+	it( 'logs the message to the console using console.warn()', () => {
+		let log = modules.log;
+		let spy = sinon.stub( console, 'warn' );
+		let data = { bar: 1 };
 
 
 		log.warn( 'foo', data );
 		log.warn( 'foo', data );
 
 
@@ -33,11 +33,11 @@ describe( 'warn()', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'error()', function() {
-	it( 'logs the message to the console using console.error()', function() {
-		var log = modules.log;
-		var spy = sinon.stub( console, 'error' );
-		var data = { bar: 1 };
+describe( 'error()', () => {
+	it( 'logs the message to the console using console.error()', () => {
+		let log = modules.log;
+		let spy = sinon.stub( console, 'error' );
+		let data = { bar: 1 };
 
 
 		log.error( 'foo', data );
 		log.error( 'foo', data );
 
 

+ 0 - 183
packages/ckeditor5-engine/tests/mvc/collection/collection.js

@@ -1,183 +0,0 @@
-/**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-var modules = bender.amd.require( 'collection', 'model' );
-
-bender.tools.createSinonSandbox();
-
-describe( 'add', function() {
-	it( 'should change the length and enable get', function() {
-		var Model = modules.model;
-
-		var box = getCollection();
-
-		expect( box ).to.have.length( 0 );
-
-		box.add( getItem() );
-
-		expect( box ).to.have.length( 1 );
-
-		expect( box.get( 0 ) ).to.be.an.instanceof( Model );
-	} );
-
-	it( 'should fire the "add" event', function() {
-		var spy = sinon.spy();
-
-		var box = getCollection();
-		box.on( 'add', spy );
-
-		var item = getItem();
-		box.add( item );
-
-		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item );
-	} );
-} );
-
-describe( 'get', function() {
-	it( 'should return null if index does not exist', function() {
-		var box = getCollection();
-		box.add( getItem() );
-
-		expect( box.get( 1 ) ).to.be.null;
-	} );
-} );
-
-describe( 'remove', function() {
-	it( 'should remove the model by index', function() {
-		var box = getCollection();
-		var item = getItem();
-
-		box.add( item );
-
-		expect( box ).to.have.length( 1 );
-
-		box.remove( 0 );
-
-		expect( box ).to.have.length( 0 );
-	} );
-
-	it( 'should remove the model by model', function() {
-		var box = getCollection();
-		var item = getItem();
-
-		box.add( item );
-
-		expect( box ).to.have.length( 1 );
-
-		box.remove( item );
-
-		expect( box ).to.have.length( 0 );
-	} );
-
-	it( 'should fire the "remove" event', function() {
-		var box = getCollection();
-		var item1 = getItem();
-		var item2 = getItem();
-
-		box.add( item1 );
-		box.add( item2 );
-
-		var spy = sinon.spy();
-
-		box.on( 'remove', spy );
-
-		box.remove( 1 );		// by index
-		box.remove( item1 );	// by model
-
-		sinon.assert.calledTwice( spy );
-		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item1 );
-		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item2 );
-	} );
-
-	it( 'should throw an error on invalid index', function() {
-		var CKEditorError = modules.ckeditorerror;
-
-		var box = getCollection();
-		box.add( getItem() );
-
-		expect( function() {
-			box.remove( 1 );
-		} ).to.throw( CKEditorError, /^collection-index-404/ );
-	} );
-
-	it( 'should throw an error on invalid model', function() {
-		var CKEditorError = modules.ckeditorerror;
-
-		var box = getCollection();
-		box.add( getItem() );
-
-		expect( function() {
-			box.remove( getItem() );
-		} ).to.throw( CKEditorError, /^collection-model-404/ );
-	} );
-} );
-
-describe( 'forEach', function() {
-	it( 'uses native forEach', function() {
-		var collection = getCollection();
-		collection.add( getItem() );
-
-		var spy = bender.sinon.spy( Array.prototype, 'forEach' );
-		var ctx = {};
-
-		collection.forEach( callback, ctx );
-
-		sinon.assert.calledWithExactly( spy, callback, ctx );
-
-		function callback() {}
-	} );
-} );
-
-describe( 'find', function() {
-	it( 'uses native find', function() {
-		var collection = getCollection();
-		var needl = getItem();
-
-		var spy = bender.sinon.stub( Array.prototype, 'find', function() {
-			return needl;
-		} );
-		var ctx = {};
-
-		var ret = collection.find( callback, ctx );
-
-		sinon.assert.calledWithExactly( spy, callback, ctx );
-		expect( ret ).to.equal( needl, 'ret value was forwarded' );
-
-		function callback() {}
-	} );
-} );
-
-describe( 'filter', function() {
-	it( 'uses native filter', function() {
-		var collection = getCollection();
-		var needl = getItem();
-
-		var spy = bender.sinon.stub( Array.prototype, 'filter', function() {
-			return needl;
-		} );
-		var ctx = {};
-
-		var ret = collection.filter( callback, ctx );
-
-		sinon.assert.calledWithExactly( spy, callback, ctx );
-		expect( ret ).to.equal( needl, 'ret value was forwarded' );
-
-		function callback() {}
-	} );
-} );
-
-function getCollection() {
-	var Collection = modules.collection;
-
-	return new Collection();
-}
-
-function getItem() {
-	var Model = modules.model;
-
-	return new Model();
-}

+ 0 - 254
packages/ckeditor5-engine/tests/mvc/collection/namedcollection.js

@@ -1,254 +0,0 @@
-/**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-var modules = bender.amd.require( 'namedcollection', 'model', 'ckeditorerror' );
-
-describe( 'add', function() {
-	it( 'changes the length and enables get', function() {
-		var box = getCollection();
-
-		expect( box ).to.have.length( 0 );
-
-		var item = getItem( 'foo' );
-		box.add( item );
-
-		expect( box ).to.have.length( 1 );
-
-		expect( box.get( 'foo' ) ).to.equal( item );
-	} );
-
-	it( 'fires the "add" event', function() {
-		var spy = sinon.spy();
-
-		var box = getCollection();
-		box.on( 'add', spy );
-
-		var item = getItem( 'foo' );
-		box.add( item );
-
-		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item );
-	} );
-
-	it( 'throws an error if model is not named', function() {
-		var Model = modules.model;
-		var CKEditorError = modules.ckeditorerror;
-		var box = getCollection();
-
-		expect( box ).to.have.length( 0 );
-
-		var item = new Model();
-
-		expect( function() {
-			box.add( item );
-		} ).to.throw( CKEditorError, /^namedcollection-add/ );
-	} );
-
-	it( 'throws an error if some model already exists under the same name', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var box = getCollection();
-
-		expect( box ).to.have.length( 0 );
-
-		box.add( getItem( 'foo' ) );
-
-		expect( function() {
-			box.add( getItem( 'foo' ) );
-		} ).to.throw( CKEditorError, /^namedcollection-add/ );
-	} );
-} );
-
-describe( 'get', function() {
-	it( 'should throw an error on invalid name', function() {
-		var box = getCollection();
-		box.add( getItem( 'foo' ) );
-
-		expect( box.get( 'bar' ) ).to.be.null;
-	} );
-} );
-
-describe( 'remove', function() {
-	it( 'should remove the model by name', function() {
-		var box = getCollection();
-		var item = getItem( 'foo' );
-
-		box.add( item );
-
-		expect( box ).to.have.length( 1 );
-
-		box.remove( 'foo' );
-
-		expect( box ).to.have.length( 0 );
-	} );
-
-	it( 'should remove the model by model', function() {
-		var box = getCollection();
-		var item = getItem( 'foo' );
-
-		box.add( item );
-
-		expect( box ).to.have.length( 1 );
-
-		box.remove( item );
-
-		expect( box ).to.have.length( 0 );
-	} );
-
-	it( 'should fire the "remove" event', function() {
-		var box = getCollection();
-		var item1 = getItem( 'foo' );
-		var item2 = getItem( 'bar' );
-
-		box.add( item1 );
-		box.add( item2 );
-
-		var spy = sinon.spy();
-
-		box.on( 'remove', spy );
-
-		box.remove( 'foo' ); // by name
-		box.remove( item2 ); // by model
-
-		sinon.assert.calledTwice( spy );
-		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item1 );
-		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item2 );
-	} );
-
-	it( 'should throw an error if model is not named', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var Model = modules.model;
-		var box = getCollection();
-
-		expect( function() {
-			box.remove( new Model() );
-		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
-	} );
-
-	it( 'should throw an error if model does not exist (by name)', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var box = getCollection();
-
-		expect( function() {
-			box.remove( 'foo' );
-		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
-	} );
-
-	it( 'should throw an error if model does not exist (by model)', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var box = getCollection();
-
-		expect( function() {
-			box.remove( getItem( 'foo' ) );
-		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
-	} );
-
-	it( 'should throw an error if model does not exist (by model)', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var box = getCollection();
-
-		expect( function() {
-			box.remove( getItem( 'foo' ) );
-		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
-	} );
-
-	it( 'should throw an error if a different model exists under the same name', function() {
-		var CKEditorError = modules.ckeditorerror;
-		var box = getCollection();
-		var item = getItem( 'foo' );
-
-		box.add( item );
-
-		expect( function() {
-			box.remove( getItem( 'foo' ) );
-		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
-	} );
-} );
-
-describe( 'forEach', function() {
-	it( 'executes callback for each item', function() {
-		var collection = getCollection();
-		collection.add( getItem( 'foo' ) );
-		collection.add( getItem( 'bar' ) );
-
-		var ctx = {};
-		var models = [];
-		var names = [];
-
-		collection.forEach( callback, ctx );
-		expect( models.sort() ).deep.equals( [ 'bar', 'foo' ] );
-		expect( names.sort() ).deep.equals( [ 'bar', 'foo' ] );
-
-		function callback( model, name ) {
-			expect( this ).to.equal( ctx ); /* jshint ignore:line */
-			models.push( model.name );
-			names.push( name );
-		}
-	} );
-} );
-
-describe( 'find', function() {
-	it( 'finds the right item', function() {
-		var Model = modules.model;
-
-		var collection = getCollection();
-		var needl = getItem( 'foo' );
-		collection.add( getItem( 'bar' ) );
-		collection.add( needl );
-		collection.add( getItem( 'bom' ) );
-
-		var ctx = {};
-
-		var ret = collection.find( callback, ctx );
-		expect( ret ).to.equal( needl );
-
-		function callback( model, name ) {
-			expect( this ).to.equal( ctx ); /* jshint ignore:line */
-			expect( model ).is.an.instanceof( Model );
-			expect( name ).to.be.a( 'string' );
-
-			return model.name == 'foo';
-		}
-	} );
-} );
-
-describe( 'filter', function() {
-	it( 'finds the right items', function() {
-		var Model = modules.model;
-
-		var collection = getCollection();
-		collection.add( getItem( 'bar' ) );
-		collection.add( getItem( 'foo' ) );
-		collection.add( getItem( 'bom' ) );
-
-		var ctx = {};
-
-		var ret = collection.filter( callback, ctx );
-		expect( ret ).to.have.keys( [ 'bar', 'bom' ] );
-
-		function callback( model, name ) {
-			expect( this ).to.equal( ctx ); /* jshint ignore:line */
-			expect( model ).is.an.instanceof( Model );
-			expect( name ).to.be.a( 'string' );
-
-			return model.name[ 0 ] == 'b';
-		}
-	} );
-} );
-
-function getCollection() {
-	var NamedCollection = modules.namedcollection;
-
-	return new NamedCollection();
-}
-
-function getItem( name ) {
-	var Model = modules.model;
-
-	var model = new Model();
-	model.name = name;
-
-	return model;
-}

+ 55 - 26
packages/ckeditor5-engine/tests/mvc/model/model.js

@@ -5,13 +5,13 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'model', 'eventinfo' );
+const modules = bender.amd.require( 'model', 'eventinfo', 'ckeditorerror' );
 
 
-var Car, car;
+let Car, car;
 
 
-describe( 'Model', function() {
-	beforeEach( 'Create a test model instance', function() {
-		var Model = modules.model;
+describe( 'Model', () => {
+	beforeEach( 'Create a test model instance', () => {
+		const Model = modules.model;
 
 
 		Car = class extends Model {};
 		Car = class extends Model {};
 
 
@@ -23,28 +23,28 @@ describe( 'Model', function() {
 
 
 	//////////
 	//////////
 
 
-	it( 'should set _attributes on creation', function() {
+	it( 'should set _attributes on creation', () => {
 		expect( car._attributes ).to.deep.equal( {
 		expect( car._attributes ).to.deep.equal( {
 			color: 'red',
 			color: 'red',
 			year: 2015
 			year: 2015
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should get correctly after set', function() {
+	it( 'should get correctly after set', () => {
 		car.color = 'blue';
 		car.color = 'blue';
 
 
 		expect( car.color ).to.equal( 'blue' );
 		expect( car.color ).to.equal( 'blue' );
 		expect( car._attributes.color ).to.equal( 'blue' );
 		expect( car._attributes.color ).to.equal( 'blue' );
 	} );
 	} );
 
 
-	it( 'should get correctly after setting _attributes', function() {
+	it( 'should get correctly after setting _attributes', () => {
 		car._attributes.color = 'blue';
 		car._attributes.color = 'blue';
 
 
 		expect( car.color ).to.equal( 'blue' );
 		expect( car.color ).to.equal( 'blue' );
 	} );
 	} );
 
 
-	it( 'should add properties on creation', function() {
-		var car = new Car( null, {
+	it( 'should add properties on creation', () => {
+		let car = new Car( null, {
 			prop: 1
 			prop: 1
 		} );
 		} );
 
 
@@ -53,8 +53,8 @@ describe( 'Model', function() {
 
 
 	//////////
 	//////////
 
 
-	describe( 'set', function() {
-		it( 'should work when passing an object', function() {
+	describe( 'set', () => {
+		it( 'should work when passing an object', () => {
 			car.set( {
 			car.set( {
 				color: 'blue',	// Override
 				color: 'blue',	// Override
 				wheels: 4,
 				wheels: 4,
@@ -69,7 +69,7 @@ describe( 'Model', function() {
 			} );
 			} );
 		} );
 		} );
 
 
-		it( 'should work when passing a key/value pair', function() {
+		it( 'should work when passing a key/value pair', () => {
 			car.set( 'color', 'blue' );
 			car.set( 'color', 'blue' );
 			car.set( 'wheels', 4 );
 			car.set( 'wheels', 4 );
 
 
@@ -80,13 +80,13 @@ describe( 'Model', function() {
 			} );
 			} );
 		} );
 		} );
 
 
-		it( 'should fire the "change" event', function() {
-			var EventInfo = modules.eventinfo;
+		it( 'should fire the "change" event', () => {
+			const EventInfo = modules.eventinfo;
 
 
-			var spy = sinon.spy();
-			var spyColor = sinon.spy();
-			var spyYear = sinon.spy();
-			var spyWheels = sinon.spy();
+			let spy = sinon.spy();
+			let spyColor = sinon.spy();
+			let spyYear = sinon.spy();
+			let spyWheels = sinon.spy();
 
 
 			car.on( 'change', spy );
 			car.on( 'change', spy );
 			car.on( 'change:color', spyColor );
 			car.on( 'change:color', spyColor );
@@ -119,9 +119,9 @@ describe( 'Model', function() {
 			sinon.assert.calledWithExactly( spyWheels, sinon.match.instanceOf( EventInfo ), 4, sinon.match.typeOf( 'undefined' ) );
 			sinon.assert.calledWithExactly( spyWheels, sinon.match.instanceOf( EventInfo ), 4, sinon.match.typeOf( 'undefined' ) );
 		} );
 		} );
 
 
-		it( 'should not fire the "change" event for the same attribute value', function() {
-			var spy = sinon.spy();
-			var spyColor = sinon.spy();
+		it( 'should not fire the "change" event for the same attribute value', () => {
+			let spy = sinon.spy();
+			let spyColor = sinon.spy();
 
 
 			car.on( 'change', spy );
 			car.on( 'change', spy );
 			car.on( 'change:color', spyColor );
 			car.on( 'change:color', spyColor );
@@ -134,15 +134,44 @@ describe( 'Model', function() {
 			sinon.assert.notCalled( spy );
 			sinon.assert.notCalled( spy );
 			sinon.assert.notCalled( spyColor );
 			sinon.assert.notCalled( spyColor );
 		} );
 		} );
+
+		it( 'should throw when overriding already existing property', () => {
+			const CKEditorError = modules.ckeditorerror;
+
+			car.normalProperty = 1;
+
+			expect( () => {
+				car.set( 'normalProperty', 2 );
+			} ).to.throw( CKEditorError, /^model-set-cannot-override/ );
+
+			expect( car ).to.have.property( 'normalProperty', 1 );
+		} );
+
+		it( 'should throw when overriding already existing property (in the prototype)', () => {
+			const CKEditorError = modules.ckeditorerror;
+			const Model = modules.model;
+
+			class Car extends Model {
+				method() {}
+			}
+
+			car = new Car();
+
+			expect( () => {
+				car.set( 'method', 2 );
+			} ).to.throw( CKEditorError, /^model-set-cannot-override/ );
+
+			expect( car.method ).to.be.a( 'function' );
+		} );
 	} );
 	} );
 
 
-	describe( 'extend', function() {
-		it( 'should create new Model based classes', function() {
-			var Model = modules.model;
+	describe( 'extend', () => {
+		it( 'should create new Model based classes', () => {
+			const Model = modules.model;
 
 
 			class Truck extends Car {}
 			class Truck extends Car {}
 
 
-			var truck = new Truck();
+			let truck = new Truck();
 
 
 			expect( truck ).to.be.an.instanceof( Car );
 			expect( truck ).to.be.an.instanceof( Car );
 			expect( truck ).to.be.an.instanceof( Model );
 			expect( truck ).to.be.an.instanceof( Model );

+ 8 - 8
packages/ckeditor5-engine/tests/plugin/plugin.js

@@ -5,20 +5,20 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'plugin', 'editor' );
-var editor;
+const modules = bender.amd.require( 'plugin', 'editor' );
+let editor;
 
 
-before( function() {
-	var Editor = modules.editor;
+before( () => {
+	const Editor = modules.editor;
 
 
 	editor = new Editor( document.body.appendChild( document.createElement( 'div' ) ) );
 	editor = new Editor( document.body.appendChild( document.createElement( 'div' ) ) );
 } );
 } );
 
 
-describe( 'constructor', function() {
-	it( 'should set the `editor` property', function() {
-		var Plugin = modules.plugin;
+describe( 'constructor', () => {
+	it( 'should set the `editor` property', () => {
+		const Plugin = modules.plugin;
 
 
-		var plugin = new Plugin( editor );
+		let plugin = new Plugin( editor );
 
 
 		expect( plugin ).to.have.property( 'editor' ).to.equal( editor );
 		expect( plugin ).to.have.property( 'editor' ).to.equal( editor );
 	} );
 	} );

+ 91 - 91
packages/ckeditor5-engine/tests/plugincollection/plugincollection.js

@@ -5,16 +5,16 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'plugincollection', 'plugin', 'editor', 'log' );
-var editor;
-var PluginA, PluginB;
+const modules = bender.amd.require( 'plugincollection', 'plugin', 'editor', 'log' );
+let editor;
+let PluginA, PluginB;
 class TestError extends Error {}
 class TestError extends Error {}
 
 
 bender.tools.createSinonSandbox();
 bender.tools.createSinonSandbox();
 
 
-before( function() {
-	var Editor = modules.editor;
-	var Plugin = modules.plugin;
+before( () => {
+	const Editor = modules.editor;
+	const Plugin = modules.plugin;
 
 
 	PluginA = class extends Plugin {};
 	PluginA = class extends Plugin {};
 	PluginB = class extends Plugin {};
 	PluginB = class extends Plugin {};
@@ -24,60 +24,60 @@ before( function() {
 
 
 // Create fake plugins that will be used on tests.
 // Create fake plugins that will be used on tests.
 
 
-CKEDITOR.define( 'plugin!A', function() {
+CKEDITOR.define( 'plugin!A', () => {
 	return PluginA;
 	return PluginA;
 } );
 } );
 
 
-CKEDITOR.define( 'plugin!B', function() {
+CKEDITOR.define( 'plugin!B', () => {
 	return PluginB;
 	return PluginB;
 } );
 } );
 
 
-CKEDITOR.define( 'plugin!C', [ 'plugin', 'plugin!B' ], function( Plugin ) {
+CKEDITOR.define( 'plugin!C', [ 'plugin', 'plugin!B' ], ( Plugin ) => {
 	return class extends Plugin {};
 	return class extends Plugin {};
 } );
 } );
 
 
-CKEDITOR.define( 'plugin!D', [ 'plugin', 'plugin!A', 'plugin!C' ], function( Plugin ) {
+CKEDITOR.define( 'plugin!D', [ 'plugin', 'plugin!A', 'plugin!C' ], ( Plugin ) => {
 	return class extends Plugin {};
 	return class extends Plugin {};
 } );
 } );
 
 
-CKEDITOR.define( 'plugin!E', [ 'plugin', 'plugin!F' ], function( Plugin ) {
+CKEDITOR.define( 'plugin!E', [ 'plugin', 'plugin!F' ], ( Plugin ) => {
 	return class extends Plugin {};
 	return class extends Plugin {};
 } );
 } );
 
 
-CKEDITOR.define( 'plugin!F', [ 'plugin', 'plugin!E' ], function( Plugin ) {
+CKEDITOR.define( 'plugin!F', [ 'plugin', 'plugin!E' ], ( Plugin ) => {
 	return class extends Plugin {};
 	return class extends Plugin {};
 } );
 } );
 
 
-CKEDITOR.define( 'plugin!G', function() {
+CKEDITOR.define( 'plugin!G', () => {
 	throw new TestError( 'Some error inside a plugin' );
 	throw new TestError( 'Some error inside a plugin' );
 } );
 } );
 
 
-CKEDITOR.define( 'plugin!H', [ 'plugin', 'plugin!H/a' ], function( Plugin ) {
+CKEDITOR.define( 'plugin!H', [ 'plugin', 'plugin!H/a' ], ( Plugin ) => {
 	return class extends Plugin {};
 	return class extends Plugin {};
 } );
 } );
 
 
-var spies = {};
+let spies = {};
 // Note: This is NOT a plugin.
 // Note: This is NOT a plugin.
-CKEDITOR.define( 'plugin!H/a', [ 'plugin!H/a/b' ], function() {
+CKEDITOR.define( 'plugin!H/a', [ 'plugin!H/a/b' ], () => {
 	return ( spies[ 'plugin!H/a' ] = sinon.spy() );
 	return ( spies[ 'plugin!H/a' ] = sinon.spy() );
 } );
 } );
 
 
 // Note: This is NOT a plugin.
 // Note: This is NOT a plugin.
-CKEDITOR.define( 'plugin!H/a/b', [ 'c' ], function() {
+CKEDITOR.define( 'plugin!H/a/b', [ 'c' ], () => {
 	return ( spies[ 'plugin!H/a/b' ] = sinon.spy() );
 	return ( spies[ 'plugin!H/a/b' ] = sinon.spy() );
 } );
 } );
 
 
 // Note: This is NOT a plugin.
 // Note: This is NOT a plugin.
-CKEDITOR.define( 'c', function() {
+CKEDITOR.define( 'c', () => {
 	return ( spies.c = sinon.spy() );
 	return ( spies.c = sinon.spy() );
 } );
 } );
 
 
-CKEDITOR.define( 'plugin!I', [ 'plugin', 'plugin!J' ], function( Plugin ) {
+CKEDITOR.define( 'plugin!I', [ 'plugin', 'plugin!J' ], ( Plugin ) => {
 	return class extends Plugin {};
 	return class extends Plugin {};
 } );
 } );
 
 
 // Note: This is NOT a plugin.
 // Note: This is NOT a plugin.
-CKEDITOR.define( 'plugin!J', function() {
+CKEDITOR.define( 'plugin!J', () => {
 	return function() {
 	return function() {
 		return ( spies.jSpy = sinon.spy() );
 		return ( spies.jSpy = sinon.spy() );
 	};
 	};
@@ -85,36 +85,36 @@ CKEDITOR.define( 'plugin!J', function() {
 
 
 /////////////
 /////////////
 
 
-describe( 'load', function() {
-	it( 'should not fail when trying to load 0 plugins (empty string)', function() {
-		var PluginCollection = modules.plugincollection;
+describe( 'load', () => {
+	it( 'should not fail when trying to load 0 plugins (empty string)', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( '' )
 		return plugins.load( '' )
-			.then( function() {
+			.then( () => {
 				expect( plugins.length ).to.equal( 0 );
 				expect( plugins.length ).to.equal( 0 );
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should not fail when trying to load 0 plugins (undefined)', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should not fail when trying to load 0 plugins (undefined)', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load()
 		return plugins.load()
-			.then( function() {
+			.then( () => {
 				expect( plugins.length ).to.equal( 0 );
 				expect( plugins.length ).to.equal( 0 );
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should add collection items for loaded plugins', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should add collection items for loaded plugins', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( 'A,B' )
 		return plugins.load( 'A,B' )
-			.then( function() {
+			.then( () => {
 				expect( plugins.length ).to.equal( 2 );
 				expect( plugins.length ).to.equal( 2 );
 
 
 				expect( plugins.get( 'A' ) ).to.be.an.instanceof( PluginA );
 				expect( plugins.get( 'A' ) ).to.be.an.instanceof( PluginA );
@@ -122,14 +122,14 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should load dependency plugins', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should load dependency plugins', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
-		var spy = sinon.spy( plugins, 'add' );
+		let plugins = new PluginCollection( editor );
+		let spy = sinon.spy( plugins, 'add' );
 
 
 		return plugins.load( 'A,C' )
 		return plugins.load( 'A,C' )
-			.then( function( loadedPlugins ) {
+			.then( ( loadedPlugins ) => {
 				expect( plugins.length ).to.equal( 3 );
 				expect( plugins.length ).to.equal( 3 );
 
 
 				expect( getPluginNamesFromSpy( spy ) ).to.deep.equal( [ 'A', 'B', 'C' ], 'order by plugins.add()' );
 				expect( getPluginNamesFromSpy( spy ) ).to.deep.equal( [ 'A', 'B', 'C' ], 'order by plugins.add()' );
@@ -137,14 +137,14 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should be ok when dependencies are loaded first', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should be ok when dependencies are loaded first', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
-		var spy = sinon.spy( plugins, 'add' );
+		let plugins = new PluginCollection( editor );
+		let spy = sinon.spy( plugins, 'add' );
 
 
 		return plugins.load( 'A,B,C' )
 		return plugins.load( 'A,B,C' )
-			.then( function( loadedPlugins ) {
+			.then( ( loadedPlugins ) => {
 				expect( plugins.length ).to.equal( 3 );
 				expect( plugins.length ).to.equal( 3 );
 
 
 				expect( getPluginNamesFromSpy( spy ) ).to.deep.equal( [ 'A', 'B', 'C' ], 'order by plugins.add()' );
 				expect( getPluginNamesFromSpy( spy ) ).to.deep.equal( [ 'A', 'B', 'C' ], 'order by plugins.add()' );
@@ -152,14 +152,14 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should load deep dependency plugins', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should load deep dependency plugins', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
-		var spy = sinon.spy( plugins, 'add' );
+		let plugins = new PluginCollection( editor );
+		let spy = sinon.spy( plugins, 'add' );
 
 
 		return plugins.load( 'D' )
 		return plugins.load( 'D' )
-			.then( function( loadedPlugins ) {
+			.then( ( loadedPlugins ) => {
 				expect( plugins.length ).to.equal( 4 );
 				expect( plugins.length ).to.equal( 4 );
 
 
 				// The order must have dependencies first.
 				// The order must have dependencies first.
@@ -168,14 +168,14 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should handle cross dependency plugins', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should handle cross dependency plugins', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
-		var spy = sinon.spy( plugins, 'add' );
+		let plugins = new PluginCollection( editor );
+		let spy = sinon.spy( plugins, 'add' );
 
 
 		return plugins.load( 'A,E' )
 		return plugins.load( 'A,E' )
-			.then( function( loadedPlugins ) {
+			.then( ( loadedPlugins ) => {
 				expect( plugins.length ).to.equal( 3 );
 				expect( plugins.length ).to.equal( 3 );
 
 
 				// The order must have dependencies first.
 				// The order must have dependencies first.
@@ -184,37 +184,37 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should set the `editor` property on loaded plugins', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should set the `editor` property on loaded plugins', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( 'A,B' )
 		return plugins.load( 'A,B' )
-			.then( function() {
+			.then( () => {
 				expect( plugins.get( 'A' ).editor ).to.equal( editor );
 				expect( plugins.get( 'A' ).editor ).to.equal( editor );
 				expect( plugins.get( 'B' ).editor ).to.equal( editor );
 				expect( plugins.get( 'B' ).editor ).to.equal( editor );
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should set the `path` property on loaded plugins', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should set the `path` property on loaded plugins', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( 'A,B' )
 		return plugins.load( 'A,B' )
-			.then( function() {
+			.then( () => {
 				expect( plugins.get( 'A' ).path ).to.equal( CKEDITOR.getPluginPath( 'A' ) );
 				expect( plugins.get( 'A' ).path ).to.equal( CKEDITOR.getPluginPath( 'A' ) );
 				expect( plugins.get( 'B' ).path ).to.equal( CKEDITOR.getPluginPath( 'B' ) );
 				expect( plugins.get( 'B' ).path ).to.equal( CKEDITOR.getPluginPath( 'B' ) );
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should set the `deps` property on loaded plugins', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should set the `deps` property on loaded plugins', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( 'A,D' )
 		return plugins.load( 'A,D' )
-			.then( function() {
+			.then( () => {
 				expect( plugins.get( 'A' ).deps ).to.deep.equal( [] );
 				expect( plugins.get( 'A' ).deps ).to.deep.equal( [] );
 				expect( plugins.get( 'B' ).deps ).to.deep.equal( [] );
 				expect( plugins.get( 'B' ).deps ).to.deep.equal( [] );
 				expect( plugins.get( 'C' ).deps ).to.deep.equal( [ 'B' ] );
 				expect( plugins.get( 'C' ).deps ).to.deep.equal( [ 'B' ] );
@@ -222,20 +222,20 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should reject on invalid plugin names (forward require.js loading error)', function() {
-		var PluginCollection = modules.plugincollection;
-		var log = modules.log;
+	it( 'should reject on invalid plugin names (forward require.js loading error)', () => {
+		const PluginCollection = modules.plugincollection;
+		let log = modules.log;
 
 
-		var logSpy = bender.sinon.stub( log, 'error' );
+		let logSpy = bender.sinon.stub( log, 'error' );
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( 'A,BAD,B' )
 		return plugins.load( 'A,BAD,B' )
 			// Throw here, so if by any chance plugins.load() was resolved correctly catch() will be stil executed.
 			// Throw here, so if by any chance plugins.load() was resolved correctly catch() will be stil executed.
-			.then( function() {
+			.then( () => {
 				throw new Error( 'Test error: this promise should not be resolved successfully' );
 				throw new Error( 'Test error: this promise should not be resolved successfully' );
 			} )
 			} )
-			.catch( function( err ) {
+			.catch( ( err ) => {
 				expect( err ).to.be.an.instanceof( Error );
 				expect( err ).to.be.an.instanceof( Error );
 				// Make sure it's the Require.JS error, not the one thrown above.
 				// Make sure it's the Require.JS error, not the one thrown above.
 				expect( err.message ).to.match( /^Script error for:/ );
 				expect( err.message ).to.match( /^Script error for:/ );
@@ -245,20 +245,20 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should reject on broken plugins (forward the error thrown in a plugin)', function() {
-		var PluginCollection = modules.plugincollection;
-		var log = modules.log;
+	it( 'should reject on broken plugins (forward the error thrown in a plugin)', () => {
+		const PluginCollection = modules.plugincollection;
+		let log = modules.log;
 
 
-		var logSpy = bender.sinon.stub( log, 'error' );
+		let logSpy = bender.sinon.stub( log, 'error' );
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( 'A,G,B' )
 		return plugins.load( 'A,G,B' )
 			// Throw here, so if by any chance plugins.load() was resolved correctly catch() will be stil executed.
 			// Throw here, so if by any chance plugins.load() was resolved correctly catch() will be stil executed.
-			.then( function() {
+			.then( () => {
 				throw new Error( 'Test error: this promise should not be resolved successfully' );
 				throw new Error( 'Test error: this promise should not be resolved successfully' );
 			} )
 			} )
-			.catch( function( err ) {
+			.catch( ( err ) => {
 				expect( err ).to.be.an.instanceof( TestError );
 				expect( err ).to.be.an.instanceof( TestError );
 				expect( err ).to.have.property( 'message', 'Some error inside a plugin' );
 				expect( err ).to.have.property( 'message', 'Some error inside a plugin' );
 
 
@@ -267,14 +267,14 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should load `deps` which are not plugins', function() {
-		var PluginCollection = modules.plugincollection;
+	it( 'should load `deps` which are not plugins', () => {
+		const PluginCollection = modules.plugincollection;
 
 
-		var plugins = new PluginCollection( editor );
+		let plugins = new PluginCollection( editor );
 		expect( spies ).to.be.empty;
 		expect( spies ).to.be.empty;
 
 
 		return plugins.load( 'H' )
 		return plugins.load( 'H' )
-			.then( function() {
+			.then( () => {
 				expect( plugins.get( 'H' ).deps ).to.deep.equal( [ 'H/a' ] );
 				expect( plugins.get( 'H' ).deps ).to.deep.equal( [ 'H/a' ] );
 
 
 				// Non–plugin dependencies should be loaded (spy exists)...
 				// Non–plugin dependencies should be loaded (spy exists)...
@@ -291,9 +291,9 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should load instances of Plugin only', function() {
-		var PluginCollection = modules.plugincollection;
-		var plugins = new PluginCollection( editor );
+	it( 'should load instances of Plugin only', () => {
+		const PluginCollection = modules.plugincollection;
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( 'I' )
 		return plugins.load( 'I' )
 			.then( () => {
 			.then( () => {
@@ -304,9 +304,9 @@ describe( 'load', function() {
 			} );
 			} );
 	} );
 	} );
 
 
-	it( 'should cancel loading module which looks like a plugin but is a normal module', function() {
-		var PluginCollection = modules.plugincollection;
-		var plugins = new PluginCollection( editor );
+	it( 'should cancel loading module which looks like a plugin but is a normal module', () => {
+		const PluginCollection = modules.plugincollection;
+		let plugins = new PluginCollection( editor );
 
 
 		return plugins.load( 'J' )
 		return plugins.load( 'J' )
 			.then( () => {
 			.then( () => {
@@ -320,13 +320,13 @@ describe( 'load', function() {
 } );
 } );
 
 
 function getPluginNamesFromSpy( addSpy ) {
 function getPluginNamesFromSpy( addSpy ) {
-	return addSpy.args.map( function( arg ) {
+	return addSpy.args.map( ( arg ) => {
 		return arg[ 0 ].name;
 		return arg[ 0 ].name;
 	} );
 	} );
 }
 }
 
 
 function getPluginNames( plugins ) {
 function getPluginNames( plugins ) {
-	return plugins.map( function( arg ) {
+	return plugins.map( ( arg ) => {
 		return arg.name;
 		return arg.name;
 	} );
 	} );
 }
 }

+ 64 - 64
packages/ckeditor5-engine/tests/ui/domemittermixin.js

@@ -8,14 +8,14 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'utils', 'ui/domemittermixin', 'emittermixin' );
-var emitter, domEmitter, node;
+const modules = bender.amd.require( 'utils', 'ui/domemittermixin', 'emittermixin' );
+let emitter, domEmitter, node;
 
 
 bender.tools.createSinonSandbox();
 bender.tools.createSinonSandbox();
 
 
-var getEmitterInstance = () => modules.utils.extend( {}, modules.emittermixin );
-var getDOMEmitterInstance = () => modules.utils.extend( {}, modules[ 'ui/domemittermixin' ] );
-var getDOMNodeInstance = () => document.createElement( 'div' );
+let getEmitterInstance = () => modules.utils.extend( {}, modules.emittermixin );
+let getDOMEmitterInstance = () => modules.utils.extend( {}, modules[ 'ui/domemittermixin' ] );
+let getDOMNodeInstance = () => document.createElement( 'div' );
 
 
 function updateEmitterInstance() {
 function updateEmitterInstance() {
 	emitter = getEmitterInstance();
 	emitter = getEmitterInstance();
@@ -33,9 +33,9 @@ beforeEach( updateEmitterInstance );
 beforeEach( updateDOMEmitterInstance );
 beforeEach( updateDOMEmitterInstance );
 beforeEach( updateDOMNodeInstance );
 beforeEach( updateDOMNodeInstance );
 
 
-describe( 'listenTo', function() {
-	it( 'should listen to EmitterMixin events', function() {
-		var spy = bender.sinon.spy();
+describe( 'listenTo', () => {
+	it( 'should listen to EmitterMixin events', () => {
+		let spy = bender.sinon.spy();
 
 
 		domEmitter.listenTo( emitter, 'test', spy );
 		domEmitter.listenTo( emitter, 'test', spy );
 		emitter.fire( 'test' );
 		emitter.fire( 'test' );
@@ -43,22 +43,22 @@ describe( 'listenTo', function() {
 		sinon.assert.calledOnce( spy );
 		sinon.assert.calledOnce( spy );
 	} );
 	} );
 
 
-	it( 'should listen to native DOM events', function() {
-		var spy = bender.sinon.spy();
+	it( 'should listen to native DOM events', () => {
+		let spy = bender.sinon.spy();
 
 
 		domEmitter.listenTo( node, 'test', spy );
 		domEmitter.listenTo( node, 'test', spy );
 
 
-		var event = new Event( 'test' );
+		let event = new Event( 'test' );
 		node.dispatchEvent( event );
 		node.dispatchEvent( event );
 
 
 		sinon.assert.calledOnce( spy );
 		sinon.assert.calledOnce( spy );
 	} );
 	} );
 } );
 } );
 
 
-describe( 'stopListening', function() {
-	it( 'should stop listening to a specific event callback', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
+describe( 'stopListening', () => {
+	it( 'should stop listening to a specific event callback', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
 
 
 		domEmitter.listenTo( node, 'event1', spy1 );
 		domEmitter.listenTo( node, 'event1', spy1 );
 		domEmitter.listenTo( node, 'event2', spy2 );
 		domEmitter.listenTo( node, 'event2', spy2 );
@@ -75,10 +75,10 @@ describe( 'stopListening', function() {
 		sinon.assert.calledTwice( spy2 );
 		sinon.assert.calledTwice( spy2 );
 	} );
 	} );
 
 
-	it( 'should stop listening to an specific event', function() {
-		var spy1a = bender.sinon.spy();
-		var spy1b = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
+	it( 'should stop listening to an specific event', () => {
+		let spy1a = bender.sinon.spy();
+		let spy1b = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
 
 
 		domEmitter.listenTo( node, 'event1', spy1a );
 		domEmitter.listenTo( node, 'event1', spy1a );
 		domEmitter.listenTo( node, 'event1', spy1b );
 		domEmitter.listenTo( node, 'event1', spy1b );
@@ -101,9 +101,9 @@ describe( 'stopListening', function() {
 		sinon.assert.calledTwice( spy2 );
 		sinon.assert.calledTwice( spy2 );
 	} );
 	} );
 
 
-	it( 'should stop listening to all events from a specific node', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
+	it( 'should stop listening to all events from a specific node', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
 
 
 		domEmitter.listenTo( node, 'event1', spy1 );
 		domEmitter.listenTo( node, 'event1', spy1 );
 		domEmitter.listenTo( node, 'event2', spy2 );
 		domEmitter.listenTo( node, 'event2', spy2 );
@@ -120,12 +120,12 @@ describe( 'stopListening', function() {
 		sinon.assert.calledOnce( spy2 );
 		sinon.assert.calledOnce( spy2 );
 	} );
 	} );
 
 
-	it( 'should stop listening to everything', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
+	it( 'should stop listening to everything', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
 
 
-		var node1 = getDOMNodeInstance();
-		var node2 = getDOMNodeInstance();
+		let node1 = getDOMNodeInstance();
+		let node2 = getDOMNodeInstance();
 
 
 		domEmitter.listenTo( node1, 'event1', spy1 );
 		domEmitter.listenTo( node1, 'event1', spy1 );
 		domEmitter.listenTo( node2, 'event2', spy2 );
 		domEmitter.listenTo( node2, 'event2', spy2 );
@@ -146,11 +146,11 @@ describe( 'stopListening', function() {
 		expect( domEmitter ).to.not.have.property( '_listeningTo' );
 		expect( domEmitter ).to.not.have.property( '_listeningTo' );
 	} );
 	} );
 
 
-	it( 'should not stop other nodes when a non-listened node is provided', function() {
-		var spy = bender.sinon.spy();
+	it( 'should not stop other nodes when a non-listened node is provided', () => {
+		let spy = bender.sinon.spy();
 
 
-		var node1 = getDOMNodeInstance();
-		var node2 = getDOMNodeInstance();
+		let node1 = getDOMNodeInstance();
+		let node2 = getDOMNodeInstance();
 
 
 		domEmitter.listenTo( node1, 'test', spy );
 		domEmitter.listenTo( node1, 'test', spy );
 
 
@@ -161,14 +161,14 @@ describe( 'stopListening', function() {
 		sinon.assert.called( spy );
 		sinon.assert.called( spy );
 	} );
 	} );
 
 
-	it( 'should pass DOM Event data to the listener', function() {
-		var spy = bender.sinon.spy();
+	it( 'should pass DOM Event data to the listener', () => {
+		let spy = bender.sinon.spy();
 
 
-		var node = getDOMNodeInstance();
+		let node = getDOMNodeInstance();
 
 
 		domEmitter.listenTo( node, 'click', spy );
 		domEmitter.listenTo( node, 'click', spy );
 
 
-		var mouseEvent = new MouseEvent( 'click', {
+		let mouseEvent = new MouseEvent( 'click', {
 			screenX: 10,
 			screenX: 10,
 			screenY: 20
 			screenY: 20
 		} );
 		} );
@@ -179,14 +179,14 @@ describe( 'stopListening', function() {
 		expect( spy.args[ 0 ][ 1 ] ).to.be.equal( mouseEvent );
 		expect( spy.args[ 0 ][ 1 ] ).to.be.equal( mouseEvent );
 	} );
 	} );
 
 
-	it( 'should detach native DOM event listener proxy, specific event', function() {
-		var spy1a = bender.sinon.spy();
-		var spy1b = bender.sinon.spy();
+	it( 'should detach native DOM event listener proxy, specific event', () => {
+		let spy1a = bender.sinon.spy();
+		let spy1b = bender.sinon.spy();
 
 
 		domEmitter.listenTo( node, 'test', spy1a );
 		domEmitter.listenTo( node, 'test', spy1a );
 
 
-		var proxyEmitter = domEmitter._getProxyEmitter( node );
-		var spy2 = bender.sinon.spy( proxyEmitter, 'fire' );
+		let proxyEmitter = domEmitter._getProxyEmitter( node );
+		let spy2 = bender.sinon.spy( proxyEmitter, 'fire' );
 
 
 		node.dispatchEvent( new Event( 'test' ) );
 		node.dispatchEvent( new Event( 'test' ) );
 
 
@@ -210,16 +210,16 @@ describe( 'stopListening', function() {
 		sinon.assert.calledTwice( spy2 );
 		sinon.assert.calledTwice( spy2 );
 	} );
 	} );
 
 
-	it( 'should detach native DOM event listener proxy, specific callback', function() {
-		var spy1a = bender.sinon.spy();
-		var spy1b = bender.sinon.spy();
-		var spy1c = bender.sinon.spy();
+	it( 'should detach native DOM event listener proxy, specific callback', () => {
+		let spy1a = bender.sinon.spy();
+		let spy1b = bender.sinon.spy();
+		let spy1c = bender.sinon.spy();
 
 
 		domEmitter.listenTo( node, 'test', spy1a );
 		domEmitter.listenTo( node, 'test', spy1a );
 		domEmitter.listenTo( node, 'test', spy1b );
 		domEmitter.listenTo( node, 'test', spy1b );
 
 
-		var proxyEmitter = domEmitter._getProxyEmitter( node );
-		var spy2 = bender.sinon.spy( proxyEmitter, 'fire' );
+		let proxyEmitter = domEmitter._getProxyEmitter( node );
+		let spy2 = bender.sinon.spy( proxyEmitter, 'fire' );
 
 
 		node.dispatchEvent( new Event( 'test' ) );
 		node.dispatchEvent( new Event( 'test' ) );
 
 
@@ -253,17 +253,17 @@ describe( 'stopListening', function() {
 		sinon.assert.calledThrice( spy2 );
 		sinon.assert.calledThrice( spy2 );
 	} );
 	} );
 
 
-	it( 'should detach native DOM event listener proxy, specific emitter', function() {
-		var spy1a = bender.sinon.spy();
-		var spy1b = bender.sinon.spy();
-		var spy1c = bender.sinon.spy();
-		var spy1d = bender.sinon.spy();
+	it( 'should detach native DOM event listener proxy, specific emitter', () => {
+		let spy1a = bender.sinon.spy();
+		let spy1b = bender.sinon.spy();
+		let spy1c = bender.sinon.spy();
+		let spy1d = bender.sinon.spy();
 
 
 		domEmitter.listenTo( node, 'test1', spy1a );
 		domEmitter.listenTo( node, 'test1', spy1a );
 		domEmitter.listenTo( node, 'test2', spy1b );
 		domEmitter.listenTo( node, 'test2', spy1b );
 
 
-		var proxyEmitter = domEmitter._getProxyEmitter( node );
-		var spy2 = bender.sinon.spy( proxyEmitter, 'fire' );
+		let proxyEmitter = domEmitter._getProxyEmitter( node );
+		let spy2 = bender.sinon.spy( proxyEmitter, 'fire' );
 
 
 		node.dispatchEvent( new Event( 'test1' ) );
 		node.dispatchEvent( new Event( 'test1' ) );
 		node.dispatchEvent( new Event( 'test2' ) );
 		node.dispatchEvent( new Event( 'test2' ) );
@@ -286,8 +286,8 @@ describe( 'stopListening', function() {
 		domEmitter.listenTo( node, 'test2', spy1d );
 		domEmitter.listenTo( node, 'test2', spy1d );
 
 
 		// Old proxy emitter died when stopped listening to the node.
 		// Old proxy emitter died when stopped listening to the node.
-		var proxyEmitter2 = domEmitter._getProxyEmitter( node );
-		var spy3 = bender.sinon.spy( proxyEmitter2, 'fire' );
+		let proxyEmitter2 = domEmitter._getProxyEmitter( node );
+		let spy3 = bender.sinon.spy( proxyEmitter2, 'fire' );
 
 
 		node.dispatchEvent( new Event( 'test1' ) );
 		node.dispatchEvent( new Event( 'test1' ) );
 		node.dispatchEvent( new Event( 'test2' ) );
 		node.dispatchEvent( new Event( 'test2' ) );
@@ -302,17 +302,17 @@ describe( 'stopListening', function() {
 		sinon.assert.calledTwice( spy3 );
 		sinon.assert.calledTwice( spy3 );
 	} );
 	} );
 
 
-	it( 'should detach native DOM event listener proxy, everything', function() {
-		var spy1a = bender.sinon.spy();
-		var spy1b = bender.sinon.spy();
-		var spy1c = bender.sinon.spy();
-		var spy1d = bender.sinon.spy();
+	it( 'should detach native DOM event listener proxy, everything', () => {
+		let spy1a = bender.sinon.spy();
+		let spy1b = bender.sinon.spy();
+		let spy1c = bender.sinon.spy();
+		let spy1d = bender.sinon.spy();
 
 
 		domEmitter.listenTo( node, 'test1', spy1a );
 		domEmitter.listenTo( node, 'test1', spy1a );
 		domEmitter.listenTo( node, 'test2', spy1b );
 		domEmitter.listenTo( node, 'test2', spy1b );
 
 
-		var proxyEmitter = domEmitter._getProxyEmitter( node );
-		var spy2 = bender.sinon.spy( proxyEmitter, 'fire' );
+		let proxyEmitter = domEmitter._getProxyEmitter( node );
+		let spy2 = bender.sinon.spy( proxyEmitter, 'fire' );
 
 
 		node.dispatchEvent( new Event( 'test1' ) );
 		node.dispatchEvent( new Event( 'test1' ) );
 		node.dispatchEvent( new Event( 'test2' ) );
 		node.dispatchEvent( new Event( 'test2' ) );
@@ -335,8 +335,8 @@ describe( 'stopListening', function() {
 		domEmitter.listenTo( node, 'test2', spy1d );
 		domEmitter.listenTo( node, 'test2', spy1d );
 
 
 		// Old proxy emitter died when stopped listening to the node.
 		// Old proxy emitter died when stopped listening to the node.
-		var proxyEmitter2 = domEmitter._getProxyEmitter( node );
-		var spy3 = bender.sinon.spy( proxyEmitter2, 'fire' );
+		let proxyEmitter2 = domEmitter._getProxyEmitter( node );
+		let spy3 = bender.sinon.spy( proxyEmitter2, 'fire' );
 
 
 		node.dispatchEvent( new Event( 'test1' ) );
 		node.dispatchEvent( new Event( 'test1' ) );
 		node.dispatchEvent( new Event( 'test2' ) );
 		node.dispatchEvent( new Event( 'test2' ) );

+ 20 - 20
packages/ckeditor5-engine/tests/ui/region.js

@@ -8,29 +8,29 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'ckeditor', 'ui/region', 'ui/view', 'collection' );
+const modules = bender.amd.require( 'ckeditor', 'ui/region', 'ui/view', 'collection' );
 
 
 bender.tools.createSinonSandbox();
 bender.tools.createSinonSandbox();
 
 
-var TestViewA, TestViewB;
-var region, el;
+let TestViewA, TestViewB;
+let region, el;
 
 
 beforeEach( createRegionInstance );
 beforeEach( createRegionInstance );
 
 
-describe( 'constructor', function() {
-	it( 'accepts name and element', function() {
+describe( 'constructor', () => {
+	it( 'accepts name and element', () => {
 		expect( region ).to.have.property( 'name', 'foo' );
 		expect( region ).to.have.property( 'name', 'foo' );
 		expect( region ).to.have.property( 'el', el );
 		expect( region ).to.have.property( 'el', el );
 	} );
 	} );
 } );
 } );
 
 
-describe( 'views collection', function() {
-	it( 'is an instance of Collection', function() {
-		var Collection = modules.collection;
+describe( 'views collection', () => {
+	it( 'is an instance of Collection', () => {
+		const Collection = modules.collection;
 		expect( region.views ).to.be.an.instanceof( Collection );
 		expect( region.views ).to.be.an.instanceof( Collection );
 	} );
 	} );
 
 
-	it( 'updates DOM when adding views', function() {
+	it( 'updates DOM when adding views', () => {
 		expect( region.el.childNodes.length ).to.be.equal( 0 );
 		expect( region.el.childNodes.length ).to.be.equal( 0 );
 
 
 		region.views.add( new TestViewA() );
 		region.views.add( new TestViewA() );
@@ -40,9 +40,9 @@ describe( 'views collection', function() {
 		expect( region.el.childNodes.length ).to.be.equal( 2 );
 		expect( region.el.childNodes.length ).to.be.equal( 2 );
 	} );
 	} );
 
 
-	it( 'updates DOM when removing views', function() {
-		var viewA = new TestViewA();
-		var viewB = new TestViewB();
+	it( 'updates DOM when removing views', () => {
+		let viewA = new TestViewA();
+		let viewB = new TestViewB();
 
 
 		region.views.add( viewA );
 		region.views.add( viewA );
 		region.views.add( viewB );
 		region.views.add( viewB );
@@ -60,10 +60,10 @@ describe( 'views collection', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'destroy', function() {
-	it( 'destroys the region', function() {
+describe( 'destroy', () => {
+	it( 'destroys the region', () => {
 		// Append the region's element to some container.
 		// Append the region's element to some container.
-		var container = document.createElement( 'div' );
+		let container = document.createElement( 'div' );
 		container.appendChild( el );
 		container.appendChild( el );
 		expect( el.parentNode ).to.be.equal( container );
 		expect( el.parentNode ).to.be.equal( container );
 
 
@@ -74,9 +74,9 @@ describe( 'destroy', function() {
 		expect( region.el ).to.be.null;
 		expect( region.el ).to.be.null;
 	} );
 	} );
 
 
-	it( 'destroys children views', function() {
-		var view = new TestViewA();
-		var spy = bender.sinon.spy( view, 'destroy' );
+	it( 'destroys children views', () => {
+		let view = new TestViewA();
+		let spy = bender.sinon.spy( view, 'destroy' );
 
 
 		// Append the view to the region.
 		// Append the view to the region.
 		region.views.add( view );
 		region.views.add( view );
@@ -90,8 +90,8 @@ describe( 'destroy', function() {
 } );
 } );
 
 
 function createRegionInstance() {
 function createRegionInstance() {
-	var Region = modules[ 'ui/region' ];
-	var View = modules[ 'ui/view' ];
+	const Region = modules[ 'ui/region' ];
+	const View = modules[ 'ui/view' ];
 
 
 	class A extends View {
 	class A extends View {
 		constructor() {
 		constructor() {

+ 32 - 32
packages/ckeditor5-engine/tests/ui/template.js

@@ -8,15 +8,15 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'ckeditor', 'ui/view', 'ui/template' );
-var Template;
+const modules = bender.amd.require( 'ckeditor', 'ui/view', 'ui/template' );
+let Template;
 
 
 bender.tools.createSinonSandbox();
 bender.tools.createSinonSandbox();
 beforeEach( createClassReferences );
 beforeEach( createClassReferences );
 
 
-describe( 'constructor', function() {
-	it( 'accepts the definition', function() {
-		var def = {
+describe( 'constructor', () => {
+	it( 'accepts the definition', () => {
+		let def = {
 			tag: 'p'
 			tag: 'p'
 		};
 		};
 
 
@@ -24,13 +24,13 @@ describe( 'constructor', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'render', function() {
-	it( 'returns null when no definition', function() {
+describe( 'render', () => {
+	it( 'returns null when no definition', () => {
 		expect( new Template().render() ).to.be.null;
 		expect( new Template().render() ).to.be.null;
 	} );
 	} );
 
 
-	it( 'creates an element', function() {
-		var el = new Template( {
+	it( 'creates an element', () => {
+		let el = new Template( {
 			tag: 'p',
 			tag: 'p',
 			attrs: {
 			attrs: {
 				'class': [ 'a', 'b' ],
 				'class': [ 'a', 'b' ],
@@ -45,8 +45,8 @@ describe( 'render', function() {
 		expect( el.outerHTML ).to.be.equal( '<p class="a b" x="bar">foo</p>' );
 		expect( el.outerHTML ).to.be.equal( '<p class="a b" x="bar">foo</p>' );
 	} );
 	} );
 
 
-	it( 'creates element\'s children', function() {
-		var el = new Template( {
+	it( 'creates element\'s children', () => {
+		let el = new Template( {
 			tag: 'p',
 			tag: 'p',
 			attrs: {
 			attrs: {
 				a: 'A'
 				a: 'A'
@@ -73,12 +73,12 @@ describe( 'render', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'callback value', function() {
-	it( 'works for attributes', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
+describe( 'callback value', () => {
+	it( 'works for attributes', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
 
 
-		var el = new Template( {
+		let el = new Template( {
 			tag: 'p',
 			tag: 'p',
 			attrs: {
 			attrs: {
 				'class': spy1
 				'class': spy1
@@ -102,11 +102,11 @@ describe( 'callback value', function() {
 		expect( el.outerHTML ).to.be.equal( '<p class="foo"><span id="bar"></span></p>' );
 		expect( el.outerHTML ).to.be.equal( '<p class="foo"><span id="bar"></span></p>' );
 	} );
 	} );
 
 
-	it( 'works for "text" property', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
+	it( 'works for "text" property', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
 
 
-		var el = new Template( {
+		let el = new Template( {
 			tag: 'p',
 			tag: 'p',
 			text: spy1,
 			text: spy1,
 			children: [
 			children: [
@@ -127,13 +127,13 @@ describe( 'callback value', function() {
 		expect( el.outerHTML ).to.be.equal( '<p>foo</p>' );
 		expect( el.outerHTML ).to.be.equal( '<p>foo</p>' );
 	} );
 	} );
 
 
-	it( 'works for "on" property', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
-		var spy3 = bender.sinon.spy();
-		var spy4 = bender.sinon.spy();
+	it( 'works for "on" property', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
+		let spy3 = bender.sinon.spy();
+		let spy4 = bender.sinon.spy();
 
 
-		var el = new Template( {
+		let el = new Template( {
 			tag: 'p',
 			tag: 'p',
 			children: [
 			children: [
 				{
 				{
@@ -155,13 +155,13 @@ describe( 'callback value', function() {
 		sinon.assert.calledWithExactly( spy4, el, 'baz', null );
 		sinon.assert.calledWithExactly( spy4, el, 'baz', null );
 	} );
 	} );
 
 
-	it( 'works for "on" property with selectors', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
-		var spy3 = bender.sinon.spy();
-		var spy4 = bender.sinon.spy();
+	it( 'works for "on" property with selectors', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
+		let spy3 = bender.sinon.spy();
+		let spy4 = bender.sinon.spy();
 
 
-		var el = new Template( {
+		let el = new Template( {
 			tag: 'p',
 			tag: 'p',
 			children: [
 			children: [
 				{
 				{

+ 51 - 51
packages/ckeditor5-engine/tests/ui/view.js

@@ -8,21 +8,21 @@
 
 
 'use strict';
 'use strict';
 
 
-var modules = bender.amd.require( 'ckeditor', 'ui/view', 'ui/region', 'ckeditorerror', 'model', 'eventinfo' );
-var View, TestView;
-var view;
+const modules = bender.amd.require( 'ckeditor', 'ui/view', 'ui/region', 'ckeditorerror', 'model', 'eventinfo' );
+let View, TestView;
+let view;
 
 
 bender.tools.createSinonSandbox();
 bender.tools.createSinonSandbox();
 
 
 beforeEach( updateModuleReference );
 beforeEach( updateModuleReference );
 
 
-describe( 'constructor', function() {
-	beforeEach( function() {
+describe( 'constructor', () => {
+	beforeEach( () => {
 		setTestViewClass();
 		setTestViewClass();
 		setTestViewInstance();
 		setTestViewInstance();
 	} );
 	} );
 
 
-	it( 'accepts the model', function() {
+	it( 'accepts the model', () => {
 		setTestViewInstance( { a: 'foo', b: 42 } );
 		setTestViewInstance( { a: 'foo', b: 42 } );
 
 
 		expect( view.model ).to.be.an.instanceof( modules.model );
 		expect( view.model ).to.be.an.instanceof( modules.model );
@@ -31,33 +31,33 @@ describe( 'constructor', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'instance', function() {
-	beforeEach( function() {
+describe( 'instance', () => {
+	beforeEach( () => {
 		setTestViewClass();
 		setTestViewClass();
 		setTestViewInstance();
 		setTestViewInstance();
 	} );
 	} );
 
 
-	it( 'has no default element', function() {
+	it( 'has no default element', () => {
 		expect( () => view.el ).to.throw( modules.ckeditorerror );
 		expect( () => view.el ).to.throw( modules.ckeditorerror );
 	} );
 	} );
 
 
-	it( 'has no default template', function() {
+	it( 'has no default template', () => {
 		expect( view.template ).to.be.undefined();
 		expect( view.template ).to.be.undefined();
 	} );
 	} );
 
 
-	it( 'has no default regions', function() {
+	it( 'has no default regions', () => {
 		expect( view.regions ).to.have.length( 0 );
 		expect( view.regions ).to.have.length( 0 );
 	} );
 	} );
 } );
 } );
 
 
-describe( 'bind', function() {
+describe( 'bind', () => {
 	beforeEach( createViewInstanceWithTemplate );
 	beforeEach( createViewInstanceWithTemplate );
 
 
-	it( 'returns a function that passes arguments', function() {
+	it( 'returns a function that passes arguments', () => {
 		setTestViewInstance( { a: 'foo' } );
 		setTestViewInstance( { a: 'foo' } );
 
 
-		var spy = bender.sinon.spy();
-		var callback = view.bind( 'a', spy );
+		let spy = bender.sinon.spy();
+		let callback = view.bind( 'a', spy );
 
 
 		expect( spy.called ).to.be.false;
 		expect( spy.called ).to.be.false;
 
 
@@ -70,7 +70,7 @@ describe( 'bind', function() {
 		expect( spy.secondCall.calledWithExactly( 'el', 'bar' ) ).to.be.true;
 		expect( spy.secondCall.calledWithExactly( 'el', 'bar' ) ).to.be.true;
 	} );
 	} );
 
 
-	it( 'allows binding attribute to the model', function() {
+	it( 'allows binding attribute to the model', () => {
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
 				tag: 'p',
 				tag: 'p',
@@ -89,7 +89,7 @@ describe( 'bind', function() {
 		expect( view.el.outerHTML ).to.be.equal( '<p class="baz">abc</p>' );
 		expect( view.el.outerHTML ).to.be.equal( '<p class="baz">abc</p>' );
 	} );
 	} );
 
 
-	it( 'allows binding "text" to the model', function() {
+	it( 'allows binding "text" to the model', () => {
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
 				tag: 'p',
 				tag: 'p',
@@ -112,8 +112,8 @@ describe( 'bind', function() {
 		expect( view.el.outerHTML ).to.be.equal( '<p>qux</p>' );
 		expect( view.el.outerHTML ).to.be.equal( '<p>qux</p>' );
 	} );
 	} );
 
 
-	it( 'allows binding to the model with value processing', function() {
-		var callback = ( el, value ) =>
+	it( 'allows binding to the model with value processing', () => {
+		let callback = ( el, value ) =>
 			( value > 0 ? 'positive' : 'negative' );
 			( value > 0 ? 'positive' : 'negative' );
 
 
 		setTestViewClass( function() {
 		setTestViewClass( function() {
@@ -133,7 +133,7 @@ describe( 'bind', function() {
 		expect( view.el.outerHTML ).to.be.equal( '<p class="negative">negative</p>' );
 		expect( view.el.outerHTML ).to.be.equal( '<p class="negative">negative</p>' );
 	} );
 	} );
 
 
-	it( 'allows binding to the model with custom callback', function() {
+	it( 'allows binding to the model with custom callback', () => {
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
 				tag: 'p',
 				tag: 'p',
@@ -158,9 +158,9 @@ describe( 'bind', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'on', function() {
-	it( 'accepts plain binding', function() {
-		var spy = bender.sinon.spy();
+describe( 'on', () => {
+	it( 'accepts plain binding', () => {
+		let spy = bender.sinon.spy();
 
 
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
@@ -182,9 +182,9 @@ describe( 'on', function() {
 		);
 		);
 	} );
 	} );
 
 
-	it( 'accepts an array of event bindings', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
+	it( 'accepts an array of event bindings', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
 
 
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
@@ -211,10 +211,10 @@ describe( 'on', function() {
 		);
 		);
 	} );
 	} );
 
 
-	it( 'accepts DOM selectors', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
-		var spy3 = bender.sinon.spy();
+	it( 'accepts DOM selectors', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
+		let spy3 = bender.sinon.spy();
 
 
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
@@ -296,9 +296,9 @@ describe( 'on', function() {
 		sinon.assert.callCount( spy3, 0 );
 		sinon.assert.callCount( spy3, 0 );
 	} );
 	} );
 
 
-	it( 'accepts function callbacks', function() {
-		var spy1 = bender.sinon.spy();
-		var spy2 = bender.sinon.spy();
+	it( 'accepts function callbacks', () => {
+		let spy1 = bender.sinon.spy();
+		let spy2 = bender.sinon.spy();
 
 
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
@@ -329,8 +329,8 @@ describe( 'on', function() {
 		);
 		);
 	} );
 	} );
 
 
-	it( 'supports event delegation', function() {
-		var spy = bender.sinon.spy();
+	it( 'supports event delegation', () => {
+		let spy = bender.sinon.spy();
 
 
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
@@ -357,8 +357,8 @@ describe( 'on', function() {
 		);
 		);
 	} );
 	} );
 
 
-	it( 'works for future elements', function() {
-		var spy = bender.sinon.spy();
+	it( 'works for future elements', () => {
+		let spy = bender.sinon.spy();
 
 
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
@@ -373,7 +373,7 @@ describe( 'on', function() {
 
 
 		view.on( 'a', spy );
 		view.on( 'a', spy );
 
 
-		var div = document.createElement( 'div' );
+		let div = document.createElement( 'div' );
 		view.el.appendChild( div );
 		view.el.appendChild( div );
 
 
 		dispatchEvent( div, 'test' );
 		dispatchEvent( div, 'test' );
@@ -381,10 +381,10 @@ describe( 'on', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'render', function() {
+describe( 'render', () => {
 	beforeEach( createViewInstanceWithTemplate );
 	beforeEach( createViewInstanceWithTemplate );
 
 
-	it( 'creates an element from template', function() {
+	it( 'creates an element from template', () => {
 		setTestViewInstance( { a: 1 } );
 		setTestViewInstance( { a: 1 } );
 
 
 		expect( view.el ).to.be.an.instanceof( HTMLElement );
 		expect( view.el ).to.be.an.instanceof( HTMLElement );
@@ -392,10 +392,10 @@ describe( 'render', function() {
 	} );
 	} );
 } );
 } );
 
 
-describe( 'destroy', function() {
+describe( 'destroy', () => {
 	beforeEach( createViewInstanceWithTemplate );
 	beforeEach( createViewInstanceWithTemplate );
 
 
-	it( 'detaches the model', function() {
+	it( 'detaches the model', () => {
 		expect( view.model ).to.be.an.instanceof( modules.model );
 		expect( view.model ).to.be.an.instanceof( modules.model );
 
 
 		view.destroy();
 		view.destroy();
@@ -403,9 +403,9 @@ describe( 'destroy', function() {
 		expect( view.model ).to.be.null;
 		expect( view.model ).to.be.null;
 	} );
 	} );
 
 
-	it( 'detaches the element', function() {
+	it( 'detaches the element', () => {
 		// Append the views's element to some container.
 		// Append the views's element to some container.
-		var container = document.createElement( 'div' );
+		let container = document.createElement( 'div' );
 		container.appendChild( view.el );
 		container.appendChild( view.el );
 
 
 		expect( view.el.nodeName ).to.be.equal( 'A' );
 		expect( view.el.nodeName ).to.be.equal( 'A' );
@@ -418,10 +418,10 @@ describe( 'destroy', function() {
 		expect( view.el.parentNode ).to.be.null;
 		expect( view.el.parentNode ).to.be.null;
 	} );
 	} );
 
 
-	it( 'destroys child regions', function() {
-		var Region = modules[ 'ui/region' ];
-		var region = new Region( 'test' );
-		var spy = bender.sinon.spy( region, 'destroy' );
+	it( 'destroys child regions', () => {
+		const Region = modules[ 'ui/region' ];
+		let region = new Region( 'test' );
+		let spy = bender.sinon.spy( region, 'destroy' );
 
 
 		view.regions.add( region );
 		view.regions.add( region );
 		view.destroy();
 		view.destroy();
@@ -430,7 +430,7 @@ describe( 'destroy', function() {
 		expect( spy.calledOnce ).to.be.true;
 		expect( spy.calledOnce ).to.be.true;
 	} );
 	} );
 
 
-	it( 'detaches bound model listeners', function() {
+	it( 'detaches bound model listeners', () => {
 		setTestViewClass( function() {
 		setTestViewClass( function() {
 			return {
 			return {
 				tag: 'p',
 				tag: 'p',
@@ -441,7 +441,7 @@ describe( 'destroy', function() {
 		setTestViewInstance( { foo: 'bar' } );
 		setTestViewInstance( { foo: 'bar' } );
 
 
 		// Keep the reference after the view is destroyed.
 		// Keep the reference after the view is destroyed.
-		var model = view.model;
+		let model = view.model;
 
 
 		expect( view.el.outerHTML ).to.be.equal( '<p>bar</p>' );
 		expect( view.el.outerHTML ).to.be.equal( '<p>bar</p>' );
 
 
@@ -486,7 +486,7 @@ function setTestViewInstance( model ) {
 
 
 function dispatchEvent( el, domEvtName ) {
 function dispatchEvent( el, domEvtName ) {
 	if ( !el.parentNode ) {
 	if ( !el.parentNode ) {
-		throw( 'To dispatch an event, element must be in DOM. Otherwise #target is null.' );
+		throw new Error( 'To dispatch an event, element must be in DOM. Otherwise #target is null.' );
 	}
 	}
 
 
 	el.dispatchEvent( new Event( domEvtName, {
 	el.dispatchEvent( new Event( domEvtName, {