import { View } from '../../common'; import EditorModel from '../../editor/model/Editor'; import fetch from '../../utils/fetch'; import html from '../../utils/html'; import { AssetManagerConfig } from '../config/config'; type FileUploaderTemplateProps = { pfx: string; title: string; uploadId: string; disabled: boolean; multiUpload: boolean; }; export default class FileUploaderView extends View { options: any; config: AssetManagerConfig; pfx: string; ppfx: string; em: EditorModel; module: any; target: any; uploadId: string; disabled: boolean; multiUpload: boolean; uploadForm?: HTMLFormElement | null; template({ pfx, title, uploadId, disabled, multiUpload }: FileUploaderTemplateProps) { return html`
`; } events() { return { 'change [data-input]': 'uploadFile', }; } constructor(opts: any = {}) { super(opts); this.options = opts; const c = opts.config || {}; this.module = opts.module; this.config = c; // @ts-ignore this.em = this.config.em; this.pfx = c.stylePrefix || ''; this.ppfx = c.pStylePrefix || ''; this.target = this.options.globalCollection || {}; this.uploadId = this.pfx + 'uploadFile'; this.disabled = c.disableUpload !== undefined ? c.disableUpload : !c.upload && !c.embedAsBase64; this.multiUpload = c.multiUpload !== undefined ? c.multiUpload : true; const uploadFile = c.uploadFile; if (uploadFile) { this.uploadFile = uploadFile.bind(this); } else if (!c.upload && c.embedAsBase64) { this.uploadFile = FileUploaderView.embedAsBase64; } this.delegateEvents(); } /** * Triggered before the upload is started * @private */ onUploadStart() { const { module } = this; module && module.__propEv('asset:upload:start'); } /** * Triggered after the upload is ended * @param {Object|string} res End result * @private */ onUploadEnd(res: any) { const { $el, module } = this; module && module.__propEv('asset:upload:end', res); const input = $el.find('input'); input && input.val(''); } /** * Triggered on upload error * @param {Object} err Error * @private */ onUploadError(err: Error) { const { module } = this; console.error(err); this.onUploadEnd(err); module && module.__propEv('asset:upload:error', err); } /** * Triggered on upload response * @param {string} text Response text * @private */ onUploadResponse(text: string, clb?: (json: any) => void) { const { module, config, target } = this; let json; try { json = typeof text === 'string' ? JSON.parse(text) : text; } catch (e) { json = text; } module && module.__propEv('asset:upload:response', json); if (config.autoAdd && target) { target.add(json.data, { at: 0 }); } this.onUploadEnd(text); clb?.(json); } /** * Upload files * @param {Object} e Event * @return {Promise} * @private * */ uploadFile(e: DragEvent, clb?: () => void) { // @ts-ignore const files = e.dataTransfer ? e.dataTransfer.files : e.target.files; const { config } = this; const { beforeUpload } = config; const beforeUploadResponse = beforeUpload && beforeUpload(files); if (beforeUploadResponse === false) return; const body = new FormData(); const { params, customFetch, fetchOptions } = config; for (let param in params) { body.append(param, params[param]); } if (this.multiUpload) { for (let i = 0; i < files.length; i++) { body.append(`${config.uploadName}[]`, files[i]); } } else if (files.length) { body.append(config.uploadName!, files[0]); } const url = config.upload; const headers = config.headers!; const reqHead = 'X-Requested-With'; if (typeof headers[reqHead] == 'undefined') { headers[reqHead] = 'XMLHttpRequest'; } if (url) { this.onUploadStart(); const fetchOpts = { method: 'post', credentials: config.credentials || 'include', headers, body, }; const fetchOptsResult = fetchOptions?.(fetchOpts) || fetchOpts; const fetchResult = customFetch ? customFetch(url, fetchOptsResult) : fetch(url, fetchOptsResult).then((res: any) => ((res.status / 200) | 0) == 1 ? res.text() : res.text().then((text: string) => Promise.reject(text)) ); return fetchResult .then((text: string) => this.onUploadResponse(text, clb)) .catch((err: Error) => this.onUploadError(err)); } } /** * Make input file droppable * @private * */ initDrop() { var that = this; if (!this.uploadForm) { this.uploadForm = this.$el.find('form').get(0)!; const formEl = this.uploadForm; if ('draggable' in formEl) { this.uploadForm.ondragover = function () { formEl.className = that.pfx + 'hover'; return false; }; this.uploadForm.ondragleave = function () { formEl.className = ''; return false; }; this.uploadForm.ondrop = function (ev) { formEl.className = ''; ev.preventDefault(); that.uploadFile(ev); return; }; } } } initDropzone(ev: any) { let addedCls = 0; const c = this.config; const em = ev.model; const edEl = ev.el; const editor = em.Editor; const frameEl = em.Canvas.getBody(); const ppfx = this.ppfx; const updatedCls = `${ppfx}dropzone-active`; const dropzoneCls = `${ppfx}dropzone`; const cleanEditorElCls = () => { edEl.className = edEl.className.replace(updatedCls, '').trim(); addedCls = 0; }; const onDragOver = () => { if (!addedCls) { edEl.className += ` ${updatedCls}`; addedCls = 1; } return false; }; const onDragLeave = () => { cleanEditorElCls(); return false; }; const onDrop = (e: DragEvent) => { cleanEditorElCls(); e.preventDefault(); e.stopPropagation(); this.uploadFile(e); if (c.openAssetsOnDrop && editor) { const target = editor.getSelected(); editor.runCommand('open-assets', { target, onSelect() { editor.Modal.close(); editor.AssetManager.setTarget(null); }, }); } return false; }; ev.$el.append(`