Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit b90c8d7

Browse files
authored
Merge pull request #46 from ckeditor/t/45
Feature: The `ImageUploadCommand` now accepts `insertAt` position which allows customizing where the image will be inserted. Closes #45.
2 parents ba6de66 + 4b60c34 commit b90c8d7

File tree

2 files changed

+47
-30
lines changed

2 files changed

+47
-30
lines changed

src/imageuploadcommand.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export default class ImageUploadCommand extends Command {
2828
* @fires execute
2929
* @param {Object} options Options for executed command.
3030
* @param {File} options.file Image file to upload.
31+
* @param {module:engine/model/position~Position} [options.insertAt] Position at which the image should be inserted.
32+
* If the position won't be specified the image will be inserted next to the selection.
3133
* @param {module:engine/model/batch~Batch} [options.batch] Batch to collect all the change steps.
3234
* New batch will be created if this option is not set.
3335
*/
@@ -44,33 +46,31 @@ export default class ImageUploadCommand extends Command {
4446
}
4547

4648
doc.enqueueChanges( () => {
47-
const insertPosition = getInsertionPosition( doc );
49+
const insertAt = options.insertAt || getInsertionPosition( doc );
4850

4951
// No position to insert.
50-
if ( !insertPosition ) {
52+
if ( !insertAt ) {
5153
return;
5254
}
5355

5456
const imageElement = new ModelElement( 'image', {
5557
uploadId: fileRepository.createLoader( file ).id
5658
} );
5759
const documentFragment = new ModelDocumentFragment( [ imageElement ] );
58-
const range = new ModelRange( insertPosition );
60+
const range = new ModelRange( insertAt );
5961
const insertSelection = new ModelSelection();
60-
insertSelection.setRanges( [ range ] );
6162

63+
insertSelection.setRanges( [ range ] );
6264
editor.data.insertContent( documentFragment, insertSelection, batch );
6365
selection.setRanges( [ ModelRange.createOn( imageElement ) ] );
6466
} );
6567
}
6668
}
6769

