UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,255 lines • 62.8 kB
/** * DevExtreme (esm/ui/file_uploader.js) * Version: 23.2.6 * Build date: Wed May 01 2024 * * Copyright (c) 2012 - 2024 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import { getOffset, getWidth } from "../core/utils/size"; 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 { 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 devices from "../core/devices"; import { addNamespace, isTouchEvent } from "../events/utils/index"; import { name as clickEventName } from "../events/click"; import messageLocalization from "../localization/message"; import { isFluent, isMaterial } from "./themes"; import domAdapter from "../core/dom_adapter"; 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 DRAG_EVENT_DELTA = 1; 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 }, hoverStateEnabled: true, useNativeInputClick: false, useDragOver: true, nativeDropSupported: true, _uploadButtonType: "normal", _buttonStylingMode: "contained" }) } _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: () => "desktop" !== devices.real().deviceType, options: { nativeDropSupported: false } }, { device: () => isMaterial(), options: { _uploadButtonType: "default" } }, { device: () => isFluent(), options: { _buttonStylingMode: "text" } }]) } _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; var { multiple: multiple, accept: accept, hint: hint } = this.option(); 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 }) } var inputProps = { multiple: multiple, accept: accept, tabIndex: -1 }; if (isDefined(hint)) { inputProps.title = hint } this._$fileInput.prop(inputProps) } _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; this._activeDropZone = null } _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 accept = this.option("accept"); var allowedTypes = this._getAllowedFileTypes(accept); var fileExtension = file.value.name.substring(file.value.name.lastIndexOf(".")).toLowerCase(); if (0 !== accept.length && !this._isFileTypeAllowed(file.value, allowedTypes)) { return false } 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 = getWidth(this._$filesContainer.find("." + FILEUPLOADER_FILE_CONTAINER_CLASS).first()) || getWidth(this._$filesContainer); var $buttonContainer = this._$filesContainer.find("." + FILEUPLOADER_BUTTON_CONTAINER_CLASS).eq(0); var buttonsWidth = getWidth($buttonContainer) * (cancelButtonsCount + uploadButtonsCount); var $fileSize = this._$filesContainer.find("." + FILEUPLOADER_FILE_SIZE_CLASS).eq(0); var prevFileSize = $fileSize.text(); $fileSize.text("1000 Mb"); var fileSizeWidth = getWidth($fileSize); $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 } var { allowCanceling: allowCanceling, readOnly: readOnly, hoverStateEnabled: hoverStateEnabled, _buttonStylingMode: _buttonStylingMode } = this.option(); file.cancelButton = this._createComponent($("<div>").addClass(FILEUPLOADER_BUTTON_CLASS + " " + FILEUPLOADER_CANCEL_BUTTON_CLASS), Button, { onClick: () => this._removeFile(file), icon: "close", visible: allowCanceling, disabled: readOnly, integrationOptions: {}, hoverStateEnabled: hoverStateEnabled, stylingMode: _buttonStylingMode }); return $("<div>").addClass(FILEUPLOADER_BUTTON_CONTAINER_CLASS).append(file.cancelButton.$element()) } _getUploadButton(file) { if (!file.isValid() || "useButtons" !== this.option("uploadMode")) { return null } var { hoverStateEnabled: hoverStateEnabled, _buttonStylingMode: _buttonStylingMode } = this.option(); file.uploadButton = this._createComponent($("<div>").addClass(FILEUPLOADER_BUTTON_CLASS + " " + FILEUPLOADER_UPLOAD_BUTTON_CLASS), Button, { onClick: () => this._uploadFile(file), icon: "upload", hoverStateEnabled: hoverStateEnabled, stylingMode: _buttonStylingMode }); 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(this._files.indexOf(file), 1); var value = this.option("value").slice(); value.splice(value.indexOf(file.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"), hoverStateEnabled: this.option("hoverStateEnabled") }); 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: {}, hoverStateEnabled: this.option("hoverStateEnabled") }) } _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); eventsEngine.on(target, addNamespace("dragenter", this.NAME), this._dragEnterHandler.bind(this, isCustomTarget)); eventsEngine.on(target, addNamespace("dragover", this.NAME), this._dragOverHandler.bind(this, isCustomTarget)); 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") } _getDropZoneElement(isCustomTarget, e) { var targetList = isCustomTarget ? Array.from($(this.option("dropZone"))) : [this._$inputWrapper]; targetList = targetList.map(element => $(element).get(0)); return targetList[targetList.indexOf(e.currentTarget)] } _dragEnterHandler(isCustomTarget, e) { if (this.option("disabled")) { return false } if (!this._useInputForDrop()) { e.preventDefault() } var dropZoneElement = this._getDropZoneElement(isCustomTarget, e); if (isDefined(dropZoneElement) && this._shouldRaiseDragOver(e, dropZoneElement)) { this._activeDropZone = dropZoneElement; this._tryToggleDropZoneActive(true, isCustomTarget, e) } } _shouldRaiseDragOver(e, dropZoneElement) { return null === this._activeDropZone && this.isMouseOverElement(e, dropZoneElement, false) && e.originalEvent.dataTransfer.types.find(item => "Files" === item) } _dragOverHandler(isCustomTarget, e) { if (!this._useInputForDrop()) { e.preventDefault() } e.originalEvent.dataTransfer.dropEffect = "copy"; if (!isCustomTarget) { var dropZoneElement = this._getDropZoneElement(false, e); if (this._shouldRaiseDragOver(e, dropZoneElement)) { this._dragEnterHandler(false, e) } if (this._shouldRaiseDragLeave(e, false)) { this._dragLeaveHandler(false, e) } } } _dragLeaveHandler(isCustomTarget, e) { if (!this._useInputForDrop()) { e.preventDefault() } if (this._shouldRaiseDragLeave(e, isCustomTarget)) { this._tryToggleDropZoneActive(false, isCustomTarget, e); this._activeDropZone = null } } _shouldRaiseDragLeave(e, isCustomTarget) { return null !== this._activeDropZone && !this.isMouseOverElement(e, this._activeDropZone, !isCustomTarget, -DRAG_EVENT_DELTA) } _tryToggleDropZoneActive(active, isCustom, event) { var classAction = active ? "addClass" : "removeClass"; var mouseAction = active ? "_dropZoneEnterAction" : "_dropZoneLeaveAction"; this[mouseAction]({ event: event, dropZoneElement: this._activeDropZone }); if (!isCustom) { this.$element()[classAction](FILEUPLOADER_DRAGOVER_CLASS) } } _dropHandler(isCustomTarget, e) { this._activeDropZone = null; 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 || 0 === files.length) { return } this._changeValue(files); if ("instantly" === this.option("uploadMode")) { this._uploadFiles() } } _areAllFilesLoaded() { return this._files.every(file => !file.isValid() || file._isError || file._isLoaded || file.isAborted) } _handleAllFilesUploaded() { this._recalculateProgress(); if (this._areAllFilesLoaded()) { this._filesUploadedAction() } } _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; this._detachSelectFileDialogHandler(this.option("dialogTrigger")); this._detachDragEventHandlers(this.option("dropZone")); 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 = 0; if (isDefined(totalFilesSize)) { if (this._files.length > 0 && this._areAllFilesLoaded() && 0 === totalFilesSize && 0 === totalLoadedFilesSize) { progress = this._getProgressValue(1) } else if (totalFilesSize) { progress = this._getProgressValue(totalLoadedFilesSize / totalFilesSize) } } 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()) } isMouseOverElement(mouseEvent, element, correctPseudoElements) { var dragEventDelta = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : DRAG_EVENT_DELTA; if (!element) { return false } var beforeHeight = correctPseudoElements ? parseFloat(window.getComputedStyle(element, ":before").height) : 0; var afterHeight = correctPseudoElements ? parseFloat(window.getComputedStyle(element, ":after").height) : 0; var x = getOffset(element).left; var y = getOffset(element).top + beforeHeight; var w = element.offsetWidth; var h = element.offsetHeight - beforeHeight - afterHeight; var eventX = this._getEventX(mouseEvent); var eventY = this._getEventY(mouseEvent); return eventX + dragEventDelta >= x && eventX - dragEventDelta < x + w && eventY + dragEventDelta >= y && eventY - dragEventDelta < y + h } _getEventX(e) { return isTouchEvent(e) ? this._getTouchEventX(e) : e.clientX + this._getDocumentScrollLeft() } _getEventY(e) { return isTouchEvent(e) ? this._getTouchEventY(e) : e.clientY + this._getDocumentScrollTop() } _getTouchEventX(e) { var touchPoint = null; if (e.changedTouches.length > 0) { touchPoint = e.changedTouches } else if (e.targetTouches.length > 0) { touchPoint = e.targetTouches } return touchPoint ? touchPoint[0].pageX : 0 } _getTouchEventY(e) { var touchPoint = null; if (e.changedTouches.length > 0) { touchPoint = e.changedTouches } else if (e.targetTouches.length > 0) { touchPoint = e.targetTouches } return touchPoint ? touchPoint[0].pageY : 0 } _getDocumentScrollTop() { var document = domAdapter.getDocument(); return document.documentElement.scrollTop || document.body.scrollTop } _getDocumentScrollLeft() { var document = domAdapter.getDocument(); return document.documentElement.scrollLeft || document.body.scrollLeft } _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) } _updateHoverState() { var _this$_selectButton, _this$_uploadButton; var value = this.option("hoverStateEnabled"); null === (_this$_selectButton = this._selectButton) || void 0 === _this$_selectButton ? void 0 : _this$_selectButton.option("hoverStateEnabled", value); null === (_this$_uploadButton = this._uploadButton) || void 0 === _this$_uploadButton ? void 0 : _this$_uploadButton.option("hoverStateEnabled", value); this._files.forEach(file => { var _file$uploadButton, _file$cancelButton2; null === (_file$uploadButton = file.uploadButton) || void 0 === _file$uploadButton ? void 0 : _file$uploadButton.option("hoverStateEnabled", value); null === (_file$cancelButton2 = file.cancelButton) || void 0 === _file$cancelButton2 ? void 0 : _file$cancelButton2.option("hoverStateEnabled", value) }) } _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.clear() } break; case "readOnly": this._updateReadOnlyState(); super._optionChanged(args); break; case "disabled": this._updateInputLabelText(); 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 "_buttonStylingMode": this._files.forEach(file => { var _file$uploadButton2, _file$cancelButton3; null === (_file$uploadButton2 = file.uploadButton) || void 0 === _file$uploadButton2 ? void 0 : _file$uploadButton2.option("stylingMode", value); null === (_file$cancelButton3 = file.cancelButton) || void 0 === _file$cancelButton3 ? void 0 : _file$cancelButton3.option("stylingMode", 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 "hoverStateEnabled": this._updateHoverState(); super._optionChanged(args); break; case "allowCanceling": case "uploadMode": this.clear(); 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; case "hint": this._initFileInput(); super._optionChanged(args); break; default: super._optionChanged(args) } } _resetInputValue(force) { if ("useForm" === this.option("uploadMode") && !force) { return } this._doPreventInputChange = true; this._$fileInput.val(""); this._doPreventInputChange = false } clear() { 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 Fil