Socket
Socket
Sign inDemoInstall

@ckeditor/ckeditor5-upload

Package Overview
Dependencies
Maintainers
1
Versions
614
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ckeditor/ckeditor5-upload - npm Package Compare versions

Comparing version 0.0.1 to 0.1.0

CHANGELOG.md

40

package.json
{
"name": "@ckeditor/ckeditor5-upload",
"version": "0.0.1",
"version": "0.1.0",
"description": "Upload Feature for CKEditor 5.",
"keywords": [],
"dependencies": {
"@ckeditor/ckeditor5-core": "^0.8.0",
"@ckeditor/ckeditor5-engine": "^0.9.0",
"@ckeditor/ckeditor5-ui": "^v0.8.0",
"@ckeditor/ckeditor5-utils": "^0.9.0"
"@ckeditor/ckeditor5-core": "^0.8.1",
"@ckeditor/ckeditor5-engine": "^0.10.0",
"@ckeditor/ckeditor5-ui": "^0.9.0",
"@ckeditor/ckeditor5-utils": "^0.9.1"
},
"devDependencies": {
"@ckeditor/ckeditor5-basic-styles": "^0.8.0",
"@ckeditor/ckeditor5-clipboard": "^0.5.0",
"@ckeditor/ckeditor5-dev-lint": "^2.0.2",
"@ckeditor/ckeditor5-editor-classic": "^0.7.2",
"@ckeditor/ckeditor5-enter": "^0.9.0",
"@ckeditor/ckeditor5-heading": "^0.9.0",
"@ckeditor/ckeditor5-image": "^0.5.0",
"@ckeditor/ckeditor5-list": "^0.6.0",
"@ckeditor/ckeditor5-paragraph": "^0.7.0",
"@ckeditor/ckeditor5-typing": "^0.9.0",
"@ckeditor/ckeditor5-undo": "^0.8.0",
"gulp": "^3.9.1",
"guppy-pre-commit": "^0.4.0"
"@ckeditor/ckeditor5-basic-styles": "^0.8.1",
"@ckeditor/ckeditor5-clipboard": "^0.6.0",
"@ckeditor/ckeditor5-dev-lint": "^2.0.2",
"@ckeditor/ckeditor5-editor-classic": "^0.7.3",
"@ckeditor/ckeditor5-enter": "^0.9.1",
"@ckeditor/ckeditor5-heading": "^0.9.1",
"@ckeditor/ckeditor5-image": "^0.6.0",
"@ckeditor/ckeditor5-list": "^0.6.1",
"@ckeditor/ckeditor5-paragraph": "^0.8.0",
"@ckeditor/ckeditor5-typing": "^0.9.1",
"@ckeditor/ckeditor5-undo": "^0.8.1",
"gulp": "^3.9.1",
"guppy-pre-commit": "^0.4.0"
},
"engines": {
"node": ">=6.0.0",
"npm": ">=3.0.0"
"node": ">=6.0.0",
"npm": ">=3.0.0"
},

@@ -31,0 +31,0 @@ "author": "CKSource (http://cksource.com/)",

@@ -70,3 +70,3 @@ /**

* It might be different than the file size because of headers and additional data.
* It contains `null` if value is not available yet, so it's better to use {@link #uploadPercent} to monitor
* It contains `null` if value is not available yet, so it's better to use {@link #uploadedPercent} to monitor
* the progress.

@@ -321,3 +321,3 @@ *

/**
* Reads file using provided {@link module:upload/filereader~Adapter}.
* Reads file using provided {@link module:upload/filerepository~Adapter}.
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `filerepository-upload-wrong-status` when status

@@ -324,0 +324,0 @@ * is different than `idle`.

@@ -11,9 +11,10 @@ /**

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ImageUploadEngine from './imageuploadengine';
import FileDialogButtonView from './ui/filedialogbuttonview';
import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg';
import ImageUploadButton from './imageuploadbutton';
import ImageUploadProgress from './imageuploadprogress';
/**
* Image upload plugin.
* Adds `insertImage` button to UI component factory.
* This plugin do not do anything directly, but loads set of specific plugins to enable image uploading:
* * {@link module:upload/imageuploadbutton~ImageUploadButton},
* * {@link module:upload/imageuploadprogress~ImageUploadProgress}.
*

@@ -27,31 +28,4 @@ * @extends module:core/plugin~Plugin

static get requires() {
return [ ImageUploadEngine ];
return [ ImageUploadButton, ImageUploadProgress ];
}
/**
* @inheritDoc
*/
init() {
const editor = this.editor;
const t = editor.t;
editor.ui.componentFactory.add( 'insertImage', ( locale ) => {
const view = new FileDialogButtonView( locale );
view.set( {
label: t( 'Insert image' ),
icon: imageIcon,
tooltip: true,
acceptedType: 'image/*'
} );
view.on( 'done', ( evt, files ) => {
for ( const file of files ) {
editor.execute( 'imageUpload', { file: file } );
}
} );
return view;
} );
}
}