68-
/**
69-
* Returns correct image insertion position.
70-
*
71-
* @param {module:engine/model/document~Document} doc
72-
* @returns {module:engine/model/position~Position|undefined}
73-
*/
70+
// Returns correct image insertion position.
71+
//
72+
// @param {module:engine/model/document~Document} doc
73+
// @returns {module:engine/model/position~Position|undefined}
7474
function getInsertionPosition( doc ) {
7575
const selection = doc.selection;
7676
const selectedElement = selection.getSelectedElement();

tests/imageuploadcommand.js

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ import { setData as setModelData, getData as getModelData } from '@ckeditor/cked
1111
import Image from '@ckeditor/ckeditor5-image/src/image/imageengine';
1212
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
1313
import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
14+
import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';
1415

1516
describe( 'ImageUploadCommand', () => {
16-
let editor, command, adapterMock, document, fileRepository;
17+
let editor, command, adapterMock, doc, fileRepository;
1718

1819
beforeEach( () => {
1920
return VirtualTestEditor.create( {
@@ -29,9 +30,9 @@ describe( 'ImageUploadCommand', () => {
2930
return adapterMock;
3031
};
3132

32-
document = editor.document;
33+
doc = editor.document;
3334

34-
const schema = document.schema;
35+
const schema = doc.schema;
3536
schema.allow( { name: 'image', attributes: [ 'uploadId' ], inside: '$root' } );
3637
schema.requireAttributes( 'image', [ 'uploadId' ] );
3738
} );
@@ -40,79 +41,95 @@ describe( 'ImageUploadCommand', () => {
4041
describe( 'execute()', () => {
4142
it( 'should insert image', () => {
4243
const file = createNativeFileMock();
43-
setModelData( document, '<paragraph>[]foo</paragraph>' );
44+
setModelData( doc, '<paragraph>[]foo</paragraph>' );
4445

4546
command.execute( { file } );
4647

4748
const id = fileRepository.getLoader( file ).id;
48-
expect( getModelData( document ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
49+
expect( getModelData( doc ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
4950
} );
5051

5152
it( 'should insert image after block if selection is at its end', () => {
5253
const file = createNativeFileMock();
53-
setModelData( document, '<paragraph>foo[]</paragraph>' );
54+
setModelData( doc, '<paragraph>foo[]</paragraph>' );
5455

5556
command.execute( { file } );
5657

5758
const id = fileRepository.getLoader( file ).id;
58-
expect( getModelData( document ) ).to.equal( `<paragraph>foo</paragraph>[<image uploadId="${ id }"></image>]` );
59+
expect( getModelData( doc ) ).to.equal( `<paragraph>foo</paragraph>[<image uploadId="${ id }"></image>]` );
5960
} );
6061

6162
it( 'should insert image before block if selection is in the middle', () => {
6263
const file = createNativeFileMock();
63-
setModelData( document, '<paragraph>f{}oo</paragraph>' );
64+
setModelData( doc, '<paragraph>f{}oo</paragraph>' );
6465

6566
command.execute( { file } );
6667

6768
const id = fileRepository.getLoader( file ).id;
68-
expect( getModelData( document ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
69+
expect( getModelData( doc ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
6970
} );
7071

7172
it( 'should insert image after other image', () => {
7273
const file = createNativeFileMock();
73-
setModelData( document, '[<image src="image.png"></image>]' );
74+
setModelData( doc, '[<image src="image.png"></image>]' );
7475

7576
command.execute( { file } );
7677

7778
const id = fileRepository.getLoader( file ).id;
78-
expect( getModelData( document ) ).to.equal( `<image src="image.png"></image>[<image uploadId="${ id }"></image>]` );
79+
expect( getModelData( doc ) ).to.equal( `<image src="image.png"></image>[<image uploadId="${ id }"></image>]` );
80+
} );
81+
82+
it( 'should allow to insert image at some custom position (options.insertAt)', () => {
83+
const file = createNativeFileMock();
84+
setModelData( doc, '<paragraph>[foo]</paragraph><paragraph>bar</paragraph><paragraph>bom</paragraph>' );
85+
86+
const customPosition = new ModelPosition( doc.getRoot(), [ 2 ] ); // <p>foo</p><p>bar</p>^<p>bom</p>
87+
88+
command.execute( { file, insertAt: customPosition } );
89+
90+
const id = fileRepository.getLoader( file ).id;
91+
expect( getModelData( doc ) ).to.equal(
92+
'<paragraph>foo</paragraph><paragraph>bar</paragraph>' +
93+
`[<image uploadId="${ id }"></image>]` +
94+
'<paragraph>bom</paragraph>'
95+
);
7996
} );
8097

8198
it( 'should not insert image when proper insert position cannot be found', () => {
8299
const file = createNativeFileMock();
83-
document.schema.registerItem( 'other' );
84-
document.schema.allow( { name: 'other', inside: '$root' } );
100+
doc.schema.registerItem( 'other' );
101+
doc.schema.allow( { name: 'other', inside: '$root' } );
85102
buildModelConverter().for( editor.editing.modelToView )
86103
.fromElement( 'other' )
87104
.toElement( 'span' );
88105

89-
setModelData( document, '<other>[]</other>' );
106+
setModelData( doc, '<other>[]</other>' );
90107

91108
command.execute( { file } );
92109

93-
expect( getModelData( document ) ).to.equal( '<other>[]</other>' );
110+
expect( getModelData( doc ) ).to.equal( '<other>[]</other>' );
94111
} );
95112

96113
it( 'should not insert non-image', () => {
97114
const file = createNativeFileMock();
98115
file.type = 'audio/mpeg3';
99-
setModelData( document, '<paragraph>foo[]</paragraph>' );
116+
setModelData( doc, '<paragraph>foo[]</paragraph>' );
100117
command.execute( { file } );
101118

102-
expect( getModelData( document ) ).to.equal( '<paragraph>foo[]</paragraph>' );
119+
expect( getModelData( doc ) ).to.equal( '<paragraph>foo[]</paragraph>' );
103120
} );
104121

105122
it( 'should allow to provide batch instance', () => {
106-
const batch = document.batch();
123+
const batch = doc.batch();
107124
const file = createNativeFileMock();
108125
const spy = sinon.spy( batch, 'insert' );
109126

110-
setModelData( document, '<paragraph>[]foo</paragraph>' );
127+
setModelData( doc, '<paragraph>[]foo</paragraph>' );
111128

112129
command.execute( { batch, file } );
113130
const id = fileRepository.getLoader( file ).id;
114131

115-
expect( getModelData( document ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
132+
expect( getModelData( doc ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
116133
sinon.assert.calledOnce( spy );
117134
} );
118135
} );

0 commit comments

Comments
 (0)