From eb8bafb5d429b8ae853149d1c98cf740df04b820 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Mon, 16 Oct 2017 12:55:00 +0200 Subject: [PATCH 01/10] Add CKEDITOR.tools.object.merge. --- core/tools.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/tools.js b/core/tools.js index 1959f483e94..34d01c584e2 100644 --- a/core/tools.js +++ b/core/tools.js @@ -1991,6 +1991,30 @@ } return null; + }, + + /** + * Merges two objects and returns new one. + * + * @param {Object} obj1 A base object. + * @param {Object} obj2 An object, which properties will be merged to the base one. + * @returns {Object} Merged object. + * @member CKEDITOR.tools.object + */ + merge: function merge( obj1, obj2 ) { + var copy1 = CKEDITOR.tools.clone( obj1 ), + copy2 = CKEDITOR.tools.clone( obj2 ), + tools = CKEDITOR.tools; + + tools.array.forEach( tools.objectKeys( copy2 ), function( key ) { + if ( typeof copy2[ key ] === 'object' && typeof copy1[ key ] === 'object' ) { + copy1[ key ] = merge( copy1[ key ], copy2[ key ] ); + } else { + copy1[ key ] = copy2[ key ]; + } + } ); + + return copy1; } } }; From 13c2d7d26a2c1965cea9653f1942d7286cf060e4 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Mon, 16 Oct 2017 12:55:41 +0200 Subject: [PATCH 02/10] Add unit test. --- tests/core/tools/object.js | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/core/tools/object.js b/tests/core/tools/object.js index 84798427a11..3f46bf10423 100644 --- a/tests/core/tools/object.js +++ b/tests/core/tools/object.js @@ -1,4 +1,5 @@ /* bender-tags: editor */ +/* bender-include: %BASE_PATH%/plugins/copyformatting/_helpers/tools.js */ ( function() { 'use strict'; @@ -62,6 +63,68 @@ returned = this.object.findKey( inputObject, innerObject ); assert.areSame( returned, 'c' ); + }, + + // #1053 + 'test object.merge': function() { + var obj1 = { + one: 1, + conflicted: 10, + falsy: false, + nully: null, + obj: { + nested1: 1, + nestedObj: { + a: 3 + } + }, + array: [ 1, 2 ] + }, + obj2 = { + two: 2, + conflicted: 20, + truthy: true, + undef: undefined, + obj: { + nested2: 2, + nestedObj: { + b: 4 + } + }, + array: [ 3, 4 ] + }, + expected = { + one: 1, + two: 2, + conflicted: 20, + falsy: false, + truthy: true, + nully: null, + undef: undefined, + obj: { + nested1: 1, + nested2: 2, + nestedObj: { + a: 3, + b: 4 + } + }, + array: [ 3, 4 ] + }, + actual = this.object.merge( obj1, obj2 ); + + assert.areNotSame( obj1, actual, 'Merging does not modify obj1' ); + assert.areNotSame( obj2, actual, 'Merging does not modify obj2' ); + objectAssert.areDeepEqual( expected, actual, 'Merging produces correct object' ); + + // Reversed merge should produce same object, but with different conflicted and array properties. + expected.conflicted = 10; + expected.array = [ 1, 2 ]; + actual = this.object.merge( obj2, obj1 ); + + assert.areNotSame( obj1, actual, 'Merging does not modify obj1' ); + assert.areNotSame( obj2, actual, 'Merging does not modify obj2' ); + objectAssert.areDeepEqual( expected, actual, 'Merging produces correct object' ); } } ); } )(); From 0a9877c865dd5652756310a7b8a2d47fb19cae6a Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Mon, 16 Oct 2017 12:56:11 +0200 Subject: [PATCH 03/10] Update objectAssert.areDeepEqual implementation. --- tests/plugins/copyformatting/_helpers/tools.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/plugins/copyformatting/_helpers/tools.js b/tests/plugins/copyformatting/_helpers/tools.js index c739a1f9d88..c02492d631a 100644 --- a/tests/plugins/copyformatting/_helpers/tools.js +++ b/tests/plugins/copyformatting/_helpers/tools.js @@ -5,10 +5,9 @@ 'use strict'; // Based on http://yuilibrary.com/yui/docs/api/files/test_js_ObjectAssert.js.html#l12. -YUITest.ObjectAssert.areDeepEqual = function( expected, actual, message ) { +YUITest.ObjectAssert.areDeepEqual = function areDeepEqual( expected, actual, message ) { var expectedKeys = YUITest.Object.keys( expected ), - actualKeys = YUITest.Object.keys( actual ), - areEqual = YUITest.ObjectAssert.areEqual; + actualKeys = YUITest.Object.keys( actual ); YUITest.Assert._increment(); @@ -21,8 +20,8 @@ YUITest.ObjectAssert.areDeepEqual = function( expected, actual, message ) { // Then check values. for ( var name in expected ) { if ( expected.hasOwnProperty( name ) ) { - if ( typeof expected[ name ] === 'object' ) { - areEqual( expected[ name ], actual[ name ] ); + if ( expected[ name ] && typeof expected[ name ] === 'object' ) { + areDeepEqual( expected[ name ], actual[ name ] ); } else if ( expected[ name ] !== actual[ name ] ) { throw new YUITest.ComparisonFailure( YUITest.Assert._formatMessage( message, From adb70bad8193deb43d5a73ffab843da3fe6403d6 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Mon, 16 Oct 2017 13:04:16 +0200 Subject: [PATCH 04/10] Move objectAssert.areDeepEqual to shared bender extensions. --- tests/_benderjs/ckeditor/static/extensions.js | 34 +++++++++++++++++++ tests/core/tools/object.js | 1 - .../plugins/copyformatting/_helpers/tools.js | 27 --------------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/tests/_benderjs/ckeditor/static/extensions.js b/tests/_benderjs/ckeditor/static/extensions.js index 5000c87759f..234396f3173 100644 --- a/tests/_benderjs/ckeditor/static/extensions.js +++ b/tests/_benderjs/ckeditor/static/extensions.js @@ -135,6 +135,40 @@ assert.areSame( expected, bender.tools.compatHtml( actual, false, true, false, true, true ), message ); }; + /** + * Asserts that two objects are deep equal. + * + * @param {Object} expected + * @param {Object} actual + * @param {String} [message] + */ + bender.objectAssert.areDeepEqual = function areDeepEqual( expected, actual, message ) { + // Based on http://yuilibrary.com/yui/docs/api/files/test_js_ObjectAssert.js.html#l12. + var expectedKeys = YUITest.Object.keys( expected ), + actualKeys = YUITest.Object.keys( actual ); + + YUITest.Assert._increment(); + + // First check keys array length. + if ( expectedKeys.length != actualKeys.length ) { + YUITest.Assert.fail( YUITest.Assert._formatMessage( message, + 'Object should have ' + expectedKeys.length + ' keys but has ' + actualKeys.length ) ); + } + + // Then check values. + for ( var name in expected ) { + if ( expected.hasOwnProperty( name ) ) { + if ( expected[ name ] && typeof expected[ name ] === 'object' ) { + areDeepEqual( expected[ name ], actual[ name ] ); + } + else if ( expected[ name ] !== actual[ name ] ) { + throw new YUITest.ComparisonFailure( YUITest.Assert._formatMessage( message, + 'Values should be equal for property ' + name ), expected[ name ], actual[ name ] ); + } + } + } + }; + // Add support test ignore. YUITest.Ignore = function() {}; diff --git a/tests/core/tools/object.js b/tests/core/tools/object.js index 3f46bf10423..dba3e2e8402 100644 --- a/tests/core/tools/object.js +++ b/tests/core/tools/object.js @@ -1,5 +1,4 @@ /* bender-tags: editor */ -/* bender-include: %BASE_PATH%/plugins/copyformatting/_helpers/tools.js */ ( function() { 'use strict'; diff --git a/tests/plugins/copyformatting/_helpers/tools.js b/tests/plugins/copyformatting/_helpers/tools.js index c02492d631a..2b0662b2eaa 100644 --- a/tests/plugins/copyformatting/_helpers/tools.js +++ b/tests/plugins/copyformatting/_helpers/tools.js @@ -4,33 +4,6 @@ 'use strict'; -// Based on http://yuilibrary.com/yui/docs/api/files/test_js_ObjectAssert.js.html#l12. -YUITest.ObjectAssert.areDeepEqual = function areDeepEqual( expected, actual, message ) { - var expectedKeys = YUITest.Object.keys( expected ), - actualKeys = YUITest.Object.keys( actual ); - - YUITest.Assert._increment(); - - // First check keys array length. - if ( expectedKeys.length != actualKeys.length ) { - YUITest.Assert.fail( YUITest.Assert._formatMessage( message, - 'Object should have ' + expectedKeys.length + ' keys but has ' + actualKeys.length ) ); - } - - // Then check values. - for ( var name in expected ) { - if ( expected.hasOwnProperty( name ) ) { - if ( expected[ name ] && typeof expected[ name ] === 'object' ) { - areDeepEqual( expected[ name ], actual[ name ] ); - } - else if ( expected[ name ] !== actual[ name ] ) { - throw new YUITest.ComparisonFailure( YUITest.Assert._formatMessage( message, - 'Values should be equal for property ' + name ), expected[ name ], actual[ name ] ); - } - } - } -}; - // Safari and IE8 use text selection and all other browsers use element selection. Therefore we must normalize it. function fixHtml( html ) { if ( ( CKEDITOR.env.webkit && !CKEDITOR.env.chrome ) || ( CKEDITOR.env.ie && CKEDITOR.env.version === 8 ) ) { From cc89bab4e1a6f393c7f1bb3371cc171b4659b691 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Mon, 16 Oct 2017 15:42:54 +0200 Subject: [PATCH 05/10] Simplify merge implementation. --- core/tools.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/tools.js b/core/tools.js index 34d01c584e2..3e7a4a9f362 100644 --- a/core/tools.js +++ b/core/tools.js @@ -2001,14 +2001,14 @@ * @returns {Object} Merged object. * @member CKEDITOR.tools.object */ - merge: function merge( obj1, obj2 ) { - var copy1 = CKEDITOR.tools.clone( obj1 ), - copy2 = CKEDITOR.tools.clone( obj2 ), - tools = CKEDITOR.tools; + merge: function( obj1, obj2 ) { + var tools = CKEDITOR.tools, + copy1 = tools.clone( obj1 ), + copy2 = tools.clone( obj2 ); tools.array.forEach( tools.objectKeys( copy2 ), function( key ) { if ( typeof copy2[ key ] === 'object' && typeof copy1[ key ] === 'object' ) { - copy1[ key ] = merge( copy1[ key ], copy2[ key ] ); + copy1[ key ] = tools.object.merge( copy1[ key ], copy2[ key ] ); } else { copy1[ key ] = copy2[ key ]; } From b935d8542da011bd4e3ae849a8824b0687b69877 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Mon, 16 Oct 2017 16:03:15 +0200 Subject: [PATCH 06/10] Update API docs. --- core/tools.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/core/tools.js b/core/tools.js index 3e7a4a9f362..e380e864e06 100644 --- a/core/tools.js +++ b/core/tools.js @@ -1996,6 +1996,35 @@ /** * Merges two objects and returns new one. * + * var obj1 = { + * a: 1, + * conflicted: 10, + * obj: { + * c: 1 + * } + * }, + * obj2 = { + * b: 2, + * conflicted: 20, + * obj: { + * d: 2 + * } + * }; + * + * CKEDITOR.tools.object.merge( obj1, obj2 ); + * + * This code produces the following object; + * + * { + * a: 1, + * b: 2, + * conflicted: 20, + * obj: { + * c: 1, + * d: 2 + * } + * } + * * @param {Object} obj1 A base object. * @param {Object} obj2 An object, which properties will be merged to the base one. * @returns {Object} Merged object. From d2e699223c09eab562b40442fb43399adb4242e9 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Mon, 16 Oct 2017 16:04:20 +0200 Subject: [PATCH 07/10] Update implementation of bender.objectAssert.areDeepEqual. --- tests/_benderjs/ckeditor/static/extensions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/_benderjs/ckeditor/static/extensions.js b/tests/_benderjs/ckeditor/static/extensions.js index 234396f3173..1abf8b11581 100644 --- a/tests/_benderjs/ckeditor/static/extensions.js +++ b/tests/_benderjs/ckeditor/static/extensions.js @@ -142,7 +142,7 @@ * @param {Object} actual * @param {String} [message] */ - bender.objectAssert.areDeepEqual = function areDeepEqual( expected, actual, message ) { + bender.objectAssert.areDeepEqual = function( expected, actual, message ) { // Based on http://yuilibrary.com/yui/docs/api/files/test_js_ObjectAssert.js.html#l12. var expectedKeys = YUITest.Object.keys( expected ), actualKeys = YUITest.Object.keys( actual ); @@ -159,7 +159,7 @@ for ( var name in expected ) { if ( expected.hasOwnProperty( name ) ) { if ( expected[ name ] && typeof expected[ name ] === 'object' ) { - areDeepEqual( expected[ name ], actual[ name ] ); + bender.objectAssert.areDeepEqual( expected[ name ], actual[ name ] ); } else if ( expected[ name ] !== actual[ name ] ) { throw new YUITest.ComparisonFailure( YUITest.Assert._formatMessage( message, From 7b619c780156ded7fcf297e5460e786848004133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Mon, 16 Oct 2017 18:03:34 +0200 Subject: [PATCH 08/10] Issue reference fix. --- tests/core/tools/object.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/tools/object.js b/tests/core/tools/object.js index dba3e2e8402..7f2cbdddf12 100644 --- a/tests/core/tools/object.js +++ b/tests/core/tools/object.js @@ -64,7 +64,7 @@ assert.areSame( returned, 'c' ); }, - // #1053 + // (#1053) 'test object.merge': function() { var obj1 = { one: 1, From d420169a967523177bfb22b5c297a56fdffdeb02 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Tue, 17 Oct 2017 10:32:15 +0200 Subject: [PATCH 09/10] Update API docs. --- core/tools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tools.js b/core/tools.js index e380e864e06..33bc7decc7a 100644 --- a/core/tools.js +++ b/core/tools.js @@ -2025,7 +2025,7 @@ * } * } * - * @param {Object} obj1 A base object. + * @param {Object} obj1 Source object, which will be used to create a new base object. * @param {Object} obj2 An object, which properties will be merged to the base one. * @returns {Object} Merged object. * @member CKEDITOR.tools.object From 27ab4a9ae7cc3dc82847f47104935fbc59d14668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 17 Oct 2017 10:55:31 +0200 Subject: [PATCH 10/10] Changelog entry. [skip ci] --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 6d7d187c81a..ef7de3da7ee 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ New Features: * [#607](https://github.com/ckeditor/ckeditor-dev/issues/607): Manually inserted hex color is prefixed with hash tag if needed. It ensures a valid hex color is used when setting table cell border or background color via [Color Dialog](http://ckeditor.com/addon/colordialog) window. * [#584](https://github.com/ckeditor/ckeditor-dev/issues/584): [Font size and Family](http://ckeditor.com/addon/font) and [Format](http://ckeditor.com/addon/format) drop-downs are not toggleable anymore. Default option to reset styles added. * [#856](https://github.com/ckeditor/ckeditor-dev/issues/856): Introduced [`CKEDITOR.tools.keystrokeToArray`](https://docs.ckeditor.com/ckeditor-docs/build/#!/api/CKEDITOR.tools-method-keystrokeToArray). It converts given keystroke into its string representation, returning every key name as separate array element. +* [#1053](https://github.com/ckeditor/ckeditor-dev/issues/1053): Introduced [`CKEDITOR.tools.object.merge`](https://docs.ckeditor.com/ckeditor4/docs/#!/api/CKEDITOR.tools.object-method-merge). It allows to merge two objects, returning the new object with all properties from both objects deeply cloned. Fixed Issues: