Преглед на файлове

Changed the attribute class, because it need to work with the collaborating editing so attributes need to be serializable and deserializable without loosing any important informations.

Piotr Jasiun преди 10 години
родител
ревизия
22c6aff594

+ 87 - 9
packages/ckeditor5-utils/src/document/attribute.js

@@ -5,14 +5,9 @@
 
 'use strict';
 
-CKEDITOR.define( function() {
+CKEDITOR.define( [ 'utils' ], function( utils ) {
 	/**
-	 * Linear data attribute. Attributes can store any additional information, its meaning is defined by the code which
-	 * use the.
-	 *
-	 *
-	 * Note that if two attributes has the same meaning the same `Attribute` instance should be used.
-	 * To handle this use {@link document.AttributeFactory}.
+	 * Attributes can store any additional information for nodes in the data model.
 	 *
 	 * @class Attribute
 	 */
@@ -25,7 +20,7 @@ CKEDITOR.define( function() {
 		 */
 		constructor( key, value ) {
 			/**
-			 * Attribute key
+			 * Attribute key.
 			 *
 			 * @readonly
 			 * @property {String} key
@@ -33,14 +28,97 @@ CKEDITOR.define( function() {
 			this.key = key;
 
 			/**
-			 * Attribute value
+			 * Attribute value. Note that value may be any type, including objects.
 			 *
 			 * @readonly
 			 * @property {Mixed} value
 			 */
 			this.value = value;
+
+			/**
+			 * Attribute hash. Used to compare attribute. Two attributes with the same key and value will have the same hash.
+			 *
+			 * @readonly
+			 * @private
+			 * @property {String} _hash
+			 */
+			this._hash = JSON.stringify( this.key ) + ': ' + JSON.stringify( this.value, sort );
+
+			// If attribute is registered the registered one should be returned.
+			if ( Attribute._register[ this._hash ] ) {
+				return Attribute._register[ this._hash ];
+			}
+
+			// We do no care about the order so collections with the same elements should return the same hash.
+			function sort( key, value ) {
+				if ( !utils.isArray( value ) && utils.isObject( value ) ) {
+					var sorted = {};
+
+					// Sort keys and fill up the sorted object.
+					Object.keys( value ).sort().forEach( function( key ) {
+						sorted[ key ] = value[ key ];
+					} );
+
+					return sorted;
+				} else {
+					return value;
+				}
+			}
+		}
+
+		/**
+		 * Compare two attributes. Returns true is two attributes have the same key and value even if the order of value
+		 * elements is different.
+		 *
+		 *	var attr1 = new Attribute( 'foo', { a: 1, b: 2 } );
+		 *	var attr2 = new Attribute( 'foo', { b: 2, a: 1 } );
+		 *	attr1.isEqual( attr2 ); // true
+		 *
+		 * @param {document.Attribute} otherAttr other attribute
+		 * @returns {Boolean} True if attributes equals.
+		 */
+		isEqual( otherAttr ) {
+			return this._hash === otherAttr._hash;
+		}
+
+		/**
+		 * To save memory common used attributes may be registered. If an attribute is registered the constructor will
+		 * always return the same instance of this attribute.
+		 *
+		 * Note that attributes are registered globally.
+		 *
+		 *	var attr1 = Attribute.register( 'bold', true );
+		 *	var attr2 = Attribute.register( 'bold', true );
+		 *	var attr3 = new Attribute( 'bold', true );
+		 *	attr1 === attr2 // true
+		 *	attr1 === attr3 // true
+		 *
+		 * @static
+		 * @param {String} key Attribute key
+		 * @param {Mixed} value Attribute value
+		 * @returns {document.Attribute} Registered attribute.
+		 */
+		static register( key, value ) {
+			var attr = new Attribute( key, value );
+
+			if ( this._register[ attr._hash ] ) {
+				return this._register[ attr._hash ];
+			} else {
+				this._register[ attr._hash ] = attr;
+
+				return attr;
+			}
 		}
 	}
 
+	/**
+	 * Attribute register where all registered attributes are stored.
+	 *
+	 * @static
+	 * @private
+	 * @property {String} _hash
+	 */
+	Attribute._register = {};
+
 	return Attribute;
 } );

+ 0 - 71
packages/ckeditor5-utils/src/document/attributefactory.js

@@ -1,71 +0,0 @@
-/**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-CKEDITOR.define( [ 'document/attribute', 'utils' ], function( Attribute, utils ) {
-	/**
-	 * Factory to use the same attribute for the same key-values pair, so common attributes use the same object instance.
-	 *
-	 * @class document.AttributeFactory
-	 */
-	class AttributeFactory {
-		/**
-		 * Creates new attribute factory.
-		 */
-		constructor() {
-			this._stored = {};
-		}
-
-		/**
-		 * Create a new attribute or return the one which already exists. Calling `create` with the same key and value
-		 * the same object instance will be used. This methods should be used to get common attributes, like bold.
-		 *
-		 * @param {String} key Attribute key
-		 * @param {Mixed} value Attribute value
-		 * @returns {document.Attribute} New attribute or stored one.
-		 */
-		create( key, value ) {
-			var hash = this._stringify( key, value );
-
-			if ( !this._stored[ hash ] ) {
-				this._stored[ hash ] = new Attribute( key, value );
-			}
-
-			return this._stored[ hash ];
-		}
-
-		/**
-		 * Produce a string hash of an item.
-		 *
-		 * @private
-		 *
-		 * @param {String} key Attribute key
-		 * @param {Mixed} value Attribute value
-		 * @returns {document.Attribute} New attribute or stored one.
-		 */
-		_stringify( key, value ) {
-			return JSON.stringify( key ) + ': ' + JSON.stringify( value, sort );
-
-			// We do no care about the order so collections with the same elements should return the same hash.
-			function sort( key, value ) {
-				if ( !utils.isArray( value ) && utils.isObject( value ) ) {
-					var sorted = {};
-
-					// Sort keys and fill up the sorted object.
-					Object.keys( value ).sort().forEach( function( key ) {
-						sorted[ key ] = value[ key ];
-					} );
-
-					return sorted;
-				} else {
-					return value;
-				}
-			}
-		}
-	}
-
-	return AttributeFactory;
-} );

+ 65 - 1
packages/ckeditor5-utils/tests/document/attribute.js

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

+ 0 - 55
packages/ckeditor5-utils/tests/document/attributefactory.js

@@ -1,55 +0,0 @@
-/**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: document */
-
-'use strict';
-
-var modules = bender.amd.require( 'document/attributefactory' );
-
-describe( 'create', function() {
-	it( 'should create the same instance', function() {
-		var AttributeFactory = modules[ 'document/attributefactory' ];
-
-		var factory = new AttributeFactory();
-
-		var attr1 = factory.create( 'foo', 'bar' );
-		var attr2 = factory.create( 'foo', 'bar' );
-
-		expect( attr1 ).to.equals( attr2 );
-		expect( attr1 ).to.have.property( 'key' ).that.equals( 'foo' );
-		expect( attr1 ).to.have.property( 'value' ).that.equals( 'bar' );
-	} );
-
-	it( 'should create the same instance even if object has different order', function() {
-		var AttributeFactory = modules[ 'document/attributefactory' ];
-
-		var factory = new AttributeFactory();
-
-		var attr1 = factory.create( 'foo', { a: 1, b: 2 } );
-		var attr2 = factory.create( 'foo', { b: 2, a: 1 } );
-
-		expect( attr1 ).to.equals( attr2 );
-		expect( attr1 ).to.have.property( 'key' ).that.equals( 'foo' );
-		expect( attr1 ).to.have.property( 'value' ).that.deep.equals( { a: 1, b: 2 } );
-	} );
-
-	it( 'should create different objects', function() {
-		var AttributeFactory = modules[ 'document/attributefactory' ];
-
-		var factory = new AttributeFactory();
-
-		var attr1 = factory.create( 'foo', 'bar' );
-		var attr2 = factory.create( 'foo', 'baz' );
-
-		expect( attr1 ).to.not.equals( attr2 );
-
-		expect( attr1 ).to.have.property( 'key' ).that.equals( 'foo' );
-		expect( attr1 ).to.have.property( 'value' ).that.equals( 'bar' );
-
-		expect( attr1 ).to.have.property( 'key' ).that.equals( 'foo' );
-		expect( attr2 ).to.have.property( 'value' ).that.equals( 'baz' );
-	} );
-} );