/** * EGroupware eTemplate2 - JS Number object * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api * @link https://www.egroupware.org * @author Nathan Gray * @copyright Nathan Gray 2011 */ /*egw:uses et2_core_inputWidget; api.Resumable.resumable; */ import { et2_inputWidget } from "./et2_core_inputWidget"; import { et2_register_widget } from "./et2_core_widget"; import { ClassWithAttributes } from "./et2_core_inheritance"; import { et2_no_init } from "./et2_core_common"; import { et2_vfsSize } from "./et2_widget_vfs"; /** * Class which implements file upload * * @augments et2_inputWidget */ export class et2_file extends et2_inputWidget { /** * Constructor * * @memberOf et2_file */ constructor(_parent, _attrs, _child) { // Call the inherited constructor super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_file._attributes, _child || {})); this.asyncOptions = {}; this.input = null; this.progress = null; this.span = null; this.node = null; this.input = null; this.progress = null; this.span = null; // Contains all submit buttons need to be disabled during upload process this.disabled_buttons = jQuery("input[type='submit'], button"); // Make sure it's an object, not an array, or values get lost when sent to server this.options.value = jQuery.extend({}, this.options.value); if (!this.options.id) { console.warn("File widget needs an ID. Used 'file_widget'."); this.options.id = "file_widget"; } // Legacy - id ending in [] means multiple if (this.options.id.substr(-2) == "[]") { this.options.multiple = true; } // If ID ends in /, it's a directory - allow multiple else if (this.options.id.substr(-1) === "/") { this.options.multiple = true; _attrs.multiple = true; } // Set up the URL to have the request ID & the widget ID var instance = this.getInstanceManager(); let self = this; this.asyncOptions = jQuery.extend({}, this.getAsyncOptions(this)); this.asyncOptions.fieldName = this.options.id; this.createInputWidget(); this.set_readonly(this.options.readonly); } destroy() { super.destroy(); this.set_drop_target(null); this.node = null; this.input = null; this.span = null; this.progress = null; } createInputWidget() { this.node = jQuery(document.createElement("div")).addClass("et2_file"); this.span = jQuery(document.createElement("span")) .addClass('et2_file_span et2_button') .appendTo(this.node); if (this.options.label != '') this.span.addClass('et2_button_text'); let span = this.span; this.input = jQuery(document.createElement("input")) .attr("type", "file").attr("placeholder", this.options.blur) .addClass("et2_file_upload") .appendTo(this.node) .hover(function () { jQuery(span) .toggleClass('et2_file_spanHover'); }) .on({ mousedown: function () { jQuery(span).addClass('et2_file_spanActive'); }, mouseup: function () { jQuery(span).removeClass('et2_file_spanActive'); } }); if (this.options.accept) this.input.attr('accept', this.options.accept); let self = this; // trigger native input upload file if (!this.options.readonly) this.span.click(function () { self.input.click(); }); // Check for File interface, should fall back to normal form submit if missing if (typeof File != "undefined" && typeof (new XMLHttpRequest()).upload != "undefined") { this.resumable = new Resumable(this.asyncOptions); this.resumable.assignBrowse(this.input); this.resumable.on('fileAdded', jQuery.proxy(this._fileAdded, this)); this.resumable.on('fileProgress', jQuery.proxy(this._fileProgress, this)); this.resumable.on('fileSuccess', jQuery.proxy(this.finishUpload, this)); this.resumable.on('complete', jQuery.proxy(this.onFinish, this)); } else { // This may be a problem submitting via ajax } if (this.options.progress) { let widget = this.getRoot().getWidgetById(this.options.progress); if (widget) { //may be not available at createInputWidget time this.progress = jQuery(widget.getDOMNode()); } } if (!this.progress) { this.progress = jQuery(document.createElement("div")).appendTo(this.node); } this.progress.addClass("progress"); if (this.options.multiple) { this.input.attr("multiple", "multiple"); } this.setDOMNode(this.node[0]); // set drop target to widget dom node if no target option is specified if (!this.options.drop_target) this.resumable.assignDrop([this.getDOMNode()]); } /** * Get any specific async upload options */ getAsyncOptions(self) { return { // Callbacks onStart: function (event, file_count) { return self.onStart(event, file_count); }, onFinish: function (event, file_count) { self.onFinish.apply(self, [event, file_count]); }, onStartOne: function (event, file_name, index, file_count) { }, onFinishOne: function (event, response, name, number, total) { return self.finishUpload(event, response, name, number, total); }, onProgress: function (event, progress, name, number, total) { return self.onProgress(event, progress, name, number, total); }, onError: function (event, name, error) { return self.onError(event, name, error); }, beforeSend: function (form) { return self.beforeSend(form); }, chunkSize: this.options.chunk_size || 1024 * 1024, target: egw.ajaxUrl("EGroupware\\Api\\Etemplate\\Widget\\File::ajax_upload"), query: function (file) { return self.beforeSend(file); }, // Disable checking for already uploaded chunks testChunks: false }; } /** * Set a widget or DOM node as a HTML5 file drop target * * @param {string} new_target widget ID or DOM node ID to be used as a new target */ set_drop_target(new_target) { // Cancel old drop target if (this.options.drop_target) { let widget = this.getRoot().getWidgetById(this.options.drop_target); let drop_target = widget && widget.getDOMNode() || document.getElementById(this.options.drop_target); if (drop_target) { this.resumable.unAssignDrop(drop_target); } } this.options.drop_target = new_target; if (!this.options.drop_target) return; // Set up new drop target let widget = this.getRoot().getWidgetById(this.options.drop_target); let drop_target = widget && widget.getDOMNode() || document.getElementById(this.options.drop_target); if (drop_target) { this.resumable.assignDrop([drop_target]); } else { this.egw().debug("warn", "Did not find file drop target %s", this.options.drop_target); } } attachToDOM() { let res = super.attachToDOM(); // Override parent's change, file widget will fire change when finished uploading this.input.unbind("change.et2_inputWidget"); return res; } getValue() { return this.options.value ? this.options.value : this.input.val(); } /** * Set the value of the file widget. * * If you pass a FileList or list of files, it will trigger the async upload * * @param {FileList|File[]|false} value List of files to be uploaded, or false to reset. * @param {Event} event Most browsers require the user to initiate file transfers in some way. * Pass the event in, if you have it. */ set_value(value, event) { if (!value || typeof value == "undefined") { value = {}; } if (jQuery.isEmptyObject(value)) { this.options.value = {}; if (this.resumable.progress() == 1) this.progress.empty(); // Reset the HTML element this.input.wrap('