diff --git a/CHANGES.md b/CHANGES.md
index e9238818adf..c0f654cd621 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,6 +10,7 @@ New Features:
* [#3256](https://github.com/ckeditor/ckeditor-dev/issues/3256): Triple-clicking in last table cell and deleting content no longer pulls content below into table.
* [#3118](https://github.com/ckeditor/ckeditor-dev/issues/3118): Selecting a paragraph with triple-click and applying heading applies heading only to selected paragraph.
* [#3161](https://github.com/ckeditor/ckeditor-dev/issues/3161): Double click on a `span` element containing one word only creates correct selection including clicked `span` only.
+* [#3359](https://github.com/ckeditor/ckeditor-dev/issues/3359): Improved [dialog](https://ckeditor.com/cke4/addon/dialog) positioning and behaviour when browser window is resized, dialog resized or moved.
Fixed Issues:
@@ -27,8 +28,10 @@ Fixed Issues:
* [#3101](https://github.com/ckeditor/ckeditor-dev/issues/3101): Fixed: [`CKEDITOR.dom.range#_getTableElement()`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_dom_range.html#method-_getTableElement) returns `null` instead of a table element for edge cases.
* [#3287](https://github.com/ckeditor/ckeditor-dev/issues/3287): Fixed: [`CKEDITOR.tools.promise`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_tools_promise.html) incorrectly initialized if an AMD loader is present.
* [#3379](https://github.com/ckeditor/ckeditor-dev/issues/3379): Fixed: Incorrect [`CKEDITOR.editor#getData`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_editor.html#method-getData) call when inserting content into the editor.
-* [#3136](https://github.com/ckeditor/ckeditor-dev/issues/3136) [Firefox] Fixed: Clicking on [Balloon Toolbar](https://ckeditor.com/cke4/addon/balloontoolbar) items removes native table selection.
+* [#3136](https://github.com/ckeditor/ckeditor-dev/issues/3136): [Firefox] Fixed: Clicking on [Balloon Toolbar](https://ckeditor.com/cke4/addon/balloontoolbar) items removes native table selection.
* [#3381](https://github.com/ckeditor/ckeditor-dev/issues/3381): [IE8] Fixed: [`CKEDITOR.tools.object.keys`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_tools_object.html#method-keys) does not accept non-objects.
+* [#2395](https://github.com/ckeditor/ckeditor-dev/issues/2395): [Android] Fixed: Focused input in [dialog](https://ckeditor.com/cke4/addon/dialog) is scrolled out of the viewport when soft keyboard appears.
+* [#453](https://github.com/ckeditor/ckeditor-dev/issues/453): Fixed: [Link](https://ckeditor.com/cke4/addon/link) dialog has invalid width when editor is maximized and browser window resized.
API Changes:
diff --git a/core/dom/element.js b/core/dom/element.js
index f1975473fcd..97f39d8249f 100644
--- a/core/dom/element.js
+++ b/core/dom/element.js
@@ -682,6 +682,19 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab
return new CKEDITOR.dom.nodeList( this.$.childNodes );
},
+ /**
+ * Gets the elements `clientWidth` and `clientHeight`.
+ *
+ * @since 4.13.0
+ * @returns {Object} An object containing width and height values.
+ */
+ getClientSize: function() {
+ return {
+ width: this.$.clientWidth,
+ height: this.$.clientHeight
+ };
+ },
+
/**
* Gets the current computed value of one of the element CSS style
* properties.
diff --git a/plugins/dialog/plugin.js b/plugins/dialog/plugin.js
index c2d034b5f49..eee9fe04731 100644
--- a/plugins/dialog/plugin.js
+++ b/plugins/dialog/plugin.js
@@ -135,31 +135,34 @@ CKEDITOR.DIALOG_STATE_BUSY = 2;
input && input.removeAttribute( 'aria-invalid' );
}
- var templateSource = '
'
];
@@ -2174,72 +2255,19 @@ CKEDITOR.DIALOG_STATE_BUSY = 2;
editor.focusManager.add( coverElement );
currentCover = coverElement;
- var resizeFunc = function() {
- var size = win.getViewPaneSize();
- coverElement.setStyles( {
- width: size.width + 'px',
- height: size.height + 'px'
- } );
- };
-
- var scrollFunc = function() {
- var pos = win.getScrollPosition(),
- cursor = CKEDITOR.dialog._.currentTop;
- coverElement.setStyles( {
- left: pos.x + 'px',
- top: pos.y + 'px'
- } );
- if ( cursor ) {
- do {
- var dialogPos = cursor.getPosition();
- cursor.move( dialogPos.x, dialogPos.y );
- } while ( ( cursor = cursor._.parentDialog ) );
- }
- };
-
- resizeCover = resizeFunc;
- win.on( 'resize', resizeFunc );
- resizeFunc();
// Using Safari/Mac, focus must be kept where it is (https://dev.ckeditor.com/ticket/7027)
if ( !( CKEDITOR.env.mac && CKEDITOR.env.webkit ) )
coverElement.focus();
-
- if ( CKEDITOR.env.ie6Compat ) {
- // IE BUG: win.$.onscroll assignment doesn't work.. it must be window.onscroll.
- // So we need to invent a really funny way to make it work.
- var myScrollHandler = function() {
- scrollFunc();
- myScrollHandler.prevScrollHandler.apply( this, arguments );
- };
- win.$.setTimeout( function() {
- myScrollHandler.prevScrollHandler = window.onscroll ||
- function() {};
- window.onscroll = myScrollHandler;
- }, 0 );
- scrollFunc();
- }
}
function hideCover( editor ) {
+ CKEDITOR.document.getBody().removeClass( 'cke_dialog_open' );
if ( !currentCover )
return;
editor.focusManager.remove( currentCover );
- var win = CKEDITOR.document.getWindow();
currentCover.hide();
-
- // Remove the current cover reference once the cover is removed (#589).
- currentCover = null;
- win.removeListener( 'resize', resizeCover );
-
- if ( CKEDITOR.env.ie6Compat ) {
- win.$.setTimeout( function() {
- var prevScrollHandler = window.onscroll && window.onscroll.prevScrollHandler;
- window.onscroll = prevScrollHandler || null;
- }, 0 );
- }
- resizeCover = null;
}
function removeCovers() {
@@ -3349,9 +3377,16 @@ CKEDITOR.DIALOG_STATE_BUSY = 2;
} );
} )();
+var stylesLoaded = false;
+
CKEDITOR.plugins.add( 'dialog', {
requires: 'dialogui',
init: function( editor ) {
+ if ( !stylesLoaded ) {
+ CKEDITOR.document.appendStyleSheet( this.path + 'styles/dialog.css' );
+ stylesLoaded = true;
+ }
+
editor.on( 'doubleclick', function( evt ) {
if ( evt.data.dialog )
editor.openDialog( evt.data.dialog );
diff --git a/plugins/dialog/styles/dialog.css b/plugins/dialog/styles/dialog.css
new file mode 100644
index 00000000000..add83eb2258
--- /dev/null
+++ b/plugins/dialog/styles/dialog.css
@@ -0,0 +1,18 @@
+.cke_dialog_open {
+ overflow: hidden;
+}
+
+.cke_dialog_container {
+ position: fixed;
+ overflow-y: auto;
+ overflow-x: auto;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ z-index: 10010;
+}
+
+.cke_dialog_body {
+ position: relative;
+}
diff --git a/plugins/floatpanel/plugin.js b/plugins/floatpanel/plugin.js
index b1fc8cae791..85a890eaf80 100644
--- a/plugins/floatpanel/plugin.js
+++ b/plugins/floatpanel/plugin.js
@@ -367,15 +367,15 @@ CKEDITOR.plugins.add( 'floatpanel', {
// position and horizontal scrolls. Here we have a
// series of hacks to workaround it. (https://dev.ckeditor.com/ticket/6146)
if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) {
- var offsetParent = new CKEDITOR.dom.element( element.$.offsetParent ),
+ var offsetParent = element.$.offsetParent && new CKEDITOR.dom.element( element.$.offsetParent ),
scrollParent = offsetParent;
// Quirks returns , but standards returns .
- if ( scrollParent.getName() == 'html' ) {
+ if ( scrollParent && scrollParent.getName() == 'html' ) {
scrollParent = scrollParent.getDocument().getBody();
}
- if ( scrollParent.getComputedStyle( 'direction' ) == 'rtl' ) {
+ if ( scrollParent && scrollParent.getComputedStyle( 'direction' ) == 'rtl' ) {
// For IE8, there is not much logic on this, but it works.
if ( CKEDITOR.env.ie8Compat ) {
left -= element.getDocument().getDocumentElement().$.scrollLeft * 2;
diff --git a/tests/core/dom/element/element.js b/tests/core/dom/element/element.js
index 91e52fa6b9d..879e2098a37 100644
--- a/tests/core/dom/element/element.js
+++ b/tests/core/dom/element/element.js
@@ -1230,6 +1230,20 @@ bender.test( appendDomObjectTests(
assert.areSame( String( leftMouseButton ), link.getAttribute( 'data-button' ),
'Proper event data was passed' );
+ },
+
+ 'test getClientSize': function() {
+ var element = new CKEDITOR.dom.element( 'div' );
+
+ element.$ = {
+ clientWidth: 100,
+ clientHeight: 100
+ };
+
+ var size = element.getClientSize();
+
+ assert.areSame( 100, size.width );
+ assert.areSame( 100, size.height );
}
}
) );
diff --git a/tests/plugins/dialog/manual/inputfocus.html b/tests/plugins/dialog/manual/inputfocus.html
new file mode 100644
index 00000000000..3b198cbe022
--- /dev/null
+++ b/tests/plugins/dialog/manual/inputfocus.html
@@ -0,0 +1,21 @@
+
+
+
Left to right editor:
+
+
Right to left editor:
+
+
+
+
diff --git a/tests/plugins/dialog/manual/inputfocus.md b/tests/plugins/dialog/manual/inputfocus.md
new file mode 100644
index 00000000000..3dcbc8391b7
--- /dev/null
+++ b/tests/plugins/dialog/manual/inputfocus.md
@@ -0,0 +1,32 @@
+@bender-tags: 2395, bug, 4.13.0
+@bender-ui: collapsed
+@bender-ckeditor-plugins: wysiwygarea, toolbar, link
+
+1. Read expected.
+1. Scroll down until editor is visible.
+1. Press link button.
+1. Resize browser window.
+1. Resize dialog window.
+1. Drag dialog window.
+1. Close and reopen dialog.
+
+## Expected
+
+- When dialog is shown scrollbars are invisible.
+- When resizing browser window dialog is still horizontally and vertically centered.
+- When window is too small to fit dialog window scrollbars should appear allowing to scroll dialog.
+- Dialog window can be resized and dragged around.
+- When closing and reopening dialog it has same size and position as it had before when it was closed.
+
+### Note
+When you reposition dialog it won't be centered when resizing browser window. Repositioned dialog will move in a way that it will have same percentage of free space on each side. E.g. Consider there is when there is free space around dialog:
+- on the left 10px
+- on the right 90px
+
+Then when window width is increased so that there will be 200px of free space around dialog now there will be:
+- on the left 20px
+- on the right 180px
+
+If instead window width is decreased so there is 50px free space then there will be:
+- on the left 5px
+- on the right 45px
diff --git a/tests/plugins/dialog/manual/inputfocusmobile.html b/tests/plugins/dialog/manual/inputfocusmobile.html
new file mode 100644
index 00000000000..4887cca642d
--- /dev/null
+++ b/tests/plugins/dialog/manual/inputfocusmobile.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
Left to right editor:
+
+
Right to left editor:
+
+
+
+
+
diff --git a/tests/plugins/dialog/manual/inputfocusmobile.md b/tests/plugins/dialog/manual/inputfocusmobile.md
new file mode 100644
index 00000000000..7d2f69f2fe0
--- /dev/null
+++ b/tests/plugins/dialog/manual/inputfocusmobile.md
@@ -0,0 +1,17 @@
+@bender-tags: 2395, bug, 4.13.0
+@bender-ui: collapsed
+@bender-ckeditor-plugins: wysiwygarea, toolbar, link
+
+1. Scroll down until editor is visible and press link button.
+1. Focus other input in dialog.
+
+## Expected:
+
+- After each step page should be scrolled in a way caret is visible.
+
+## Unexpected
+
+- Caret is outside of current viewport.
+
+## Note
+When testing with device which has small screen you should hide test steps with `X` button, so editor has enough space.
diff --git a/tests/plugins/dialog/manual/skins/kama.html b/tests/plugins/dialog/manual/skins/kama.html
new file mode 100644
index 00000000000..eb377a35504
--- /dev/null
+++ b/tests/plugins/dialog/manual/skins/kama.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/tests/plugins/dialog/manual/skins/kama.md b/tests/plugins/dialog/manual/skins/kama.md
new file mode 100644
index 00000000000..262e3f58b43
--- /dev/null
+++ b/tests/plugins/dialog/manual/skins/kama.md
@@ -0,0 +1,5 @@
+@bender-tags: 2395, bug, 4.13.0
+@bender-ui: collapsed
+@bender-ckeditor-plugins: wysiwygarea, toolbar, link
+
+Press link button and play around with dialog window to see if it's styling looks correct with no visible glitches.
diff --git a/tests/plugins/dialog/manual/skins/moono.html b/tests/plugins/dialog/manual/skins/moono.html
new file mode 100644
index 00000000000..3dab26144ec
--- /dev/null
+++ b/tests/plugins/dialog/manual/skins/moono.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/tests/plugins/dialog/manual/skins/moono.md b/tests/plugins/dialog/manual/skins/moono.md
new file mode 100644
index 00000000000..e9ae8aa3446
--- /dev/null
+++ b/tests/plugins/dialog/manual/skins/moono.md
@@ -0,0 +1,5 @@
+@bender-tags: 2395, bug, 4.13.0
+@bender-ui: collapsed
+@bender-ckeditor-plugins: wysiwygarea, toolbar, link
+
+Play around with dialog window to see if it's styling looks correct with no visible glitches.
diff --git a/tests/plugins/dialog/manual/skins/moonolisa.html b/tests/plugins/dialog/manual/skins/moonolisa.html
new file mode 100644
index 00000000000..1db119f7615
--- /dev/null
+++ b/tests/plugins/dialog/manual/skins/moonolisa.html
@@ -0,0 +1,5 @@
+
+
+
diff --git a/tests/plugins/dialog/manual/skins/moonolisa.md b/tests/plugins/dialog/manual/skins/moonolisa.md
new file mode 100644
index 00000000000..e9ae8aa3446
--- /dev/null
+++ b/tests/plugins/dialog/manual/skins/moonolisa.md
@@ -0,0 +1,5 @@
+@bender-tags: 2395, bug, 4.13.0
+@bender-ui: collapsed
+@bender-ckeditor-plugins: wysiwygarea, toolbar, link
+
+Play around with dialog window to see if it's styling looks correct with no visible glitches.
diff --git a/tests/plugins/dialog/positioning.js b/tests/plugins/dialog/positioning.js
new file mode 100644
index 00000000000..40da612cac6
--- /dev/null
+++ b/tests/plugins/dialog/positioning.js
@@ -0,0 +1,226 @@
+/* bender-tags: editor */
+/* bender-ckeditor-plugins: dialog, link, toolbar */
+
+( function() {
+ 'use strict';
+
+ bender.editor = {};
+
+ bender.test( {
+ setUp: function() {
+ // Make sure page is scrollable on vertical screens.
+ CKEDITOR.document.getBody().appendHtml( '
' );
+ },
+ tearDown: function() {
+ var dialog = CKEDITOR.dialog.getCurrent();
+ if ( dialog ) {
+ dialog.hide();
+ }
+ },
+
+ // (#2395).
+ 'test body has hidden scrollbars when dialog is opened': function() {
+ if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) {
+ assert.ignore();
+ }
+ var bot = this.editorBot,
+ body = CKEDITOR.document.getBody();
+
+ bot.dialog( 'link', function( dialog ) {
+ assert.isTrue( body.hasClass( 'cke_dialog_open' ), 'Body should have proper class.' );
+
+ dialog.hide();
+
+ assert.isFalse( body.hasClass( 'cke_dialog_open' ), 'Body shouldn\'t have class.' );
+ } );
+ },
+
+ // (#2395).
+ 'test dialog is initially centered': function() {
+ if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) {
+ assert.ignore();
+ }
+ this.editorBot.dialog( 'link', function( dialog ) {
+ var container = dialog._.element,
+ element = container.getFirst();
+
+ assert.areEqual( container.getStyle( 'display' ), 'flex', 'Dialog container should have `display:flex`.' );
+ assert.areEqual( element.getStyle( 'position' ), '', 'Dialog element should\'t have position style.' );
+ assert.areEqual( element.getStyle( 'margin' ), 'auto', 'Dialog element should have `margin:auto`.' );
+ } );
+ },
+
+ // (#2395).
+ 'test dialog cover styles': function() {
+ this.editorBot.dialog( 'link', function() {
+ var cover = CKEDITOR.document.findOne( '.cke_dialog_background_cover' );
+
+ assert.areEqual( cover.getStyle( 'width' ), '100%', 'Dialog element should have `width:100%`.' );
+ assert.areEqual( cover.getStyle( 'height' ), '100%', 'Dialog element should have `height:100%`.' );
+ } );
+ },
+
+ // When drag starts, dialog becomes centered with `position:absolute`, then it moves together with mouse (#2395).
+ //
+ // A - mouse cursor
+ //
+ // Drag start:
+ // +----------------------------------+
+ // | Editor viewport |
+ // | |
+ // | +-------------A--+ |
+ // | | | |
+ // | | Dialog | |
+ // | | | |
+ // | +----------------+ |
+ // | |
+ // +----------------------------------+
+ //
+ // Drag end:
+ // +----------------------------------+
+ // | Editor viewport |
+ // | |
+ // | |
+ // | +-------------A--+|
+ // | | ||
+ // | | Dialog ||
+ // | | ||
+ // | +----------------+|
+ // +----------------------------------+
+ //
+ 'test dialog move': function() {
+ var viewPaneSize = {
+ width: 1000,
+ height: 1000
+ },
+ stubs = mockWindowGetViewPaneSize( viewPaneSize );
+
+ this.editorBot.dialog( 'link', function( dialog ) {
+ var element = dialog._.element.getFirst(),
+ dialogSize = dialog.getSize(),
+ expectedX = Math.floor( ( viewPaneSize.width - dialogSize.width ) / 2 ),
+ expectedY = Math.floor( ( viewPaneSize.height - dialogSize.height ) / 2 );
+
+ dialog.parts.title.fire( 'mousedown', {
+ $: {
+ screenX: 0,
+ screenY: 0
+ },
+ preventDefault: function() {}
+ } );
+
+ var actualX = parseInt( element.getStyle( 'left' ), 10 ),
+ actualY = parseInt( element.getStyle( 'top' ), 10 ),
+ elementStyle = element.getStyle( 'position' );
+
+ CKEDITOR.document.fire( 'mousemove', {
+ $: {
+ screenX: 100,
+ screenY: 100
+ },
+ preventDefault: function() {}
+ } );
+
+ for ( var key in stubs ) {
+ stubs[ key ].restore();
+ }
+
+ assert.areEqual( 'absolute', elementStyle, 'Dialog element should have `position:absolute`.' );
+ assert.areEqual( expectedX, actualX, 'Dialog should be horizontally centered.' );
+ assert.areEqual( expectedY, actualY, 'Dialog should be vertically centered.' );
+
+ actualX = parseInt( element.getStyle( 'left' ), 10 );
+ actualY = parseInt( element.getStyle( 'top' ), 10 );
+
+ assert.areEqual( 100, actualX - expectedX, 'Dialog should be moved by 100px to the right.' );
+ assert.areEqual( 100, actualY - expectedY, 'Dialog should be moved by 100px down.' );
+
+ CKEDITOR.document.fire( 'mouseup' );
+ } );
+ },
+
+ // (#2395)
+ 'test dialog resize': function() {
+ // Mobile browsers doesn't support dialog resize.
+ if ( bender.tools.env.mobile ) {
+ assert.ignore();
+ }
+ var viewPaneSize = {
+ width: 1000,
+ height: 1000
+ },
+ stubs = mockWindowGetViewPaneSize( viewPaneSize );
+
+ this.editorBot.dialog( 'link', function( dialog ) {
+ var dialogSize = dialog.getSize(),
+ resizer = dialog._.element.findOne( '.cke_resizer' ),
+ evt = {
+ screenX: 0,
+ screenY: 0
+ },
+ sizeChange;
+
+ // IE8 doesn't pass fake evt when manually calling resizer.$.onmousedown.
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) {
+ var str = resizer.$.onmousedown.toString();
+ str = str.match( /[(](.*?),/ )[ 1 ];
+
+ CKEDITOR.tools.callFunction( Number( str ), evt );
+ } else {
+ resizer.$.onmousedown( evt );
+ }
+
+ CKEDITOR.document.fire( 'mousemove', {
+ $: {
+ screenX: 10,
+ screenY: 10
+ },
+ preventDefault: function() {}
+ } );
+
+ CKEDITOR.document.fire( 'mouseup' );
+
+ sizeChange = {
+ width: dialog.getSize().width - dialogSize.width,
+ height: dialog.getSize().height - dialogSize.height
+ };
+
+ for ( var key in stubs ) {
+ stubs[ key ].restore();
+ }
+
+ // When resizing some floats might be rounded.
+ assert.isNumberInRange( 10, sizeChange.width - 1, sizeChange.width );
+ assert.isNumberInRange( 10, sizeChange.height - 1, sizeChange.height );
+ } );
+ }
+ } );
+
+ function mockWindowGetViewPaneSize( viewPaneSize ) {
+ var window = CKEDITOR.document.getWindow(),
+ stubs = {
+ getWindow: sinon.stub( CKEDITOR.document, 'getWindow' ),
+ getViewPane: sinon.stub( window, 'getViewPaneSize' ),
+ getContainerSize: mockDialogContainerSize( viewPaneSize )
+ };
+
+ stubs.getWindow.returns( window );
+ stubs.getViewPane.returns( viewPaneSize );
+
+ return stubs;
+ }
+
+ function mockDialogContainerSize( sizes ) {
+ var originalMethod = CKEDITOR.dom.element.prototype.getClientSize;
+
+ CKEDITOR.dom.element.prototype.getClientSize = function() {
+ return this.hasClass( 'cke_dialog_container' ) ? sizes : originalMethod.call( this );
+ };
+
+ return {
+ restore: function() {
+ CKEDITOR.dom.element.prototype.getClientSize = originalMethod;
+ }
+ };
+ }
+} )();