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

Merge branch 'master' into ckeditor5/t/1214

Piotrek Koszuliński 7 лет назад
Родитель
Сommit
a24171d87d

+ 11 - 0
packages/ckeditor5-utils/CHANGELOG.md

@@ -1,6 +1,17 @@
 Changelog
 =========
 
+## [11.1.0](https://github.com/ckeditor/ckeditor5-utils/compare/v11.0.0...v11.1.0) (2018-12-05)
+
+### Features
+
+* Implemented `env#isGecko()`. See [ckeditor/ckeditor5-engine#1439](https://github.com/ckeditor/ckeditor5-engine/issues/1439). ([53b7c94](https://github.com/ckeditor/ckeditor5-utils/commit/53b7c94))
+
+### Other changes
+
+* Vairous fixes in the API docs. Thanks to [@denisname](https://github.com/denisname)!
+
+
 ## [11.0.0](https://github.com/ckeditor/ckeditor5-utils/compare/v10.2.1...v11.0.0) (2018-10-08)
 
 ### Other changes

+ 0 - 44
packages/ckeditor5-utils/dev/tasks/lodash/tasks.js

@@ -1,44 +0,0 @@
-/**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* eslint-env node */
-
-'use strict';
-
-const gulp = require( 'gulp' );
-const build = require( 'lodash-cli' );
-const del = require( 'del' );
-
-const DEST_PATH = 'src/lib/lodash';
-
-module.exports = function() {
-	const tasks = {
-		lodash() {
-			return del( DEST_PATH )
-				.then( buildLodash );
-		}
-	};
-
-	gulp.task( 'lodash', tasks.lodash );
-
-	return tasks;
-};
-
-function buildLodash() {
-	return new Promise( ( resolve, reject ) => {
-		build( [
-			'modularize',
-			'exports=es',
-			'--development',
-			'--output', DEST_PATH
-		], err => {
-			if ( err instanceof Error ) {
-				reject( err );
-			} else {
-				resolve( null );
-			}
-		} );
-	} );
-}

+ 190 - 98
packages/ckeditor5-utils/docs/framework/guides/deep-dive/observables.md

@@ -14,7 +14,7 @@ Any class can become observable; all you need to do is mix the {@link module:uti
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 
-export default class AnyClass {
+class AnyClass {
 	// ...
 }
 
@@ -27,73 +27,140 @@ Observables can also [decorate their methods](#decorating-object-methods) which
 
 ## Making properties observable
 
-Having mixed the {@link module:utils/observablemixin~ObservableMixin} into your class, you can define observable properties. To do that, use the {@link module:utils/observablemixin~ObservableMixin#set `set()` method}. Let's set a couple of properties and see what they look like in a simple `Command` class:
+Having mixed the {@link module:utils/observablemixin~ObservableMixin} into your class, you can define observable properties. To do that, use the {@link module:utils/observablemixin~ObservableMixin#set `set()` method}.
+
+Let's create a simple UI view (component) named `Button` with a couple of properties and see what they look like:
 
 ```js
-export default class Command {
-	constructor( name ) {
+class Button extends View {
+	constructor() {
+		super();
+
 		// This property is not observable.
-		// Not all properties must be observable, it's up to you!
-		this.name = name;
+		// Not all properties must be observable, it's always up to you!
+		this.type = 'button';
+
+		const bind = this.bindTemplate;
 
-		// this.value is observable but undefined.
-		this.set( 'value' );
+		// this.label is observable but undefined.
+		this.set( 'label' );
 
-		// this.isEnabled is observable and false.
-		this.set( 'isEnabled', false );
+		// this.isOn is observable and false.
+		this.set( 'isOn', false );
+
+		// this.isEnabled is observable and true.
+		this.set( 'isEnabled', true );
+
+		// ...
 	}
 }
-
-mix( Command, ObservableMixin );
 ```
 
+Note that because `Button` extends the {@link module:ui/view~View `View`} class (which is already observable), you do not need to mix the `ObservableMixin`.
+
 <info-box info>
 	The `set()` method can accept an object of key/value pairs to shorten the code. Knowing that, making properties observable can be as simple as:
 
 	```js
 	this.set( {
-		value: undefined,
-		isEnabled: false
+		label: undefined,
+		isOn: false,
+		isEnabled: true
 	} );
 	```
 </info-box>
 
-Finally, let's create a new command and see how it communicates with the world.
+Finally, let's create a new view and see how it communicates with the world.
 
-Each time the `value` property changes, the command fires the `change:value` event containing information about its state in the past and the new value. The corresponding `change:isEnabled` will be fired when the `isEnabled` property changes too.
+Each time the `label` property changes, the view fires the `change:label` event containing information about its state in the past and the new value. The `change:isEnabled` and `change:isOn` events will be fired for changes of `isEnabled` and `isOn`, respectively.
 
 ```js
-const command = new Command( 'bold' );
+const view = new Button();
 
-command.on( 'change:value', ( evt, propertyName, newValue, oldValue ) => {
+view.on( 'change:label', ( evt, propertyName, newLabel, oldLabel ) => {
     console.log(
-        `${ propertyName } has changed from ${ oldValue } to ${ newValue }`
+        `#${ propertyName } has changed from "${ oldValue }" to "${ newValue }"`
     );
 } )
 
-command.value = true; // -> 'value has changed from undefined to true'
-command.value = false; // -> 'value has changed from true to false'
+view.label = 'Hello world!'; // -> #label has changed from "undefined" to "Hello world!"
+view.label = 'Bold'; // -> #label has changed from "Hello world!" to "Bold"
+
+view.type = 'submit'; // Changing a regular property fires no event.
+```
+
+The events fired by the view are used to update the DOM and make the component dynamic. Let's give our view some template and bind it to the observable properties we created.
 
-command.name = 'italic'; // -> changing a regular property fires no event
+<info-box>
+	You can learn more about the UI of the editor and template system in the dedicated {@link framework/guides/architecture/ui-library#templates guide}.
+</info-box>
+
+```js
+class Button extends View {
+	constructor() {
+		super();
+
+		// ...
+
+		// This template will have the following symbolic representation in DOM:
+		//
+		// 	<button class="[ck-disabled] ck-[on|off]" type="button">
+		// 		{{ this.label }}
+		// 	</button>
+		//
+		this.setTemplate( {
+			tag: 'button',
+			attributes: {
+				class: [
+					// The 'ck-on' and 'ck-off' classes toggle according to the #isOn property.
+					bind.to( 'isOn', value => value ? 'ck-on' : 'ck-off' ),
+
+					// The 'ck-enabled' class appears when the #isEnabled property is false.
+					bind.if( 'isEnabled', 'ck-disabled', value => !value )
+				],
+				type: this.type
+			},
+			children: [
+				{
+					// The text of the button is bound to the #label property.
+					text: bind.to( 'label' )
+				}
+			]
+		} );
+	}
+}
 ```
 
-During its life cycle, an instance of the `Command` can be enabled and disabled many times just as its `value` can change very often and different parts of the application will certainly be interested in that state.
+Because `label`, `isOn`, and `isEnabled` are observables, any change will be immediately reflected in DOM:
 
-For instance, some commands can be represented by a button, which should be able to figure out its look ("pushed", disabled, etc.) as soon as possible. Using observable properties makes it a lot easier because all the button must know about its command is the names of properties to listen to apply changes instantly.
+```js
+const button = new Button();
+
+// Render the button to create its #element.
+button.render();
 
-Additionally, as the number of observable properties increases, you can save yourself the hassle of creating and maintaining multiple `command.on( 'change:property', () => { ... } )` listeners by sharing command's state with the button using [bound properties](#property-bindings), which are the key topic of the next chapter.
+button.label = 'Bold';     // <button class="ck-off" type="button">Bold</button>
+button.isOn = true;        // <button class="ck-on" type="button">Bold</button>
+button.label = 'B';        // <button class="ck-on" type="button">B</button>
+button.isOff = false;      // <button class="ck-off" type="button">B</button>
+button.isEnabled = false;  // <button class="ck-off ck-disabled" type="button">B</button>
+```
 
 ## Property bindings
 
-One observable can also propagate its state (or part of it) to another observable to simplify the code and avoid numerous `change:property` event listeners. First, make sure both objects (classes) mix the {@link module:utils/observablemixin~ObservableMixin}, then use the {@link module:utils/observablemixin~ObservableMixin#bind `bind()`} method to create the binding.
+One observable can also propagate its state (or part of it) to another observable to simplify the code, e.g. to avoid numerous `change:property` event listeners. To start binding object properties, make sure both objects (classes) mix the {@link module:utils/observablemixin~ObservableMixin}, then use the {@link module:utils/observablemixin~ObservableMixin#bind `bind()`} method to create the binding.
 
 ### Simple bindings
 
-Let's consider two objects: a `command` and a corresponding `button` (both {@link module:utils/observablemixin~Observable}).
+Let's use our bold button instance from the previous chapter and bind it to the bold command. That will let the button use certain command properties and automate the user interface in just a couple of lines.
+
+The bold command is an actual command of the editor (registered by the {@link module:basic-styles/bold/boldediting~BoldEditing `BoldEditing`}) and offers two observable properties: `value` and `isEnabled`. To get the command, use `editor.commands.get( 'bold' )`.
+
+Note that both `Button` and {@link module:core/command~Command `Command`} classes are {@link module:utils/observablemixin~Observable observable}, which is why we can bind their properties.
 
 ```js
-const command = new Command( 'bold' );
 const button = new Button();
+const command = editor.commands.get( 'bold' );
 ```
 
 Any "decent" button must update its look when the command becomes disabled. A simple property binding doing that could look as follows:
@@ -105,71 +172,75 @@ button.bind( 'isEnabled' ).to( command );
 After that:
 
 * `button.isEnabled` **instantly equals** `command.isEnabled`,
-* whenever `command.isEnabled` changes, `button.isEnabled` will immediately reflect its value.
+* whenever `command.isEnabled` changes, `button.isEnabled` will immediately reflect its value,
+* because the template of the button has its class bound to `button.isEnabled`, the DOM element of the button will also be updated.
 
-Note that `command.isEnabled` **must** be defined using the `set()` method for the binding to be dynamic &mdash; we did that in the [previous chapter](#making-properties-observable). The `button.isEnabled` property does not need to exist prior to the `bind()` call and in such case, it will be created on demand. If the `button.isEnabled` property is already observable, don't worry: binding it to the command will do no harm.
-
-By creating the binding, we allowed the button to simply use its own `isEnabled` property, e.g. in the dynamic template (check out {@link framework/guides/architecture/ui-library#template this guide} to learn how).
+Note that `command.isEnabled` **must** be defined using the `set()` method for the binding to be dynamic. In this case we are lucky because {@link module:core/command~Command#isEnabled `isEnabled`} is a standard observable property of every command in the editor. But keep in mind that when you create your own observable class, using `set()` method is the only way to define observable properties.
 
 #### Renaming properties
 
-Now let's dive into the `bind( ... ).to( ... )` syntax for a minute. The last example corresponds to the following code:
+Now let's dive into the `bind( ... ).to( ... )` syntax for a minute. As a matter of fact, the last example corresponds to the following code:
 
 ```js
+const button = new Button();
+const command = editor.commands.get( 'bold' );
+
 button.bind( 'isEnabled' ).to( command, 'isEnabled' );
 ```
 
-You probably noticed the `to( ... )` interface which helps specify the name of the property ("rename" the property in the binding).
+You probably noticed the `to( ... )` interface which helps specify the name of the property (or just "rename" the property in the binding).
 
-What if instead of `isEnabled`, the `Command` class implemented the `isWorking` property, which does not quite fit into the button object? Let's bind two properties that have different names then:
+Both `Button` and `Command` class share the same `isEnabled` property, which allowed us to shorten the code. But if we decided to bind the `Button#isOn` to the `Command#value`, the code would be as follows:
 
 ```js
-button.bind( 'isEnabled' ).to( command, 'isWorking' );
+button.bind( 'isOn' ).to( command, 'value' );
 ```
 
-From now on, whenever `command.isWorking` changes, the value of `button.isEnabled` will reflect it.
+The property has been "renamed" in the binding and from now on, whenever `command.value` changes, the value of `button.isOn` will reflect it.
 
 ### Binding multiple properties
 
-It is also possible to bind more that one property at a time to simplify the code:
+It is possible to bind more that one property at a time to simplify the code:
 
 ```js
-button.bind( 'isEnabled', 'value' ).to( command );
+const button = new Button();
+const command = editor.commands.get( 'bold' );
+
+button.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
 ```
 
-which is the same as
+which is the same as:
 
 ```js
-button.bind( 'isEnabled' ).to( command );
-button.bind( 'value' ).to( command );
+button.bind( 'isOn' ).to( command, 'value' );
+button.bind( 'isEnabled' ).to( command, 'isEnabled' );
 ```
 
-In the above binding, the value of `button.isEnabled` will reflect `command.isEnabled` and the value of `button.value` will reflect `command.value`.
+In the above binding, the value of `button.isEnabled` will reflect `command.isEnabled` and the value of `button.isOn` will reflect `command.value`.
 
-Renaming is still possible when binding multiple properties. Consider the following example which binds `button.isEnabled` to `command.isWorking` and `button.currentState` to `command.value`:
-
-```js
-button.bind( 'isEnabled', 'currentState' ).to( command, 'isWorking', 'value' );
-```
+Note that the `value` property of the command has also been "renamed" in the binding like in the [previous example](#renaming-properties).
 
 ### Binding with multiple observables
 
-The binding can include more than one observable, combining multiple properties in a custom callback function. Let's create a button that gets enabled only when the `command` is enabled and the `ui` (also an `Observable`) is visible:
+The binding can include more than one observable, combining multiple properties in a custom callback function. Let's create a button that gets enabled only when the `command` is enabled and the {@link module:engine/view/document~Document editing document} (also an `Observable`) is focused:
 
 ```js
-button.bind( 'isEnabled' ).to( command, 'isEnabled', ui, 'isVisible',
-	( isCommandEnabled, isUIVisible ) => isCommandEnabled && isUIVisible );
+const button = new Button();
+const command = editor.commands.get( 'bold' );
+const editingDocument = editor.editing.view.document;
+
+button.bind( 'isEnabled' ).to( command, 'isEnabled', editingDocument, 'isFocused',
+	( isCommandEnabled, isDocumentFocused ) => isCommandEnabled && isDocumentFocused );
 ```
 
-From now on, the value of `button.isEnabled` depends both on `command.isEnabled` and `ui.isVisible`
-as specified by the function: both must be `true` for the button to become enabled.
+The binding makes the value of `button.isEnabled` depend both on `command.isEnabled` and `editingDocument.isFocused` as specified by the function: both must be `true` for the button to become enabled.
 
 ### Binding with an array of observables
 
-It is possible to bind to the same property in an array of observables. Let's bind a `button` to multiple commands so that each and every one must be enabled for the button
-to become enabled:
+It is possible to bind the same property to an array of observables. Let's bind our button to multiple commands so that each and every one must be enabled for the button to become enabled:
 
 ```js
+const button = new Button();
 const commands =  [ commandA, commandB, commandC ];
 
 button.bind( 'isEnabled' ).toMany( commands, 'isEnabled', ( isAEnabled, isBEnabled, isCEnabled ) => {
@@ -187,6 +258,8 @@ button.bind( 'isEnabled' ).toMany( commands, 'isEnabled', ( ...areEnabled ) => {
 } );
 ```
 
+This kind of binding can be useful e.g. when a button opens a dropdown containing a number of other commands' buttons and it should be disabled when none of the commands is enabled.
+
 ### Releasing the bindings
 
 If you don't want your object's properties to be bound any longer, you can use the {@link module:utils/observablemixin~ObservableMixin#unbind `unbind()`} method.
@@ -194,23 +267,29 @@ If you don't want your object's properties to be bound any longer, you can use t
 You can specify the names of the properties to selectively unbind them
 
 ```js
-button.bind( 'isEnabled', 'value' ).to( command );
+const button = new Button();
+const command = editor.commands.get( 'bold' );
+
+button.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
 
 // ...
 
-// From now on, button.isEnabled is no longer bound to the command.
+// From now on, button#isEnabled is no longer bound to the command.
 button.unbind( 'isEnabled' );
 ```
 
 or you can dismiss all bindings by calling the method without arguments
 
 ```js
-button.bind( 'isEnabled', 'value' ).to( command );
+const button = new Button();
+const command = editor.commands.get( 'bold' );
+
+button.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
 
 // ...
 
-// Both "isEnabled" and "value" properties are independent back again.
-// They will retain the values determined by the bindings, though.
+// Both #isEnabled and #isOn properties are independent back again.
+// They will retain the last values determined by the bindings, though.
 button.unbind();
 ```
 
@@ -220,44 +299,58 @@ Decorating object methods transforms them into event–driven ones without chang
 
 When a method is decorated, an event of the same name is created and fired each time the method is executed. By listening to the event it is possible to cancel the execution, change the arguments or the value returned by the method. This offers an additional flexibility, e.g. giving a third–party code some way to interact with core classes that decorate their methods.
 
-Decorating is possible using the {@link module:utils/observablemixin~ObservableMixin#decorate `decorate()`} method. Having [mixed](#observables) the {@link module:utils/observablemixin~ObservableMixin}, we are going to use the `Command` class from previous sections of this guide to show the potential use–cases:
+Decorating is possible using the {@link module:utils/observablemixin~ObservableMixin#decorate `decorate()`} method. Let's decorate a `focus` method of a `Button` class we created in the [previous chapters](#making-properties-observable) and see what if offers:
 
 ```js
-import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
-import mix from '@ckeditor/ckeditor5-utils/src/mix';
-
-class Command {
+class Button extends View {
 	constructor() {
-		this.decorate( 'execute' );
+		// ...
+
+		this.decorate( 'focus' );
 	}
 
-	// Because the method is decorated, it always fires the #execute event.
-	execute( value ) {
-		console.log( `Executed the command with value="${ value }"` );
+	/**
+	 * Focuses the button.
+	 *
+	 * @param {Boolean} force When `true`, the button will be focused again, even if already
+	 * focused in DOM.
+	 * @returns {Boolean} `true` when the DOM element was focused in DOM, `false` otherwise.
+	 */
+	focus( force ) {
+		console.log( `Focusing button, force argument="${ force }"` );
+
+		// Unless forced, the button will only focus when not already focused.
+		if ( force || document.activeElement != this.element ) {
+			this.element.focus();
+
+			return true;
+		}
+
+		return false;
 	}
 }
-
-mix( Command, ObservableMixin );
 ```
 
 ### Cancelling the execution
 
-Because the `execute()` method is event–driven, it can be controlled externally. E.g. the execution could be stopped for certain arguments. Note the `high` listener {@link module:utils/priorities~PriorityString priority} used to intercept the default action:
+Because the `focus()` method is now event–driven, it can be controlled externally. E.g. the focusing could be stopped for certain arguments. Note the `high` listener {@link module:utils/priorities~PriorityString priority} used to intercept the default action:
 
 ```js
-const command = new Command();
+const button = new Button();
 
-// ...
+// Render the button to create its #element.
+button.render();
 
-// Some code interested in controlling this particular command.
-command.on( 'execute', ( evt, args ) => {
-	if ( args[ 0 ] !== 'bold' ) {
+// The logic controlling the behavior of the button.
+button.on( 'focus', ( evt, [ isForced ] ) => {
+	// Disallow forcing the focus of this button.
+	if ( isForced === true ) {
 		evt.stop();
 	}
 }, { priority: 'high' } );
 
-command.execute( 'bold' ); // -> 'Executed the command with value="bold"'
-command.execute( 'italic' ); // Nothing is logged, the execution has been stopped.
+button.focus(); // -> 'Focusing button, force argument="undefined"'
+button.focus( true ); // Nothing is logged, the execution has been stopped.
 ```
 
 ### Changing the returned value
@@ -265,22 +358,21 @@ command.execute( 'italic' ); // Nothing is logged, the execution has been stoppe
 It is possible to control the returned value of a decorated method using an event listener. The returned value is passed in the event data as a `return` property:
 
 ```js
-const command = new Command();
+const button = new Button();
 
-// ...
+// Render the button to create its #element.
+button.render();
 
-// Some code interested in controlling this particular command.
-command.on( 'execute', ( evt ) => {
-	if ( args[ 0 ] == 'bold' ) {
-		evt.return = true;
-	} else {
+// The logic controlling the behavior of the button.
+button.on( 'focus', ( evt, [ isForced ] ) => {
+	// Pretend the button wasn't focused if the focus was forced.
+	if ( isForced === true ) {
 		evt.return = false;
 	}
 } );
 
-console.log( command.execute( 'bold' ) ); // -> true
-console.log( command.execute( 'italic' ) ); // -> false
-console.log( command.execute() ); // -> false
+console.log( button.focus() ); // -> true
+console.log( button.focus( true ) ); // -> false
 ```
 
 ### Changing arguments on the fly
@@ -289,17 +381,17 @@ Just like the returned value, the arguments passed to the method can be changed
 
 
 ```js
-const command = new Command();
+const button = new Button();
 
-// ...
+// Render the button to create its #element.
+button.render();
 
-// Some code interested in controlling this particular command.
-command.on( 'execute', ( evt, args ) => {
-	if ( args[ 0 ] === 'bold' ) {
-		args[ 0 ] = 'underline';
-	}
+// The logic controlling the behavior of the button.
+button.on( 'focus', ( evt, args ) => {
+	// Always force the focus.
+	args[ 0 ] = true;
 }, { priority: 'high' } );
 
-command.execute( 'bold' ); // -> 'Executed the command with value="underline"'
-command.execute( 'italic' ); // -> 'Executed the command with value="italic"'
+button.focus(); // -> 'Focusing button, force="true"'
+button.focus( true ); // -> 'Focusing button, force="true"'
 ```

+ 6 - 7
packages/ckeditor5-utils/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@ckeditor/ckeditor5-utils",
-  "version": "11.0.0",
+  "version": "11.1.0",
   "description": "Miscellaneous utils used by CKEditor 5.",
   "keywords": [
     "ckeditor",
@@ -9,18 +9,17 @@
     "ckeditor5-lib"
   ],
   "dependencies": {
-    "ckeditor5": "^11.1.0",
+    "ckeditor5": "^11.2.0",
     "lodash-es": "^4.17.10"
   },
   "devDependencies": {
-    "@ckeditor/ckeditor5-core": "^11.0.1",
-    "@ckeditor/ckeditor5-engine": "^11.0.0",
+    "@ckeditor/ckeditor5-core": "^11.1.0",
+    "@ckeditor/ckeditor5-engine": "^12.0.0",
     "del": "^2.2.0",
     "eslint": "^5.5.0",
-    "eslint-config-ckeditor5": "^1.0.7",
+    "eslint-config-ckeditor5": "^1.0.9",
     "husky": "^0.14.3",
-    "lint-staged": "^7.0.0",
-    "lodash-cli": "^4"
+    "lint-staged": "^7.0.0"
   },
   "engines": {
     "node": ">=6.9.0",

+ 2 - 2
packages/ckeditor5-utils/src/ckeditorerror.js

@@ -51,9 +51,9 @@ export default class CKEditorError extends Error {
 		this.name = 'CKEditorError';
 
 		/**
-		 * The additional error data passed to the constructor.
+		 * The additional error data passed to the constructor. Undefined if none was passed.
 		 *
-		 * @member {Object}
+		 * @member {Object|undefined}
 		 */
 		this.data = data;
 	}

+ 51 - 24
packages/ckeditor5-utils/src/collection.js

@@ -209,20 +209,37 @@ export default class Collection {
 		return item || null;
 	}
 
+	/**
+	 * Returns a boolean indicating whether the collection contains an item.
+	 *
+	 * @param {Object|String} itemOrId The item or its id in the collection.
+	 * @returns {Boolean} `true` if the collection contains the item, `false` otherwise.
+	 */
+	has( itemOrId ) {
+		if ( typeof itemOrId == 'string' ) {
+			return this._itemMap.has( itemOrId );
+		} else { // Object
+			const idProperty = this._idProperty;
+			const id = itemOrId[ idProperty ];
+
+			return this._itemMap.has( id );
+		}
+	}
+
 	/**
 	 * Gets index of item in the collection.
 	 * When item is not defined in the collection then index will be equal -1.
 	 *
-	 * @param {String|Object} idOrItem The item or its id in the collection.
+	 * @param {Object|String} itemOrId The item or its id in the collection.
 	 * @returns {Number} Index of given item.
 	 */
-	getIndex( idOrItem ) {
+	getIndex( itemOrId ) {
 		let item;
 
-		if ( typeof idOrItem == 'string' ) {
-			item = this._itemMap.get( idOrItem );
+		if ( typeof itemOrId == 'string' ) {
+			item = this._itemMap.get( itemOrId );
 		} else {
-			item = idOrItem;
+			item = itemOrId;
 		}
 
 		return this._items.indexOf( item );
@@ -290,7 +307,7 @@ export default class Collection {
 	 * @param {Function} callback
 	 * @param {Object} callback.item
 	 * @param {Number} callback.index
-	 * @params {Object} ctx Context in which the `callback` will be called.
+	 * @param {Object} ctx Context in which the `callback` will be called.
 	 * @returns {Array} The result of mapping.
 	 */
 	map( callback, ctx ) {
@@ -303,8 +320,8 @@ export default class Collection {
 	 * @param {Function} callback
 	 * @param {Object} callback.item
 	 * @param {Number} callback.index
+	 * @param {Object} ctx Context in which the `callback` will be called.
 	 * @returns {Object} The item for which `callback` returned a true value.
-	 * @params {Object} ctx Context in which the `callback` will be called.
 	 */
 	find( callback, ctx ) {
 		return this._items.find( callback, ctx );
@@ -316,7 +333,7 @@ export default class Collection {
 	 * @param {Function} callback
 	 * @param {Object} callback.item
 	 * @param {Number} callback.index
-	 * @params {Object} ctx Context in which the `callback` will be called.
+	 * @param {Object} ctx Context in which the `callback` will be called.
 	 * @returns {Object[]} The array with matching items.
 	 */
 	filter( callback, ctx ) {
@@ -433,8 +450,7 @@ export default class Collection {
 	 *
 	 * @param {module:utils/collection~Collection} externalCollection A collection to be bound.
 	 * @returns {Object}
-	 * @returns {module:utils/collection~Collection#bindTo#as} return.as
-	 * @returns {module:utils/collection~Collection#bindTo#using} return.using
+	 * @returns {module:utils/collection~CollectionBindToChain} The binding chain object.
 	 */
 	bindTo( externalCollection ) {
 		if ( this._bindToCollection ) {
@@ -449,24 +465,10 @@ export default class Collection {
 		this._bindToCollection = externalCollection;
 
 		return {
-			/**
-			 * Creates the class factory binding.
-			 *
-			 * @static
-			 * @param {Function} Class Specifies which class factory is to be initialized.
-			 */
 			as: Class => {
 				this._setUpBindToBinding( item => new Class( item ) );
 			},
 
-			/**
-			 * Creates callback or property binding.
-			 *
-			 * @static
-			 * @param {Function|String} callbackOrProperty When the function is passed, it is used to
-			 * produce the items. When the string is provided, the property value is used to create
-			 * the bound collection items.
-			 */
 			using: callbackOrProperty => {
 				if ( typeof callbackOrProperty == 'function' ) {
 					this._setUpBindToBinding( item => callbackOrProperty( item ) );
@@ -628,3 +630,28 @@ export default class Collection {
 }
 
 mix( Collection, EmitterMixin );
+
+/**
+ * An object returned by the {@link module:utils/collection~Collection#bindTo `bindTo()`} method
+ * providing functions that specify the type of the binding.
+ *
+ * See the {@link module:utils/collection~Collection#bindTo `bindTo()`} documentation for examples.
+ *
+ * @interface module:utils/collection~CollectionBindToChain
+ */
+
+/**
+ * Creates a callback or a property binding.
+ *
+ * @method #using
+ * @param {Function|String} callbackOrProperty  When the function is passed, it should return
+ * the collection items. When the string is provided, the property value is used to create the bound collection items.
+ */
+
+/**
+ * Creates the class factory binding in which items of the source collection are passed to
+ * the constructor of the specified class.
+ *
+ * @method #as
+ * @param {Function} Class The class constructor used to create instances in the factory.
+ */

+ 19 - 3
packages/ckeditor5-utils/src/config.js

@@ -7,7 +7,7 @@
  * @module utils/config
  */
 
-import { isPlainObject } from 'lodash-es';
+import { isPlainObject, isElement, cloneDeepWith } from 'lodash-es';
 
 /**
  * Handles a configuration dictionary.
@@ -197,8 +197,8 @@ export default class Config {
 			source = source[ part ];
 		}
 
-		// Always returns undefined for non existing configuration
-		return source ? source[ name ] : undefined;
+		// Always returns undefined for non existing configuration.
+		return source ? cloneConfig( source[ name ] ) : undefined;
 	}
 
 	/**
@@ -215,3 +215,19 @@ export default class Config {
 		} );
 	}
 }
+
+// Clones configuration object or value.
+// @param {*} source Source configuration
+// @returns {*} Cloned configuration value.
+function cloneConfig( source ) {
+	return cloneDeepWith( source, leaveDOMReferences );
+}
+
+// A customizer function for cloneDeepWith.
+// It will leave references to DOM Elements instead of cloning them.
+//
+// @param {*} value
+// @returns {Element|undefined}
+function leaveDOMReferences( value ) {
+	return isElement( value ) ? value : undefined;
+}

+ 2 - 2
packages/ckeditor5-utils/src/dom/createelement.js

@@ -20,8 +20,8 @@ import { isString } from 'lodash-es';
  *
  * @param {Document} doc Document used to create element.
  * @param {String} name Name of the element.
- * @param {Object} attributes Object keys will become attributes keys and object values will became attributes values.
- * @param {Node|String|Array.<Node|String>} children Child or array of children. Strings will be automatically turned
+ * @param {Object} [attributes] Object keys will become attributes keys and object values will became attributes values.
+ * @param {Node|String|Array.<Node|String>} [children] Child or array of children. Strings will be automatically turned
  * into Text nodes.
  * @returns {Element} Created element.
  */

+ 1 - 1
packages/ckeditor5-utils/src/dom/getborderwidths.js

@@ -11,7 +11,7 @@
  * Returns an object containing CSS border widths of a specified HTML element.
  *
  * @param {HTMLElement} element An element which has CSS borders.
- * @param {Object} An object containing `top`, `left`, `right` and `bottom` properties
+ * @returns {Object} An object containing `top`, `left`, `right` and `bottom` properties
  * with numerical values of the `border-[top,left,right,bottom]-width` CSS styles.
  */
 export default function getBorderWidths( element ) {

+ 1 - 1
packages/ckeditor5-utils/src/dom/getpositionedancestor.js

@@ -12,7 +12,7 @@ import global from './global';
 /**
  * For a given element, returns the nearest ancestor element which CSS position is not "static".
  *
- * @param {HTMLElement} element Native DOM element to be checked.
+ * @param {HTMLElement} element The native DOM element to be checked.
  * @returns {HTMLElement|null}
  */
 export default function getPositionedAncestor( element ) {

+ 1 - 1
packages/ckeditor5-utils/src/dom/position.js

@@ -67,7 +67,7 @@ import { isFunction } from 'lodash-es';
  *
  *		// The best position which fits into document.body and the viewport. May be useful
  *		// to set proper class on the `element`.
- *		console.log( name ); -> "myNorthEastPosition"
+ *		console.log( name ); // -> "myNorthEastPosition"
  *
  *		// Using the absolute coordinates which has been found to position the element
  *		// as in the diagram depicting the "myNorthEastPosition" position.

+ 1 - 1
packages/ckeditor5-utils/src/emittermixin.js

@@ -635,5 +635,5 @@ function removeCallback( emitter, event, callback ) {
  *
  * @method #to
  * @param {module:utils/emittermixin~Emitter} emitter An `EmitterMixin` instance which is the destination for delegated events.
- * @param {String|Function} nameOrFunction A custom event name or function which converts the original name string.
+ * @param {String|Function} [nameOrFunction] A custom event name or function which converts the original name string.
  */

+ 1 - 1
packages/ckeditor5-utils/src/keystrokehandler.js

@@ -82,7 +82,7 @@ export default class KeystrokeHandler {
 	 * the {@link module:utils/keyboard~parseKeystroke} function.
 	 * @param {Function} callback A function called with the
 	 * {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
-	 * a helper to both `preventDefault` and `stopPropagation` of the event.
+	 * a helper funcion to call both `preventDefault()` and `stopPropagation()` on the underlying event.
 	 * @param {Object} [options={}] Additional options.
 	 * @param {module:utils/priorities~PriorityString|Number} [options.priority='normal'] The priority of the keystroke
 	 * callback. The higher the priority value the sooner the callback will be executed. Keystrokes having the same priority

+ 1 - 1
packages/ckeditor5-utils/src/locale.js

@@ -44,7 +44,7 @@ export default class Locale {
 		 *
 		 * @method #t
 		 * @param {String} str The string to translate.
-		 * @param {String[]} values Values that should be used to interpolate the string.
+		 * @param {String[]} [values] Values that should be used to interpolate the string.
 		 */
 		this.t = ( ...args ) => this._t( ...args );
 	}

+ 2 - 0
packages/ckeditor5-utils/src/mapsequal.js

@@ -10,6 +10,8 @@
 /**
  * Checks whether given {Map}s are equal, that is has same size and same key-value pairs.
  *
+ * @param {Map} mapA The first map to compare.
+ * @param {Map} mapB The second map to compare.
  * @returns {Boolean} `true` if given maps are equal, `false` otherwise.
  */
 export default function mapsEqual( mapA, mapB ) {

+ 28 - 0
packages/ckeditor5-utils/tests/collection.js

@@ -321,6 +321,34 @@ describe( 'Collection', () => {
 		} );
 	} );
 
+	describe( 'has()', () => {
+		it( 'should return true if collection contains item with given id', () => {
+			collection.add( getItem( 'foo' ) );
+
+			expect( collection.has( 'foo' ) ).to.equal( true );
+		} );
+
+		it( 'should return false if collection does not contain item with given id', () => {
+			collection.add( getItem( 'foo' ) );
+
+			expect( collection.has( 'bar' ) ).to.equal( false );
+		} );
+
+		it( 'should return true if collection contains item', () => {
+			const item = getItem( 'foo' );
+
+			collection.add( item );
+
+			expect( collection.has( item ) ).to.equal( true );
+		} );
+
+		it( 'should return false if collection does not contains item', () => {
+			collection.add( getItem( 'foo' ) );
+
+			expect( collection.has( getItem( 'bar' ) ) ).to.equal( false );
+		} );
+	} );
+
 	describe( 'getIndex()', () => {
 		it( 'should return index of given item', () => {
 			const item1 = { foo: 'bar' };

+ 89 - 2
packages/ckeditor5-utils/tests/config.js

@@ -3,6 +3,8 @@
  * For licensing, see LICENSE.md.
  */
 
+/* global document */
+
 import Config from '../src/config';
 
 describe( 'Config', () => {
@@ -19,7 +21,14 @@ describe( 'Config', () => {
 					path: 'xyz'
 				}
 			},
-			toolbar: 'top'
+			toolbar: 'top',
+			options: {
+				foo: [
+					{ bar: 'b' },
+					{ bar: 'a' },
+					{ bar: 'z' }
+				]
+			}
 		} );
 	} );
 
@@ -334,7 +343,7 @@ describe( 'Config', () => {
 			expect( config.get( 'resize.icon.path' ) ).to.equal( 'xyz' );
 		} );
 
-		it( 'should retrieve a object of the configuration', () => {
+		it( 'should retrieve an object of the configuration', () => {
 			const resize = config.get( 'resize' );
 
 			expect( resize ).to.be.an( 'object' );
@@ -371,5 +380,83 @@ describe( 'Config', () => {
 				config.resize.maxHeight;
 			} ).to.throw();
 		} );
+
+		it( 'should not be possible to alter config object by altering returned value', () => {
+			expect( config.get( 'resize.icon.path' ) ).to.equal( 'xyz' );
+
+			const icon = config.get( 'resize.icon' );
+			icon.path = 'foo/bar';
+
+			expect( config.get( 'resize.icon.path' ) ).to.equal( 'xyz' );
+
+			const resize = config.get( 'resize' );
+			resize.icon.path = 'foo/baz';
+
+			expect( config.get( 'resize.icon.path' ) ).to.equal( 'xyz' );
+		} );
+
+		it( 'should not be possible to alter array in config by altering returned value', () => {
+			expect( config.get( 'options.foo' ) ).to.deep.equal( [ { bar: 'b' }, { bar: 'a' }, { bar: 'z' } ] );
+
+			const fooOptions = config.get( 'options.foo' );
+			fooOptions.pop();
+
+			expect( config.get( 'options.foo' ) ).to.deep.equal( [ { bar: 'b' }, { bar: 'a' }, { bar: 'z' } ] );
+
+			const options = config.get( 'options' );
+			options.foo.pop();
+
+			expect( config.get( 'options.foo' ) ).to.deep.equal( [ { bar: 'b' }, { bar: 'a' }, { bar: 'z' } ] );
+		} );
+
+		it( 'should return class & functions references from config array', () => {
+			class Foo {}
+
+			function bar() {
+				return 'bar';
+			}
+
+			const baz = () => 'baz';
+
+			config.set( 'plugins', [ Foo, bar, baz ] );
+
+			expect( config.get( 'plugins' ) ).to.deep.equal( [ Foo, bar, baz ] );
+
+			const plugins = config.get( 'plugins' );
+
+			expect( plugins[ 0 ] ).to.equal( Foo );
+			expect( plugins[ 1 ] ).to.equal( bar );
+			expect( plugins[ 2 ] ).to.equal( baz );
+
+			const pluginsAgain = config.get( 'plugins' );
+
+			// The returned array should be a new instance:
+			expect( pluginsAgain ).to.not.equal( plugins );
+
+			// But array members should remain the same contents should be equal:
+			expect( pluginsAgain ).to.deep.equal( plugins );
+		} );
+
+		it( 'should return DOM nodes references from config array', () => {
+			const foo = document.createElement( 'div' );
+
+			config.set( 'node', foo );
+			config.set( 'nodes', [ foo ] );
+
+			expect( config.get( 'node' ) ).to.equal( foo );
+			expect( config.get( 'nodes' ) ).to.deep.equal( [ foo ] );
+
+			const nodes = config.get( 'nodes' );
+
+			expect( nodes[ 0 ] ).to.equal( foo );
+
+			const nodesAgain = config.get( 'nodes' );
+
+			// The returned array should be a new instance:
+			expect( nodesAgain ).to.not.equal( nodes );
+
+			// But array members should remain the same contents should be equal:
+			expect( nodesAgain ).to.deep.equal( nodes );
+		} );
 	} );
 } );