@@ -9,2 +9,3 @@ /**

import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range';
import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';
import ModelSelection from '@ckeditor/ckeditor5-engine/src/model/selection';

@@ -39,2 +40,3 @@ import FileRepository from './filerepository';

const file = options.file;
const selection = doc.selection;
const fileRepository = editor.plugins.get( FileRepository );

@@ -47,2 +49,22 @@

doc.enqueueChanges( () => {
let insertPosition;
const selectedElement = selection.getSelectedElement();
// If selected element is placed directly in root - put image after it.
if ( selectedElement && selectedElement.parent.is( 'rootElement' ) ) {
insertPosition = ModelPosition.createAfter( selectedElement );
} else {
// If selection is inside some block - put image before it.
const firstBlock = doc.selection.getSelectedBlocks().next().value;
if ( firstBlock ) {
insertPosition = ModelPosition.createBefore( firstBlock );
}
}
// No position to insert.
if ( !insertPosition ) {
return;
}
const imageElement = new ModelElement( 'image', {

@@ -52,5 +74,3 @@ uploadId: fileRepository.createLoader( file ).id

const documentFragment = new ModelDocumentFragment( [ imageElement ] );
const firstBlock = doc.selection.getSelectedBlocks().next().value;
const range = ModelRange.createFromParentsAndOffsets( firstBlock, 0, firstBlock, 0 );
const range = new ModelRange( insertPosition );
const insertSelection = new ModelSelection();

@@ -60,4 +80,5 @@ insertSelection.setRanges( [ range ] );

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

@@ -11,7 +11,5 @@ /**

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import { eventNameToConsumableType } from '@ckeditor/ckeditor5-engine/src/conversion/model-to-view-converters';
import FileRepository from './filerepository';
import ImageUploadCommand from './imageuploadcommand';
import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification';
import uploadingPlaceholder from '../theme/icons/image_placeholder.svg';
import { isImageType } from './utils';

@@ -25,14 +23,2 @@

export default class ImageUploadEngine extends Plugin {
constructor( editor ) {
super( editor );
/**
* Image's placeholder that is displayed before real image data can be accessed.
*
* @protected
* @member {String} #placeholder
*/
this.placeholder = 'data:image/svg+xml;utf8,' + uploadingPlaceholder;
}
/**

@@ -55,2 +41,3 @@ * @inheritDoc

schema.allow( { name: 'image', attributes: [ 'uploadId' ], inside: '$root' } );
schema.allow( { name: 'image', attributes: [ 'uploadStatus' ], inside: '$root' } );
schema.requireAttributes( 'image', [ 'uploadId' ] );

@@ -62,3 +49,3 @@

// Execute imageUpload command when image is dropped or pasted.
editor.editing.view.on( 'input', ( evt, data ) => {
editor.editing.view.on( 'clipboardInput', ( evt, data ) => {
for ( const file of data.dataTransfer.files ) {

@@ -92,15 +79,2 @@ if ( isImageType( file ) ) {

} );
// Model to view converter for image's `uploadId` attribute.
editor.editing.modelToView.on( 'addAttribute:uploadId:image', ( evt, data, consumable ) => {
if ( !consumable.consume( data.item, eventNameToConsumableType( evt.name ) ) ) {
return;
}
const modelImage = data.item;
const viewFigure = editor.editing.mapper.toViewElement( modelImage );
const viewImg = viewFigure.getChild( 0 );
viewImg.setAttribute( 'src', this.placeholder );
} );
}

@@ -123,2 +97,6 @@

doc.enqueueChanges( () => {
batch.setAttribute( imageElement, 'uploadStatus', 'reading' );
} );
loader.read()

@@ -128,9 +106,16 @@ .then( data => {

const viewImg = viewFigure.getChild( 0 );
const promise = loader.upload();
viewImg.setAttribute( 'src', data );
editor.editing.view.render();
return loader.upload();
doc.enqueueChanges( () => {
batch.setAttribute( imageElement, 'uploadStatus', 'uploading' );
} );
return promise;
} )
.then( data => {
doc.enqueueChanges( () => {
batch.setAttribute( imageElement, 'uploadStatus', 'complete' );
batch.setAttribute( imageElement, 'src', data.original );

@@ -153,2 +138,3 @@ } );

batch.removeAttribute( imageElement, 'uploadId' );
batch.removeAttribute( imageElement, 'uploadStatus' );
} );

@@ -155,0 +141,0 @@

@@ -32,3 +32,3 @@ /**

* @protected
* @member module:upload/ui/filedialogbuttonview~FileDialogButtonView #fileInputView
* @member module:upload/ui/filedialogbuttonview~FileInputView #fileInputView
*/

