UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

1,424 lines (1,334 loc) 76 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ sap.ui.define([ "sap/ui/core/Control", "sap/ui/events/KeyCodes", "sap/base/Log", "sap/base/util/deepEqual", "sap/m/library", "sap/m/Button", "sap/m/Dialog", "sap/m/List", "sap/m/MessageBox", "sap/m/OverflowToolbar", "sap/m/StandardListItem", "sap/m/Text", "sap/m/ToolbarSpacer", "sap/ui/unified/FileUploader", "sap/m/upload/UploadSetItem", "sap/m/upload/Uploader", "sap/m/upload/UploadSetRenderer", "sap/m/upload/UploaderHttpRequestMethod", "sap/ui/core/dnd/DragDropInfo", "sap/ui/core/dnd/DropInfo", "sap/m/library", "sap/m/upload/UploadSetToolbarPlaceholder", "sap/m/IllustratedMessage", "sap/m/IllustratedMessageType", "sap/m/IllustratedMessageSize", "sap/ui/core/Core", "sap/ui/core/InvisibleText", "sap/m/Menu", "sap/m/MenuItem", "sap/m/MenuButton" ], function (Control, KeyCodes, Log, deepEqual, MobileLibrary, Button, Dialog, List, MessageBox, OverflowToolbar, StandardListItem, Text, ToolbarSpacer, FileUploader, UploadSetItem, Uploader, Renderer, UploaderHttpRequestMethod, DragDropInfo, DropInfo, Library, UploadSetToolbarPlaceholder, IllustratedMessage,IllustratedMessageType, IllustratedMessageSize, Core, InvisibleText, Menu, MenuItem, MenuButton) { "use strict"; var UploadType = Library.UploadType; /** * Constructor for a new UploadSet. * * @param {string} [sId] id for the new control, generated automatically if no id is given. * @param {object} [mSettings] Initial settings for the new control. * @class This control allows you to upload one or more files from your devices (desktop, tablet, or phone) * and attach them to your application.<br> * This control builds on the {@link sap.m.UploadCollection} control, providing better handling of headers * and requests, unified behavior of instant and deferred uploads, as well as improved progress indication. * @extends sap.ui.core.Control * @author SAP SE * @version 1.117.4 * @constructor * @public * @since 1.63 * @alias sap.m.upload.UploadSet */ var UploadSet = Control.extend("sap.m.upload.UploadSet", { metadata: { library: "sap.m", properties: { /** * Allowed file types for files to be uploaded. * <br>If this property is not set, any file can be uploaded. */ fileTypes: {type: "string[]", defaultValue: null}, /** * Maximum length of names of files to be uploaded. * <br>If set to <code>null</code> or <code>0</code>, any files can be uploaded, * regardless of their names length. */ maxFileNameLength: {type: "int", defaultValue: null}, /** * Size limit in megabytes for files to be uploaded. * <br>If set to <code>null</code> or <code>0</code>, files of any size can be uploaded. */ maxFileSize: {type: "float", defaultValue: null}, /** * Allowed media types for files to be uploaded. * <br>If this property is not set, any file can be uploaded. */ mediaTypes: {type: "string[]", defaultValue: null}, /** * Defines custom text for the 'No data' text label. */ noDataText: {type: "string", defaultValue: null}, /** * Defines custom text for the 'No data' description label. */ noDataDescription: {type: "string", defaultValue: null}, /** * Determines which illustration type is displayed when the control holds no data. * @since 1.117 */ noDataIllustrationType: {type: "sap.m.IllustratedMessageType", group: "Appearance", defaultValue: IllustratedMessageType.NoData}, /** * Defines custom text for the drag and drop text label. */ dragDropText: {type: "string", defaultValue: null}, /** * Defines custom text for the drag and drop description label. */ dragDropDescription: {type: "string", defaultValue: null}, /** * Defines whether the upload process should be triggered as soon as the file is added.<br> * If set to <code>false</code>, no upload is triggered when a file is added. */ instantUpload: {type: "boolean", defaultValue: true}, /** * Defines whether file icons should be displayed. */ showIcons: {type: "boolean", defaultValue: true}, /** * Defines whether it is allowed to terminate the upload process. */ terminationEnabled: {type: "boolean", defaultValue: true}, /** * Defines whether the upload action is allowed. */ uploadEnabled: {type: "boolean", defaultValue: true}, /** * URL where the uploaded files will be stored. */ uploadUrl: {type: "string", defaultValue: null}, /** * If set to true, the button used for uploading files become invisible. * @since 1.99.0 */ uploadButtonInvisible: {type: "boolean", group: "Appearance", defaultValue: false}, /** * Allows the user to use the same name for a file while editing the file name. *'Same name' refers to an already existing file name in the list. * @since 1.100.0 */ sameFilenameAllowed: {type: "boolean", group: "Behavior", defaultValue: false}, /** * HTTP request method chosen for file upload. * @since 1.90 */ httpRequestMethod: {type: "sap.m.upload.UploaderHttpRequestMethod", defaultValue: UploaderHttpRequestMethod.Post}, /** * Lets the user select multiple files from the same folder and then upload them. * * If multiple property is set to false, the control shows an error message if more than one file is chosen for drag & drop. */ multiple: {type: "boolean", group: "Behavior", defaultValue: false}, /** * Defines the selection mode of the control (e.g. None, SingleSelect, MultiSelect, SingleSelectLeft, SingleSelectMaster). * Since the UploadSet reacts like a list for attachments, the API is close to the ListBase Interface. * sap.m.ListMode.Delete mode is not supported and will be automatically set to sap.m.ListMode.None. * In addition, if instant upload is set to false the mode sap.m.ListMode.MultiSelect is not supported and will be automatically set to sap.m.ListMode.None. * @since 1.100.0 */ mode: {type: "sap.m.ListMode", group: "Behavior", defaultValue: Library.ListMode.MultiSelect}, /** * Enables CloudFile picker feature to upload files from cloud. * @experimental Since 1.106. */ cloudFilePickerEnabled: {type: "boolean", group: "Behavior", defaultValue: false}, /** * Url of the FileShare OData V4 service supplied for CloudFile picker control. * @experimental Since 1.106. */ cloudFilePickerServiceUrl: {type: "sap.ui.core.URI", group: "Data", defaultValue: ""}, /** * The text of the CloudFile picker button. The default text is "Upload from cloud" (translated to the respective language). * @experimental Since 1.106. */ cloudFilePickerButtonText: {type: 'string', defaultValue: ""}, /** * Lets the user upload entire files from directories and sub directories. * @since 1.107 */ directory: {type: "boolean", group: "Behavior", defaultValue: false} }, defaultAggregation: "items", aggregations: { /** * Items representing files that have already been uploaded. */ items: {type: "sap.m.upload.UploadSetItem", multiple: true, singularName: "item"}, /** * Items representing files yet to be uploaded. */ incompleteItems: {type: "sap.m.upload.UploadSetItem", multiple: true, singularName: "incompleteItem"}, /** * Header fields to be included in the header section of an XHR request. */ headerFields: {type: "sap.ui.core.Item", multiple: true, singularName: "headerField"}, /** * Main toolbar of the <code>UploadSet</code> control. */ toolbar: {type: "sap.m.OverflowToolbar", multiple: false}, /** * Defines the uploader to be used. If not specified, the default implementation is used. */ uploader: {type: "sap.m.upload.Uploader", multiple: false}, /** * An illustrated message is displayed when no data is loaded or provided * @private */ _illustratedMessage: { type: "sap.m.IllustratedMessage", multiple: false, visibility: "hidden" } }, events: { /** * This event is fired when a new file is added to the set of items to be uploaded. */ afterItemAdded: { parameters: { /** * The file that has just been added. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * The event is triggered when the file name is changed. * @since 1.100.0 */ fileRenamed: { parameters: { /** * The renamed UI element as an UploadSetItem. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * This event is fired after the item is removed on click of ok button in confirmation dialog. * @since 1.83 */ afterItemRemoved: { parameters: { /** * The item removed from the set of items to be uploaded. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * This event is fired after item edit is confirmed. * @since 1.83 */ afterItemEdited: { parameters: { /** * The item edited. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * This event is fired just before a new file is added to the set of items to be uploaded. */ beforeItemAdded: { parameters: { /** * The file to be added to the set of items to be uploaded. */ item: {type: "sap.m.upload.UploadSetItem"} }, allowPreventDefault: true }, /** * This event is fired just before the confirmation dialog for 'Remove' action is displayed. */ beforeItemRemoved: { parameters: { /** * The item to be removed from the set of items to be uploaded. */ item: {type: "sap.m.upload.UploadSetItem"} }, allowPreventDefault: true }, /** * This event is fired when the edit button is clicked for an item and no other item is being edited * at the same time. * <br>If there is another item that has unsaved changes, the editing of the clicked item cannot be started. */ beforeItemEdited: { parameters: { /** * The item to be edited. */ item: {type: "sap.m.upload.UploadSetItem"} }, allowPreventDefault: true }, /** * This event is fired right before the upload process begins. */ beforeUploadStarts: { parameters: { /** * The file whose upload is just about to start. */ item: {type: "sap.m.upload.UploadSetItem"} }, allowPreventDefault: true }, /** * This event is fired right after the upload process is finished. */ uploadCompleted: { parameters: { /** * The file whose upload has just been completed. */ item: {type: "sap.m.upload.UploadSetItem"}, /** * Response message which comes from the server. * * On the server side this response has to be put within the &quot;body&quot; tags of the response * document of the iFrame. It can consist of a return code and an optional message. This does not * work in cross-domain scenarios. */ response : {type : "string"}, /** * ReadyState of the XHR request. * * Required for receiving a <code>readyState</code> is to set the property <code>sendXHR</code> * to true. This property is not supported by Internet Explorer 9. */ readyState : {type : "string"}, /** * Status of the XHR request. * * Required for receiving a <code>status</code> is to set the property <code>sendXHR</code> to true. * This property is not supported by Internet Explorer 9. */ status : {type : "string"}, /** * Http-Response which comes from the server. * * Required for receiving <code>responseXML</code> is to set the property <code>sendXHR</code> to true. * * This property is not supported by Internet Explorer 9. */ responseXML : {type : "string"}, /** * Http-Response-Headers which come from the server. * * Provided as a JSON-map, i.e. each header-field is reflected by a property in the <code>headers</code> * object, with the property value reflecting the header-field's content. * * Required for receiving <code>headers</code> is to set the property <code>sendXHR</code> to true. * This property is not supported by Internet Explorer 9. */ headers : {type : "object"} } }, /** * This event is fired right before the upload is terminated. */ beforeUploadTermination: { parameters: { /** * The file whose upload is about to be terminated. */ item: {type: "sap.m.upload.UploadSetItem"} }, allowPreventDefault: true }, /** * This event is fired right after the upload is terminated. */ uploadTerminated: { parameters: { /** * The file whose upload has just been terminated. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * This event is fired in either of the following cases: * <ul> * <li>When a file that is selected to be uploaded fails to meet the file type restriction * (<code>fileType</code> property).</li> * <li>When the file type restriction changes, and the file to be uploaded fails to meet the new * restriction.</li> * </ul> */ fileTypeMismatch: { parameters: { /** * The file that fails to meet the file type restriction specified in the * <code>fileType</code> property. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * This event is fired in either of the following cases: * <ul> * <li>When a file that is selected to be uploaded fails to meet the file name length restriction specified in the * <code>maxFileNameLength</code> property.</li> * <li>When the file name length restriction changes, and the file to be uploaded fails to meet the new * restriction.</li> * <li>Listeners can use the item parameter to remove the incomplete item that failed to meet the restriction</li> * </ul> */ fileNameLengthExceeded: { parameters: { /** * The file that fails to meet the file name length restriction specified in the * <code>maxFileNameLength</code> property. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * This event is fired in either of the following cases: * <ul> * <li>When a file that is selected to be uploaded fails to meet the file size restriction specified in the * <code>maxFileSize</code> property.</li> * <li>When the file size restriction changes, and the file to be uploaded fails to meet the new * restriction.</li> * <li>Listeners can use the item parameter to remove the incomplete item that failed to meet the restriction</li> * </ul> */ fileSizeExceeded: { parameters: { /** * The file that fails to meet the file size restriction specified in the * <code>maxFileSize</code> property. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * This event is fired in either of the following cases: * <ul> * <li>When a file that is selected to be uploaded fails to meet the media type restriction specified in the * <code>mediaTypes</code> property.</li> * <li>When the media type restriction changes, and the file to be uploaded fails to meet the new * restriction.</li> * </ul> */ mediaTypeMismatch: { parameters: { /** * The file that fails to meet the media type restriction specified in the * <code>mediaTypes</code> property. */ item: {type: "sap.m.upload.UploadSetItem"} } }, /** * This event is fired simultaneously with the respective event in the inner {@link sap.m.List} control. */ selectionChanged: { parameters: { /** * Items whose selection status has just been changed. */ items: {type: "sap.m.upload.UploadSetItem[]"} } }, /** * This event is fired when the user starts dragging an uploaded item. * @event * @param {sap.ui.base.Event} oControlEvent * @param {sap.ui.base.EventProvider} oControlEvent.getSource * @param {object} oControlEvent.getParameters * @param {sap.ui.core.Element} oControlEvent.getParameters.target The target element that will be dragged * @param {sap.ui.core.dnd.DragSession} oControlEvent.getParameters.dragSession The UI5 <code>dragSession</code> object that exists only during drag and drop * @param {Event} oControlEvent.getParameters.browserEvent The underlying browser event * @public * @since 1.99 */ itemDragStart: { }, /** * This event is fired when an uploaded item is dropped on the new list position. * @event * @param {sap.ui.base.Event} oControlEvent * @param {sap.ui.base.EventProvider} oControlEvent.getSource * @param {object} oControlEvent.getParameters * @param {sap.ui.core.dnd.DragSession} oControlEvent.getParameters.dragSession The UI5 <code>dragSession</code> object that exists only during drag and drop * @param {sap.ui.core.Element} oControlEvent.getParameters.draggedControl The element being dragged * @param {sap.ui.core.Element} oControlEvent.getParameters.droppedControl The element being dropped * @param {sap.ui.core.dnd.RelativeDropPosition} oControlEvent.getParameters.dropPosition The calculated position of the drop action relative to the <code>droppedControl</code>. * @param {Event} oControlEvent.getParameters.browserEvent The underlying browser event * @public * @since 1.99 */ itemDrop: { } } }, renderer: Renderer }); var UploadState = MobileLibrary.UploadState; /* ================== */ /* Lifecycle handling */ /* ================== */ UploadSet.prototype.init = function () { this._oRb = sap.ui.getCore().getLibraryResourceBundle("sap.m"); this._oList = null; this._oEditedItem = null; this._oItemToBeDeleted = null; this._mListItemIdToItemMap = {}; this._oUploadButton = null; this._oDragIndicator = false; // Drag&drop this._$Body = null; this._$DragDropArea = null; this._oLastEnteredTarget = null; this._aGroupHeadersAdded = []; this._iFileUploaderPH = null; this._oItemToUpdate = null; //Setting invisible text this._oInvisibleText = new InvisibleText(); this._oInvisibleText.toStatic(); var oIllustratedMessage = new IllustratedMessage({ illustrationType: this.getNoDataIllustrationType(), illustrationSize: IllustratedMessageSize.Auto, title: this.getNoDataText(), description: this.getNoDataDescription() }); this.setAggregation("_illustratedMessage", oIllustratedMessage); oIllustratedMessage.addIllustrationAriaLabelledBy(this._oInvisibleText.getId()); this._oInvisibleText.setText(this._oRb.getText("UPLOAD_SET_ILLUSTRATED_MESSAGE")); this._cloudFilePickerControl = null; this._oListEventDelegate = null; this.addDependent(this._oInvisibleText); }; UploadSet.prototype.exit = function () { if (this._oList) { this._oList.destroy(); this._oList = null; } if (this._oToolbar) { this._oToolbar.destroy(); this._oToolbar = null; } if (this._oFileUploader) { this._oFileUploader.destroy(); this._oFileUploader = null; } if (this._oUploader) { this._oUploader.destroy(); this._oUploader = null; } }; /* ===================== */ /* Overriden API methods */ /* ===================== */ UploadSet.prototype.onBeforeRendering = function (oEvent) { if (this._oListEventDelegate) { this._oList.removeEventDelegate(this._oListEventDelegate); this._oListEventDelegate = null; } this._aGroupHeadersAdded = []; this._clearGroupHeaders(); this._fillListWithUploadSetItems(this.getItems()); }; UploadSet.prototype.onAfterRendering = function () { var oInput; if (this._oEditedItem) { oInput = this._oEditedItem._getFileNameEdit().$("inner"); if (oInput) { oInput.on("focus", function () { oInput.selectText(0, oInput.val().length); }); oInput.trigger("focus"); } if (this._oEditedItem && this._oEditedItem.getEditState()) { var oMarkerContainerDomRef = this._oEditedItem.getListItem() ? this._oEditedItem.getListItem().getDomRef() : null; var oMarkerContainer = oMarkerContainerDomRef ? oMarkerContainerDomRef.querySelector(".sapMUSObjectMarkerContainer") : null; if (oMarkerContainer) { oMarkerContainer.setAttribute("style", "display: none"); } } } this._oListEventDelegate = { onclick: function(event) { this._handleClick(event, this._oEditedItem); }.bind(this) }; this._oList.addDelegate(this._oListEventDelegate); if (this._bItemRemoved) { this._bItemRemoved = false; var aList = this.getList(); var aListItems = aList.getItems(); if (aListItems.length > 0) { aListItems[0].focus(); } else { aList.getDomRef().querySelector(".sapMUCNoDataPage").focus(); } } if (this.getCloudFilePickerEnabled()) { this._oFileUploader.addStyleClass("sapMUSTFileUploaderVisibility"); } }; /** * Handling of 'click' of the list (items + header) * @param {sap.ui.base.Event} oEvent Event of the 'click' * @param {Object} item List item * @private */ UploadSet.prototype._handleClick = function (oEvent, item) { var $Button = oEvent.target.closest("button"); var sId = ""; if ($Button) { sId = $Button.id; } if (sId.lastIndexOf("editButton") === -1) { if (sId.lastIndexOf("cancelButton") !== -1) { if (item) { this._handleItemEditCancelation(oEvent, item); } } else if (oEvent.target.id.lastIndexOf("thumbnail") < 0 && oEvent.target.id.lastIndexOf("icon") < 0 && oEvent.target.id.lastIndexOf("deleteButton") < 0 && oEvent.target.id.lastIndexOf("fileNameEdit-inner") < 0) { if (item) { this._handleItemEditConfirmation(oEvent, item); } } } }; UploadSet.prototype.onkeydown = function (oEvent) { var oListItem, oItem; // Check the case when focus is inside an edited item if (this._oEditedItem && this._oEditedItem._getFileNameEdit().$("inner")[0] === oEvent.target) { oItem = this._oEditedItem; } else if (oEvent.target) { oListItem = sap.ui.getCore().byId(oEvent.target.id); if (oListItem) { oItem = this._mListItemIdToItemMap[oListItem.getId()]; } } // No item no fun if (!oItem) { return; } switch (oEvent.keyCode) { case KeyCodes.F2: if (oItem._bInEditMode) { this._handleItemEditConfirmation(oEvent, oItem); } else { this._handleItemEdit(oEvent, oItem); } break; case KeyCodes.ESCAPE: this._handleItemEditCancelation(oEvent, oItem); break; case KeyCodes.DELETE: if (!oItem.$("fileNameEdit").hasClass("sapMInputFocused") && oItem.getEnabledRemove() && oItem.getVisibleRemove()) { this._handleItemDelete(oEvent, oItem); } break; case KeyCodes.ENTER: if (oItem === this._oEditedItem) { this._handleItemEditConfirmation(oEvent, oItem); } else { oItem._handleFileNamePressed(); } break; default: return; } }; UploadSet.prototype.getToolbar = function () { if (!this._oToolbar) { var oUploader = this.getCloudFilePickerEnabled() ? this._getCloudFilePicker() : this.getDefaultFileUploader(); this._oToolbar = this.getAggregation("toolbar"); if (!this._oToolbar) { this._oToolbar = new OverflowToolbar(this.getId() + "-toolbar", { content: [this._oNumberOfAttachmentsTitle, new ToolbarSpacer(), oUploader] }); this._iFileUploaderPH = 2; this.addDependent(this._oToolbar); } else { this._iFileUploaderPH = this._getFileUploaderPlaceHolderPosition(this._oToolbar); if (this._oToolbar && this._iFileUploaderPH > -1) { this._setFileUploaderInToolbar(oUploader); } else if (this._oToolbar) { // fallback position to add file uploader control if UploadSetToolbarPlaceHolder instance not found this._oToolbar.addContent(oUploader); } } if (this.getCloudFilePickerEnabled()) { this._oToolbar.addContent(this.getDefaultFileUploader()); } } return this._oToolbar; }; UploadSet.prototype._openFileUploaderPicker = function () { this._oFileUploader.oFileUpload.click(); }; UploadSet.prototype._openCloudFilePicker = function () { this._invokeCloudFilePicker(); }; UploadSet.prototype._itemSelectedCallback = function (oEvent) { var oItem = oEvent.getParameter("item"); // eslint-disable-next-line default-case switch (oItem.getText()) { case this.getCloudFilePickerButtonText() ? this.getCloudFilePickerButtonText() : this._oRb.getText("UPLOAD_SET_DEFAULT_CFP_BUTTON_TEXT"): this._oMenuButton .detachEvent("defaultAction", this._openFileUploaderPicker.bind(this)) .attachEvent("defaultAction", this._openCloudFilePicker.bind(this)); this._openCloudFilePicker(); this._oMenuButton.setText(oItem.getText()); break; case this._oRb.getText("UPLOAD_SET_DEFAULT_LFP_BUTTON_TEXT"): this._oMenuButton .detachEvent("defaultAction", this._openCloudFilePicker.bind(this)) .attachEvent("defaultAction", this._openFileUploaderPicker.bind(this)); this._openFileUploaderPicker(); this._oMenuButton.setText(oItem.getText()); break; } }; // Functions returns sNoDataText which is combination of Title and Description from the IllustratedMessage UploadSet.prototype._setListNoDataText = function (sText, bIsDescription) { var sNoDataText = ""; var oIllustratedMessage = this.getAggregation("_illustratedMessage"); if (!sText) { sNoDataText = oIllustratedMessage.getTitle() + " " + oIllustratedMessage.getDescription(); } else if (sText) { if (bIsDescription) { sNoDataText = oIllustratedMessage.getTitle() + " " + sText; } else { sNoDataText = sText + " " + oIllustratedMessage.getDescription(); } } return sNoDataText; }; UploadSet.prototype.getNoDataText = function () { var sNoDataText = this.getProperty("noDataText"); sNoDataText = sNoDataText || this._oRb.getText("UPLOAD_SET_NO_DATA_TEXT"); if (this._oList) { this._oList.setNoDataText(this._setListNoDataText(sNoDataText)); } return sNoDataText; }; UploadSet.prototype.getNoDataDescription = function () { var sNoDataDescription = this.getProperty("noDataDescription"); sNoDataDescription = sNoDataDescription || this._oRb.getText("UPLOADCOLLECTION_NO_DATA_DESCRIPTION"); if (this._oList) { this._oList.setNoDataText(this._setListNoDataText(sNoDataDescription, true)); } return sNoDataDescription; }; UploadSet.prototype.getDragDropText = function () { var sDragDropText = this.getProperty("dragDropText"); sDragDropText = sDragDropText || this._oRb.getText("IllustratedMessage_TITLE_UploadCollection"); return sDragDropText; }; UploadSet.prototype.getDragDropDescription = function () { var sDragDropDescription = this.getProperty("dragDropDescription"); sDragDropDescription = sDragDropDescription || this._oRb.getText("IllustratedMessage_DESCRIPTION_UploadCollection"); return sDragDropDescription; }; UploadSet.prototype.setToolbar = function (oToolbar) { this.setAggregation("toolbar", oToolbar); this.getToolbar(); return this; }; UploadSet.prototype.addAggregation = function (sAggregationName, oObject, bSuppressInvalidate) { Control.prototype.addAggregation.call(this, sAggregationName, oObject, bSuppressInvalidate); if (oObject && (sAggregationName === 'items' || sAggregationName === "incompleteItems")) { this._projectToNewListItem(oObject); this._refreshInnerListStyle(); } }; UploadSet.prototype.insertAggregation = function (sAggregationName, oObject, iIndex, bSuppressInvalidate) { Control.prototype.insertAggregation.call(this, sAggregationName, oObject, iIndex, bSuppressInvalidate); if (oObject && (sAggregationName === 'items' || sAggregationName === 'incompleteItems')) { this._projectToNewListItem(oObject, iIndex || 0); this._refreshInnerListStyle(); } }; UploadSet.prototype.removeAggregation = function (sAggregationName, oObject, bSuppressInvalidate) { var oListItem,oItems; Control.prototype.removeAggregation.call(this, sAggregationName, oObject, bSuppressInvalidate); if (sAggregationName === "items" || sAggregationName === "incompleteItems") { if (typeof oObject === 'number') { // "oObject" is the index now oItems = this.getItems(); oListItem = oItems[oObject]; } else if (typeof oObject === 'object') { // the object itself is given or has just been retrieved if (this.getList() && this.getList().getItems().length) { // listItem should not be created if destruction started. oListItem = oObject.isDestroyStarted() ? oObject : oObject._getListItem(); } } var oItem = this.getList().removeAggregation("items", oListItem, bSuppressInvalidate); if (oItem && oObject) { oItem.destroy(); oObject.destroy(); } this._refreshInnerListStyle(); } }; UploadSet.prototype.removeAllAggregation = function (sAggregationName, bSuppressInvalidate) { if (sAggregationName === "items") { this.getItems().forEach(function (oItem) { if (this._oList) { this._oList.removeAggregation("items", oItem._getListItem(), bSuppressInvalidate); } }.bind(this)); } else if (sAggregationName === "incompleteItems") { this.getIncompleteItems().forEach(function (oItem) { if (this._oList) { this._oList.removeAggregation("items", oItem._getListItem(), bSuppressInvalidate); } }.bind(this)); } Control.prototype.removeAllAggregation.call(this, sAggregationName, bSuppressInvalidate); }; UploadSet.prototype.setFileTypes = function (aNewTypes) { var aTypes = aNewTypes || null; if (typeof aTypes === "string") { aTypes = aTypes.split(","); } aTypes = (aTypes || []).map(function (s) { return s ? s.toLowerCase() : ""; }); if (!deepEqual(this.getFileTypes(), aTypes)) { this.setProperty("fileTypes", aTypes, true); this.getDefaultFileUploader().setFileType(aTypes); this._checkRestrictions(); } return this; }; UploadSet.prototype.setMaxFileNameLength = function (iNewMax) { if (this.getMaxFileNameLength() !== iNewMax) { this.setProperty("maxFileNameLength", iNewMax, true); this.getDefaultFileUploader().setMaximumFilenameLength(iNewMax); this._checkRestrictions(); } return this; }; UploadSet.prototype.setMaxFileSize = function (iNewMax) { if (this.getMaxFileSize() !== iNewMax) { this.setProperty("maxFileSize", iNewMax, true); this.getDefaultFileUploader().setMaximumFileSize(iNewMax); this._checkRestrictions(); } return this; }; UploadSet.prototype.setMediaTypes = function (aNewTypes) { var aTypes = aNewTypes || null; if (typeof aTypes === "string") { aTypes = aTypes.split(","); } aTypes = (aTypes || []).map(function (s) { return s ? s.toLowerCase() : ""; }); if (!deepEqual(this.getMediaTypes(), aTypes)) { this.setProperty("mediaTypes", aTypes, true); this.getDefaultFileUploader().setMimeType(aTypes); this._checkRestrictions(); } return this; }; UploadSet.prototype.setShowIcons = function (bShow) { if (bShow !== this.getShowIcons()) { this._getAllItems().forEach(function (oItem) { oItem._getIcon().setVisible(bShow); }); this.setProperty("showIcons", bShow, false); } return this; }; UploadSet.prototype.setTerminationEnabled = function (bEnable) { if (bEnable !== this.getTerminationEnabled()) { this._getAllItems().forEach(function (oItem) { if (oItem.getUploadState() === UploadState.Uploading) { oItem._getTerminateButton().setVisible(bEnable); } }); this.setProperty("terminationEnabled", bEnable, false); } return this; }; UploadSet.prototype.setUploadButtonInvisible = function (bUploadButtonInvisible) { if (bUploadButtonInvisible !== this.getUploadButtonInvisible()) { var bVisible = !bUploadButtonInvisible; this.getDefaultFileUploader().setVisible(bVisible); if (this._oUploadButton) { this._oUploadButton.setVisible(bVisible); } this.setProperty("uploadButtonInvisible", bUploadButtonInvisible, true); } return this; }; UploadSet.prototype.setUploadEnabled = function (bEnable) { if (bEnable !== this.getUploadEnabled()) { this.getDefaultFileUploader().setEnabled(bEnable); // TODO: This can go, FileUploader doesn't upload anymore if (this._oUploadButton) { this._oUploadButton.setEnabled(bEnable); } this.setProperty("uploadEnabled", bEnable, false); } return this; }; UploadSet.prototype.setMultiple = function (bMultiple) { if (this.getMultiple() !== bMultiple) { this.setProperty("multiple", bMultiple); this.getDefaultFileUploader().setMultiple(bMultiple); } return this; }; UploadSet.prototype.setDirectory = function (bDirectory) { if (this.getDirectory() !== bDirectory) { this.setProperty("directory", bDirectory); this.getDefaultFileUploader().setDirectory(bDirectory); if (bDirectory) { this.setProperty("multiple", false); // disable multiple files selection when directory selection is enabled. } } return this; }; UploadSet.prototype.setMode = function(sMode) { if (sMode === Library.ListMode.Delete) { this.setProperty("mode", Library.ListMode.None); Log.info("sap.m.ListMode.Delete is not supported by UploadSet. Value has been resetted to 'None'"); } else if (sMode === Library.ListMode.MultiSelect && !this.getInstantUpload()) { this.setProperty("mode", Library.ListMode.None); Log.info("sap.m.ListMode.MultiSelect is not supported by UploadSet for Pending Upload. Value has been reset to 'None'"); } else { this.setProperty("mode", sMode); } if (this._oList) { this._oList.setMode(this.getMode()); } return this; }; UploadSet.prototype._getIllustratedMessage = function () { var oAggregation = this.getAggregation("_illustratedMessage"); // Invoke rendering of illustrated message only when the list is empty else no scope of illustrated message. if (oAggregation && this._oList && this._oList.getItems && !this._oList.getItems().length) { if (this._getDragIndicator()) { oAggregation.setIllustrationType(IllustratedMessageType.UploadCollection); oAggregation.setTitle(this.getDragDropText()); oAggregation.setDescription(this.getDragDropDescription()); oAggregation.removeAllAdditionalContent(); } else { oAggregation.setIllustrationType(this.getNoDataIllustrationType()); oAggregation.setTitle(this.getNoDataText()); oAggregation.setDescription(this.getNoDataDescription()); oAggregation.addAdditionalContent(this.getUploadButtonForIllustratedMessage()); } } return oAggregation; }; UploadSet.prototype.getUploadButtonForIllustratedMessage = function () { if (!this._oUploadButton) { this._oUploadButton = new Button({ id: this.getId() + "-uploadButton", type: MobileLibrary.ButtonType.Standard, enabled: this.getUploadEnabled(), visible: !this.getUploadButtonInvisible(), text: this._oRb.getText("UPLOADCOLLECTION_UPLOAD"), ariaDescribedBy: this.getAggregation("_illustratedMessage").getId(), press: function () { var FileUploader = this.getDefaultFileUploader(); FileUploader.$().find("input[type=file]").trigger("click"); }.bind(this) }); } return this._oUploadButton; }; UploadSet.prototype.setUploadUrl = function (sUploadUrl) { this.setProperty("uploadUrl", sUploadUrl); if (this._oUploader) { this._oUploader.setUploadUrl(sUploadUrl); } return this; }; /* ============== */ /* Public methods */ /* ============== */ /** * Provides access to the instance of the inner {@link sap.m.List} control, so that it can be customized. * @return {sap.m.List} The inner {@link sap.m.List} control. * @public */ UploadSet.prototype.getList = function () { if (!this._oList) { this._oList = new List(this.getId() + "-list", { selectionChange: [this._handleSelectionChange, this], headerToolbar: this.getToolbar(), dragDropConfig: [ new DragDropInfo({ dropPosition: "Between", sourceAggregation: "items", targetAggregation: "items", dragStart: [this._onDragStartItem, this], drop: [this._onDropItem, this] }) , new DropInfo({ dropEffect:"Move", dropPosition:"OnOrBetween", dragEnter: [this._onDragEnterFile, this], drop: [this._onDropFile, this] }) ], mode: this.getMode(), noDataText: this._setListNoDataText() }); this._oList.addStyleClass("sapMUCList"); this.addDependent(this._oList); } return this._oList; }; UploadSet.prototype._onDragStartItem = function (oEvent) { this.fireItemDragStart(oEvent); }; UploadSet.prototype._onDropItem = function (oEvent) { this.fireItemDrop(oEvent); }; UploadSet.prototype._onDragEnterFile = function (oEvent) { var oDragSession = oEvent.getParameter("dragSession"); var oDraggedControl = oDragSession.getDragControl(); this._oDragIndicator = true; this._getIllustratedMessage(); if (oDraggedControl) { oEvent.preventDefault(); } }; UploadSet.prototype._onDropFile = function (oEvent) { this._oDragIndicator = false; this._getIllustratedMessage(); oEvent.preventDefault(); if (!this.getUploadEnabled()) { return; } var oItems = oEvent.getParameter("browserEvent").dataTransfer.items; oItems = Array.from(oItems); // Filtering out only webkitentries (files/folders system entries) by excluding non file / directory types. oItems = oItems.filter(function(item){ return item.webkitGetAsEntry() ? true : false; }); var aEntryTypes = oItems.map(function (oEntry) { var oWebKitEntry = oEntry.webkitGetAsEntry(); return { entryType: oWebKitEntry && oWebKitEntry.isFile ? 'File' : 'Directory' }; }); // handlding multiple property drag & drop scenarios if (oItems && oItems.length > 1 && !this.getMultiple() && !this.getDirectory()) { // Handling drag and drop of multiple files to upload with multiple property set var sMessage = this._oRb.getText("UPLOADCOLLECTION_MULTIPLE_FALSE"); Log.warning("Multiple files upload is retsricted for this multiple property set"); MessageBox.error(sMessage); return; } else if (oItems && oItems.length > 1 && this.getMultiple() && !isFileOrFolderEntry('File', aEntryTypes)) { var sMessageDropFilesOnly = this._oRb.getText("UPLOAD_SET_DIRECTORY_FALSE"); Log.warning("Multiple files upload is retsricted, drag & drop only files"); MessageBox.error(sMessageDropFilesOnly); return; } // handling directory property drag & drop scenarios if (oItems && oItems.length && !this.getDirectory() && isFileOrFolderEntry('Directory', aEntryTypes)) { var sMessageDirectory = this._oRb.getText("UPLOAD_SET_DIRECTORY_FALSE"); Log.warning("Directory of files upload is retsricted for this directory property set"); MessageBox.error(sMessageDirectory); return; } else if (oItems && oItems.length && this.getDirectory() && !isFileOrFolderEntry('Directory', aEntryTypes)) { var sMessageDragDropDirectory = this._oRb.getText("UPLOAD_SET_DROP_DIRECTORY"); Log.warning("Directory of files upload is retsricted, drag & drop only directories here."); MessageBox.error(sMessageDragDropDirectory); return; } if (oItems && oItems.length) { this._getFilesFromDataTransferItems(oItems).then(function (oFiles) { if (oFiles && oFiles.length) { this._processNewFileObjects(oFiles); } }.bind(this)); } function isFileOrFolderEntry(sType, aEntries) { return aEntries.every(function (oEntry) { return oEntry.entryType === sType; }); } }; UploadSet.prototype._getFilesFromDataTransferItems = function (dataTransferItems) { var aFiles = []; return new Promise(function (resolve, reject) { var aEntriesPromises = []; for (var i = 0; i < dataTransferItems.length; i++) { aEntriesPromises.push(traverseFileTreePromise(dataTransferItems[i].webkitGetAsEntry())); } Promise.all(aEntriesPromises) .then(function (entries) { resolve(aFiles); }, function(err) { reject(err); }); }); function traverseFileTreePromise(item) { return new Promise(function (resolve, reject) { if (item.isFile) { item.file(function (oFile) { aFiles.push(oFile); resolve(oFile); }, function(err){ reject(err); }); } else if (item.isDirectory) { var dirReader = item.createReader(); dirReader.readEntries(function (entries) { var aEntriesPromises = []; for (var i = 0; i < entries.length; i++) { aEntriesPromises.push(traverseFileTreePromise(entries[i])); } resolve(Promise.all(aEntriesPromises)); }); } }); } }; UploadSet.prototype._getDragIndicator = function () { return this._oDragIndicator; }; /** * Starts uploading all files that comply with the restrictions defined in the <code>fileTypes</code>, * <code>maxFileNameLength</code>, <code>maxFileSize</code>, and <code>mediaTypes</code> properties. * <br>This method works only when the <code>uploadEnabled</code> property is set to <code>true</code>. * @public */ UploadSet.prototype.upload = function () { if (!this.getUploadEnabled()) { Log.warning("Upload is currently disabled for this upload set."); return; } this.getIncompleteItems().forEach(function (oItem) { this._uploadItemIfGoodToGo(oItem); }.bind(this)); }; /** * Starts uploading the file if it complies with the restrictions defined in the <code>fileTypes</code>, * <code>maxFileNameLength</code>, <code>maxFileSize</code>, and <code>mediaTypes</code> * properties. * <br>This method works only when the <code>uploadEnabled</code> property is set to <code>true</code>. * @param {object} oItem File to upload. * @public */ UploadSet.prototype.uploadItem = function (oItem) { this._uploadItemIfGoodToGo(oItem); }; /** * Returns an instance of the default <code>sap.ui.unified.FileUploader</code> used for adding files using * the operating system's open file dialog, so that it can be customized, for example made invisible or assigned a different icon. * @return {sap.ui.unified.FileUploader} Instance of the default <code>sap.ui.unified.FileUploader</code>. * @public */ UploadSet.prototype.getDefaultFileUploader = function () { var sTooltip = this._oRb.getText("UPLOADCOLLECTION_UPLOAD"); if (!this._oFileUploader) { this._oFileUploader = new FileUploader(this.getId() + "-uploader", { buttonOnly: true, buttonText: sTooltip, tooltip: sTooltip, iconOnly: false, enabled: this.getUploadEnabled(), fileType: this.getFileTypes(), mimeType: this.getMediaTypes(), maximumFilenameLength: this.getMaxFileNameLength(), maximumFileSize: this.getMaxFileSize(), icon: "", iconFirst: false, multiple: this.getDirectory() ? false : this.getMultiple(), style: "Transparent", name: "uploadSetFileUploader", sameFilenameAllowed: true, useMultipart: false, sendXHR: true, change: [this._onFileUploaderChange, this], uploadStart: [this._onUploadStarted, this], uploadProgress: [this._onUploadProgressed, this], uploadComplete: [this._onUploadCompleted, this], uploadAborted: [this._onUploadAborted, this], typeMissmatch: [this._fireFileTypeMismatch, this], fileSizeExceed: [this._fireFileSizeExceed, this], filenameLengthExceed: [this._fireFilenameLengthExceed, this], visible: !this.getUploadButtonInvisible(), directory: this.getDirectory() }); } return this._oFileUploader; }; /** * Attaches all necessary handlers to the given uploader instance, so that the progress and status of the upload can be * displayed and monitored. * This is necessary in case when custom uploader is used. * @param {sap.m.upload.Uploader} oUploader Instance of <code>sap.m.upload.Uploader</code> to which the default request handlers are attached. * @public */ UploadSet.prototype.registerUploaderEvents = function (oUploader) { oUploader.attachUploadStarted(this._onUploadStarted.bind(this)); oUploader.attachUploadProgressed(this._onUploadProgressed.bind(this)); oUploader.attachUploadCompleted(this._onUploadCompleted.bind(this)); oUploader.attachUploadAborted(this._onUploadAborted.bind(this)); }; /** * Returns an array containing the selected UploadSetItems. * @returns {sap.m.upload.UploadSetItem[]} Array of all selected items * @public * @since 1.100.0 */ UploadSet.prototype.getSelectedItems = function() { var aSelectedListItems = this._oList.getSelectedItems(); return this._getUploadSetItemsByListItems(aSelectedListItems); }; /** * Retrieves the currently selected UploadSetItem. * @returns {sap.m.upload.UploadSetItem | null} The currently selected item or <code>null</code> * @public * @since 1.100.0 */ UploadSet.prototype.getSelectedItem = function() { var oSelectedListItem = this._oList.getSelectedItem(); if (oSelectedListItem) { return this._getUploadSetItemsByListItems([oSelectedListItem]); } return null; }; /** * Sets an UploadSetItem to be selected by ID. In single selection mode, the method removes the previous selection. * @param {string} id The ID of the item whose selection is to be changed. * @param {boolean} [select=true] The selection state of the item. * @returns {this} this to allow method chaining * @public * @since 1.100.0 */ UploadSet.prototype.setSelectedItemById = function(id, select) { this._oList.setSelectedItemById(id + "-listItem", select); this._setSelectedForItems([this._getUploadSetItemById(id)], select); return this; }; /** * Selects or deselects the given list item. * @param {sap.m.upload.UploadSetItem} uploadSetItem The item whose selection is to be changed. This parameter is mandatory. * @param {boolean} [select=true] The selection state of the item. * @returns {this} this to allow method chaining * @public * @since 1.100.0 */ UploadSet.prototype.setSelectedItem = function(uploadSetItem, select) { return this.setSelectedItemById(uploadSetItem.getId(), select); }; /** * Select all items in "MultiSelection" mode. * @returns {this} this to allow method chaining * @public * @since 1.100.0 */ UploadSet.prototype.selectAll = function() { var aSelectedList = this._oList.selectAll(); if (aSelectedList.getItems().length !== this.getItems().length) { Log.info("Internal 'List' and external 'UploadSet' are not in sync."); } this._setSelectedForItems(this.getItems(), true); return this; }; /** * Opens the FileUploader dialog. When an UploadSetItem is provided, this method can be used to update a file with a new version. * @param {sap.m.upload.UploadSetItem} item The UploadSetItem to update with a new version. This parameter is mandatory. * @returns {this} this to allow method chaining * @since 1.103.0 * @public */ UploadSet.prototype.openFileDialog = function(item) { if (this._oFileUploader) { if (item) { if (!this._oFileUploader.getMultiple()) { this._oItemToUpdate = item; this._oFileUploader.$().find("input[type=file]").trigger("click"); } else { Log.warning("Version Upload cannot be used in multiple upload mode"); } } else { this._oFileUploader.$().find("input[type=file]").trigger("click"); } } return this; }; /* ============== */ /* Event handlers */ /* ============== */ UploadSet.prototype._onFileUploaderChange = function (oEvent) { var oFiles = oEvent.getParameter("files"); this._processNewFileObjects(oFiles); }; UploadSet.prototype._onUploadStarted = function (oEvent) { var oItem = oEvent.getParameter("item"); oItem.setUploadState(UploadState.Uploading); }; UploadSet.prototype._onUploadProgressed = function (oEvent) { var oItem = oEvent.getParameter("item"), iPercentUploaded = Math.round(oEvent.getParameter("loaded") / oEvent.getParameter("total") * 100); oItem.setProgress(iPercentUploaded); }; UploadSet.prototype._onUploadCompleted = function (oEvent) { var oItem = oEvent.getParameter("item"), oResponseXHRParams = oEvent.getParameter("responseXHR"), sResponse = null; if (oResponseXHRParams.responseXML) { sResponse = oResponseXHRParams.responseXML.documentElement.textContent; } var oXhrParams = { "item": oItem, "response": oResponseXHRParams.response, "responseXML": sResponse, "readyState": oResponseXHRParams.readyState, "status": oResponseXHRParams.status, "headers": oResponseXHRParams.headers }; oItem.setProgress(100); if (this._oItemToUpdate && this.getInstantUpload()) { this.removeAggregation('items', this._oItemToUpdate, false); } this.insertItem(oItem, 0); oItem.setUploadState(UploadState.Complete); this._oItemToUpdate = null; this.fireUploadCompleted(oXhrParams); }; UploadSet.prototype._onUploadAborted = function (oEvent) { var oItem = oEvent.getParameter("item"); oItem.setUploadState(UploadState.Error); this.fireUploadTerminated({item: oItem}); }; UploadSet.prototype._handleItemEdit = function (oEvent, oItem) { if (this._oEditedItem) { this._oEditedItem =