@rickx/ckeditor5-upload-adapter
Version:
Upload adapter plugin for CKEditor5
265 lines (261 loc) • 8.46 kB
JavaScript
import { Plugin, FileRepository } from 'ckeditor5';
/**
* The Upload adapter allows uploading images to an application running on your server using
*
* ```ts
* ClassicEditor
* .create(document.querySelector('#editor'), {
* customUpload: {
* onUpload: (file) => {
* // do any upload stuff here with the JS-File-Object
* return Promise.resolve({ url: 'http://server/default-size.image.png' })
* },
* onAbort: () => {
* // abort the upload here. The promise from onImageUpload should be rejected after that.
* }
* }
* })
* .then( ... )
* .catch( ... );
* ```
*/ class CustomUploadAdapter extends Plugin {
static get requires() {
return [
FileRepository
];
}
static get pluginName() {
return 'CustomUploadAdapter';
}
init() {
const options = this.editor.config.get('customUpload');
if (!options) return;
if (!options.onUpload) {
console.warn('custom-upload-adapter-missing-onupload');
return;
}
this.editor.plugins.get(FileRepository).createUploadAdapter = (loader)=>{
return new Adapter$1(loader, options);
};
}
}
/**
* Upload adapter.
*/ let Adapter$1 = class Adapter {
/**
* FileLoader instance to use during the upload.
*/ loader;
/**
* The configuration of the adapter.
*/ options;
/**
* Creates a new adapter instance.
*/ constructor(loader, options){
this.loader = loader;
this.options = options;
}
/**
* Executes the upload process.
* This method should return a promise that will resolve when data will be uploaded to server. Promise should be
* resolved with an object containing information about uploaded file:
*
* ```json
* {
* default: 'http://server/default-size.image.png'
* }
* ```
*
* Additionally, other image sizes can be provided:
*
* ```json
* {
* default: 'http://server/default-size.image.png',
* '160': 'http://server/size-160.image.png',
* '500': 'http://server/size-500.image.png',
* '1000': 'http://server/size-1000.image.png',
* '1052': 'http://server/default-size.image.png'
* }
* ```
*
* You can also pass additional properties from the server. In this case you need to wrap URLs
* in the `urls` object and pass additional properties along the `urls` property.
*
* ```json
* {
* myCustomProperty: 'foo',
* urls: {
* default: 'http://server/default-size.image.png',
* '160': 'http://server/size-160.image.png',
* '500': 'http://server/size-500.image.png',
* '1000': 'http://server/size-1000.image.png',
* '1052': 'http://server/default-size.image.png'
* }
* }
* ```
*
* NOTE: When returning multiple images, the widest returned one should equal the default one. It is essential to
* correctly set `width` attribute of the image. See this discussion:
* https://github.com/ckeditor/ckeditor5-easy-image/issues/4 for more information.
*
* @returns Promise that should be resolved when data is uploaded.
*/ async upload() {
const file = await this.loader.file;
const response = await this.options.onUpload(file);
const urls = response.url ? {
default: response.url
} : response.urls;
return {
...response,
urls
};
}
/**
* Aborts the upload process.
*/ abort() {
if (this.options.onAbort) this.options.onAbort();
}
};
/**
* The Upload adapter allows uploading images to an application running on your server using
* the [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) API with a
* minimal ImageUploadConfig
*
* ```ts
* ClassicEditor
* .create(document.querySelector('#editor'), {
* imageUpload: {
* uploadUrl: 'http://example.com',
* withCredentials: true,
* headers: {
* 'X-CSRF-TOKEN': 'CSRF-Token',
* 'Authorization': 'Bearer <JSON Web Token>',
* },
* params: {
* foo: 'bar',
* }
* }
* } )
* .then( ... )
* .catch( ... );
* ```
*/ class ImageUploadAdapter extends Plugin {
static get requires() {
return [
FileRepository
];
}
static get pluginName() {
return 'ImageUploadAdapter';
}
init() {
const options = this.editor.config.get('imageUpload');
if (!options) return;
if (!options.uploadUrl) {
console.warn('image-upload-adapter-missing-uploadurl');
return;
}
this.editor.plugins.get(FileRepository).createUploadAdapter = (loader)=>{
return new Adapter(loader, options);
};
}
}
/**
* Upload adapter.
*/ class Adapter {
/**
* FileLoader instance to use during the upload.
*/ loader;
/**
* The configuration of the adapter.
*/ options;
xhr;
/**
* Creates a new adapter instance.
*/ constructor(loader, options){
this.loader = loader;
this.options = options;
}
/**
* Starts the upload process.
*/ async upload() {
const file = await this.loader.file;
return new Promise((resolve, reject)=>{
this._initRequest();
this._initListeners(resolve, reject, file);
this._sendRequest(file);
});
}
/**
* Aborts the upload process.
*/ abort() {
if (this.xhr) this.xhr.abort();
}
/**
* Initializes the `XMLHttpRequest` object using the URL specified as
* uploadUrl in the editor's configuration.
*/ _initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
xhr.open('POST', this.options.uploadUrl, true);
xhr.responseType = 'json';
}
/**
* Initializes XMLHttpRequest listeners
*
* @param resolve Callback function to be called when the request is successful.
* @param reject Callback function to be called when the request cannot be completed.
* @param file Native File object.
*/ _initListeners(resolve, reject, file) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = `Couldn't upload file: ${file.name}.`;
xhr.addEventListener('error', ()=>reject(genericErrorText));
xhr.addEventListener('abort', ()=>reject());
xhr.addEventListener('load', ()=>{
const response = xhr.response;
if (!response || response.error) return reject(response && response.error && response.error.message ? response.error.message : genericErrorText);
const urls = response.url ? {
default: response.url
} : response.urls;
// Resolve with the normalized `urls` property and pass the rest of the response
// to allow customizing the behavior of features relying on the upload adapters.
resolve({
...response,
urls
});
});
// Upload progress when it is supported.
if (xhr.upload) {
xhr.upload.addEventListener('progress', (evt)=>{
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
/**
* Prepares the data and sends the request.
*
* @param file File instance to be uploaded.
*/ _sendRequest(file) {
// Set headers if specified.
const headers = this.options.headers || {};
// Set FormData is specified.
const params = this.options.params || {};
// Use the withCredentials flag if specified.
const withCredentials = this.options.withCredentials || false;
for (const headerName of Object.keys(headers))this.xhr.setRequestHeader(headerName, headers[headerName]);
this.xhr.withCredentials = withCredentials;
// Prepare the form data.
const data = new FormData();
data.append('upload', file);
// Append custom FormData
for (const paramName of Object.keys(params)){
if (params[paramName]) data.append(paramName, params[paramName]);
}
// Send the request.
this.xhr.send(data);
}
}
export { CustomUploadAdapter, ImageUploadAdapter };
//# sourceMappingURL=index.js.map