@@ -49,2 +49,11 @@ this.fileInputView = new FileInputView( locale );

/**
* Indicates if multiple files can be selected. Defaults to `true`.
*
* @observable
* @member {Boolean} #allowMultipleFiles
*/
this.set( 'allowMultipleFiles', false );
this.fileInputView.bind( 'allowMultipleFiles' ).to( this, 'allowMultipleFiles' );
/**
* Fired when file dialog is closed with file selected.

@@ -104,2 +113,10 @@ *

/**
* Indicates if multiple files can be selected. Defaults to `false`.
*
* @observable
* @member {Boolean} #allowMultipleFiles
*/
this.set( 'allowMultipleFiles', false );
const bind = this.bindTemplate;

@@ -116,3 +133,4 @@

tabindex: '-1',
accept: bind.to( 'acceptedType' )
accept: bind.to( 'acceptedType' ),
multiple: bind.to( 'allowMultipleFiles' )
},

@@ -119,0 +137,0 @@

@@ -10,6 +10,5 @@ /**

import Image from '@ckeditor/ckeditor5-image/src/image';
import FileDialogButtonView from '../src/ui/filedialogbuttonview';
import ImageUpload from '../src/imageupload';
import ImageUploadEngine from '../src/imageuploadengine';
import { createNativeFileMock } from './_utils/mocks';
import ImageUploadProgress from '../src/imageuploadprogress';
import ImageUploadButton from '../src/imageuploadbutton';

@@ -31,23 +30,10 @@ describe( 'ImageUpload', () => {

it( 'should load ImageUploadEngine', () => {
expect( editor.plugins.get( ImageUploadEngine ) ).to.be.instanceOf( ImageUploadEngine );
it( 'should include ImageUploadProgress', () => {
expect( editor.plugins.get( ImageUploadProgress ) ).to.be.instanceOf( ImageUploadProgress );
} );
it( 'should register insertImage button', () => {
const button = editor.ui.componentFactory.create( 'insertImage' );
expect( button ).to.be.instanceOf( FileDialogButtonView );
it( 'should include ImageUploadButton', () => {
expect( editor.plugins.get( ImageUploadButton ) ).to.be.instanceOf( ImageUploadButton );
} );
it( 'should execute imageUpload command', () => {
const executeStub = sinon.stub( editor, 'execute' );
const button = editor.ui.componentFactory.create( 'insertImage' );
const files = [ createNativeFileMock() ];
button.fire( 'done', files );
sinon.assert.calledOnce( executeStub );
expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
expect( executeStub.firstCall.args[ 1 ].file ).to.equal( files[ 0 ] );
} );
} );

@@ -13,2 +13,3 @@ /**

import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';

@@ -44,9 +45,34 @@ describe( 'ImageUploadCommand', () => {

setModelData( document, '<paragraph>foo[]</paragraph>' );
command._doExecute( { file } );
const id = fileRepository.getLoader( file ).id;
expect( getModelData( document ) ).to.equal( `[<image uploadId="${ id }"></image>]<paragraph>foo</paragraph>` );
} );
expect( getModelData( document ) ).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>]' );
command._doExecute( { file } );
const id = fileRepository.getLoader( file ).id;
expect( getModelData( document ) ).to.equal( `<image src="image.png"></image>[<image uploadId="${ id }"></image>]` );
} );
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' } );
buildModelConverter().for( editor.editing.modelToView )
.fromElement( 'other' )
.toElement( 'span' );
setModelData( document, '<other>[]</other>' );
command._doExecute( { file } );
expect( getModelData( document ) ).to.equal( '<other>[]</other>' );
} );
it( 'should not insert non-image', () => {

@@ -71,3 +97,3 @@ const file = createNativeFileMock();

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

@@ -74,0 +100,0 @@ } );

@@ -18,3 +18,2 @@ /**

import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
import imagePlaceholder from '../theme/icons/image_placeholder.svg';
import { eventNameToConsumableType } from '@ckeditor/ckeditor5-engine/src/conversion/model-to-view-converters';

@@ -68,3 +67,3 @@ import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';

viewDocument.fire( 'input', { dataTransfer } );
viewDocument.fire( 'clipboardInput', { dataTransfer } );

@@ -76,3 +75,3 @@ sinon.assert.calledOnce( spy );

expect( getModelData( document ) ).to.equal(
`<image uploadId="${ id }"></image><paragraph>foo bar baz[]</paragraph>`
`[<image uploadId="${ id }" uploadStatus="reading"></image>]<paragraph>foo bar baz</paragraph>`
);

@@ -91,3 +90,3 @@ } );

viewDocument.fire( 'input', { dataTransfer } );
viewDocument.fire( 'clipboardInput', { dataTransfer } );

@@ -97,12 +96,2 @@ sinon.assert.notCalled( spy );

it( 'should convert image\'s uploadId attribute from model to view', () => {
setModelData( document, '<image uploadId="1234"></image>' );
expect( getViewData( viewDocument ) ).to.equal(
'[]<figure class="image ck-widget" contenteditable="false">' +
`<img src="data:image/svg+xml;utf8,${ imagePlaceholder }"></img>` +
'</figure>'
);
} );
it( 'should not convert image\'s uploadId attribute if is consumed already', () => {

@@ -121,3 +110,3 @@ editor.editing.modelToView.on( 'addAttribute:uploadId:image', ( evt, data, consumable ) => {

it( 'should replace placeholder with read data once it is present', ( done ) => {
it( 'should use read data once it is present', ( done ) => {
const file = createNativeFileMock();

@@ -127,11 +116,12 @@ setModelData( document, '<paragraph>{}foo bar</paragraph>' );

adapterMock.uploadStartedCallback = () => {
document.once( 'changesDone', () => {
expect( getViewData( viewDocument ) ).to.equal(
'<figure class="image ck-widget" contenteditable="false">' +
`<img src="${ base64Sample }"></img>` +
'</figure>' +
'<p>{}foo bar</p>' );
'[<figure class="image ck-widget" contenteditable="false">' +
`<img src="${ base64Sample }"></img>` +
'</figure>]' +
'<p>foo bar</p>' );
expect( loader.status ).to.equal( 'uploading' );
done();
};
} );

@@ -147,6 +137,6 @@ expect( loader.status ).to.equal( 'reading' );

adapterMock.uploadStartedCallback = () => {
document.once( 'changesDone', () => {
document.once( 'changesDone', () => {
expect( getViewData( viewDocument ) ).to.equal(
'<figure class="image ck-widget" contenteditable="false"><img src="image.png"></img></figure><p>{}foo bar</p>'
'[<figure class="image ck-widget" contenteditable="false"><img src="image.png"></img></figure>]<p>foo bar</p>'
);

@@ -159,3 +149,3 @@ expect( loader.status ).to.equal( 'idle' );

adapterMock.mockSuccess( { original: 'image.png' } );
};
} );

@@ -209,14 +199,2 @@ nativeReaderMock.mockSuccess( base64Sample );

} );
it( 'should allow to customize placeholder image', () => {
const uploadEngine = editor.plugins.get( ImageUploadEngine );
uploadEngine.placeholder = base64Sample;
setModelData( document, '<image uploadId="1234"></image>' );
expect( getViewData( viewDocument ) ).to.equal(
'[]<figure class="image ck-widget" contenteditable="false">' +
`<img src="${ base64Sample }"></img>` +
'</figure>'
);
} );
} );

@@ -6,3 +6,3 @@ /**

/* globals document, window, console */
/* globals document, console */

