diff --git a/CHANGES.md b/CHANGES.md index fc03a4f769d..adba4e65fd4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,14 @@ API Changes: ## CKEditor 4.11.3 +New Features: + +* [#2048](https://github.com/ckeditor/ckeditor-dev/issues/2048): [Enhanced Image](https://ckeditor.com/cke4/addon/image2) added config option [CKEDITOR.config.image2_maxSize](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html#cfg-image2_maxSize) that allows seting maximum size that image can be resized to with resizer. + +Fixed Issues: + +* [#2672](https://github.com/ckeditor/ckeditor-dev/issues/2672): Fixed: When resizing [Enhanced Image](https://ckeditor.com/cke4/addon/image2) to minimum size with resizer imaga dialog doesn't show actual values. + ## CKEditor 4.11.2 Fixed Issues: diff --git a/plugins/image2/plugin.js b/plugins/image2/plugin.js index 2a5c685d35a..b2d6802567e 100644 --- a/plugins/image2/plugin.js +++ b/plugins/image2/plugin.js @@ -1160,6 +1160,15 @@ resizer.on( 'mousedown', function( evt ) { var image = widget.parts.image, + // Don't update attributes if less than 15. + // This is to prevent images to visually disappear. + min = { + width: 15, + height: 15 + }, + + max = getMaxSize(), + // "factor" can be either 1 or -1. I.e.: For right-aligned images, we need to // subtract the difference to get proper width, etc. Without "factor", // resizer starts working the opposite way. @@ -1315,13 +1324,9 @@ } } - // Don't update attributes if less than 10. - // This is to prevent images to visually disappear. - if ( newWidth >= 15 && newHeight >= 15 ) { - image.setAttributes( { width: newWidth, height: newHeight } ); - updateData = true; - } else { - updateData = false; + if ( isAllowedSize( newWidth, newHeight ) ) { + updateData = { width: newWidth, height: newHeight }; + image.setAttributes( updateData ); } } @@ -1338,7 +1343,7 @@ resizer.removeClass( 'cke_image_resizing' ); if ( updateData ) { - widget.setData( { width: newWidth, height: newHeight } ); + widget.setData( updateData ); // Save another undo snapshot: after resizing. editor.fire( 'saveSnapshot' ); @@ -1347,6 +1352,29 @@ // Don't update data twice or more. updateData = false; } + + function getMaxSize() { + var maxSize = editor.config.image2_maxSize, + natural; + + if ( !maxSize ) { + return null; + } + + maxSize = CKEDITOR.tools.copy( maxSize ); + natural = CKEDITOR.plugins.image2.getNatural( image ); + + maxSize.width = Math.max( maxSize.width === 'natural' ? natural.width : maxSize.width, min.width ); + maxSize.height = Math.max( maxSize.height === 'natural' ? natural.height : maxSize.height, min.width ); + + return maxSize; + } + + function isAllowedSize( width, height ) { + var isTooSmall = width < min.width || height < min.height, + isTooBig = max && ( width > max.width || height > max.height ); + return !isTooSmall && !isTooBig; + } } ); // Change the position of the widget resizer when data changes. @@ -1721,3 +1749,33 @@ CKEDITOR.config.image2_captionedClass = 'image'; * @cfg {Boolean} [image2_altRequired=false] * @member CKEDITOR.config */ + +/** + * Determines maximum size that image can be resized to with resize handler. + * + * It holds two properties: `width` and `height`, which can be set with one of two types: + * + * A number representing value that limits max size in pixel units: + * + * ```javascript + * config.image2_maxSize = { + * height: 300, + * width: 250 + * }; + * ``` + * + * A string representing image natural size, so each image resize is limited to it's own natural height/width: + * + * ```javascript + * config.image2_maxSize = { + * height: 'natural', + * width: 'natural' + * } + * ``` + * + * Note: Image can still be resized to bigger dimensions, when using image dialog. + * + * @since 4.12.0 + * @cfg {Object.} [image2_maxSize] + * @member CKEDITOR.config + */ diff --git a/tests/plugins/image2/manual/dialogafterresize.html b/tests/plugins/image2/manual/dialogafterresize.html new file mode 100644 index 00000000000..c10e9059475 --- /dev/null +++ b/tests/plugins/image2/manual/dialogafterresize.html @@ -0,0 +1,24 @@ +
+ +
+

Width:
+ Height: +

+ diff --git a/tests/plugins/image2/manual/dialogafterresize.md b/tests/plugins/image2/manual/dialogafterresize.md new file mode 100644 index 00000000000..3ba5ec5bc2e --- /dev/null +++ b/tests/plugins/image2/manual/dialogafterresize.md @@ -0,0 +1,19 @@ +@bender-tags: 4.12.0, bug, 2672 +@bender-ui: collapsed +@bender-ckeditor-plugins: wysiwygarea, toolbar, image2, sourcearea, undo + +1. Press and hold resize handler. + +1. Shrink image to minimum size. + +1. Release resize handler. + +1. Open dialog for this image. + +## Expected: + +Dialog fields are populated with same values as presented below editor. + +## Unexpected: + +Dialog fields have other values. diff --git a/tests/plugins/image2/manual/maxsize.html b/tests/plugins/image2/manual/maxsize.html new file mode 100644 index 00000000000..cbeb8e1dd90 --- /dev/null +++ b/tests/plugins/image2/manual/maxsize.html @@ -0,0 +1,42 @@ +

Editor 1:

+

+ Max width: 350
+ Max height: 350 +

