blob: 66a9f749796dc234077fbaa6539320ab2638e0b6 [file] [log] [blame]
/*
* 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,
BoxButtons,
ClickActiveElementKeyStroke,
CloseKeyStroke,
Device,
dragAndDrop,
Event,
FileInput,
FocusAdjacentElementKeyStroke,
FocusRule,
Form,
FormLayout,
GlassPaneRenderer,
HtmlComponent,
keys,
KeyStrokeContext,
MessageBoxes,
scout,
scrollbars,
Status,
Widget
} from '../index';
import * as $ from 'jquery';
export default class FileChooser extends Widget {
constructor() {
super();
this.displayParent = null;
this.files = [];
this._glassPaneRenderer;
this.maximumUploadSize = FileInput.DEFAULT_MAXIMUM_UPLOAD_SIZE;
}
_init(model) {
super._init(model);
this._setDisplayParent(this.displayParent);
this._glassPaneRenderer = new GlassPaneRenderer(this);
this.fileInput = scout.create('FileInput', {
parent: this,
acceptTypes: this.acceptTypes,
maximumUploadSize: this.maximumUploadSize,
multiSelect: this.multiSelect,
visible: !Device.get().supportsFile()
});
this.fileInput.on('change', this._onFileChange.bind(this));
}
/**
* @override
*/
_createKeyStrokeContext() {
return new KeyStrokeContext();
}
/**
* @override
*/
_initKeyStrokeContext() {
super._initKeyStrokeContext();
this.keyStrokeContext.registerKeyStroke([
new FocusAdjacentElementKeyStroke(this.session, this),
new ClickActiveElementKeyStroke(this, [
keys.SPACE, keys.ENTER
]),
new CloseKeyStroke(this, function() {
return this.$cancelButton;
}.bind(this))
]);
}
_render() {
// Render modality glasspanes (must precede adding the file chooser to the DOM)
this._glassPaneRenderer.renderGlassPanes();
this.$container = this.$parent.appendDiv('file-chooser')
.on('mousedown', this._onMouseDown.bind(this));
var $handle = this.$container.appendDiv('drag-handle');
this.$container.draggable($handle);
this.$container.appendDiv('closable')
.on('click', function() {
this.cancel();
}.bind(this));
this.$content = this.$container.appendDiv('file-chooser-content');
this.$title = this.$content.appendDiv('file-chooser-title')
.text(this.session.text(this.multiSelect ? 'ui.ChooseFiles' : 'ui.ChooseFile'));
this.fileInput.render(this.$content);
// DnD and Multiple files are only supported with the new file api
if (!this.fileInput.legacy) {
// Install DnD support
this.$container.on('dragenter', this._onDragEnterOrOver.bind(this))
.on('dragover', this._onDragEnterOrOver.bind(this))
.on('drop', this._onDrop.bind(this));
// explanation for file chooser
this.$content.appendDiv('file-chooser-label')
.text(this.session.text('ui.FileChooserHint'));
// List of files
this.$files = this.$content.appendElement('<ul>', 'file-chooser-files');
this._installScrollbars();
}
// Buttons
this.$buttons = this.$container.appendDiv('file-chooser-buttons');
var boxButtons = new BoxButtons(this.$buttons);
if (!this.fileInput.legacy) {
this.$addFileButton = boxButtons.addButton({
text: this.session.text('ui.Browse'),
onClick: this._onAddFileButtonClicked.bind(this),
needsClick: true
});
}
this.$uploadButton = boxButtons.addButton({
text: this.session.text('ui.Upload'),
onClick: this._onUploadButtonClicked.bind(this),
enabled: false
});
this.$cancelButton = boxButtons.addButton({
text: this.session.text('Cancel'),
onClick: this._onCancelButtonClicked.bind(this)
});
this.htmlComp = HtmlComponent.install(this.$container, this.session);
this.htmlComp.setLayout(new FormLayout(this));
this.$container.addClassForAnimation('animate-open');
// Prevent resizing when file chooser is dragged off the viewport
this.$container.addClass('calc-helper');
var windowSize = this.$container.windowSize();
// Use css width, but ensure that it is not larger than the window (mobile)
var w = Math.min(this.$container.width(), windowSize.width - 20);
this.$container.css('min-width', w);
this.$container.css('max-width', w);
this.$container.removeClass('calc-helper');
boxButtons.updateButtonWidths(this.$container.width());
// Now that all texts, paddings, widths etc. are set, we can calculate the position
this._position();
}
_renderProperties() {
super._renderProperties();
if (this.fileInput.legacy) {
// Files may not be set into native control -> clear list in order to be sync again
this.setFiles([]);
}
this._renderFiles();
}
_postRender() {
super._postRender();
this._installFocusContext();
}
_remove() {
this._glassPaneRenderer.removeGlassPanes();
this._uninstallFocusContext();
super._remove();
}
_installFocusContext() {
this.session.focusManager.installFocusContext(this.$container, FocusRule.AUTO);
}
_uninstallFocusContext() {
this.session.focusManager.uninstallFocusContext(this.$container);
}
/**
* @override
*/
get$Scrollable() {
return this.$files;
}
_position() {
this.$container.cssMarginLeft(-this.$container.outerWidth() / 2);
}
setDisplayParent(displayParent) {
this.setProperty('displayParent', displayParent);
}
_setDisplayParent(displayParent) {
this._setProperty('displayParent', displayParent);
if (displayParent) {
this.setParent(this.findDesktop().computeParentForDisplayParent(displayParent));
}
}
setMaximumUploadSize(maximumUploadSize) {
this.setProperty('maximumUploadSize', maximumUploadSize);
this.fileInput.setMaximumUploadSize(maximumUploadSize);
}
/**
* Renders the file chooser and links it with the display parent.
*/
open() {
this.setDisplayParent(this.displayParent || this.session.desktop);
this.displayParent.fileChooserController.registerAndRender(this);
}
/**
* Destroys the file chooser and unlinks it from the display parent.
*/
close() {
if (!this.rendered) {
this.cancel();
return;
}
if (this.$cancelButton && this.session.focusManager.requestFocus(this.$cancelButton)) {
this.$cancelButton.click();
}
}
cancel() {
var event = new Event();
this.trigger('cancel', event);
if (!event.defaultPrevented) {
this._close();
}
}
/**
* Destroys the file chooser and unlinks it from the display parent.
*/
_close() {
if (this.displayParent) {
this.displayParent.fileChooserController.unregisterAndRemove(this);
}
this.destroy();
}
browse() {
this.fileInput.browse();
}
setAcceptTypes(acceptTypes) {
this.setProperty('acceptTypes', acceptTypes);
this.fileInput.setAcceptTypes(acceptTypes);
}
setMultiSelect(multiSelect) {
this.setProperty('multiSelect', multiSelect);
this.fileInput.setMultiSelect(multiSelect);
}
addFiles(files) {
if (files instanceof FileList) {
files = FileInput.fileListToArray(files);
}
files = arrays.ensure(files);
if (files.length === 0) {
return;
}
if (!this.multiSelect || this.fileInput.legacy) {
files = [files[0]];
this.setFiles([files[0]]);
} else {
// copy so that parameter stays untouched
files = files.slice();
// append new files to existing ones
arrays.insertAll(files, this.files, 0);
this.setFiles(files);
}
}
removeFile(file) {
var files = this.files.slice();
arrays.remove(files, file);
this.setFiles(files);
// Clear the input, otherwise user could not choose the file which he has removed previously
this.fileInput.clear();
}
setFiles(files) {
if (files instanceof FileList) {
files = FileInput.fileListToArray(files);
}
files = arrays.ensure(files);
try {
this.fileInput.validateMaximumUploadSize(files);
} catch (errorMessage) {
MessageBoxes.createOk(this)
.withHeader(this.session.text('ui.FileSizeLimitTitle'))
.withBody(errorMessage)
.withSeverity(Status.Severity.ERROR)
.buildAndOpen();
return;
}
this.setProperty('files', files);
}
_renderFiles() {
var files = this.files;
if (!this.fileInput.legacy) {
this.$files.empty();
files.forEach(function(file) {
var $file = this.$files.appendElement('<li>', 'file', file.name);
// Append a space to allow the browser to break the line here when it gets too long
$file.append(' ');
var $remove = $file
.appendSpan('remove menu-item')
.on('click', this.removeFile.bind(this, file));
var $removeLink = $file.makeElement('<a>', 'remove-link', this.session.text('Remove'));
$remove.appendTextNode('(');
$remove.append($removeLink);
$remove.appendTextNode(')');
}, this);
scrollbars.update(this.$files);
}
this.$uploadButton.setEnabled(files.length > 0);
}
_onDragEnterOrOver(event) {
dragAndDrop.verifyDataTransferTypesScoutTypes(event, dragAndDrop.SCOUT_TYPES.FILE_TRANSFER);
}
_onDrop(event) {
if (dragAndDrop.dataTransferTypesContainsScoutTypes(event.originalEvent.dataTransfer, dragAndDrop.SCOUT_TYPES.FILE_TRANSFER)) {
$.suppressEvent(event);
this.addFiles(event.originalEvent.dataTransfer.files);
}
}
_onUploadButtonClicked(event) {
this.trigger('upload');
}
_onCancelButtonClicked(event) {
this.cancel();
}
_onAddFileButtonClicked(event) {
this.browse();
}
_onFileChange(event) {
this.addFiles(event.files);
}
_onMouseDown(event, option) {
// If there is a dialog in the parent-hierarchy activate it in order to bring it on top of other dialogs.
var parent = this.findParent(function(p) {
return p instanceof Form && p.isDialog();
});
if (parent) {
parent.activate();
}
}
/**
* @override Widget.js
*/
_attach() {
this.$parent.append(this.$container);
super._attach();
}
/**
* @override Widget.js
*/
_detach() {
this.$container.detach();
super._detach();
}
}