UNPKG

grapesjs_codeapps

Version:

Free and Open Source Web Builder Framework/SC Modification

364 lines (326 loc) 10.1 kB
import _ from 'underscore'; import Backbone from 'backbone'; import fetch from 'utils/fetch'; module.exports = Backbone.View.extend( { template: _.template(` <form> <div id="<%= pfx %>title"><%= title %></div> <input type="file" id="<%= uploadId %>" name="file" accept="*/*" <%= disabled ? 'disabled' : '' %> <%= multiUpload ? 'multiple' : '' %>/> <div style="clear:both;"></div> </form> `), events: {}, initialize(opts = {}) { this.options = opts; const c = opts.config || {}; this.config = c; 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; this.events['change #' + this.uploadId] = 'uploadFile'; let uploadFile = c.uploadFile; if (uploadFile) { this.uploadFile = uploadFile.bind(this); } else if (c.embedAsBase64) { this.uploadFile = this.constructor.embedAsBase64; } this.delegateEvents(); }, /** * Triggered before the upload is started * @private */ onUploadStart(data) { const em = this.config.em; em && em.trigger('asset:upload:start', data); }, /** * Triggered after the upload is ended * @param {Object|string} res End result * @private */ onUploadEnd(res) { const em = this.config.em; em && em.trigger('asset:upload:end', res); }, /** * Triggered on upload error * @param {Object} err Error * @private */ onUploadError(err) { const em = this.config.em; console.error(err); this.onUploadEnd(err); em && em.trigger('asset:upload:error', err); }, /** * Triggered on upload response * @param {string} text Response text * @private */ onUploadResponse(text, clb) { const em = this.config.em; const config = this.config; const target = this.target; let json; try { json = typeof text === 'string' ? JSON.parse(text) : text; } catch (e) { json = text; } em && em.trigger('asset:upload:response', json); if (config.autoAdd && target) { target.add(json.data, { at: 0 }); } this.onUploadEnd(text); clb && clb(json); }, /** * Upload files * @param {Object} e Event * @return {Promise} * @private * */ uploadFile(e, clb) { const files = e.dataTransfer ? e.dataTransfer.files : e.target.files; const body = new FormData(); const config = this.config; const params = config.params; 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]); } var target = this.target; 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(body); // return fetch(url, { // method: 'post', // mode: 'no-cors', // credentials: config.credentials || 'include', // headers, // body // }) // .then( // res => // ((res.status / 200) | 0) == 1 // ? res.text() // : res.text().then(text => Promise.reject(text)) // ) // .then(text => this.onUploadResponse(text, clb)) // .catch(err => this.onUploadError(err)); } }, /** * Make input file droppable * @private * */ initDrop() { var that = this; if (!this.uploadForm) { this.uploadForm = this.$el.find('form').get(0); if ('draggable' in this.uploadForm) { var uploadFile = this.uploadFile; this.uploadForm.ondragover = function() { this.className = that.pfx + 'hover'; return false; }; this.uploadForm.ondragleave = function() { this.className = ''; return false; }; this.uploadForm.ondrop = function(e) { this.className = ''; e.preventDefault(); that.uploadFile(e); return; }; } } }, initDropzone(ev) { let addedCls = 0; const c = this.config; const em = ev.model; const edEl = ev.el; const editor = em.get('Editor'); const container = em.get('Config').el; const frameEl = em.get('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 => { 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(`<div class="${dropzoneCls}">${c.dropzoneContent}</div>`); cleanEditorElCls(); if ('draggable' in edEl) { [edEl, frameEl].forEach(item => { item.ondragover = onDragOver; item.ondragleave = onDragLeave; item.ondrop = onDrop; }); } }, render() { this.$el.html( this.template({ title: this.config.uploadText, uploadId: this.uploadId, disabled: this.disabled, multiUpload: this.multiUpload, pfx: this.pfx }) ); this.initDrop(); this.$el.attr('class', this.pfx + 'file-uploader'); return this; } }, { embedAsBase64: function(e, clb) { // List files dropped const files = e.dataTransfer ? e.dataTransfer.files : e.target.files; const response = { data: [] }; // Unlikely, widely supported now if (!FileReader) { this.onUploadError( new Error('Unsupported platform, FileReader is not defined') ); return; } const promises = []; const mimeTypeMatcher = /^(.+)\/(.+)$/; for (const file of files) { // For each file a reader (to read the base64 URL) // and a promise (to track and merge results and errors) const promise = new Promise((resolve, reject) => { const reader = new FileReader(); reader.addEventListener('load', event => { let type; const name = file.name; // Try to find the MIME type of the file. const match = mimeTypeMatcher.exec(file.type); if (match) { type = match[1]; // The first part in the MIME, "image" in image/png } else { type = file.type; } /* // Show local video files, http://jsfiddle.net/dsbonev/cCCZ2/embedded/result,js,html,css/ var URL = window.URL || window.webkitURL var file = this.files[0] var type = file.type var videoNode = document.createElement('video'); var canPlay = videoNode.canPlayType(type) // can use also for 'audio' types if (canPlay === '') canPlay = 'no' var message = 'Can play type "' + type + '": ' + canPlay var isError = canPlay === 'no' displayMessage(message, isError) if (isError) { return } var fileURL = URL.createObjectURL(file) videoNode.src = fileURL */ // If it's an image, try to find its size if (type === 'image') { const data = { src: reader.result, name, type, height: 0, width: 0 }; const image = new Image(); image.addEventListener('error', error => { reject(error); }); image.addEventListener('load', () => { data.height = image.height; data.width = image.width; resolve(data); }); image.src = data.src; } else if (type) { // Not an image, but has a type resolve({ src: reader.result, name, type }); } else { // No type found, resolve with the URL only resolve(reader.result); } }); reader.addEventListener('error', error => { reject(error); }); reader.addEventListener('abort', error => { reject('Aborted'); }); reader.readAsDataURL(file); }); promises.push(promise); } Promise.all(promises).then( data => { response.data = data; this.onUploadResponse(response, clb); }, error => { this.onUploadError(error); } ); } } );