+
+ +
+

Editor 2:

+

+ Max width: 'natural'
+ Max height: 'natural' +

+
+ +
+

Editor 3:

+

+ Max width: unset
+ Max height: unset +

+
+ +
+ + diff --git a/tests/plugins/image2/manual/maxsize.md b/tests/plugins/image2/manual/maxsize.md new file mode 100644 index 00000000000..13a8355d1f3 --- /dev/null +++ b/tests/plugins/image2/manual/maxsize.md @@ -0,0 +1,17 @@ +@bender-tags: 4.12.0, feature, 2048 +@bender-ui: collapsed +@bender-ckeditor-plugins: wysiwygarea, toolbar, image2, sourcearea + +# For each editors + +1. Use resize handler to increase image to maximum size. + +## Expected: + +- Editor 1, Editor 2: Images can't be resized beyond maximum values. + +- Editor 3: Image can be resized to any size. + +## Unexpected: + +- Editor 1, Editor 2: Images can be resized beyond maximum values. diff --git a/tests/plugins/image2/resizehandler.js b/tests/plugins/image2/resizehandler.js new file mode 100644 index 00000000000..00f2e12a5bf --- /dev/null +++ b/tests/plugins/image2/resizehandler.js @@ -0,0 +1,138 @@ +/* bender-tags: editor,widget */ +/* bender-ckeditor-plugins: image2,toolbar */ +/* bender-include: %BASE_PATH%/plugins/uploadfile/_helpers/waitForImage.js */ +/* global waitForImage */ + +( function() { + 'use strict'; + + bender.editors = { + limitedSize: { + config: { + image2_maxSize: { + width: 350, + height: 350 + } + } + }, + naturalSize: { + config: { + image2_maxSize: { + width: 'natural', + height: 'natural' + } + } + } + }; + + var tests = { + // (#2672) + 'test resize close to minimum size': testResize( { + data: { + screenX: 40, + screenY: 15 + }, + expected: { + width: 40, + height: 15 + } + } ), + + // (#2672) + 'test resize lower than minimum size': testResize( { + data: { + screenX: 14, + screenY: 14 + }, + expected: { + width: null, + height: null + } + } ), + + // (#2048) + 'test resize close to maximum size': testResize( { + limitedSize: { + data: { + screenX: 350, + screenY: 131 + }, + expected: { + width: 350, + height: 131 + } + }, + naturalSize: { + data: { + screenX: 163, + screenY: 61 + }, + expected: { + width: 163, + height: 61 + } + } + } ), + + // (#2048) + 'test resize above maximum size': testResize( { + limitedSize: { + data: { + screenX: 351, + screenY: 132 + }, + expected: { + width: null, + height: null + } + }, + naturalSize: { + data: { + screenX: 165, + screenY: 65 + }, + expected: { + width: null, + height: null + } + } + } ) + }; + + bender.test( bender.tools.createTestsForEditors( CKEDITOR.tools.objectKeys( bender.editors ), tests ) ); + + function testResize( options ) { + return function( editor, bot ) { + bot.setData( '', function() { + var image = editor.editable().findOne( 'img' ); + + waitForImage( image, function() { + editor.widgets.instances[ editor.widgets._.nextId - 1 ].resizer.fire( 'mousedown', { + $: { + screenX: 163, + screenY: 61 + } + } ); + + var doc = CKEDITOR.document, + data = options[ editor.name ] ? options[ editor.name ].data : options.data, + expected = options[ editor.name ] ? options[ editor.name ].expected : options.expected, + actual; + + doc.fire( 'mousemove', { + $: data + } ); + + doc.fire( 'mouseup' ); + + actual = { + width: image.getAttribute( 'width' ), + height: image.getAttribute( 'height' ) + }; + + assert.isTrue( CKEDITOR.tools.objectCompare( actual, expected ) ); + } ); + } ); + }; + } +} )(); diff --git a/tests/plugins/uploadfile/_helpers/waitForImage.js b/tests/plugins/uploadfile/_helpers/waitForImage.js index 1a8dbc55c6a..b81a24a4cf2 100644 --- a/tests/plugins/uploadfile/_helpers/waitForImage.js +++ b/tests/plugins/uploadfile/_helpers/waitForImage.js @@ -4,10 +4,17 @@ function waitForImage( image, callback ) { // IE needs to wait for image to be loaded so it can read width and height of the image. if ( CKEDITOR.env.ie ) { wait( callback, 100 ); + return; + } + + if ( image.$.complete ) { + setTimeout( function() { + resume( callback ); + } ); } else { image.once( 'load', function() { resume( callback ); } ); - wait(); } + wait(); } diff --git a/tests/utils/testhelpers.js b/tests/utils/testhelpers.js new file mode 100644 index 00000000000..3b79aec9db2 --- /dev/null +++ b/tests/utils/testhelpers.js @@ -0,0 +1,30 @@ +/* bender-tags: editor */ +/* bender-include: %BASE_PATH%/plugins/uploadfile/_helpers/waitForImage.js */ +/* global waitForImage */ + +'use strict'; + +bender.test( { + // (#2714) + 'test waitForImage when image is loaded': function() { + if ( CKEDITOR.env.ie ) { + assert.ignore(); + } + + var called = false, + imageMock = { + $: { complete: true } + }; + + setTimeout( function() { + resume( function() { + assert.isTrue( called, 'waitForImage callback called' ); + } ); + }, 100 ); + + waitForImage( imageMock, function() { + called = true; + wait(); + } ); + } +} );