123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- /*!
- * Pintura Image Editor 8.13.1
- * (c) 2018-2021 PQINA Inc. - All Rights Reserved
- * License: https://pqina.nl/pintura/license/
- */
- /* eslint-disable */
- var isFile = (v) => v instanceof File;
- var isImage = (file) => /^image/.test(file.type);
- var isString = (v) => typeof v === 'string';
- let isSafari = null;
- var isSafari$1 = () => {
- if (isSafari === null)
- isSafari = isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
- return isSafari;
- };
- var getImageElementSize = (imageElement) => new Promise((resolve, reject) => {
- let shouldAutoRemove = false;
- // test if image is attached to DOM, if not attached, attach so measurement is correct on Safari
- if (!imageElement.parentNode && isSafari$1()) {
- shouldAutoRemove = true;
- // has width 0 and height 0 to prevent rendering very big SVGs (without width and height) that will for one frame overflow the window and show a scrollbar
- imageElement.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;left:0;top:0;width:0;height:0;`;
- document.body.appendChild(imageElement);
- }
- // start testing size
- const measure = () => {
- const width = imageElement.naturalWidth;
- const height = imageElement.naturalHeight;
- const hasSize = width && height;
- if (!hasSize)
- return;
- // clean up image if was attached for measuring
- if (shouldAutoRemove)
- imageElement.parentNode.removeChild(imageElement);
- clearInterval(intervalId);
- resolve({ width, height });
- };
- imageElement.onerror = (err) => {
- clearInterval(intervalId);
- reject(err);
- };
- const intervalId = setInterval(measure, 1);
- measure();
- });
- var getImageSize = async (image) => {
- // the image element we'll use to load the image
- let imageElement = image;
- // if is not an image element, it must be a valid image source
- if (!imageElement.src) {
- imageElement = new Image();
- imageElement.src = isString(image) ? image : URL.createObjectURL(image);
- }
- let size;
- try {
- size = await getImageElementSize(imageElement);
- }
- finally {
- isFile(image) && URL.revokeObjectURL(imageElement.src);
- }
- return size;
- };
- var isBlob = (v) => v instanceof Blob && !(v instanceof File);
- var noop = (...args) => { };
- let result$3 = null;
- var isBrowser = () => {
- if (result$3 === null)
- result$3 = typeof window !== 'undefined' && typeof window.document !== 'undefined';
- return result$3;
- };
- var h = (name, attributes, children = []) => {
- const el = document.createElement(name);
- // @ts-ignore
- const descriptors = Object.getOwnPropertyDescriptors(el.__proto__);
- for (const key in attributes) {
- if (key === 'style') {
- el.style.cssText = attributes[key];
- }
- else if ((descriptors[key] && descriptors[key].set) ||
- /textContent|innerHTML/.test(key) ||
- typeof attributes[key] === 'function') {
- el[key] = attributes[key];
- }
- else {
- el.setAttribute(key, attributes[key]);
- }
- }
- children.forEach((child) => el.appendChild(child));
- return el;
- };
- var releaseCanvas = (canvas) => {
- canvas.width = 1;
- canvas.height = 1;
- const ctx = canvas.getContext('2d');
- ctx && ctx.clearRect(0, 0, 1, 1);
- };
- let result$2 = null;
- var supportsWebGL2 = () => {
- if (result$2 === null) {
- if ('WebGL2RenderingContext' in window) {
- let canvas;
- try {
- canvas = h('canvas');
- result$2 = !!canvas.getContext('webgl2');
- }
- catch (err) {
- result$2 = false;
- }
- canvas && releaseCanvas(canvas);
- }
- else {
- result$2 = false;
- }
- }
- return result$2;
- };
- var getWebGLContext = (canvas, attrs) => {
- if (supportsWebGL2())
- return canvas.getContext('webgl2', attrs);
- return (canvas.getContext('webgl', attrs) ||
- canvas.getContext('experimental-webgl', attrs));
- };
- let result$1 = null;
- var supportsWebGL = () => {
- if (result$1 === null) {
- const canvas = h('canvas');
- result$1 = !!getWebGLContext(canvas);
- releaseCanvas(canvas);
- }
- return result$1;
- };
- const isOperaMini = () => Object.prototype.toString.call(window['operamini']) === '[object OperaMini]';
- const hasPromises = () => 'Promise' in window;
- const hasCreateObjectURL = () => 'URL' in window && 'createObjectURL' in window.URL;
- const hasVisibility = () => 'visibilityState' in document;
- const hasTiming = () => 'performance' in window; // iOS 8.x
- const hasFileConstructor = () => 'File' in window; // excludes IE11
- let result = null;
- var isModernBrowser = () => {
- if (result === null)
- result =
- isBrowser() &&
- // Can't run on Opera Mini due to lack of everything
- !isOperaMini() &&
- // Require these APIs to feature detect a modern browser
- hasVisibility() &&
- hasPromises() &&
- hasFileConstructor() &&
- hasCreateObjectURL() &&
- hasTiming();
- return result;
- };
- /**
- * Image Edit Proxy Plugin
- */
- const plugin = (_) => {
- const { addFilter, utils, views } = _;
- const { Type, createRoute } = utils;
- const { fileActionButton } = views;
- const getEditorSafe = (editor) => (editor === null ? {} : editor);
- 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_EDITOR') &&
- query('GET_IMAGE_EDITOR_ALLOW_EDIT') &&
- query('GET_IMAGE_EDITOR_SUPPORT_EDIT') &&
- query('GET_IMAGE_EDITOR_SUPPORT_IMAGE')(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_EDITOR') ||
- !query('GET_IMAGE_EDITOR_INSTANT_EDIT' ))
- return resolve(item);
- // exit if this is not an image
- if (!query('GET_IMAGE_EDITOR_SUPPORT_IMAGE')(file))
- return resolve(item);
- // request editing of a file
- const requestEdit = () => {
- if (!editRequestQueue.length)
- return;
- const { item, resolve, reject } = editRequestQueue[0];
- dispatch('EDIT_ITEM', {
- id: item.id,
- handleEditorResponse: createEditorResponseHandler(item, resolve, reject),
- });
- };
- // is called when the user confirms editing the file
- 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();
- };
- 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;
- };
- const couldTransformFile = (query) => {
- // no editor defined, then exit
- const { imageProcessor, imageReader, imageWriter } = getEditorSafe(query('GET_IMAGE_EDITOR'));
- return (query('GET_IMAGE_EDITOR_WRITE_IMAGE') &&
- query('GET_IMAGE_EDITOR_SUPPORT_WRITE_IMAGE') &&
- imageProcessor &&
- imageReader &&
- imageWriter);
- };
- // 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_EDITOR'))
- return;
- if (!query('GET_IMAGE_EDITOR_SUPPORT_WRITE_IMAGE'))
- return;
- const supportsFilePoster = query('GET_ALLOW_FILE_POSTER');
- // only run for either the file or the file info panel
- const shouldExtendView = (is('file-info') && !supportsFilePoster) || (is('file') && supportsFilePoster);
- if (!shouldExtendView)
- return;
- // no editor defined, then exit
- const { createEditor, imageProcessor, imageReader, imageWriter, editorOptions, legacyDataToImageState, imageState: imageBaseState, } = getEditorSafe(query('GET_IMAGE_EDITOR'));
- if (!imageReader || !imageWriter || !editorOptions || !editorOptions.locale)
- return;
- // remove default image reader and writer if set
- delete editorOptions.imageReader;
- delete editorOptions.imageWriter;
- const [createImageReader, imageReaderOptions] = imageReader;
- const [createImageWriter = noop, imageWriterOptions] = imageWriter;
- // tests if file item has poster
- const getItemByProps = (props) => {
- const { id } = props;
- const item = query('GET_ITEM', id);
- return item;
- };
- const hasPoster = (root, props) => {
- if (!query('GET_ALLOW_FILE_POSTER'))
- return false;
- const item = getItemByProps(props);
- if (!item)
- return;
- // test if is filtered
- if (!query('GET_FILE_POSTER_FILTER_ITEM')(item))
- return false;
- const poster = item.getMetadata('poster');
- return !!poster;
- };
- // generate preview
- const getPosterTargetSize = (root, targetSize) => {
- const posterHeight = root.query('GET_FILE_POSTER_HEIGHT');
- const maxPosterHeight = root.query('GET_FILE_POSTER_MAX_HEIGHT');
- if (posterHeight) {
- targetSize.width = posterHeight * 2;
- targetSize.height = posterHeight * 2;
- }
- else if (maxPosterHeight) {
- targetSize.width = maxPosterHeight * 2;
- targetSize.height = maxPosterHeight * 2;
- }
- return targetSize;
- };
- const createEditorOptions = (root) => {
- const targetSize = getPosterTargetSize(root, {
- width: 512,
- height: 512,
- });
- return {
- ...editorOptions,
- imageReader: createImageReader(imageReaderOptions),
- imageWriter: createImageWriter({
- // poster size
- targetSize,
- // can optionally overwrite poster size
- ...(imageWriterOptions || {}),
- }),
- };
- };
- const createImagePoster = ({ root, props }) => {
- // need image processor to create image poster
- if (!imageProcessor)
- return;
- const item = getItemByProps(props);
- if (!item)
- return;
- const file = item.file;
- const imageState = item.getMetadata('imageState');
- const options = {
- ...createEditorOptions(root),
- imageState: {
- ...imageBaseState,
- ...imageState,
- },
- };
- imageProcessor(file, options).then(({ dest }) => {
- item.setMetadata('poster', URL.createObjectURL(dest), true);
- });
- };
- // opens the editor, if it does not already exist, it creates the editor
- const openImageEditor = ({ root, props, action }) => {
- const { handleEditorResponse } = action;
- // get item
- const item = getItemByProps(props);
- // file to open
- const file = item.file;
- // open the editor (sets editor properties and imageState property)
- const editor = createEditor({
- ...createEditorOptions(root),
- src: file,
- });
- // when the image has loaded, update the editor
- editor.on('load', ({ size }) => {
- // get current image edit state
- let imageState = item.getMetadata('imageState');
- imageState = legacyDataToImageState
- ? legacyDataToImageState(editor, size, imageState)
- : imageState;
- // update editor view based on image edit state
- editor.imageState = {
- ...imageBaseState,
- ...imageState,
- };
- });
- editor.on('process', ({ imageState, dest }) => {
- // if already has post URL, try to revoke
- const poster = item.getMetadata('poster');
- poster && URL.revokeObjectURL(item.getMetadata('poster'));
- // store state, two seperate actions because we want to trigger preparefile when setting `imageState`
- item.setMetadata('poster', URL.createObjectURL(dest));
- item.setMetadata('imageState', imageState);
- // used in instant edit mode
- if (!handleEditorResponse)
- return;
- handleEditorResponse(true);
- });
- editor.on('close', () => {
- // used in instant edit mode
- if (!handleEditorResponse)
- return;
- handleEditorResponse(false);
- });
- };
- //#region view
- const didLoadItem = ({ root, props }) => {
- 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 (!query('GET_IMAGE_EDITOR_SUPPORT_IMAGE')(file))
- return;
- if (query('GET_ALLOW_FILE_POSTER') && !item.getMetadata('poster')) {
- root.dispatch('REQUEST_CREATE_IMAGE_POSTER', { id });
- }
- if (!query('GET_IMAGE_EDITOR_ALLOW_EDIT') || !query('GET_IMAGE_EDITOR_SUPPORT_EDIT'))
- return;
- // handle interactions
- root.ref.handleEdit = (e) => {
- e.stopPropagation();
- root.dispatch('EDIT_ITEM', { id });
- };
- updateEditButton(root, props);
- };
- const updateEditButton = (root, props) => {
- root.ref.buttonEditItem && root.removeChildView(root.ref.buttonEditItem);
- if (root.ref.editButton && root.ref.editButton.parentNode) {
- root.ref.editButton.parentNode.removeChild(root.ref.editButton);
- }
- if (hasPoster(root, props)) {
- // add edit button to preview
- const buttonView = view.createChildView(fileActionButton, {
- label: 'edit',
- icon: query('GET_IMAGE_EDITOR_ICON_EDIT'),
- opacity: 0,
- });
- // edit item classname
- buttonView.element.classList.add('filepond--action-edit-item');
- buttonView.element.dataset.align = query('GET_STYLE_IMAGE_EDITOR_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_EDITOR_ICON_EDIT') + '<span>edit</span>';
- editButton.addEventListener('click', root.ref.handleEdit);
- filenameElement.appendChild(editButton);
- root.ref.editButton = editButton;
- }
- };
- const didUpdateItemMetadata = ({ root, props, action }) => {
- if (!/poster/.test(action.change.key))
- return;
- if (!query('GET_IMAGE_EDITOR_ALLOW_EDIT') || !query('GET_IMAGE_EDITOR_SUPPORT_EDIT'))
- return;
- updateEditButton(root, props);
- };
- 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: openImageEditor,
- DID_LOAD_ITEM: didLoadItem,
- DID_UPDATE_ITEM_METADATA: didUpdateItemMetadata,
- DID_REMOVE_ITEM: ({ props }) => {
- const { id } = props;
- const item = query('GET_ITEM', id);
- if (!item)
- return;
- const poster = item.getMetadata('poster');
- poster && URL.revokeObjectURL(poster);
- },
- REQUEST_CREATE_IMAGE_POSTER: createImagePoster,
- DID_FILE_POSTER_LOAD: undefined,
- };
- if (supportsFilePoster) {
- // view is file
- const didPosterUpdate = ({ root }) => {
- if (!root.ref.buttonEditItem)
- return;
- root.ref.buttonEditItem.opacity = 1;
- };
- routes.DID_FILE_POSTER_LOAD = didPosterUpdate;
- }
- //#endregion
- // start writing
- view.registerWriter(createRoute(routes));
- });
- //#region write image
- addFilter('SHOULD_PREPARE_OUTPUT', (shouldPrepareOutput, { query, change, item }) => new Promise((resolve) => {
- if (!query('GET_IMAGE_EDITOR_SUPPORT_IMAGE')(item.file))
- return resolve(false);
- if (change && !/imageState/.test(change.key))
- return resolve(false);
- resolve(!query('IS_ASYNC'));
- }));
- const shouldTransformFile = (query, file, item) => new Promise((resolve) => {
- // invalid item
- if (!couldTransformFile(query) ||
- item.archived ||
- (!isFile(file) && !isBlob(file)) ||
- !query('GET_IMAGE_EDITOR_SUPPORT_IMAGE')(file)) {
- return resolve(false);
- }
- // if size can't be read this browser doesn't support image
- getImageSize(file)
- .then(() => {
- const fn = query('GET_IMAGE_TRANSFORM_IMAGE_FILTER');
- if (fn) {
- const filterResult = fn(file);
- if (typeof filterResult === 'boolean') {
- return resolve(filterResult);
- }
- if (typeof filterResult.then === 'function') {
- return filterResult.then(resolve);
- }
- }
- resolve(true);
- })
- .catch(() => {
- resolve(false);
- });
- });
- // subscribe to file transformations
- addFilter('PREPARE_OUTPUT', (file, { query, item }) => {
- const writeOutputImage = (file) => new Promise((resolve, reject) => {
- const imageState = item.getMetadata('imageState');
- // no editor defined, then exit
- const { imageProcessor, imageReader, imageWriter, editorOptions, imageState: imageBaseState, } = getEditorSafe(query('GET_IMAGE_EDITOR'));
- if (!imageProcessor || !imageReader || !imageWriter || !editorOptions)
- return;
- const [createImageReader, imageReaderOptions] = imageReader;
- const [createImageWriter = noop, imageWriterOptions] = imageWriter;
- imageProcessor(file, {
- ...editorOptions,
- imageReader: createImageReader(imageReaderOptions),
- imageWriter: createImageWriter(imageWriterOptions),
- imageState: {
- ...imageBaseState,
- ...imageState,
- },
- })
- .then(resolve)
- .catch(reject);
- });
- return new Promise((resolve) => {
- shouldTransformFile(query, file, item).then((shouldWrite) => {
- if (!shouldWrite)
- return resolve(file);
- writeOutputImage(file).then((res) => {
- const afterFn = query('GET_IMAGE_EDITOR_AFTER_WRITE_IMAGE');
- if (afterFn)
- return afterFn(res).then(resolve);
- // @ts-ignore
- resolve(res.dest);
- });
- });
- });
- });
- //#endregion
- // Expose plugin options
- return {
- options: {
- // enable or disable image editing
- allowImageEditor: [true, Type.BOOLEAN],
- // open editor when image is dropped
- imageEditorInstantEdit: [false, Type.BOOLEAN],
- // allow editing
- imageEditorAllowEdit: [true, Type.BOOLEAN],
- // cannot edit if no WebGL or is <=IE11
- imageEditorSupportEdit: [
- isBrowser() && isModernBrowser() && supportsWebGL(),
- Type.BOOLEAN,
- ],
- // receives file and should return true if can edit
- imageEditorSupportImage: [isImage, Type.FUNCTION],
- // cannot write if is <= IE11
- imageEditorSupportWriteImage: [isModernBrowser(), Type.BOOLEAN],
- // should output image
- imageEditorWriteImage: [true, Type.BOOLEAN],
- // receives written image and can return single or more images
- imageEditorAfterWriteImage: [undefined, Type.FUNCTION],
- // editor object
- imageEditor: [null, Type.OBJECT],
- // the icon to use for the edit button
- imageEditorIconEdit: [
- '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M8.5 17h1.586l7-7L15.5 8.414l-7 7V17zm-1.707-2.707l8-8a1 1 0 0 1 1.414 0l3 3a1 1 0 0 1 0 1.414l-8 8A1 1 0 0 1 10.5 19h-3a1 1 0 0 1-1-1v-3a1 1 0 0 1 .293-.707z" fill="currentColor" fill-rule="nonzero"/></svg>',
- Type.STRING,
- ],
- // location of processing button
- styleImageEditorButtonEditItemPosition: ['bottom center', Type.STRING],
- },
- };
- };
- // fire pluginloaded event if running in browser, this allows registering the plugin when using async script tags
- if (isBrowser())
- document.dispatchEvent(new CustomEvent('FilePond:pluginloaded', { detail: plugin }));
- export default plugin;
|