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

Commit

Permalink
Merge pull request #46 from ckeditor/t/45
Browse files Browse the repository at this point in the history
Feature: The `ImageUploadCommand` now accepts `insertAt` position which allows customizing where the image will be inserted. Closes #45.
  • Loading branch information
Reinmar authored Jul 14, 2017
2 parents ba6de66 + 4b60c34 commit b90c8d7
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 30 deletions.
20 changes: 10 additions & 10 deletions src/imageuploadcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export default class ImageUploadCommand extends Command {
* @fires execute
* @param {Object} options Options for executed command.
* @param {File} options.file Image file to upload.
* @param {module:engine/model/position~Position} [options.insertAt] Position at which the image should be inserted.
* If the position won't be specified the image will be inserted next to the selection.
* @param {module:engine/model/batch~Batch} [options.batch] Batch to collect all the change steps.
* New batch will be created if this option is not set.
*/
Expand All @@ -44,33 +46,31 @@ export default class ImageUploadCommand extends Command {
}

doc.enqueueChanges( () => {
const insertPosition = getInsertionPosition( doc );
const insertAt = options.insertAt || getInsertionPosition( doc );

// No position to insert.
if ( !insertPosition ) {
if ( !insertAt ) {
return;
}

const imageElement = new ModelElement( 'image', {
uploadId: fileRepository.createLoader( file ).id
} );
const documentFragment = new ModelDocumentFragment( [ imageElement ] );
const range = new ModelRange( insertPosition );
const range = new ModelRange( insertAt );
const insertSelection = new ModelSelection();
insertSelection.setRanges( [ range ] );

insertSelection.setRanges( [ range ] );
editor.data.insertContent( documentFragment, insertSelection, batch );
selection.setRanges( [ ModelRange.createOn( imageElement ) ] );
} );
}
}

/**
* Returns correct image insertion position.
*
* @param {module:engine/model/document~Document} doc
* @returns {module:engine/model/position~Position|undefined}
*/
// Returns correct image insertion position.
//
// @param {module:engine/model/document~Document} doc
// @returns {module:engine/model/position~Position|undefined}
function getInsertionPosition( doc ) {
const selection = doc.selection;
const selectedElement = selection.getSelectedElement();
Expand Down
57 changes: 37 additions & 20 deletions tests/imageuploadcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import { setData as setModelData, getData as getModelData } from '@ckeditor/cked
import Image from '@ckeditor/ckeditor5-image/src/image/imageengine';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';

describe( 'ImageUploadCommand', () => {
let editor, command, adapterMock, document, fileRepository;
let editor, command, adapterMock, doc, fileRepository;

beforeEach( () => {
return VirtualTestEditor.create( {
Expand All @@ -29,9 +30,9 @@ describe( 'ImageUploadCommand', () => {
return adapterMock;
};

document = editor.document;
doc = editor.document;

const schema = document.schema;
const schema = doc.schema;
schema.allow( { name: 'image', attributes: [ 'uploadId' ], inside: '$root' } );
schema.requireAttributes( 'image', [ 'uploadId' ] );
} );
Expand All @@ -40,79 +41,95 @@ describe( 'ImageUploadCommand', () => {
describe( 'execute()', () => {
it( 'should insert image', () => {
const file = createNativeFileMock();
setModelData( document, '<paragraph>[]foo</paragraph>' );
setModelData( doc, '<paragraph>[]foo</paragraph>' );

command.execute( { file } );

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

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

command.execute( { file } );

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

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

command.execute( { file } );

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

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

command.execute( { file } );

const id = fileRepository.getLoader( file ).id;
expect( getModelData( document ) ).to.equal( `<image src="image.png"></image>[<image uploadId="${ id }"></image>]` );
expect( getModelData( doc ) ).to.equal( `<image src="image.png"></image>[<image uploadId="${ id }"></image>]` );
} );

it( 'should allow to insert image at some custom position (options.insertAt)', () => {
const file = createNativeFileMock();
setModelData( doc, '<paragraph>[foo]</paragraph><paragraph>bar</paragraph><paragraph>bom</paragraph>' );

const customPosition = new ModelPosition( doc.getRoot(), [ 2 ] ); // <p>foo</p><p>bar</p>^<p>bom</p>

command.execute( { file, insertAt: customPosition } );

const id = fileRepository.getLoader( file ).id;
expect( getModelData( doc ) ).to.equal(
'<paragraph>foo</paragraph><paragraph>bar</paragraph>' +
`[<image uploadId="${ id }"></image>]` +
'<paragraph>bom</paragraph>'
);
} );

it( 'should not insert image when proper insert position cannot be found', () => {
const file = createNativeFileMock();
document.schema.registerItem( 'other' );
document.schema.allow( { name: 'other', inside: '$root' } );
doc.schema.registerItem( 'other' );
doc.schema.allow( { name: 'other', inside: '$root' } );
buildModelConverter().for( editor.editing.modelToView )
.fromElement( 'other' )
.toElement( 'span' );

setModelData( document, '<other>[]</other>' );
setModelData( doc, '<other>[]</other>' );

command.execute( { file } );

expect( getModelData( document ) ).to.equal( '<other>[]</other>' );
expect( getModelData( doc ) ).to.equal( '<other>[]</other>' );
} );

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

expect( getModelData( document ) ).to.equal( '<paragraph>foo[]</paragraph>' );
expect( getModelData( doc ) ).to.equal( '<paragraph>foo[]</paragraph>' );
} );

it( 'should allow to provide batch instance', () => {
const batch = document.batch();
const batch = doc.batch();
const file = createNativeFileMock();
const spy = sinon.spy( batch, 'insert' );

setModelData( document, '<paragraph>[]foo</paragraph>' );
setModelData( doc, '<paragraph>[]foo</paragraph>' );

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

expect( getModelData( document ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
expect( getModelData( doc ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
sinon.assert.calledOnce( spy );
} );
} );
Expand Down

0 comments on commit b90c8d7

Please sign in to comment.