UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,293 lines (1,292 loc) • 56.2 kB
/** * DevExtreme (esm/ui/file_uploader.js) * Version: 21.1.4 * Build date: Mon Jun 21 2021 * * Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import $ from "../core/renderer"; import Guid from "../core/guid"; import { getWindow } from "../core/utils/window"; import eventsEngine from "../events/core/events_engine"; import registerComponent from "../core/component_registrator"; import Callbacks from "../core/utils/callbacks"; import { isDefined, isFunction, isNumeric } from "../core/utils/type"; import { each } from "../core/utils/iterator"; import { extend } from "../core/utils/extend"; import { inArray } from "../core/utils/array"; import { Deferred, fromPromise } from "../core/utils/deferred"; import ajax from "../core/utils/ajax"; import Editor from "./editor/editor"; import Button from "./button"; import ProgressBar from "./progress_bar"; import browser from "../core/utils/browser"; import devices from "../core/devices"; import { addNamespace } from "../events/utils/index"; import { name as clickEventName } from "../events/click"; import messageLocalization from "../localization/message"; import { isMaterial } from "./themes"; var window = getWindow(); var FILEUPLOADER_CLASS = "dx-fileuploader"; var FILEUPLOADER_EMPTY_CLASS = "dx-fileuploader-empty"; var FILEUPLOADER_SHOW_FILE_LIST_CLASS = "dx-fileuploader-show-file-list"; var FILEUPLOADER_DRAGOVER_CLASS = "dx-fileuploader-dragover"; var FILEUPLOADER_WRAPPER_CLASS = "dx-fileuploader-wrapper"; var FILEUPLOADER_CONTAINER_CLASS = "dx-fileuploader-container"; var FILEUPLOADER_CONTENT_CLASS = "dx-fileuploader-content"; var FILEUPLOADER_INPUT_WRAPPER_CLASS = "dx-fileuploader-input-wrapper"; var FILEUPLOADER_INPUT_CONTAINER_CLASS = "dx-fileuploader-input-container"; var FILEUPLOADER_INPUT_LABEL_CLASS = "dx-fileuploader-input-label"; var FILEUPLOADER_INPUT_CLASS = "dx-fileuploader-input"; var FILEUPLOADER_FILES_CONTAINER_CLASS = "dx-fileuploader-files-container"; var FILEUPLOADER_FILE_CONTAINER_CLASS = "dx-fileuploader-file-container"; var FILEUPLOADER_FILE_INFO_CLASS = "dx-fileuploader-file-info"; var FILEUPLOADER_FILE_STATUS_MESSAGE_CLASS = "dx-fileuploader-file-status-message"; var FILEUPLOADER_FILE_CLASS = "dx-fileuploader-file"; var FILEUPLOADER_FILE_NAME_CLASS = "dx-fileuploader-file-name"; var FILEUPLOADER_FILE_SIZE_CLASS = "dx-fileuploader-file-size"; var FILEUPLOADER_BUTTON_CLASS = "dx-fileuploader-button"; var FILEUPLOADER_BUTTON_CONTAINER_CLASS = "dx-fileuploader-button-container"; var FILEUPLOADER_CANCEL_BUTTON_CLASS = "dx-fileuploader-cancel-button"; var FILEUPLOADER_UPLOAD_BUTTON_CLASS = "dx-fileuploader-upload-button"; var FILEUPLOADER_INVALID_CLASS = "dx-fileuploader-invalid"; var FILEUPLOADER_AFTER_LOAD_DELAY = 400; var FILEUPLOADER_CHUNK_META_DATA_NAME = "chunkMetadata"; var renderFileUploaderInput = () => $("<input>").attr("type", "file"); var isFormDataSupported = () => !!window.FormData; class FileUploader extends Editor { _supportedKeys() { var click = e => { e.preventDefault(); var $selectButton = this._selectButton.$element(); eventsEngine.trigger($selectButton, clickEventName) }; return extend(super._supportedKeys(), { space: click, enter: click }) } _setOptionsByReference() { super._setOptionsByReference(); extend(this._optionsByReference, { value: true }) } _getDefaultOptions() { return extend(super._getDefaultOptions(), { chunkSize: 0, value: [], selectButtonText: messageLocalization.format("dxFileUploader-selectFile"), uploadButtonText: messageLocalization.format("dxFileUploader-upload"), labelText: messageLocalization.format("dxFileUploader-dropFile"), name: "files[]", multiple: false, accept: "", uploadUrl: "/", allowCanceling: true, showFileList: true, progress: 0, dialogTrigger: void 0, dropZone: void 0, readyToUploadMessage: messageLocalization.format("dxFileUploader-readyToUpload"), uploadedMessage: messageLocalization.format("dxFileUploader-uploaded"), uploadFailedMessage: messageLocalization.format("dxFileUploader-uploadFailedMessage"), uploadAbortedMessage: messageLocalization.format("dxFileUploader-uploadAbortedMessage"), uploadMode: "instantly", uploadMethod: "POST", uploadHeaders: {}, uploadCustomData: {}, onBeforeSend: null, onUploadStarted: null, onUploaded: null, onFilesUploaded: null, onProgress: null, onUploadError: null, onUploadAborted: null, onDropZoneEnter: null, onDropZoneLeave: null, allowedFileExtensions: [], maxFileSize: 0, minFileSize: 0, inputAttr: {}, invalidFileExtensionMessage: messageLocalization.format("dxFileUploader-invalidFileExtension"), invalidMaxFileSizeMessage: messageLocalization.format("dxFileUploader-invalidMaxFileSize"), invalidMinFileSizeMessage: messageLocalization.format("dxFileUploader-invalidMinFileSize"), extendSelection: true, validationMessageMode: "always", uploadFile: null, uploadChunk: null, abortUpload: null, validationMessageOffset: { h: 0, v: 0 }, useNativeInputClick: false, useDragOver: true, nativeDropSupported: true, _uploadButtonType: "normal" }) } _defaultOptionsRules() { return super._defaultOptionsRules().concat([{ device: () => "desktop" === devices.real().deviceType && !devices.isSimulator(), options: { focusStateEnabled: true } }, { device: [{ platform: "android" }], options: { validationMessageOffset: { v: 0 } } }, { device: () => "desktop" !== devices.real().deviceType, options: { useDragOver: false } }, { device: () => !isFormDataSupported(), options: { uploadMode: "useForm" } }, { device: () => browser.msie || "desktop" !== devices.real().deviceType, options: { nativeDropSupported: false } }, { device: () => isMaterial(), options: { _uploadButtonType: "default" } }]) } _initOptions(options) { var isLabelTextDefined = "labelText" in options; super._initOptions(options); if (!isLabelTextDefined && !this._shouldDragOverBeRendered()) { this.option("labelText", "") } } _init() { super._init(); this._initFileInput(); this._initLabel(); this._setUploadStrategy(); this._createFiles(); this._createBeforeSendAction(); this._createUploadStartedAction(); this._createUploadedAction(); this._createFilesUploadedAction(); this._createProgressAction(); this._createUploadErrorAction(); this._createUploadAbortedAction(); this._createDropZoneEnterAction(); this._createDropZoneLeaveAction() } _setUploadStrategy() { var strategy = null; if (this.option("chunkSize") > 0) { var uploadChunk = this.option("uploadChunk"); strategy = uploadChunk && isFunction(uploadChunk) ? new CustomChunksFileUploadStrategy(this) : new DefaultChunksFileUploadStrategy(this) } else { var uploadFile = this.option("uploadFile"); strategy = uploadFile && isFunction(uploadFile) ? new CustomWholeFileUploadStrategy(this) : new DefaultWholeFileUploadStrategy(this) } this._uploadStrategy = strategy } _initFileInput() { this._isCustomClickEvent = false; if (!this._$fileInput) { this._$fileInput = renderFileUploaderInput(); eventsEngine.on(this._$fileInput, "change", this._inputChangeHandler.bind(this)); eventsEngine.on(this._$fileInput, "click", e => { e.stopPropagation(); this._resetInputValue(); return this.option("useNativeInputClick") || this._isCustomClickEvent }) } this._$fileInput.prop({ multiple: this.option("multiple"), accept: this.option("accept"), tabIndex: -1 }) } _inputChangeHandler() { if (this._doPreventInputChange) { return } var fileName = this._$fileInput.val().replace(/^.*\\/, ""); var files = this._$fileInput.prop("files"); if (files && !files.length && "useForm" !== this.option("uploadMode")) { return } var value = files ? this._getFiles(files) : [{ name: fileName }]; this._changeValue(value); if ("instantly" === this.option("uploadMode")) { this._uploadFiles() } } _shouldFileListBeExtended() { return "useForm" !== this.option("uploadMode") && this.option("extendSelection") && this.option("multiple") } _changeValue(value) { var files = this._shouldFileListBeExtended() ? this.option("value").slice() : []; this.option("value", files.concat(value)) } _getFiles(fileList) { var values = []; each(fileList, (_, value) => values.push(value)); return values } _getFile(fileData) { var targetFileValue = isNumeric(fileData) ? this.option("value")[fileData] : fileData; return this._files.filter(file => file.value === targetFileValue)[0] } _initLabel() { if (!this._$inputLabel) { this._$inputLabel = $("<div>") } this._updateInputLabelText() } _updateInputLabelText() { var correctedValue = this._isInteractionDisabled() ? "" : this.option("labelText"); this._$inputLabel.text(correctedValue) } _focusTarget() { return this.$element().find("." + FILEUPLOADER_BUTTON_CLASS) } _getSubmitElement() { return this._$fileInput } _initMarkup() { super._initMarkup(); this.$element().addClass(FILEUPLOADER_CLASS); this._renderWrapper(); this._renderInputWrapper(); this._renderSelectButton(); this._renderInputContainer(); this._renderUploadButton(); this._preventRecreatingFiles = true } _render() { this._preventRecreatingFiles = false; this._attachDragEventHandlers(this._$inputWrapper); this._attachDragEventHandlers(this.option("dropZone")); this._renderFiles(); super._render() } _createFileProgressBar(file) { file.progressBar = this._createProgressBar(file.value.size); file.progressBar.$element().appendTo(file.$file); this._initStatusMessage(file); this._ensureCancelButtonInitialized(file) } _setStatusMessage(file, message) { setTimeout(() => { if (this.option("showFileList")) { if (file.$statusMessage) { file.$statusMessage.text(message); file.$statusMessage.css("display", ""); file.progressBar.$element().remove() } } }, FILEUPLOADER_AFTER_LOAD_DELAY) } _getUploadAbortedStatusMessage() { return "instantly" === this.option("uploadMode") ? this.option("uploadAbortedMessage") : this.option("readyToUploadMessage") } _createFiles() { var value = this.option("value"); if (this._files && (0 === value.length || !this._shouldFileListBeExtended())) { this._preventFilesUploading(this._files); this._files = null } if (!this._files) { this._files = [] } each(value.slice(this._files.length), (_, value) => { var file = this._createFile(value); this._validateFile(file); this._files.push(file) }) } _preventFilesUploading(files) { files.forEach(file => this._uploadStrategy.abortUpload(file)) } _validateFile(file) { file.isValidFileExtension = this._validateFileExtension(file); file.isValidMinSize = this._validateMinFileSize(file); file.isValidMaxSize = this._validateMaxFileSize(file) } _validateFileExtension(file) { var allowedExtensions = this.option("allowedFileExtensions"); var fileExtension = file.value.name.substring(file.value.name.lastIndexOf(".")).toLowerCase(); if (0 === allowedExtensions.length) { return true } for (var i = 0; i < allowedExtensions.length; i++) { if (fileExtension === allowedExtensions[i].toLowerCase()) { return true } } return false } _validateMaxFileSize(file) { var fileSize = file.value.size; var maxFileSize = this.option("maxFileSize"); return maxFileSize > 0 ? fileSize <= maxFileSize : true } _validateMinFileSize(file) { var fileSize = file.value.size; var minFileSize = this.option("minFileSize"); return minFileSize > 0 ? fileSize >= minFileSize : true } _createBeforeSendAction() { this._beforeSendAction = this._createActionByOption("onBeforeSend", { excludeValidators: ["readOnly"] }) } _createUploadStartedAction() { this._uploadStartedAction = this._createActionByOption("onUploadStarted", { excludeValidators: ["readOnly"] }) } _createUploadedAction() { this._uploadedAction = this._createActionByOption("onUploaded", { excludeValidators: ["readOnly"] }) } _createFilesUploadedAction() { this._filesUploadedAction = this._createActionByOption("onFilesUploaded", { excludeValidators: ["readOnly"] }) } _createProgressAction() { this._progressAction = this._createActionByOption("onProgress", { excludeValidators: ["readOnly"] }) } _createUploadAbortedAction() { this._uploadAbortedAction = this._createActionByOption("onUploadAborted", { excludeValidators: ["readOnly"] }) } _createUploadErrorAction() { this._uploadErrorAction = this._createActionByOption("onUploadError", { excludeValidators: ["readOnly"] }) } _createDropZoneEnterAction() { this._dropZoneEnterAction = this._createActionByOption("onDropZoneEnter") } _createDropZoneLeaveAction() { this._dropZoneLeaveAction = this._createActionByOption("onDropZoneLeave") } _createFile(value) { return { value: value, loadedSize: 0, onProgress: Callbacks(), onAbort: Callbacks(), onLoad: Callbacks(), onError: Callbacks(), onLoadStart: Callbacks(), isValidFileExtension: true, isValidMaxSize: true, isValidMinSize: true, isValid() { return this.isValidFileExtension && this.isValidMaxSize && this.isValidMinSize }, isInitialized: false } } _resetFileState(file) { file.isAborted = false; file.uploadStarted = false; file.isStartLoad = false; file.loadedSize = 0; file.chunksData = void 0; file.request = void 0 } _renderFiles() { var _this$_validationMess; var value = this.option("value"); if (!this._$filesContainer) { this._$filesContainer = $("<div>").addClass(FILEUPLOADER_FILES_CONTAINER_CLASS).appendTo(this._$content) } else if (!this._shouldFileListBeExtended() || 0 === value.length) { this._$filesContainer.empty() } var showFileList = this.option("showFileList"); if (showFileList) { each(this._files, (_, file) => { if (!file.$file) { this._renderFile(file) } }) } this.$element().toggleClass(FILEUPLOADER_SHOW_FILE_LIST_CLASS, showFileList); this._toggleFileUploaderEmptyClassName(); this._updateFileNameMaxWidth(); null === (_this$_validationMess = this._validationMessage) || void 0 === _this$_validationMess ? void 0 : _this$_validationMess.repaint() } _renderFile(file) { var value = file.value; var $fileContainer = $("<div>").addClass(FILEUPLOADER_FILE_CONTAINER_CLASS).appendTo(this._$filesContainer); this._renderFileButtons(file, $fileContainer); file.$file = $("<div>").addClass(FILEUPLOADER_FILE_CLASS).appendTo($fileContainer); var $fileInfo = $("<div>").addClass(FILEUPLOADER_FILE_INFO_CLASS).appendTo(file.$file); file.$statusMessage = $("<div>").addClass(FILEUPLOADER_FILE_STATUS_MESSAGE_CLASS).appendTo(file.$file); $("<div>").addClass(FILEUPLOADER_FILE_NAME_CLASS).text(value.name).appendTo($fileInfo); if (isDefined(value.size)) { $("<div>").addClass(FILEUPLOADER_FILE_SIZE_CLASS).text(this._getFileSize(value.size)).appendTo($fileInfo) } if (file.isValid()) { file.$statusMessage.text(this.option("readyToUploadMessage")) } else { if (!file.isValidFileExtension) { file.$statusMessage.append(this._createValidationElement("invalidFileExtensionMessage")) } if (!file.isValidMaxSize) { file.$statusMessage.append(this._createValidationElement("invalidMaxFileSizeMessage")) } if (!file.isValidMinSize) { file.$statusMessage.append(this._createValidationElement("invalidMinFileSizeMessage")) } $fileContainer.addClass(FILEUPLOADER_INVALID_CLASS) } } _createValidationElement(key) { return $("<span>").text(this.option(key)) } _updateFileNameMaxWidth() { var cancelButtonsCount = this.option("allowCanceling") && "useForm" !== this.option("uploadMode") ? 1 : 0; var uploadButtonsCount = "useButtons" === this.option("uploadMode") ? 1 : 0; var filesContainerWidth = this._$filesContainer.find("." + FILEUPLOADER_FILE_CONTAINER_CLASS).first().width() || this._$filesContainer.width(); var $buttonContainer = this._$filesContainer.find("." + FILEUPLOADER_BUTTON_CONTAINER_CLASS).eq(0); var buttonsWidth = $buttonContainer.width() * (cancelButtonsCount + uploadButtonsCount); var $fileSize = this._$filesContainer.find("." + FILEUPLOADER_FILE_SIZE_CLASS).eq(0); var prevFileSize = $fileSize.text(); $fileSize.text("1000 Mb"); var fileSizeWidth = $fileSize.width(); $fileSize.text(prevFileSize); this._$filesContainer.find("." + FILEUPLOADER_FILE_NAME_CLASS).css("maxWidth", filesContainerWidth - buttonsWidth - fileSizeWidth) } _renderFileButtons(file, $container) { var $cancelButton = this._getCancelButton(file); $cancelButton && $container.append($cancelButton); var $uploadButton = this._getUploadButton(file); $uploadButton && $container.append($uploadButton) } _getCancelButton(file) { if ("useForm" === this.option("uploadMode")) { return null } file.cancelButton = this._createComponent($("<div>").addClass(FILEUPLOADER_BUTTON_CLASS + " " + FILEUPLOADER_CANCEL_BUTTON_CLASS), Button, { onClick: () => this._removeFile(file), icon: "close", visible: this.option("allowCanceling"), disabled: this.option("readOnly"), integrationOptions: {} }); return $("<div>").addClass(FILEUPLOADER_BUTTON_CONTAINER_CLASS).append(file.cancelButton.$element()) } _getUploadButton(file) { if (!file.isValid() || "useButtons" !== this.option("uploadMode")) { return null } file.uploadButton = this._createComponent($("<div>").addClass(FILEUPLOADER_BUTTON_CLASS + " " + FILEUPLOADER_UPLOAD_BUTTON_CLASS), Button, { onClick: () => this._uploadFile(file), icon: "upload" }); file.onLoadStart.add(() => file.uploadButton.option({ visible: false, disabled: true })); file.onAbort.add(() => file.uploadButton.option({ visible: true, disabled: false })); return $("<div>").addClass(FILEUPLOADER_BUTTON_CONTAINER_CLASS).append(file.uploadButton.$element()) } _removeFile(file) { var _file$$file; null === (_file$$file = file.$file) || void 0 === _file$$file ? void 0 : _file$$file.parent().remove(); this._files.splice(inArray(file, this._files), 1); var value = this.option("value").slice(); value.splice(inArray(file.value, value), 1); this._preventRecreatingFiles = true; this.option("value", value); this._preventRecreatingFiles = false; this._toggleFileUploaderEmptyClassName(); this._resetInputValue(true) } removeFile(fileData) { if ("useForm" === this.option("uploadMode") || !isDefined(fileData)) { return } var file = this._getFile(fileData); if (file) { if (file.uploadStarted) { this._preventFilesUploading([file]) } this._removeFile(file) } } _toggleFileUploaderEmptyClassName() { this.$element().toggleClass(FILEUPLOADER_EMPTY_CLASS, !this._files.length || this._hasInvalidFile(this._files)) } _hasInvalidFile(files) { for (var i = 0; i < files.length; i++) { if (!files[i].isValid()) { return true } } return false } _getFileSize(size) { var i = 0; var labels = [messageLocalization.format("dxFileUploader-bytes"), messageLocalization.format("dxFileUploader-kb"), messageLocalization.format("dxFileUploader-Mb"), messageLocalization.format("dxFileUploader-Gb")]; var count = labels.length - 1; while (i < count && size >= 1024) { size /= 1024; i++ } return Math.round(size) + " " + labels[i] } _renderSelectButton() { var $button = $("<div>").addClass(FILEUPLOADER_BUTTON_CLASS).appendTo(this._$inputWrapper); this._selectButton = this._createComponent($button, Button, { text: this.option("selectButtonText"), focusStateEnabled: false, integrationOptions: {}, disabled: this.option("readOnly") }); this._selectFileDialogHandler = this._selectButtonClickHandler.bind(this); if ("desktop" === devices.real().deviceType) { this._selectButton.option("onClick", this._selectFileDialogHandler) } else { this._attachSelectFileDialogHandler(this._selectButton.$element()) } this._attachSelectFileDialogHandler(this.option("dialogTrigger")) } _selectButtonClickHandler() { if (this.option("useNativeInputClick")) { return } if (this._isInteractionDisabled()) { return false } this._isCustomClickEvent = true; eventsEngine.trigger(this._$fileInput, "click"); this._isCustomClickEvent = false } _attachSelectFileDialogHandler(target) { if (!isDefined(target)) { return } this._detachSelectFileDialogHandler(target); eventsEngine.on($(target), "click", this._selectFileDialogHandler) } _detachSelectFileDialogHandler(target) { if (!isDefined(target)) { return } eventsEngine.off($(target), "click", this._selectFileDialogHandler) } _renderUploadButton() { if ("useButtons" !== this.option("uploadMode")) { return } var $uploadButton = $("<div>").addClass(FILEUPLOADER_BUTTON_CLASS).addClass(FILEUPLOADER_UPLOAD_BUTTON_CLASS).appendTo(this._$content); this._uploadButton = this._createComponent($uploadButton, Button, { text: this.option("uploadButtonText"), onClick: this._uploadButtonClickHandler.bind(this), type: this.option("_uploadButtonType"), integrationOptions: {} }) } _uploadButtonClickHandler() { this._uploadFiles() } _shouldDragOverBeRendered() { return !this.option("readOnly") && ("useForm" !== this.option("uploadMode") || this.option("nativeDropSupported")) } _isInteractionDisabled() { return this.option("readOnly") || this.option("disabled") } _renderInputContainer() { this._$inputContainer = $("<div>").addClass(FILEUPLOADER_INPUT_CONTAINER_CLASS).appendTo(this._$inputWrapper); this._$fileInput.addClass(FILEUPLOADER_INPUT_CLASS); this._renderInput(); var labelId = "dx-fileuploader-input-label-".concat(new Guid); this._$inputLabel.attr("id", labelId).addClass(FILEUPLOADER_INPUT_LABEL_CLASS).appendTo(this._$inputContainer); this.setAria("labelledby", labelId, this._$fileInput) } _renderInput() { if (this.option("useNativeInputClick")) { this._selectButton.option("template", this._selectButtonInputTemplate.bind(this)) } else { this._$fileInput.appendTo(this._$inputContainer); this._selectButton.option("template", "content") } this._applyInputAttributes(this.option("inputAttr")) } _selectButtonInputTemplate(data, content) { var $content = $(content); var $text = $("<span>").addClass("dx-button-text").text(data.text); $content.append($text).append(this._$fileInput); return $content } _renderInputWrapper() { this._$inputWrapper = $("<div>").addClass(FILEUPLOADER_INPUT_WRAPPER_CLASS).appendTo(this._$content) } _detachDragEventHandlers(target) { if (!isDefined(target)) { return } eventsEngine.off($(target), addNamespace("", this.NAME)) } _attachDragEventHandlers(target) { var isCustomTarget = target !== this._$inputWrapper; if (!isDefined(target) || !this._shouldDragOverBeRendered()) { return } this._detachDragEventHandlers(target); target = $(target); this._dragEventsTargets = []; eventsEngine.on(target, addNamespace("dragenter", this.NAME), this._dragEnterHandler.bind(this, isCustomTarget)); eventsEngine.on(target, addNamespace("dragover", this.NAME), this._dragOverHandler.bind(this)); eventsEngine.on(target, addNamespace("dragleave", this.NAME), this._dragLeaveHandler.bind(this, isCustomTarget)); eventsEngine.on(target, addNamespace("drop", this.NAME), this._dropHandler.bind(this, isCustomTarget)) } _applyInputAttributes(customAttributes) { this._$fileInput.attr(customAttributes) } _useInputForDrop() { return this.option("nativeDropSupported") && "useForm" === this.option("uploadMode") } _dragEnterHandler(isCustomTarget, e) { if (this.option("disabled")) { return false } if (!this._useInputForDrop()) { e.preventDefault() } this._tryToggleDropZoneActive(true, isCustomTarget, e); this._updateEventTargets(e) } _dragOverHandler(e) { if (!this._useInputForDrop()) { e.preventDefault() } e.originalEvent.dataTransfer.dropEffect = "copy" } _dragLeaveHandler(isCustomTarget, e) { if (!this._useInputForDrop()) { e.preventDefault() } this._updateEventTargets(e); this._tryToggleDropZoneActive(false, isCustomTarget, e) } _updateEventTargets(e) { var targetIndex = this._dragEventsTargets.indexOf(e.target); var isTargetExists = -1 !== targetIndex; if ("dragenter" === e.type) { !isTargetExists && this._dragEventsTargets.push(e.target) } else { isTargetExists && this._dragEventsTargets.splice(targetIndex, 1) } } _tryToggleDropZoneActive(active, isCustom, event) { var classAction = active ? "addClass" : "removeClass"; var mouseAction = active ? "_dropZoneEnterAction" : "_dropZoneLeaveAction"; if (!this._dragEventsTargets.length) { this[mouseAction]({ event: event, dropZoneElement: event.currentTarget }); if (!isCustom) { this.$element()[classAction](FILEUPLOADER_DRAGOVER_CLASS) } } } _dropHandler(isCustomTarget, e) { this._dragEventsTargets = []; if (!isCustomTarget) { this.$element().removeClass(FILEUPLOADER_DRAGOVER_CLASS) } if (this._useInputForDrop() || isCustomTarget && this._isInteractionDisabled()) { return } e.preventDefault(); var fileList = e.originalEvent.dataTransfer.files; var files = this._getFiles(fileList); if (!this.option("multiple") && files.length > 1) { return } this._changeValue(this._filterFiles(files)); if ("instantly" === this.option("uploadMode")) { this._uploadFiles() } } _handleAllFilesUploaded() { var areAllFilesLoaded = this._files.every(file => !file.isValid() || file._isError || file._isLoaded || file.isAborted); if (areAllFilesLoaded) { this._filesUploadedAction() } } _filterFiles(files) { if (!files.length) { return files } var accept = this.option("accept"); if (!accept.length) { return files } var result = []; var allowedTypes = this._getAllowedFileTypes(accept); for (var i = 0, n = files.length; i < n; i++) { if (this._isFileTypeAllowed(files[i], allowedTypes)) { result.push(files[i]) } } return result } _getAllowedFileTypes(acceptSting) { if (!acceptSting.length) { return [] } return acceptSting.split(",").map(item => item.trim()) } _isFileTypeAllowed(file, allowedTypes) { for (var i = 0, n = allowedTypes.length; i < n; i++) { var allowedType = allowedTypes[i]; if ("." === allowedType[0]) { allowedType = allowedType.replace(".", "\\."); if (file.name.match(new RegExp(allowedType + "$", "i"))) { return true } } else { allowedType = allowedType.replace(new RegExp("\\*", "g"), ""); if (file.type.match(new RegExp(allowedType, "i"))) { return true } } } return false } _renderWrapper() { var $wrapper = $("<div>").addClass(FILEUPLOADER_WRAPPER_CLASS).appendTo(this.$element()); var $container = $("<div>").addClass(FILEUPLOADER_CONTAINER_CLASS).appendTo($wrapper); this._$content = $("<div>").addClass(FILEUPLOADER_CONTENT_CLASS).appendTo($container) } _clean() { this._$fileInput.detach(); delete this._$filesContainer; if (this._files) { this._files.forEach(file => { file.$file = null; file.$statusMessage = null }) } super._clean() } abortUpload(fileData) { if ("useForm" === this.option("uploadMode")) { return } if (isDefined(fileData)) { var file = this._getFile(fileData); if (file) { this._preventFilesUploading([file]) } } else { this._preventFilesUploading(this._files) } } upload(fileData) { if ("useForm" === this.option("uploadMode")) { return } if (isDefined(fileData)) { var file = this._getFile(fileData); if (file && isFormDataSupported()) { this._uploadFile(file) } } else { this._uploadFiles() } } _uploadFiles() { if (isFormDataSupported()) { each(this._files, (_, file) => this._uploadFile(file)) } } _uploadFile(file) { this._uploadStrategy.upload(file) } _updateProgressBar(file, loadedFileData) { file.progressBar && file.progressBar.option({ value: loadedFileData.loaded, showStatus: true }); this._progressAction({ file: file.value, segmentSize: loadedFileData.currentSegmentSize, bytesLoaded: loadedFileData.loaded, bytesTotal: loadedFileData.total, event: loadedFileData.event, request: file.request }) } _updateTotalProgress(totalFilesSize, totalLoadedFilesSize) { var progress = totalFilesSize ? this._getProgressValue(totalLoadedFilesSize / totalFilesSize) : 0; this.option("progress", progress); this._setLoadedSize(totalLoadedFilesSize) } _getProgressValue(ratio) { return Math.floor(100 * ratio) } _initStatusMessage(file) { file.$statusMessage.css("display", "none") } _ensureCancelButtonInitialized(file) { if (file.isInitialized) { return } file.cancelButton.option("onClick", () => { this._preventFilesUploading([file]); this._removeFile(file) }); var hideCancelButton = () => { setTimeout(() => { file.cancelButton.option({ visible: false }) }, FILEUPLOADER_AFTER_LOAD_DELAY) }; file.onLoad.add(hideCancelButton); file.onError.add(hideCancelButton) } _createProgressBar(fileSize) { return this._createComponent($("<div>"), ProgressBar, { value: void 0, min: 0, max: fileSize, statusFormat: ratio => this._getProgressValue(ratio) + "%", showStatus: false, statusPosition: "right" }) } _getTotalFilesSize() { if (!this._totalFilesSize) { this._totalFilesSize = 0; each(this._files, (_, file) => { this._totalFilesSize += file.value.size }) } return this._totalFilesSize } _getTotalLoadedFilesSize() { if (!this._totalLoadedFilesSize) { this._totalLoadedFilesSize = 0; each(this._files, (_, file) => { this._totalLoadedFilesSize += file.loadedSize }) } return this._totalLoadedFilesSize } _setLoadedSize(value) { this._totalLoadedFilesSize = value } _recalculateProgress() { this._totalFilesSize = 0; this._totalLoadedFilesSize = 0; this._updateTotalProgress(this._getTotalFilesSize(), this._getTotalLoadedFilesSize()) } _updateReadOnlyState() { var readOnly = this.option("readOnly"); this._selectButton.option("disabled", readOnly); this._files.forEach(file => { var _file$cancelButton; return null === (_file$cancelButton = file.cancelButton) || void 0 === _file$cancelButton ? void 0 : _file$cancelButton.option("disabled", readOnly) }); this._updateInputLabelText(); this._attachDragEventHandlers(this._$inputWrapper) } _optionChanged(args) { var { name: name, value: value, previousValue: previousValue } = args; switch (name) { case "height": case "width": this._updateFileNameMaxWidth(); super._optionChanged(args); break; case "value": !value.length && this._$fileInput.val(""); if (!this._preventRecreatingFiles) { this._createFiles(); this._renderFiles() } this._recalculateProgress(); super._optionChanged(args); break; case "name": this._initFileInput(); super._optionChanged(args); break; case "accept": this._initFileInput(); break; case "multiple": this._initFileInput(); if (!args.value) { this.reset() } break; case "readOnly": this._updateReadOnlyState(); super._optionChanged(args); break; case "selectButtonText": this._selectButton.option("text", value); break; case "uploadButtonText": this._uploadButton && this._uploadButton.option("text", value); break; case "_uploadButtonType": this._uploadButton && this._uploadButton.option("type", value); break; case "dialogTrigger": this._detachSelectFileDialogHandler(previousValue); this._attachSelectFileDialogHandler(value); break; case "dropZone": this._detachDragEventHandlers(previousValue); this._attachDragEventHandlers(value); break; case "maxFileSize": case "minFileSize": case "allowedFileExtensions": case "invalidFileExtensionMessage": case "invalidMaxFileSizeMessage": case "invalidMinFileSizeMessage": case "readyToUploadMessage": case "uploadedMessage": case "uploadFailedMessage": case "uploadAbortedMessage": this._invalidate(); break; case "labelText": this._updateInputLabelText(); break; case "showFileList": if (!this._preventRecreatingFiles) { this._renderFiles() } break; case "uploadFile": case "uploadChunk": case "chunkSize": this._setUploadStrategy(); break; case "abortUpload": case "uploadUrl": case "progress": case "uploadMethod": case "uploadHeaders": case "uploadCustomData": case "extendSelection": break; case "allowCanceling": case "uploadMode": this.reset(); this._invalidate(); break; case "onBeforeSend": this._createBeforeSendAction(); break; case "onUploadStarted": this._createUploadStartedAction(); break; case "onUploaded": this._createUploadedAction(); break; case "onFilesUploaded": this._createFilesUploadedAction(); break; case "onProgress": this._createProgressAction(); break; case "onUploadError": this._createUploadErrorAction(); break; case "onUploadAborted": this._createUploadAbortedAction(); break; case "onDropZoneEnter": this._createDropZoneEnterAction(); break; case "onDropZoneLeave": this._createDropZoneLeaveAction(); break; case "useNativeInputClick": this._renderInput(); break; case "useDragOver": this._attachDragEventHandlers(this._$inputWrapper); break; case "nativeDropSupported": this._invalidate(); break; case "inputAttr": this._applyInputAttributes(this.option(name)); break; default: super._optionChanged(args) } } _resetInputValue(force) { if ("useForm" === this.option("uploadMode") && !force) { return } this._doPreventInputChange = true; this._$fileInput.val(""); this._doPreventInputChange = false } reset() { this.option("value", []) } } class FileBlobReader { constructor(file, chunkSize) { this.file = file; this.chunkSize = chunkSize; this.index = 0 } read() { if (!this.file) { return null } var result = this.createBlobResult(this.file, this.index, this.chunkSize); if (result.isCompleted) { this.file = null } this.index++; return result } createBlobResult(file, index, chunkSize) { var currentPosition = index * chunkSize; return { blob: this.sliceFile(file, currentPosition, chunkSize), index: index, isCompleted: currentPosition + chunkSize >= file.size } } sliceFile(file, startPos, length) { if (file.slice) { return file.slice(startPos, startPos + length) } if (file.webkitSlice) { return file.webkitSlice(startPos, startPos + length) } return null } } class FileUploadStrategyBase { constructor(fileUploader) { this.fileUploader = fileUploader } upload(file) { if (file.isInitialized && file.isAborted) { this.fileUploader._resetFileState(file) } if (file.isValid() && !file.uploadStarted) { this._prepareFileBeforeUpload(file); this._uploadCore(file) } } abortUpload(file) { if (file._isError || file._isLoaded || file.isAborted || !file.uploadStarted) { return } file.isAborted = true; file.request && file.request.abort(); if (this._isCustomCallback("abortUpload")) { var abortUpload = this.fileUploader.option("abortUpload"); var arg = this._createUploadArgument(file); var deferred = null; try { var result = abortUpload(file.value, arg); deferred = fromPromise(result) } catch (error) { deferred = (new Deferred).reject(error).promise() } deferred.done(() => file.onAbort.fire()).fail(error => this._handleFileError(file, error)) } } _beforeSend(xhr, file) { var arg = this._createUploadArgument(file); this.fileUploader._beforeSendAction({ request: xhr, file: file.value, uploadInfo: arg }); file.request = xhr } _createUploadArgument(file) {} _uploadCore(file) {} _isCustomCallback(name) { var callback = this.fileUploader.option(name); return callback && isFunction(callback) } _handleFileError(file, error) { file._isError = true; file.onError.fire(error) } _prepareFileBeforeUpload(file) { if (file.$file) { var _file$progressBar; null === (_file$progressBar = file.progressBar) || void 0 === _file$progressBar ? void 0 : _file$progressBar.dispose(); this.fileUploader._createFileProgressBar(file) } if (file.isInitialized) { return } file.onLoadStart.add(this._onUploadStarted.bind(this, file)); file.onLoad.add(this._onLoadedHandler.bind(this, file)); file.onError.add(this._onErrorHandler.bind(this, file)); file.onAbort.add(this._onAbortHandler.bind(this, file)); file.onProgress.add(this._onProgressHandler.bind(this, file)); file.isInitialized = true } _isStatusError(status) { return 400 <= status && status < 500 || 500 <= status && status < 600 } _onUploadStarted(file, e) { file.uploadStarted = true; this.fileUploader._uploadStartedAction({ file: file.value, event: e, request: file.request }) } _onAbortHandler(file, e) { var args = { file: file.value, event: e, request: file.request, message: this.fileUploader._getUploadAbortedStatusMessage() }; this.fileUploader._uploadAbortedAction(args); this.fileUploader._setStatusMessage(file, args.message); this.fileUploader._handleAllFilesUploaded() } _onErrorHandler(file, error) { var args = { file: file.value, event: void 0, request: file.request, error: error, message: this.fileUploader.option("uploadFailedMessage") }; this.fileUploader._uploadErrorAction(args); this.fileUploader._setStatusMessage(file, args.message); this.fileUploader._handleAllFilesUploaded() } _onLoadedHandler(file, e) { var args = { file: file.value, event: e, request: file.request, message: this.fileUploader.option("uploadedMessage") }; file._isLoaded = true; this.fileUploader._uploadedAction(args); this.fileUploader._setStatusMessage(file, args.message); this.fileUploader._handleAllFilesUploaded() } _onProgressHandler(file, e) { if (file) { var totalFilesSize = this.fileUploader._getTotalFilesSize(); var totalLoadedFilesSize = this.fileUploader._getTotalLoadedFilesSize(); var loadedSize = Math.min(e.loaded, file.value.size); var segmentSize = loadedSize - file.loadedSize; file.loadedSize = loadedSize; this.fileUploader._updateTotalProgress(totalFilesSize, totalLoadedFilesSize + segmentSize); this.fileUploader._updateProgressBar(file, this._getLoadedData(loadedSize, e.total, segmentSize, e)) } } _getLoadedData(loaded, total, currentSegmentSize, event) { return { loaded: loaded, total: total, currentSegmentSize: currentSegmentSize } } _extendFormData(formData) { var formDataEntries = this.fileUploader.option("uploadCustomData"); for (var entryName in formDataEntries) { if (Object.prototype.hasOwnProperty.call(formDataEntries, entryName) && isDefined(formDataEntries[entryName])) { formData.append(entryName, formDataEntries[entryName]) } } } } class ChunksFileUploadStrategyBase extends FileUploadStrategyBase { constructor(fileUploader) { super(fileUploader); this.chunkSize = this.fileUploader.option("chunkSize") } _uploadCore(file) { var realFile = file.value; var chunksData = { name: realFile.name, loadedBytes: 0, type: realFile.type, blobReader: new FileBlobReader(realFile, this.chunkSize), guid: new Guid, fileSize: realFile.size, count: Math.ceil(realFile.size / this.chunkSize), customData: {} }; file.chunksData = chunksData; this._sendChunk(file, chunksData) } _sendChunk(file, chunksData) { var chunk = chunksData.blobReader.read(); chunksData.currentChunk = chunk; if (chunk) { this._sendChunkCore(file, chunksData, chunk).done(() => {