|
@@ -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.
|
|
|
*/
|
|
*/
|
|
|
|
|
|