/*!
* FilePondPluginImageEdit 1.6.3
* Licensed under MIT, https://opensource.org/licenses/MIT/
* Please visit https://pqina.nl/filepond/ for details.
*/
/* eslint-disable */
const isPreviewableImage = file => /^image/.test(file.type);
/**
* Image Edit Proxy Plugin
*/
const plugin = _ => {
const { addFilter, utils, views } = _;
const { Type, createRoute, createItemAPI = item => item } = utils;
const { fileActionButton } = views;
addFilter(
'SHOULD_REMOVE_ON_REVERT',
(shouldRemove, { item, query }) =>
new Promise(resolve => {
const { file } = item;
// if this file is editable it shouldn't be removed immidiately even when instant uploading
const canEdit =
query('GET_ALLOW_IMAGE_EDIT') &&
query('GET_IMAGE_EDIT_ALLOW_EDIT') &&
isPreviewableImage(file);
// if the file cannot be edited it should be removed on revert
resolve(!canEdit);
})
);
// open editor when loading a new item
addFilter(
'DID_LOAD_ITEM',
(item, { query, dispatch }) =>
new Promise((resolve, reject) => {
// if is temp or local file
if (item.origin > 1) {
resolve(item);
return;
}
// get file reference
const { file } = item;
if (
!query('GET_ALLOW_IMAGE_EDIT') ||
!query('GET_IMAGE_EDIT_INSTANT_EDIT')
) {
resolve(item);
return;
}
// exit if this is not an image
if (!isPreviewableImage(file)) {
resolve(item);
return;
}
const createEditorResponseHandler = (
item,
resolve,
reject
) => userDidConfirm => {
// remove item
editRequestQueue.shift();
// handle item
if (userDidConfirm) {
resolve(item);
} else {
reject(item);
}
// TODO: Fix, should not be needed to kick the internal loop in case no processes are running
dispatch('KICK');
// handle next item!
requestEdit();
};
const requestEdit = () => {
if (!editRequestQueue.length) return;
const { item, resolve, reject } = editRequestQueue[0];
dispatch('EDIT_ITEM', {
id: item.id,
handleEditorResponse: createEditorResponseHandler(
item,
resolve,
reject
)
});
};
queueEditRequest({ item, resolve, reject });
if (editRequestQueue.length === 1) {
requestEdit();
}
})
);
// extend item methods
addFilter('DID_CREATE_ITEM', (item, { query, dispatch }) => {
item.extend('edit', () => {
dispatch('EDIT_ITEM', { id: item.id });
});
});
const editRequestQueue = [];
const queueEditRequest = editRequest => {
editRequestQueue.push(editRequest);
return editRequest;
};
// called for each view that is created right after the 'create' method
addFilter('CREATE_VIEW', viewAPI => {
// get reference to created view
const { is, view, query } = viewAPI;
if (!query('GET_ALLOW_IMAGE_EDIT')) return;
const canShowImagePreview = query('GET_ALLOW_IMAGE_PREVIEW');
// only run for either the file or the file info panel
const shouldExtendView =
(is('file-info') && !canShowImagePreview) ||
(is('file') && canShowImagePreview);
if (!shouldExtendView) return;
// no editor defined, then exit
const editor = query('GET_IMAGE_EDIT_EDITOR');
if (!editor) return;
// set default FilePond options and add bridge once
if (!editor.filepondCallbackBridge) {
editor.outputData = true;
editor.outputFile = false;
editor.filepondCallbackBridge = {
onconfirm: editor.onconfirm || (() => {}),
oncancel: editor.oncancel || (() => {})
};
}
// opens the editor, if it does not already exist, it creates the editor
const openEditor = ({ root, props, action }) => {
const { id } = props;
const { handleEditorResponse } = action;
// update editor props that could have changed
editor.cropAspectRatio =
root.query('GET_IMAGE_CROP_ASPECT_RATIO') || editor.cropAspectRatio;
editor.outputCanvasBackgroundColor =
root.query('GET_IMAGE_TRANSFORM_CANVAS_BACKGROUND_COLOR') ||
editor.outputCanvasBackgroundColor;
// get item
const item = root.query('GET_ITEM', id);
if (!item) return;
// file to open
const file = item.file;
// crop data to pass to editor
const crop = item.getMetadata('crop');
const cropDefault = {
center: {
x: 0.5,
y: 0.5
},
flip: {
horizontal: false,
vertical: false
},
zoom: 1,
rotation: 0,
aspectRatio: null
};
// size data to pass to editor
const resize = item.getMetadata('resize');
// filter and color data to pass to editor
const filter = item.getMetadata('filter') || null;
const filters = item.getMetadata('filters') || null;
const colors = item.getMetadata('colors') || null;
const markup = item.getMetadata('markup') || null;
// build parameters object
const imageParameters = {
crop: crop || cropDefault,
size: resize
? {
upscale: resize.upscale,
mode: resize.mode,
width: resize.size.width,
height: resize.size.height
}
: null,
filter: filters
? filters.id || filters.matrix
: root.query('GET_ALLOW_IMAGE_FILTER') &&
root.query('GET_IMAGE_FILTER_COLOR_MATRIX') &&
!colors
? filter
: null,
color: colors,
markup
};
editor.onconfirm = ({ data }) => {
const { crop, size, filter, color, colorMatrix, markup } = data;
// create new metadata object
const metadata = {};
// append crop data
if (crop) {
metadata.crop = crop;
}
// append size data
if (size) {
const initialSize = (item.getMetadata('resize') || {}).size;
const targetSize = {
width: size.width,
height: size.height
};
if (!(targetSize.width && targetSize.height) && initialSize) {
targetSize.width = initialSize.width;
targetSize.height = initialSize.height;
}
if (targetSize.width || targetSize.height) {
metadata.resize = {
upscale: size.upscale,
mode: size.mode,
size: targetSize
};
}
}
if (markup) {
metadata.markup = markup;
}
// set filters and colors so we can restore them when re-editing the image
metadata.colors = color;
metadata.filters = filter;
// set merged color matrix to use in preview plugin
metadata.filter = colorMatrix;
// update crop metadata
item.setMetadata(metadata);
// call
editor.filepondCallbackBridge.onconfirm(data, createItemAPI(item));
// used in instant edit mode
if (!handleEditorResponse) return;
editor.onclose = () => {
handleEditorResponse(true);
editor.onclose = null;
};
};
editor.oncancel = () => {
// call
editor.filepondCallbackBridge.oncancel(createItemAPI(item));
// used in instant edit mode
if (!handleEditorResponse) return;
editor.onclose = () => {
handleEditorResponse(false);
editor.onclose = null;
};
};
editor.open(file, imageParameters);
};
/**
* Image Preview related
*/
// create the image edit plugin, but only do so if the item is an image
const didLoadItem = ({ root, props }) => {
if (!query('GET_IMAGE_EDIT_ALLOW_EDIT')) return;
const { id } = props;
// try to access item
const item = query('GET_ITEM', id);
if (!item) return;
// get the file object
const file = item.file;
// exit if this is not an image
if (!isPreviewableImage(file)) return;
// handle interactions
root.ref.handleEdit = e => {
e.stopPropagation();
root.dispatch('EDIT_ITEM', { id });
};
if (canShowImagePreview) {
// add edit button to preview
const buttonView = view.createChildView(fileActionButton, {
label: 'edit',
icon: query('GET_IMAGE_EDIT_ICON_EDIT'),
opacity: 0
});
// edit item classname
buttonView.element.classList.add('filepond--action-edit-item');
buttonView.element.dataset.align = query(
'GET_STYLE_IMAGE_EDIT_BUTTON_EDIT_ITEM_POSITION'
);
buttonView.on('click', root.ref.handleEdit);
root.ref.buttonEditItem = view.appendChildView(buttonView);
} else {
// view is file info
const filenameElement = view.element.querySelector(
'.filepond--file-info-main'
);
const editButton = document.createElement('button');
editButton.className = 'filepond--action-edit-item-alt';
editButton.innerHTML =
query('GET_IMAGE_EDIT_ICON_EDIT') + 'edit';
editButton.addEventListener('click', root.ref.handleEdit);
filenameElement.appendChild(editButton);
root.ref.editButton = editButton;
}
};
view.registerDestroyer(({ root }) => {
if (root.ref.buttonEditItem) {
root.ref.buttonEditItem.off('click', root.ref.handleEdit);
}
if (root.ref.editButton) {
root.ref.editButton.removeEventListener('click', root.ref.handleEdit);
}
});
const routes = {
EDIT_ITEM: openEditor,
DID_LOAD_ITEM: didLoadItem
};
if (canShowImagePreview) {
// view is file
const didPreviewUpdate = ({ root }) => {
if (!root.ref.buttonEditItem) return;
root.ref.buttonEditItem.opacity = 1;
};
routes.DID_IMAGE_PREVIEW_SHOW = didPreviewUpdate;
} else {
}
// start writing
view.registerWriter(createRoute(routes));
});
// Expose plugin options
return {
options: {
// enable or disable image editing
allowImageEdit: [true, Type.BOOLEAN],
// location of processing button
styleImageEditButtonEditItemPosition: ['bottom center', Type.STRING],
// open editor when image is dropped
imageEditInstantEdit: [false, Type.BOOLEAN],
// allow editing
imageEditAllowEdit: [true, Type.BOOLEAN],
// the icon to use for the edit button
imageEditIconEdit: [
'',
Type.STRING
],
// editor object
imageEditEditor: [null, Type.OBJECT]
}
};
};
// fire pluginloaded event if running in browser, this allows registering the plugin when using async script tags
const isBrowser =
typeof window !== 'undefined' && typeof window.document !== 'undefined';
if (isBrowser) {
document.dispatchEvent(
new CustomEvent('FilePond:pluginloaded', { detail: plugin })
);
}
export default plugin;