| /* |
| * Copyright (c) 2014-2018 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| */ |
| import {arrays, Device, dragAndDrop, InputFieldKeyStrokeContext, strings, URL, Widget} from '../index'; |
| import * as $ from 'jquery'; |
| |
| export default class FileInput extends Widget { |
| |
| constructor() { |
| super(); |
| this.acceptTypes = null; |
| this.maximumUploadSize = FileInput.DEFAULT_MAXIMUM_UPLOAD_SIZE; |
| this.multiSelect = false; |
| this.files = []; |
| this.legacyFileUploadUrl = null; |
| this.text = null; |
| } |
| |
| static DEFAULT_MAXIMUM_UPLOAD_SIZE = 50 * 1024 * 1024; // 50 MB |
| |
| _init(model) { |
| super._init(model); |
| this.uploadController = model.uploadController || model.parent; |
| var url = new URL(model.legacyFileUploadUrl || 'upload/' + this.session.uiSessionId + '/' + this.uploadController.id); |
| url.setParameter('legacy', true); |
| this.legacyFileUploadUrl = url.toString(); |
| this.legacy = !Device.get().supportsFile(); |
| } |
| |
| /** |
| * @override |
| */ |
| _initKeyStrokeContext() { |
| // Need to create keystroke context here because this.legacy is not set at the time the constructor is executed |
| this.keyStrokeContext = this._createKeyStrokeContext(); |
| super._initKeyStrokeContext(); |
| } |
| |
| _createKeyStrokeContext() { |
| if (this.legacy) { |
| // native input control is a text field -> use input field context to make sure backspace etc. does not bubble up |
| return new InputFieldKeyStrokeContext(); |
| } |
| } |
| |
| _render() { |
| this.$fileInput = this.$parent.makeElement('<input>') |
| .attr('type', 'file') |
| .on('change', this._onFileChange.bind(this)); |
| |
| if (!this.legacy) { |
| this.$container = this.$parent.appendDiv('file-input input-field') |
| .on('dragenter', this._onDragEnterOrOver.bind(this)) |
| .on('dragover', this._onDragEnterOrOver.bind(this)) |
| .on('drop', this._onDrop.bind(this)); |
| this.$fileInput.appendTo(this.$container); |
| this.$container.on('mousedown', this._onMouseDown.bind(this)); |
| this.$text = this.$container.appendDiv('file-input-text'); |
| } else { |
| this._renderLegacyMode(); |
| } |
| |
| if (this.legacy) { |
| // Files may not be set into native control -> clear list in order to be sync again |
| this.clear(); |
| } |
| } |
| |
| _renderLegacyMode() { |
| this.$legacyFormTarget = this.$fileInput.appendElement('<iframe>') |
| .attr('name', 'legacyFileUpload' + this.uploadController.id) |
| .on('load', function() { |
| // Manually handle JSON response from iframe |
| try { |
| // "onAjaxDone" |
| var text = this.$legacyFormTarget.contents().text(); |
| if (strings.hasText(text)) { |
| // Manually handle JSON response |
| var json = $.parseJSON(text); |
| this.session.responseQueue.process(json); |
| } |
| } finally { |
| // "onAjaxAlways" |
| this.session.setBusy(false); |
| } |
| }.bind(this)); |
| this.$fileInput |
| .attr('name', 'file') |
| .addClass('legacy-upload-file-input'); |
| this.$legacyForm = this.$parent.appendElement('<form>', 'legacy-upload-form') |
| .attr('action', this.legacyFileUploadUrl) |
| .attr('enctype', 'multipart/form-data') |
| .attr('method', 'post') |
| .attr('target', 'legacyFileUpload' + this.uploadController.id) |
| .append(this.$fileInput); |
| this.$container = this.$legacyForm; |
| } |
| |
| _renderProperties() { |
| super._renderProperties(); |
| this._renderText(); |
| this._renderAcceptTypes(); |
| this._renderMultiSelect(); |
| } |
| |
| _renderEnabled() { |
| super._renderEnabled(); |
| |
| if (this.legacy) { |
| this.$fileInput.setEnabled(this.enabledComputed); |
| } else { |
| this.$container.setTabbable(this.enabledComputed); |
| } |
| } |
| |
| setText(text) { |
| this.setProperty('text', text); |
| } |
| |
| _renderText() { |
| if (this.legacy) { |
| return; |
| } |
| var text = this.text || ''; |
| this.$text.text(text); |
| } |
| |
| setAcceptTypes(acceptTypes) { |
| this.setProperty('acceptTypes', acceptTypes); |
| } |
| |
| _renderAcceptTypes() { |
| var acceptTypes = this.acceptTypes || ''; |
| this.$fileInput.attr('accept', acceptTypes); |
| } |
| |
| setMultiSelect(multiSelect) { |
| this.setProperty('multiSelect', multiSelect); |
| } |
| |
| _renderMultiSelect() { |
| this.$fileInput.prop('multiple', this.multiSelect); |
| } |
| |
| setMaximumUploadSize(maximumUploadSize) { |
| this.setProperty('maximumUploadSize', maximumUploadSize); |
| } |
| |
| clear() { |
| this._setFiles([]); |
| // _setFiles actually sets the text as well, but only if files have changed. |
| // Make sure text is cleared as well if there are no files but a text set. |
| this.setText(null); |
| if (this.rendered) { |
| this.$fileInput.val(null); |
| } |
| } |
| |
| _setFiles(files) { |
| if (files instanceof FileList) { |
| files = FileInput.fileListToArray(files); |
| } |
| files = arrays.ensure(files); |
| if (arrays.equals(this.files, files)) { |
| return; |
| } |
| var name = ''; |
| if (files.length > 0) { |
| if (this.legacy) { |
| name = files[0]; |
| } else { |
| name = files[0].name; |
| } |
| } |
| this.files = files; |
| this.setText(name); |
| this.trigger('change', { |
| files: files |
| }); |
| } |
| |
| upload() { |
| if (this.files.length === 0) { |
| return true; |
| } |
| if (!this.legacy) { |
| return this.session.uploadFiles(this.uploadController, this.files, undefined, this.maximumUploadSize); |
| } |
| this.session.setBusy(true); |
| this.$legacyForm[0].submit(); |
| return true; |
| } |
| |
| browse() { |
| // Trigger browser's file chooser |
| this.$fileInput.click(); |
| } |
| |
| _onFileChange(event) { |
| var files = []; |
| |
| if (!this.legacy) { |
| files = this.$fileInput[0].files; |
| } else { |
| if (this.$fileInput[0].value) { |
| files.push(this.$fileInput[0].value); |
| } |
| } |
| if (files.length) { |
| this._setFiles(files); |
| } |
| } |
| |
| _onMouseDown() { |
| if (!this.enabled) { |
| return; |
| } |
| this.browse(); |
| } |
| |
| _onDragEnterOrOver(event) { |
| dragAndDrop.verifyDataTransferTypesScoutTypes(event, dragAndDrop.SCOUT_TYPES.FILE_TRANSFER); |
| } |
| |
| _onDrop(event) { |
| if (dragAndDrop.dataTransferTypesContainsScoutTypes(event.originalEvent.dataTransfer, dragAndDrop.SCOUT_TYPES.FILE_TRANSFER)) { |
| event.stopPropagation(); |
| event.preventDefault(); |
| |
| var files = event.originalEvent.dataTransfer.files; |
| if (files.length >= 1) { |
| this._setFiles(files); |
| } |
| } |
| } |
| |
| static fileListToArray(fileList) { |
| var files = [], |
| i; |
| for (i = 0; i < fileList.length; i++) { |
| files.push(fileList[i]); |
| } |
| return files; |
| } |
| |
| validateMaximumUploadSize(files) { |
| files = arrays.ensure(files); |
| if (files.length === 0) { |
| return; |
| } |
| |
| var totalSize = files.reduce(function(total, file) { |
| return total + file.size; |
| }, 0); |
| |
| if (this.maximumUploadSize !== null && totalSize > this.maximumUploadSize) { |
| throw this.session.text('ui.FileSizeLimit', (this.maximumUploadSize / 1024 / 1024)); |
| } |
| } |
| } |