Browse Source

Docs: Finalized renaming attrs to observable properties.

Piotrek Koszuliński 7 years ago
parent
commit
d80694fca5

+ 142 - 141
packages/ckeditor5-utils/src/observablemixin.js

@@ -12,9 +12,9 @@ import CKEditorError from './ckeditorerror';
 import extend from './lib/lodash/extend';
 import extend from './lib/lodash/extend';
 import isObject from './lib/lodash/isObject';
 import isObject from './lib/lodash/isObject';
 
 
-const attributesSymbol = Symbol( 'attributes' );
+const observablePropertiesSymbol = Symbol( 'observableProperties' );
 const boundObservablesSymbol = Symbol( 'boundObservables' );
 const boundObservablesSymbol = Symbol( 'boundObservables' );
-const boundAttributesSymbol = Symbol( 'boundAttributes' );
+const boundPropertiesSymbol = Symbol( 'boundProperties' );
 
 
 /**
 /**
  * Mixin that injects the "observable properties" and data binding functionality described in the
  * Mixin that injects the "observable properties" and data binding functionality described in the
@@ -31,8 +31,8 @@ const ObservableMixin = {
 	set( name, value ) {
 	set( name, value ) {
 		// If the first parameter is an Object, iterate over its properties.
 		// If the first parameter is an Object, iterate over its properties.
 		if ( isObject( name ) ) {
 		if ( isObject( name ) ) {
-			Object.keys( name ).forEach( attr => {
-				this.set( attr, name[ attr ] );
+			Object.keys( name ).forEach( property => {
+				this.set( property, name[ property ] );
 			}, this );
 			}, this );
 
 
 			return;
 			return;
@@ -40,21 +40,21 @@ const ObservableMixin = {
 
 
 		initObservable( this );
 		initObservable( this );
 
 
-		const attributes = this[ attributesSymbol ];
+		const properties = this[ observablePropertiesSymbol ];
 
 
-		if ( ( name in this ) && !attributes.has( name ) ) {
+		if ( ( name in this ) && !properties.has( name ) ) {
 			/**
 			/**
 			 * Cannot override an existing property.
 			 * Cannot override an existing property.
 			 *
 			 *
-			 * This error is thrown when trying to {@link ~Observable#set set} an attribute with
+			 * This error is thrown when trying to {@link ~Observable#set set} an property with
 			 * a name of an already existing property. For example:
 			 * a name of an already existing property. For example:
 			 *
 			 *
 			 *		let observable = new Model();
 			 *		let observable = new Model();
 			 *		observable.property = 1;
 			 *		observable.property = 1;
-			 *		observable.set( 'property', 2 );		// throws
+			 *		observable.set( 'property', 2 );			// throws
 			 *
 			 *
-			 *		observable.set( 'attr', 1 );
-			 *		observable.set( 'attr', 2 );			// ok, because this is an existing attribute.
+			 *		observable.set( 'property', 1 );
+			 *		observable.set( 'property', 2 );			// ok, because this is an existing property.
 			 *
 			 *
 			 * @error observable-set-cannot-override
 			 * @error observable-set-cannot-override
 			 */
 			 */