@@ -26,2 +26,4 @@ import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classic';

const buttonContainer = document.getElementById( 'button-container' );
ClassicEditor.create( document.querySelector( '#editor' ), {

@@ -32,39 +34,47 @@ plugins: [

],
toolbar: [ 'headings', 'undo', 'redo', 'bold', 'italic', 'bulletedList', 'numberedList', 'insertImage' ]
toolbar: [ 'headings', 'undo', 'redo', 'bold', 'italic', 'bulletedList', 'numberedList', 'insertImage' ],
image: {
toolbar: [ 'imageStyleFull', 'imageStyleSide', '|' , 'imageTextAlternative' ]
}
} )
.then( editor => {
let adapterMock, progress;
const total = 500;
window.editor = editor;
const progressButton = document.getElementById( 'progress' );
// Register fake adapter.
editor.plugins.get( 'upload/filerepository' ).createAdapter = loader => {
adapterMock = new AdapterMock( loader );
progress = 0;
loader.on( 'change:uploadedPercent', () => {
console.log( `Loader upload progress: ${ loader.uploadedPercent }%` );
} );
const adapterMock = new AdapterMock( loader );
createProgressButton( loader, adapterMock );
progressButton.removeAttribute( 'disabled' );
return adapterMock;
};
} )
.catch( err => {
console.error( err.stack );
} );
progressButton.addEventListener( 'click', () => {
if ( adapterMock ) {
progress += 100;
adapterMock.mockProgress( progress, total );
function createProgressButton( loader, adapterMock ) {
const fileName = loader.file.name;
const container = document.createElement( 'div' );
const progressInfo = document.createElement( 'span' );
progressInfo.innerHTML = `File: ${ fileName }. Progress: 0%.`;
const button = document.createElement( 'button' );
button.innerHTML = 'Upload progress';
if ( progress == total ) {
progressButton.setAttribute( 'disabled', 'true' );
adapterMock.mockSuccess( { original: './sample.jpg' } );
}
container.appendChild( button );
container.appendChild( progressInfo );
buttonContainer.appendChild( container );
let progress = 0;
const total = 500;
button.addEventListener( 'click', () => {
progress += 100;
adapterMock.mockProgress( progress, total );
if ( progress == total ) {
button.setAttribute( 'disabled', 'true' );
adapterMock.mockSuccess( { original: './sample.jpg' } );
}
progressInfo.innerHTML = `File: ${ fileName }. Progress: ${ loader.uploadedPercent }%.`;
} );
} )
.catch( err => {
console.error( err.stack );
} );
}

@@ -5,6 +5,7 @@ ## Image upload

1. Image should be read and displayed.
1. Open console.
1. Press "Upload progress" button couple times to simulate upload process.
1. After uploading is complete your image should be replaced with sample image from server.
Repeat all the steps but this time use toolbar button to add image.
Repeat all the steps with:
* dropping multiple images,
* using toolbar button to add one and multiple images.

@@ -49,2 +49,8 @@ /**

it( 'should pass allowMultipleFiles to input view', () => {
view.set( { allowMultipleFiles: true } );
expect( view.fileInputView.allowMultipleFiles ).to.be.true;
} );
it( 'should delegate input view done event', ( done ) => {

@@ -51,0 +57,0 @@ const files = [];

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc