diff --git a/src/toolbar/balloon/balloontoolbar.js b/src/toolbar/balloon/balloontoolbar.js
index f70a1548..f1e4c62a 100644
--- a/src/toolbar/balloon/balloontoolbar.js
+++ b/src/toolbar/balloon/balloontoolbar.js
@@ -202,6 +202,7 @@ export default class BalloonToolbar extends Plugin {
const editor = this.editor;
const view = editor.editing.view;
const viewDocument = view.document;
+ const viewSelection = viewDocument.selection;
// Get direction of the selection.
const isBackward = viewDocument.selection.isBackward;
@@ -212,7 +213,7 @@ export default class BalloonToolbar extends Plugin {
// computed and hence, the target is defined as a function instead of a static value.
// https://github.com/ckeditor/ckeditor5-ui/issues/195
target: () => {
- const range = viewDocument.selection.getFirstRange();
+ const range = isBackward ? viewSelection.getFirstRange() : viewSelection.getLastRange();
const rangeRects = Rect.getDomRangeRects( view.domConverter.viewRangeToDom( range ) );
// Select the proper range rect depending on the direction of the selection.
diff --git a/tests/manual/tickets/385/1.html b/tests/manual/tickets/385/1.html
new file mode 100644
index 00000000..03ff817f
--- /dev/null
+++ b/tests/manual/tickets/385/1.html
@@ -0,0 +1,13 @@
+
+
This is a line of text.
+
This is a line of text.
+
This is a line of text.
+
This is a line of text.
+
+
This is a line of text.
+
This is a line of text.
+
This is a line of text.
+
This is a line of text.
+
diff --git a/tests/manual/tickets/385/1.js b/tests/manual/tickets/385/1.js
new file mode 100644
index 00000000..da6c4e56
--- /dev/null
+++ b/tests/manual/tickets/385/1.js
@@ -0,0 +1,23 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals window, document, console:false */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+import BalloonToolbar from '../../../../src/toolbar/balloon/balloontoolbar';
+
+ClassicEditor
+ .create( document.querySelector( '#editor' ), {
+ plugins: [ ArticlePluginSet, BalloonToolbar ],
+ toolbar: [ 'bold', 'italic', 'link', 'undo', 'redo' ],
+ balloonToolbar: [ 'bold', 'italic', 'link' ]
+ } )
+ .then( editor => {
+ window.editor = editor;
+ } )
+ .catch( err => {
+ console.error( err.stack );
+ } );
diff --git a/tests/manual/tickets/385/1.md b/tests/manual/tickets/385/1.md
new file mode 100644
index 00000000..a6577405
--- /dev/null
+++ b/tests/manual/tickets/385/1.md
@@ -0,0 +1,13 @@
+## Incorrect BalloonToolbar position in a case of multi-range selection [#385](https://github.com/ckeditor/ckeditor5-ui/issues/385)
+
+### Use Firefox to this test, check the ticket for the explanation.
+
+1. Make a forward selection that starts in the text before the image and ends in the text after the image
+ ```
+
Line of text
+
Line o{f text
+
+
Line o}f text
+
Line of text
+ ```
+2. Check if the balloon toolbar is attached to the end of the selection
diff --git a/tests/manual/tickets/385/sample.jpg b/tests/manual/tickets/385/sample.jpg
new file mode 100644
index 00000000..b77d07e7
Binary files /dev/null and b/tests/manual/tickets/385/sample.jpg differ
diff --git a/tests/toolbar/balloon/balloontoolbar.js b/tests/toolbar/balloon/balloontoolbar.js
index c8c814ff..1d7f7aa3 100644
--- a/tests/toolbar/balloon/balloontoolbar.js
+++ b/tests/toolbar/balloon/balloontoolbar.js
@@ -14,7 +14,8 @@ import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model.js';
+import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import { stringify as viewStringify } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
/* global document, setTimeout, window */
@@ -199,6 +200,22 @@ describe( 'BalloonToolbar', () => {
expect( balloonAddSpy.firstCall.args[ 0 ].position.target() ).to.deep.equal( forwardSelectionRect );
} );
+ // https://github.com/ckeditor/ckeditor5-ui/issues/385
+ it( 'should attach the #_balloon to the last range in a case of multi-range forward selection', () => {
+ setData( model, 'b[ar][bi]z' );
+
+ balloonToolbar.show();
+
+ // Because attaching and pinning BalloonPanelView is stubbed for test
+ // we need to manually call function that counting rect.
+ const targetRect = balloonAddSpy.firstCall.args[ 0 ].position.target();
+
+ const targetViewRange = editingView.domConverter.viewRangeToDom.lastCall.args[ 0 ];
+
+ expect( viewStringify( targetViewRange.root, targetViewRange ) ).to.equal( '
bar
{bi}z
' );
+ expect( targetRect ).to.deep.equal( forwardSelectionRect );
+ } );
+
// https://github.com/ckeditor/ckeditor5-ui/issues/308
it( 'should ignore the zero-width orphan rect if there another one preceding it for the forward selection', () => {
// Restore previous stubSelectionRects() call.
@@ -242,6 +259,22 @@ describe( 'BalloonToolbar', () => {
expect( balloonAddSpy.firstCall.args[ 0 ].position.target() ).to.deep.equal( backwardSelectionRect );
} );
+ // https://github.com/ckeditor/ckeditor5-ui/issues/385
+ it( 'should attach the #_balloon to the first range in a case of multi-range backward selection', () => {
+ setData( model, 'b[ar][bi]z', { lastRangeBackward: true } );
+
+ balloonToolbar.show();
+
+ // Because attaching and pinning BalloonPanelView is stubbed for test
+ // we need to manually call function that counting rect.
+ const targetRect = balloonAddSpy.firstCall.args[ 0 ].position.target();
+
+ const targetViewRange = editingView.domConverter.viewRangeToDom.lastCall.args[ 0 ];
+
+ expect( viewStringify( targetViewRange.root, targetViewRange ) ).to.equal( '
b{ar}
biz
' );
+ expect( targetRect ).to.deep.equal( backwardSelectionRect );
+ } );
+
it( 'should update balloon position on view#change event while balloon is added to the #_balloon', () => {
setData( model, 'b[a]r' );