@@ -66,16 +66,16 @@ const ObservableMixin = {
 			configurable: true,
 			configurable: true,
 
 
 			get() {
 			get() {
-				return attributes.get( name );
+				return properties.get( name );
 			},
 			},
 
 
 			set( value ) {
 			set( value ) {
-				const oldValue = attributes.get( name );
+				const oldValue = properties.get( name );
 
 
 				// Allow undefined as an initial value like A.define( 'x', undefined ) (#132).
 				// Allow undefined as an initial value like A.define( 'x', undefined ) (#132).
-				// Note: When attributes map has no such own property, then its value is undefined.
-				if ( oldValue !== value || !attributes.has( name ) ) {
-					attributes.set( name, value );
+				// Note: When properties map has no such own property, then its value is undefined.
+				if ( oldValue !== value || !properties.has( name ) ) {
+					properties.set( name, value );
 					this.fire( 'change:' + name, name, value, oldValue );
 					this.fire( 'change:' + name, name, value, oldValue );
 				}
 				}
 			}
 			}
@@ -87,66 +87,66 @@ const ObservableMixin = {
 	/**
 	/**
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */
-	bind( ...bindAttrs ) {
-		if ( !bindAttrs.length || !isStringArray( bindAttrs ) ) {
+	bind( ...bindProperties ) {
+		if ( !bindProperties.length || !isStringArray( bindProperties ) ) {
 			/**
 			/**
-			 * All attributes must be strings.
+			 * All properties must be strings.
 			 *
 			 *
-			 * @error observable-bind-wrong-attrs
+			 * @error observable-bind-wrong-properties
 			 */
 			 */
-			throw new CKEditorError( 'observable-bind-wrong-attrs: All attributes must be strings.' );
+			throw new CKEditorError( 'observable-bind-wrong-properties: All properties must be strings.' );
 		}
 		}
 
 
-		if ( ( new Set( bindAttrs ) ).size !== bindAttrs.length ) {
+		if ( ( new Set( bindProperties ) ).size !== bindProperties.length ) {
 			/**
 			/**
-			 * Attributes must be unique.
+			 * Properties must be unique.
 			 *
 			 *
-			 * @error observable-bind-duplicate-attrs
+			 * @error observable-bind-duplicate-properties
 			 */
 			 */
-			throw new CKEditorError( 'observable-bind-duplicate-attrs: Attributes must be unique.' );
+			throw new CKEditorError( 'observable-bind-duplicate-properties: Properties must be unique.' );
 		}
 		}
 
 
 		initObservable( this );
 		initObservable( this );
 
 
-		const boundAttributes = this[ boundAttributesSymbol ];
+		const boundProperties = this[ boundPropertiesSymbol ];
 
 
-		bindAttrs.forEach( attrName => {
-			if ( boundAttributes.has( attrName ) ) {
+		bindProperties.forEach( propertyName => {
+			if ( boundProperties.has( propertyName ) ) {
 				/**
 				/**
-				 * Cannot bind the same attribute more that once.
+				 * Cannot bind the same property more that once.
 				 *
 				 *
 				 * @error observable-bind-rebind
 				 * @error observable-bind-rebind
 				 */
 				 */
-				throw new CKEditorError( 'observable-bind-rebind: Cannot bind the same attribute more that once.' );
+				throw new CKEditorError( 'observable-bind-rebind: Cannot bind the same property more that once.' );
 			}
 			}
 		} );
 		} );
 
 
 		const bindings = new Map();
 		const bindings = new Map();
 
 
 		// @typedef {Object} Binding
 		// @typedef {Object} Binding
-		// @property {Array} attr Attribute which is bound.
-		// @property {Array} to Array of observable–attribute components of the binding (`{ observable: ..., attr: .. }`).
+		// @property {Array} property Property which is bound.
+		// @property {Array} to Array of observable–property components of the binding (`{ observable: ..., property: .. }`).
 		// @property {Array} callback A function which processes `to` components.
 		// @property {Array} callback A function which processes `to` components.
-		bindAttrs.forEach( a => {
-			const binding = { attr: a, to: [] };
+		bindProperties.forEach( a => {
+			const binding = { property: a, to: [] };
 
 
-			boundAttributes.set( a, binding );
+			boundProperties.set( a, binding );
 			bindings.set( a, binding );
 			bindings.set( a, binding );
 		} );
 		} );
 
 
 		// @typedef {Object} BindChain
 		// @typedef {Object} BindChain
 		// @property {Function} to See {@link ~ObservableMixin#_bindTo}.
 		// @property {Function} to See {@link ~ObservableMixin#_bindTo}.
 		// @property {module:utils/observablemixin~Observable} _observable The observable which initializes the binding.
 		// @property {module:utils/observablemixin~Observable} _observable The observable which initializes the binding.
-		// @property {Array} _bindAttrs Array of `_observable` attributes to be bound.
-		// @property {Array} _to Array of `to()` observable–attributes (`{ observable: toObservable, attrs: ...toAttrs }`).
+		// @property {Array} _bindProperties Array of `_observable` properties to be bound.
+		// @property {Array} _to Array of `to()` observable–properties (`{ observable: toObservable, properties: ...toProperties }`).
 		// @property {Map} _bindings Stores bindings to be kept in
 		// @property {Map} _bindings Stores bindings to be kept in
-		// {@link ~ObservableMixin#_boundAttributes}/{@link ~ObservableMixin#_boundObservables}
+		// {@link ~ObservableMixin#_boundProperties}/{@link ~ObservableMixin#_boundObservables}
 		// initiated in this binding chain.
 		// initiated in this binding chain.
 		return {
 		return {
 			to: bindTo,
 			to: bindTo,
 
 
 			_observable: this,
 			_observable: this,
-			_bindAttrs: bindAttrs,
+			_bindProperties: bindProperties,
 			_to: [],
 			_to: [],
 			_bindings: bindings
 			_bindings: bindings
 		};
 		};
@@ -155,55 +155,55 @@ const ObservableMixin = {
 	/**
 	/**
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */
-	unbind( ...unbindAttrs ) {
+	unbind( ...unbindProperties ) {
 		// Nothing to do here if not inited yet.
 		// Nothing to do here if not inited yet.
-		if ( !( attributesSymbol in this ) ) {
+		if ( !( observablePropertiesSymbol in this ) ) {
 			return;
 			return;
 		}
 		}
 
 
-		const boundAttributes = this[ boundAttributesSymbol ];
+		const boundProperties = this[ boundPropertiesSymbol ];
 		const boundObservables = this[ boundObservablesSymbol ];
 		const boundObservables = this[ boundObservablesSymbol ];
 
 
-		if ( unbindAttrs.length ) {
-			if ( !isStringArray( unbindAttrs ) ) {
+		if ( unbindProperties.length ) {
+			if ( !isStringArray( unbindProperties ) ) {
 				/**
 				/**
-				 * Attributes must be strings.
+				 * Properties must be strings.
 				 *
 				 *
-				 * @error observable-unbind-wrong-attrs
+				 * @error observable-unbind-wrong-properties
 				 */
 				 */
-				throw new CKEditorError( 'observable-unbind-wrong-attrs: Attributes must be strings.' );
+				throw new CKEditorError( 'observable-unbind-wrong-properties: Properties must be strings.' );
 			}
 			}
 
 
-			unbindAttrs.forEach( attrName => {
-				const binding = boundAttributes.get( attrName );
+			unbindProperties.forEach( propertyName => {
+				const binding = boundProperties.get( propertyName );
 
 
 				// Nothing to do if the binding is not defined
 				// Nothing to do if the binding is not defined
 				if ( !binding ) {
 				if ( !binding ) {
 					return;
 					return;
 				}
 				}
 
 
-				let toObservable, toAttr, toAttrs, toAttrBindings;
+				let toObservable, toProperty, toProperties, toPropertyBindings;
 
 
 				binding.to.forEach( to => {
 				binding.to.forEach( to => {
 					// TODO: ES6 destructuring.
 					// TODO: ES6 destructuring.
 					toObservable = to[ 0 ];
 					toObservable = to[ 0 ];
-					toAttr = to[ 1 ];
-					toAttrs = boundObservables.get( toObservable );
-					toAttrBindings = toAttrs[ toAttr ];
+					toProperty = to[ 1 ];
+					toProperties = boundObservables.get( toObservable );
+					toPropertyBindings = toProperties[ toProperty ];
 
 
-					toAttrBindings.delete( binding );
+					toPropertyBindings.delete( binding );
 
 
-					if ( !toAttrBindings.size ) {
-						delete toAttrs[ toAttr ];
+					if ( !toPropertyBindings.size ) {
+						delete toProperties[ toProperty ];
 					}
 					}
 
 
-					if ( !Object.keys( toAttrs ).length ) {
+					if ( !Object.keys( toProperties ).length ) {
 						boundObservables.delete( toObservable );
 						boundObservables.delete( toObservable );
 						this.stopListening( toObservable, 'change' );
 						this.stopListening( toObservable, 'change' );
 					}
 					}
 				} );
 				} );
 
 
-				boundAttributes.delete( attrName );
+				boundProperties.delete( propertyName );
 			} );
 			} );
 		} else {
 		} else {
 			boundObservables.forEach( ( bindings, boundObservable ) => {
 			boundObservables.forEach( ( bindings, boundObservable ) => {
@@ -211,7 +211,7 @@ const ObservableMixin = {
 			} );
 			} );
 
 
 			boundObservables.clear();
 			boundObservables.clear();
-			boundAttributes.clear();
+			boundProperties.clear();
 		}
 		}
 	},
 	},
 
 
@@ -255,7 +255,7 @@ export default ObservableMixin;
 // @param {module:utils/observablemixin~ObservableMixin} observable
 // @param {module:utils/observablemixin~ObservableMixin} observable
 function initObservable( observable ) {
 function initObservable( observable ) {
 	// Do nothing if already inited.
 	// Do nothing if already inited.
-	if ( attributesSymbol in observable ) {
+	if ( observablePropertiesSymbol in observable ) {
 		return;
 		return;
 	}
 	}
 
 
@@ -263,13 +263,13 @@ function initObservable( observable ) {
 	//
 	//
 	// @private
 	// @private
 	// @type {Map}
 	// @type {Map}
-	Object.defineProperty( observable, attributesSymbol, {
+	Object.defineProperty( observable, observablePropertiesSymbol, {
 		value: new Map()
 		value: new Map()
 	} );
 	} );
 
 
 	// Map containing bindings to external observables. It shares the binding objects
 	// Map containing bindings to external observables. It shares the binding objects
-	// (`{ observable: A, attr: 'a', to: ... }`) with {@link module:utils/observablemixin~ObservableMixin#_boundAttributes} and
-	// it is used to observe external observables to update own attributes accordingly.
+	// (`{ observable: A, property: 'a', to: ... }`) with {@link module:utils/observablemixin~ObservableMixin#_boundProperties} and
+	// it is used to observe external observables to update own properties accordingly.
 	// See {@link module:utils/observablemixin~ObservableMixin#bind}.
 	// See {@link module:utils/observablemixin~ObservableMixin#bind}.
 	//
 	//
 	//		A.bind( 'a', 'b', 'c' ).to( B, 'x', 'y', 'x' );
 	//		A.bind( 'a', 'b', 'c' ).to( B, 'x', 'y', 'x' );
@@ -278,11 +278,11 @@ function initObservable( observable ) {
 	//			Map( {
 	//			Map( {
 	//				B: {
 	//				B: {
 	//					x: Set( [
 	//					x: Set( [
-	//						{ observable: A, attr: 'a', to: [ [ B, 'x' ] ] },
-	//						{ observable: A, attr: 'c', to: [ [ B, 'x' ] ] }
+	//						{ observable: A, property: 'a', to: [ [ B, 'x' ] ] },
+	//						{ observable: A, property: 'c', to: [ [ B, 'x' ] ] }
 	//					] ),
 	//					] ),
 	//					y: Set( [
 	//					y: Set( [
-	//						{ observable: A, attr: 'b', to: [ [ B, 'y' ] ] },
+	//						{ observable: A, property: 'b', to: [ [ B, 'y' ] ] },
 	//					] )
 	//					] )
 	//				}
 	//				}
 	//			} )
 	//			} )
@@ -293,19 +293,19 @@ function initObservable( observable ) {
 	//			Map( {
 	//			Map( {
 	//				B: {
 	//				B: {
 	//					x: Set( [
 	//					x: Set( [
-	//						{ observable: A, attr: 'a', to: [ [ B, 'x' ] ] },
-	//						{ observable: A, attr: 'c', to: [ [ B, 'x' ] ] }
+	//						{ observable: A, property: 'a', to: [ [ B, 'x' ] ] },
+	//						{ observable: A, property: 'c', to: [ [ B, 'x' ] ] }
 	//					] ),
 	//					] ),
 	//					y: Set( [
 	//					y: Set( [
-	//						{ observable: A, attr: 'b', to: [ [ B, 'y' ] ] },
+	//						{ observable: A, property: 'b', to: [ [ B, 'y' ] ] },
 	//					] ),
 	//					] ),
 	//					z: Set( [
 	//					z: Set( [
-	//						{ observable: A, attr: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
+	//						{ observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
 	//					] )
 	//					] )
 	//				},
 	//				},
 	//				C: {
 	//				C: {
 	//					w: Set( [
 	//					w: Set( [
-	//						{ observable: A, attr: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
+	//						{ observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
 	//					] )
 	//					] )
 	//				}
 	//				}
 	//			} )
 	//			} )
@@ -316,35 +316,35 @@ function initObservable( observable ) {
 		value: new Map()
 		value: new Map()
 	} );
 	} );
 
 
-	// Object that stores which attributes of this observable are bound and how. It shares
-	// the binding objects (`{ observable: A, attr: 'a', to: ... }`) with {@link utils.ObservableMixin#_boundObservables}.
+	// Object that stores which properties of this observable are bound and how. It shares
+	// the binding objects (`{ observable: A, property: 'a', to: ... }`) with {@link utils.ObservableMixin#_boundObservables}.
 	// This data structure is a reverse of {@link utils.ObservableMixin#_boundObservables} and it is helpful for
 	// This data structure is a reverse of {@link utils.ObservableMixin#_boundObservables} and it is helpful for
 	// {@link utils.ObservableMixin#unbind}.
 	// {@link utils.ObservableMixin#unbind}.
 	//
 	//
 	// See {@link utils.ObservableMixin#bind}.
 	// See {@link utils.ObservableMixin#bind}.
 	//
 	//
 	//		A.bind( 'a', 'b', 'c' ).to( B, 'x', 'y', 'x' );
 	//		A.bind( 'a', 'b', 'c' ).to( B, 'x', 'y', 'x' );
-	//		console.log( A._boundAttributes );
+	//		console.log( A._boundProperties );
 	//
 	//
 	//			Map( {
 	//			Map( {
-	//				a: { observable: A, attr: 'a', to: [ [ B, 'x' ] ] },
-	//				b: { observable: A, attr: 'b', to: [ [ B, 'y' ] ] },
-	//				c: { observable: A, attr: 'c', to: [ [ B, 'x' ] ] }
+	//				a: { observable: A, property: 'a', to: [ [ B, 'x' ] ] },
+	//				b: { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
+	//				c: { observable: A, property: 'c', to: [ [ B, 'x' ] ] }
 	//			} )
 	//			} )
 	//
 	//
 	//		A.bind( 'd' ).to( B, 'z' ).to( C, 'w' ).as( callback );
 	//		A.bind( 'd' ).to( B, 'z' ).to( C, 'w' ).as( callback );
-	//		console.log( A._boundAttributes );
+	//		console.log( A._boundProperties );
 	//
 	//
 	//			Map( {
 	//			Map( {
-	//				a: { observable: A, attr: 'a', to: [ [ B, 'x' ] ] },
-	//				b: { observable: A, attr: 'b', to: [ [ B, 'y' ] ] },
-	//				c: { observable: A, attr: 'c', to: [ [ B, 'x' ] ] },
-	//				d: { observable: A, attr: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
+	//				a: { observable: A, property: 'a', to: [ [ B, 'x' ] ] },
+	//				b: { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
+	//				c: { observable: A, property: 'c', to: [ [ B, 'x' ] ] },
+	//				d: { observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
 	//			} )
 	//			} )
 	//
 	//
 	// @private
 	// @private
 	// @type {Map}
 	// @type {Map}
-	Object.defineProperty( observable, boundAttributesSymbol, {
+	Object.defineProperty( observable, boundPropertiesSymbol, {
 		value: new Map()
 		value: new Map()
 	} );
 	} );
 }
 }
@@ -371,28 +371,28 @@ function bindTo( ...args ) {
 	// Eliminate A.bind( 'x', 'y' ).to( B, callback )
 	// Eliminate A.bind( 'x', 'y' ).to( B, callback )
 	if ( numberOfBindings > 1 && parsedArgs.callback ) {
 	if ( numberOfBindings > 1 && parsedArgs.callback ) {
 		/**
 		/**
-		 * Cannot bind multiple attributes and use a callback in one binding.
+		 * Cannot bind multiple properties and use a callback in one binding.
 		 *
 		 *
 		 * @error observable-bind-to-extra-callback
 		 * @error observable-bind-to-extra-callback
 		 */
 		 */
-		throw new CKEditorError( 'observable-bind-to-extra-callback: Cannot bind multiple attributes and use a callback in one binding.' );
+		throw new CKEditorError( 'observable-bind-to-extra-callback: Cannot bind multiple properties and use a callback in one binding.' );
 	}
 	}
 
 
 	parsedArgs.to.forEach( to => {
 	parsedArgs.to.forEach( to => {
 		// Eliminate A.bind( 'x', 'y' ).to( B, 'a' )
 		// Eliminate A.bind( 'x', 'y' ).to( B, 'a' )
-		if ( to.attrs.length && to.attrs.length !== numberOfBindings ) {
+		if ( to.properties.length && to.properties.length !== numberOfBindings ) {
 			/**
 			/**
-			 * The number of attributes must match.
+			 * The number of properties must match.
 			 *
 			 *
-			 * @error observable-bind-to-attrs-length
+			 * @error observable-bind-to-properties-length
 			 */
 			 */
-			throw new CKEditorError( 'observable-bind-to-attrs-length: The number of attributes must match.' );
+			throw new CKEditorError( 'observable-bind-to-properties-length: The number of properties must match.' );
 		}
 		}
 
 
-		// When no to.attrs specified, observing source attributes instead i.e.
+		// When no to.properties specified, observing source properties instead i.e.
 		// A.bind( 'x', 'y' ).to( B ) -> Observe B.x and B.y
 		// A.bind( 'x', 'y' ).to( B ) -> Observe B.x and B.y
-		if ( !to.attrs.length ) {
-			to.attrs = this._bindAttrs;
+		if ( !to.properties.length ) {
+			to.properties = this._bindProperties;
 		}
 		}
 	} );
 	} );
 
 
@@ -405,12 +405,12 @@ function bindTo( ...args ) {
 
 
 	attachBindToListeners( this._observable, this._to );
 	attachBindToListeners( this._observable, this._to );
 
 
-	// Update observable._boundAttributes and observable._boundObservables.
+	// Update observable._boundProperties and observable._boundObservables.
 	updateBindToBound( this );
 	updateBindToBound( this );
 
 
-	// Set initial values of bound attributes.
-	this._bindAttrs.forEach( attrName => {
-		updateBoundObservableAttr( this._observable, attrName );
+	// Set initial values of bound properties.
+	this._bindProperties.forEach( propertyName => {
+		updateBoundObservableProperty( this._observable, propertyName );
 	} );
 	} );
 }
 }
 
 
@@ -432,8 +432,8 @@ function isStringArray( arr ) {
 //
 //
 //		{
 //		{
 //			to: [
 //			to: [
-//				{ observable: B, attrs: [ 'a' ] },
-//				{ observable: C, attrs: [ 'b' ] },
+//				{ observable: B, properties: [ 'a' ] },
+//				{ observable: C, properties: [ 'b' ] },
 //			],
 //			],
 //			callback: call
 //			callback: call
 // 		}
 // 		}
@@ -461,9 +461,9 @@ function parseBindToArgs( ...args ) {
 
 
 	args.forEach( a => {
 	args.forEach( a => {
 		if ( typeof a == 'string' ) {
 		if ( typeof a == 'string' ) {
-			lastObservable.attrs.push( a );
+			lastObservable.properties.push( a );
 		} else if ( typeof a == 'object' ) {
 		} else if ( typeof a == 'object' ) {
-			lastObservable = { observable: a, attrs: [] };
+			lastObservable = { observable: a, properties: [] };
 			parsed.to.push( lastObservable );
 			parsed.to.push( lastObservable );
 		} else {
 		} else {
 			throw new CKEditorError( 'observable-bind-to-parse-error: Invalid argument syntax in `to()`.' );
 			throw new CKEditorError( 'observable-bind-to-parse-error: Invalid argument syntax in `to()`.' );
@@ -478,25 +478,25 @@ function parseBindToArgs( ...args ) {
 // @private
 // @private
 // @param {Binding} binding A binding to store in {@link Observable#_boundObservables}.
 // @param {Binding} binding A binding to store in {@link Observable#_boundObservables}.
 // @param {Observable} toObservable A observable, which is a new component of `binding`.
 // @param {Observable} toObservable A observable, which is a new component of `binding`.
-// @param {String} toAttrName A name of `toObservable`'s attribute, a new component of the `binding`.
-function updateBoundObservables( observable, binding, toObservable, toAttrName ) {
+// @param {String} toPropertyName A name of `toObservable`'s property, a new component of the `binding`.
+function updateBoundObservables( observable, binding, toObservable, toPropertyName ) {
 	const boundObservables = observable[ boundObservablesSymbol ];
 	const boundObservables = observable[ boundObservablesSymbol ];
 	const bindingsToObservable = boundObservables.get( toObservable );
 	const bindingsToObservable = boundObservables.get( toObservable );
 	const bindings = bindingsToObservable || {};
 	const bindings = bindingsToObservable || {};
 
 
-	if ( !bindings[ toAttrName ] ) {
-		bindings[ toAttrName ] = new Set();
+	if ( !bindings[ toPropertyName ] ) {
+		bindings[ toPropertyName ] = new Set();
 	}
 	}
 
 
 	// Pass the binding to a corresponding Set in `observable._boundObservables`.
 	// Pass the binding to a corresponding Set in `observable._boundObservables`.
-	bindings[ toAttrName ].add( binding );
+	bindings[ toPropertyName ].add( binding );
 
 
 	if ( !bindingsToObservable ) {
 	if ( !bindingsToObservable ) {
 		boundObservables.set( toObservable, bindings );
 		boundObservables.set( toObservable, bindings );
 	}
 	}
 }
 }
 
 
-// Synchronizes {@link Observable#_boundAttributes} and {@link Observable#_boundObservables}
+// Synchronizes {@link Observable#_boundProperties} and {@link Observable#_boundObservables}
 // with {@link BindChain}.
 // with {@link BindChain}.
 //
 //
 // Assuming the following binding being created
 // Assuming the following binding being created
@@ -506,16 +506,16 @@ function updateBoundObservables( observable, binding, toObservable, toAttrName )
 // the following bindings were initialized by {@link Observable#bind} in {@link BindChain#_bindings}:
 // the following bindings were initialized by {@link Observable#bind} in {@link BindChain#_bindings}:
 //
 //
 // 		{
 // 		{
-// 			a: { observable: A, attr: 'a', to: [] },
-// 			b: { observable: A, attr: 'b', to: [] },
+// 			a: { observable: A, property: 'a', to: [] },
+// 			b: { observable: A, property: 'b', to: [] },
 // 		}
 // 		}
 //
 //
 // Iterate over all bindings in this chain and fill their `to` properties with
 // Iterate over all bindings in this chain and fill their `to` properties with
 // corresponding to( ... ) arguments (components of the binding), so
 // corresponding to( ... ) arguments (components of the binding), so
 //
 //
 // 		{
 // 		{
-// 			a: { observable: A, attr: 'a', to: [ B, 'x' ] },
-// 			b: { observable: A, attr: 'b', to: [ B, 'y' ] },
+// 			a: { observable: A, property: 'a', to: [ B, 'x' ] },
+// 			b: { observable: A, property: 'b', to: [ B, 'y' ] },
 // 		}
 // 		}
 //
 //
 // Then update the structure of {@link Observable#_boundObservables} with updated
 // Then update the structure of {@link Observable#_boundObservables} with updated
@@ -524,10 +524,10 @@ function updateBoundObservables( observable, binding, toObservable, toAttrName )
 // 		Map( {
 // 		Map( {
 // 			B: {
 // 			B: {
 // 				x: Set( [
 // 				x: Set( [
-// 					{ observable: A, attr: 'a', to: [ [ B, 'x' ] ] }
+// 					{ observable: A, property: 'a', to: [ [ B, 'x' ] ] }
 // 				] ),
 // 				] ),
 // 				y: Set( [
 // 				y: Set( [
-// 					{ observable: A, attr: 'b', to: [ [ B, 'y' ] ] },
+// 					{ observable: A, property: 'b', to: [ [ B, 'y' ] ] },
 // 				] )
 // 				] )
 //			}
 //			}
 // 		} )
 // 		} )
@@ -535,31 +535,31 @@ function updateBoundObservables( observable, binding, toObservable, toAttrName )
 // @private
 // @private
 // @param {BindChain} chain The binding initialized by {@link Observable#bind}.
 // @param {BindChain} chain The binding initialized by {@link Observable#bind}.
 function updateBindToBound( chain ) {
 function updateBindToBound( chain ) {
-	let toAttr;
+	let toProperty;
 
 
-	chain._bindings.forEach( ( binding, attrName ) => {
+	chain._bindings.forEach( ( binding, propertyName ) => {
 		// Note: For a binding without a callback, this will run only once
 		// Note: For a binding without a callback, this will run only once
 		// like in A.bind( 'x', 'y' ).to( B, 'a', 'b' )
 		// like in A.bind( 'x', 'y' ).to( B, 'a', 'b' )
 		// TODO: ES6 destructuring.
 		// TODO: ES6 destructuring.
 		chain._to.forEach( to => {
 		chain._to.forEach( to => {
-			toAttr = to.attrs[ binding.callback ? 0 : chain._bindAttrs.indexOf( attrName ) ];
+			toProperty = to.properties[ binding.callback ? 0 : chain._bindProperties.indexOf( propertyName ) ];
 
 
-			binding.to.push( [ to.observable, toAttr ] );
-			updateBoundObservables( chain._observable, binding, to.observable, toAttr );
+			binding.to.push( [ to.observable, toProperty ] );
+			updateBoundObservables( chain._observable, binding, to.observable, toProperty );
 		} );
 		} );
 	} );
 	} );
 }
 }
 
 
-// Updates an attribute of a {@link Observable} with a value
-// determined by an entry in {@link Observable#_boundAttributes}.
+// Updates an property of a {@link Observable} with a value
+// determined by an entry in {@link Observable#_boundProperties}.
 //
 //
 // @private
 // @private
-// @param {Observable} observable A observable which attribute is to be updated.
-// @param {String} attrName An attribute to be updated.
-function updateBoundObservableAttr( observable, attrName ) {
-	const boundAttributes = observable[ boundAttributesSymbol ];
-	const binding = boundAttributes.get( attrName );
-	let attrValue;
+// @param {Observable} observable A observable which property is to be updated.
+// @param {String} propertyName An property to be updated.
+function updateBoundObservableProperty( observable, propertyName ) {
+	const boundProperties = observable[ boundPropertiesSymbol ];
+	const binding = boundProperties.get( propertyName );
+	let propertyValue;
 
 
 	// When a binding with callback is created like
 	// When a binding with callback is created like
 	//
 	//
@@ -567,21 +567,21 @@ function updateBoundObservableAttr( observable, attrName ) {
 	//
 	//
 	// collect B.b and C.c, then pass them to callback to set A.a.
 	// collect B.b and C.c, then pass them to callback to set A.a.
 	if ( binding.callback ) {
 	if ( binding.callback ) {
-		attrValue = binding.callback.apply( observable, binding.to.map( to => to[ 0 ][ to[ 1 ] ] ) );
+		propertyValue = binding.callback.apply( observable, binding.to.map( to => to[ 0 ][ to[ 1 ] ] ) );
 	} else {
 	} else {
-		attrValue = binding.to[ 0 ];
-		attrValue = attrValue[ 0 ][ attrValue[ 1 ] ];
+		propertyValue = binding.to[ 0 ];
+		propertyValue = propertyValue[ 0 ][ propertyValue[ 1 ] ];
 	}
 	}
 
 
-	if ( observable.hasOwnProperty( attrName ) ) {
-		observable[ attrName ] = attrValue;
+	if ( observable.hasOwnProperty( propertyName ) ) {
+		observable[ propertyName ] = propertyValue;
 	} else {
 	} else {
-		observable.set( attrName, attrValue );
+		observable.set( propertyName, propertyValue );
 	}
 	}
 }
 }
 
 
 // Starts listening to changes in {@link BindChain._to} observables to update
 // Starts listening to changes in {@link BindChain._to} observables to update
-// {@link BindChain._observable} {@link BindChain._bindAttrs}. Also sets the
+// {@link BindChain._observable} {@link BindChain._bindProperties}. Also sets the
 // initial state of {@link BindChain._observable}.
 // initial state of {@link BindChain._observable}.
 //
 //
 // @private
 // @private
@@ -594,14 +594,14 @@ function attachBindToListeners( observable, toBindings ) {
 		// If there's already a chain between the observables (`observable` listens to
 		// If there's already a chain between the observables (`observable` listens to
 		// `to.observable`), there's no need to create another `change` event listener.
 		// `to.observable`), there's no need to create another `change` event listener.
 		if ( !boundObservables.get( to.observable ) ) {
 		if ( !boundObservables.get( to.observable ) ) {
-			observable.listenTo( to.observable, 'change', ( evt, attrName ) => {
-				bindings = boundObservables.get( to.observable )[ attrName ];
+			observable.listenTo( to.observable, 'change', ( evt, propertyName ) => {
+				bindings = boundObservables.get( to.observable )[ propertyName ];
 
 
-				// Note: to.observable will fire for any attribute change, react
-				// to changes of attributes which are bound only.
+				// Note: to.observable will fire for any property change, react
+				// to changes of properties which are bound only.
 				if ( bindings ) {
 				if ( bindings ) {
 					bindings.forEach( binding => {
 					bindings.forEach( binding => {
-						updateBoundObservableAttr( observable, binding.attr );
+						updateBoundObservableProperty( observable, binding.property );
 					} );
 					} );
 				}
 				}
 			} );
 			} );
@@ -615,6 +615,7 @@ function attachBindToListeners( observable, toBindings ) {
  * Can be easily implemented by a class by mixing the {@link module:utils/observablemixin~ObservableMixin} mixin.
  * Can be easily implemented by a class by mixing the {@link module:utils/observablemixin~ObservableMixin} mixin.
  *
  *
  * @interface Observable
  * @interface Observable
+ * @extends module:utils/emittermixin~Emitter
  */
  */
 
 
 /**
 /**
@@ -638,15 +639,15 @@ function attachBindToListeners( observable, toBindings ) {
  * Creates and sets the value of an observable property of this object. Such an property becomes a part
  * Creates and sets the value of an observable property of this object. Such an property becomes a part
  * of the state and is be observable.
  * of the state and is be observable.
  *
  *
- * It accepts also a single object literal containing key/value pairs with propertys to be set.
+ * It accepts also a single object literal containing key/value pairs with properties to be set.
  *
  *
  * This method throws the `observable-set-cannot-override` error if the observable instance already
  * This method throws the `observable-set-cannot-override` error if the observable instance already
  * have a property with the given property name. This prevents from mistakenly overriding existing
  * have a property with the given property 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`.
  * properties and methods, but means that `foo.set( 'bar', 1 )` may be slightly slower than `foo.bar = 1`.
  *
  *
  * @method #set
  * @method #set
- * @param {String|Object} name The attribute's name or object with `name=>value` pairs.
- * @param {*} [value] The attribute's value (if `name` was passed in the first parameter).
+ * @param {String|Object} name The property's name or object with `name=>value` pairs.
+ * @param {*} [value] The property's value (if `name` was passed in the first parameter).
  */
  */
 
 
 /**
 /**
@@ -665,7 +666,7 @@ function attachBindToListeners( observable, toBindings ) {
  *		A.bind( 'a' ).to( B, 'b', C, 'd', ( b, d ) => b + d );
  *		A.bind( 'a' ).to( B, 'b', C, 'd', ( b, d ) => b + d );
  *
  *
  * @method #bind
  * @method #bind
- * @param {...String} bindAttrs Observable properties that will be bound to another observable(s).
+ * @param {...String} bindProperties Observable properties that will be bound to another observable(s).
  * @returns {Object} The bind chain with the `to()` method.
  * @returns {Object} The bind chain with the `to()` method.
  */
  */
 
 
@@ -676,7 +677,7 @@ function attachBindToListeners( observable, toBindings ) {
  *		A.unbind();
  *		A.unbind();
  *
  *
  * @method #unbind
  * @method #unbind
- * @param {...String} [unbindAttrs] Observable properties to be unbound. All the bindings will
+ * @param {...String} [unbindProperties] Observable properties to be unbound. All the bindings will
  * be released if no properties provided.
  * be released if no properties provided.
  */
  */
 
 

+ 41 - 41
packages/ckeditor5-utils/tests/observablemixin.js

@@ -31,9 +31,9 @@ describe( 'ObservableMixin', () => {
 
 
 describe( 'Observable', () => {
 describe( 'Observable', () => {
 	class Observable {
 	class Observable {
-		constructor( attrs ) {
-			if ( attrs ) {
-				this.set( attrs );
+		constructor( properties ) {
+			if ( properties ) {
+				this.set( properties );
 			}
 			}
 		}
 		}
 	}
 	}
@@ -50,7 +50,7 @@ describe( 'Observable', () => {
 		} );
 		} );
 	} );
 	} );
 
 
-	it( 'should set attributes on creation', () => {
+	it( 'should set properties on creation', () => {
 		expect( car ).to.have.property( 'color', 'red' );
 		expect( car ).to.have.property( 'color', 'red' );
 		expect( car ).to.have.property( 'year', 2015 );
 		expect( car ).to.have.property( 'year', 2015 );
 	} );
 	} );
@@ -128,7 +128,7 @@ describe( 'Observable', () => {
 			);
 			);
 		} );
 		} );
 
 
-		it( 'should not fire the "change" event for the same attribute value', () => {
+		it( 'should not fire the "change" event for the same property value', () => {
 			const spy = sinon.spy();
 			const spy = sinon.spy();
 			const spyColor = sinon.spy();
 			const spyColor = sinon.spy();
 
 
@@ -168,7 +168,7 @@ describe( 'Observable', () => {
 			expect( car.method ).to.be.a( 'function' );
 			expect( car.method ).to.be.a( 'function' );
 		} );
 		} );
 
 
-		it( 'should allow setting attributes with undefined value', () => {
+		it( 'should allow setting properties with undefined value', () => {
 			const spy = sinon.spy();
 			const spy = sinon.spy();
 
 
 			car.on( 'change', spy );
 			car.on( 'change', spy );
@@ -186,39 +186,39 @@ describe( 'Observable', () => {
 	} );
 	} );
 
 
 	describe( 'bind()', () => {
 	describe( 'bind()', () => {
-		it( 'should chain for a single attribute', () => {
+		it( 'should chain for a single property', () => {
 			expect( car.bind( 'color' ) ).to.contain.keys( 'to' );
 			expect( car.bind( 'color' ) ).to.contain.keys( 'to' );
 		} );
 		} );
 
 
-		it( 'should chain for multiple attributes', () => {
+		it( 'should chain for multiple properties', () => {
 			expect( car.bind( 'color', 'year' ) ).to.contain.keys( 'to' );
 			expect( car.bind( 'color', 'year' ) ).to.contain.keys( 'to' );
 		} );
 		} );
 
 
-		it( 'should chain for nonexistent attributes', () => {
+		it( 'should chain for nonexistent properties', () => {
 			expect( car.bind( 'nonexistent' ) ).to.contain.keys( 'to' );
 			expect( car.bind( 'nonexistent' ) ).to.contain.keys( 'to' );
 		} );
 		} );
 
 
-		it( 'should throw when attributes are not strings', () => {
+		it( 'should throw when properties are not strings', () => {
 			expect( () => {
 			expect( () => {
 				car.bind();
 				car.bind();
-			} ).to.throw( CKEditorError, /observable-bind-wrong-attrs/ );
+			} ).to.throw( CKEditorError, /observable-bind-wrong-properties/ );
 
 
 			expect( () => {
 			expect( () => {
 				car.bind( new Date() );
 				car.bind( new Date() );
-			} ).to.throw( CKEditorError, /observable-bind-wrong-attrs/ );
+			} ).to.throw( CKEditorError, /observable-bind-wrong-properties/ );
 
 
 			expect( () => {
 			expect( () => {
 				car.bind( 'color', new Date() );
 				car.bind( 'color', new Date() );
-			} ).to.throw( CKEditorError, /observable-bind-wrong-attrs/ );
+			} ).to.throw( CKEditorError, /observable-bind-wrong-properties/ );
 		} );
 		} );
 
 
-		it( 'should throw when the same attribute is used than once', () => {
+		it( 'should throw when the same property is used than once', () => {
 			expect( () => {
 			expect( () => {
 				car.bind( 'color', 'color' );
 				car.bind( 'color', 'color' );
-			} ).to.throw( CKEditorError, /observable-bind-duplicate-attrs/ );
+			} ).to.throw( CKEditorError, /observable-bind-duplicate-properties/ );
 		} );
 		} );
 
 
-		it( 'should throw when binding the same attribute more than once', () => {
+		it( 'should throw when binding the same property more than once', () => {
 			expect( () => {
 			expect( () => {
 				car.bind( 'color' );
 				car.bind( 'color' );
 				car.bind( 'color' );
 				car.bind( 'color' );
@@ -240,7 +240,7 @@ describe( 'Observable', () => {
 				} ).to.throw( CKEditorError, /observable-bind-to-parse-error/ );
 				} ).to.throw( CKEditorError, /observable-bind-to-parse-error/ );
 			} );
 			} );
 
 
-			it( 'should throw when binding multiple attributes to multiple observables', () => {
+			it( 'should throw when binding multiple properties to multiple observables', () => {
 				let vehicle = new Car();
 				let vehicle = new Car();
 				const car1 = new Car( { color: 'red', year: 1943 } );
 				const car1 = new Car( { color: 'red', year: 1943 } );
 				const car2 = new Car( { color: 'yellow', year: 1932 } );
 				const car2 = new Car( { color: 'yellow', year: 1932 } );
@@ -274,7 +274,7 @@ describe( 'Observable', () => {
 				} ).to.throw( CKEditorError, /observable-bind-to-no-callback/ );
 				} ).to.throw( CKEditorError, /observable-bind-to-no-callback/ );
 			} );
 			} );
 
 
-			it( 'should throw when binding multiple attributes but passed a callback', () => {
+			it( 'should throw when binding multiple properties but passed a callback', () => {
 				let vehicle = new Car();
 				let vehicle = new Car();
 
 
 				expect( () => {
 				expect( () => {
@@ -288,7 +288,7 @@ describe( 'Observable', () => {
 				} ).to.throw( CKEditorError, /observable-bind-to-extra-callback/ );
 				} ).to.throw( CKEditorError, /observable-bind-to-extra-callback/ );
 			} );
 			} );
 
 
-			it( 'should throw when binding a single attribute but multiple callbacks', () => {
+			it( 'should throw when binding a single property but multiple callbacks', () => {
 				let vehicle = new Car();
 				let vehicle = new Car();
 
 
 				expect( () => {
 				expect( () => {
@@ -302,33 +302,33 @@ describe( 'Observable', () => {
 				} ).to.throw( CKEditorError, /observable-bind-to-parse-error/ );
 				} ).to.throw( CKEditorError, /observable-bind-to-parse-error/ );
 			} );
 			} );
 
 
-			it( 'should throw when a number of attributes does not match', () => {
+			it( 'should throw when a number of properties does not match', () => {
 				let vehicle = new Car();
 				let vehicle = new Car();
 
 
 				expect( () => {
 				expect( () => {
 					vehicle.bind( 'color' ).to( car, 'color', 'year' );
 					vehicle.bind( 'color' ).to( car, 'color', 'year' );
-				} ).to.throw( CKEditorError, /observable-bind-to-attrs-length/ );
+				} ).to.throw( CKEditorError, /observable-bind-to-properties-length/ );
 
 
 				expect( () => {
 				expect( () => {
 					vehicle = new Car();
 					vehicle = new Car();
 
 
 					vehicle.bind( 'color', 'year' ).to( car, 'color' );
 					vehicle.bind( 'color', 'year' ).to( car, 'color' );
-				} ).to.throw( CKEditorError, /observable-bind-to-attrs-length/ );
+				} ).to.throw( CKEditorError, /observable-bind-to-properties-length/ );
 
 
 				expect( () => {
 				expect( () => {
 					vehicle = new Car();
 					vehicle = new Car();
 
 
 					vehicle.bind( 'color' ).to( car, 'color', 'year', () => {} );
 					vehicle.bind( 'color' ).to( car, 'color', 'year', () => {} );
-				} ).to.throw( CKEditorError, /observable-bind-to-attrs-length/ );
+				} ).to.throw( CKEditorError, /observable-bind-to-properties-length/ );
 
 
 				expect( () => {
 				expect( () => {
 					vehicle = new Car();
 					vehicle = new Car();
 
 
 					vehicle.bind( 'color' ).to( car, 'color', car, 'color', 'year', () => {} );
 					vehicle.bind( 'color' ).to( car, 'color', car, 'color', 'year', () => {} );
-				} ).to.throw( CKEditorError, /observable-bind-to-attrs-length/ );
+				} ).to.throw( CKEditorError, /observable-bind-to-properties-length/ );
 			} );
 			} );
 
 
-			it( 'should work when attributes don\'t exist in to() observable #1', () => {
+			it( 'should work when properties don\'t exist in to() observable #1', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 
 
 				vehicle.bind( 'color' ).to( car, 'nonexistent in car' );
 				vehicle.bind( 'color' ).to( car, 'nonexistent in car' );
@@ -342,7 +342,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should work when attributes don\'t exist in to() observable #2', () => {
+			it( 'should work when properties don\'t exist in to() observable #2', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 
 
 				vehicle.bind( 'nonexistent in car' ).to( car );
 				vehicle.bind( 'nonexistent in car' ).to( car );
@@ -356,7 +356,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should work when attributes don\'t exist in to() observable #3', () => {
+			it( 'should work when properties don\'t exist in to() observable #3', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 
 
 				vehicle.bind( 'year' ).to( car, 'color', car, 'nonexistent in car', ( a, b ) => a + b );
 				vehicle.bind( 'year' ).to( car, 'color', car, 'nonexistent in car', ( a, b ) => a + b );
@@ -370,7 +370,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should set new observable attributes', () => {
+			it( 'should set new observable properties', () => {
 				const car = new Car( { color: 'green', year: 2001, type: 'pickup' } );
 				const car = new Car( { color: 'green', year: 2001, type: 'pickup' } );
 				const vehicle = new Car( { 'not involved': true } );
 				const vehicle = new Car( { 'not involved': true } );
 
 
@@ -382,7 +382,7 @@ describe( 'Observable', () => {
 				expect( vehicle ).to.have.property( 'not involved' );
 				expect( vehicle ).to.have.property( 'not involved' );
 			} );
 			} );
 
 
-			it( 'should work when no attribute specified #1', () => {
+			it( 'should work when no property specified #1', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 
 
 				vehicle.bind( 'color' ).to( car );
 				vehicle.bind( 'color' ).to( car );
@@ -396,7 +396,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should work for a single attribute', () => {
+			it( 'should work for a single property', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 
 
 				vehicle.bind( 'color' ).to( car, 'color' );
 				vehicle.bind( 'color' ).to( car, 'color' );
@@ -410,7 +410,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should work for multiple attributes', () => {
+			it( 'should work for multiple properties', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 
 
 				vehicle.bind( 'color', 'year' ).to( car, 'color', 'year' );
 				vehicle.bind( 'color', 'year' ).to( car, 'color', 'year' );
@@ -424,7 +424,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should work for attributes that don\'t exist in the observable', () => {
+			it( 'should work for properties that don\'t exist in the observable', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 
 
 				vehicle.bind( 'nonexistent in vehicle' ).to( car, 'color' );
 				vehicle.bind( 'nonexistent in vehicle' ).to( car, 'color' );
@@ -438,7 +438,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should work when using the same attribute name more than once', () => {
+			it( 'should work when using the same property name more than once', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 
 
 				vehicle.bind( 'color', 'year' ).to( car, 'year', 'year' );
 				vehicle.bind( 'color', 'year' ).to( car, 'year', 'year' );
@@ -467,7 +467,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should work with callback – set a new observable attribute', () => {
+			it( 'should work with callback – set a new observable property', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 				const car1 = new Car( { type: 'pickup' } );
 				const car1 = new Car( { type: 'pickup' } );
 				const car2 = new Car( { type: 'truck' } );
 				const car2 = new Car( { type: 'truck' } );
@@ -713,7 +713,7 @@ describe( 'Observable', () => {
 				);
 				);
 			} );
 			} );
 
 
-			it( 'should fire a single change event per bound attribute', () => {
+			it( 'should fire a single change event per bound property', () => {
 				const vehicle = new Car();
 				const vehicle = new Car();
 				const car = new Car( { color: 'red', year: 1943 } );
 				const car = new Car( { color: 'red', year: 1943 } );
 				const spy = sinon.spy();
 				const spy = sinon.spy();
@@ -739,7 +739,7 @@ describe( 'Observable', () => {
 			observable.unbind();
 			observable.unbind();
 		} );
 		} );
 
 
-		it( 'should not fail when unbinding attribute that is not bound', () => {
+		it( 'should not fail when unbinding property that is not bound', () => {
 			const observable = new Observable();
 			const observable = new Observable();
 
 
 			observable.bind( 'foo' ).to( car, 'color' );
 			observable.bind( 'foo' ).to( car, 'color' );
@@ -747,10 +747,10 @@ describe( 'Observable', () => {
 			expect( () => observable.unbind( 'bar' ) ).to.not.throw();
 			expect( () => observable.unbind( 'bar' ) ).to.not.throw();
 		} );
 		} );
 
 
-		it( 'should throw when non-string attribute is passed', () => {
+		it( 'should throw when non-string property is passed', () => {
 			expect( () => {
 			expect( () => {
 				car.unbind( new Date() );
 				car.unbind( new Date() );
-			} ).to.throw( CKEditorError, /observable-unbind-wrong-attrs/ );
+			} ).to.throw( CKEditorError, /observable-unbind-wrong-properties/ );
 		} );
 		} );
 
 
 		it( 'should remove all bindings', () => {
 		it( 'should remove all bindings', () => {
@@ -768,7 +768,7 @@ describe( 'Observable', () => {
 			);
 			);
 		} );
 		} );
 
 
-		it( 'should remove bindings of certain attributes', () => {
+		it( 'should remove bindings of certain properties', () => {
 			const vehicle = new Car();
 			const vehicle = new Car();
 			const car = new Car( { color: 'red', year: 2000, torque: 160 } );
 			const car = new Car( { color: 'red', year: 2000, torque: 160 } );
 
 
@@ -784,7 +784,7 @@ describe( 'Observable', () => {
 			);
 			);
 		} );
 		} );
 
 
-		it( 'should remove bindings of certain attributes, callback', () => {
+		it( 'should remove bindings of certain properties, callback', () => {
 			const vehicle = new Car();
 			const vehicle = new Car();
 			const car1 = new Car( { color: 'red' } );
 			const car1 = new Car( { color: 'red' } );
 			const car2 = new Car( { color: 'blue' } );
 			const car2 = new Car( { color: 'blue' } );
@@ -802,7 +802,7 @@ describe( 'Observable', () => {
 			);
 			);
 		} );
 		} );
 
 
-		it( 'should be able to unbind two attributes from a single source observable attribute', () => {
+		it( 'should be able to unbind two properties from a single source observable property', () => {
 			const vehicle = new Car();
 			const vehicle = new Car();
 
 
 			vehicle.bind( 'color' ).to( car, 'color' );
 			vehicle.bind( 'color' ).to( car, 'color' );