UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,197 lines (1,195 loc) • 76.8 kB
/** * DevExtreme (cjs/__internal/ui/file_uploader.js) * Version: 25.1.5 * Build date: Wed Sep 03 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _events_engine = _interopRequireDefault(require("../../common/core/events/core/events_engine")); var _index = require("../../common/core/events/utils/index"); var _message = _interopRequireDefault(require("../../common/core/localization/message")); var _component_registrator = _interopRequireDefault(require("../../core/component_registrator")); var _devices = _interopRequireDefault(require("../../core/devices")); var _dom_adapter = _interopRequireDefault(require("../../core/dom_adapter")); var _guid = _interopRequireDefault(require("../../core/guid")); var _renderer = _interopRequireDefault(require("../../core/renderer")); var _ajax = _interopRequireDefault(require("../../core/utils/ajax")); var _callbacks = _interopRequireDefault(require("../../core/utils/callbacks")); var _deferred2 = require("../../core/utils/deferred"); var _extend = require("../../core/utils/extend"); var _size = require("../../core/utils/size"); var _type = require("../../core/utils/type"); var _window = require("../../core/utils/window"); var _button = _interopRequireDefault(require("../../ui/button")); var _progress_bar = _interopRequireDefault(require("../../ui/progress_bar")); var _themes = require("../../ui/themes"); var _m_deferred = require("../core/utils/m_deferred"); var _editor = _interopRequireDefault(require("../ui/editor/editor")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function(n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) { ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]) } } return n }, _extends.apply(null, arguments) } const window = (0, _window.getWindow)(); const FILEUPLOADER_CLASS = "dx-fileuploader"; const FILEUPLOADER_EMPTY_CLASS = "dx-fileuploader-empty"; const FILEUPLOADER_SHOW_FILE_LIST_CLASS = "dx-fileuploader-show-file-list"; const FILEUPLOADER_DRAGOVER_CLASS = "dx-fileuploader-dragover"; const FILEUPLOADER_WRAPPER_CLASS = "dx-fileuploader-wrapper"; const FILEUPLOADER_CONTAINER_CLASS = "dx-fileuploader-container"; const FILEUPLOADER_CONTENT_CLASS = "dx-fileuploader-content"; const FILEUPLOADER_INPUT_WRAPPER_CLASS = "dx-fileuploader-input-wrapper"; const FILEUPLOADER_INPUT_CONTAINER_CLASS = "dx-fileuploader-input-container"; const FILEUPLOADER_INPUT_LABEL_CLASS = "dx-fileuploader-input-label"; const FILEUPLOADER_INPUT_CLASS = "dx-fileuploader-input"; const FILEUPLOADER_FILES_CONTAINER_CLASS = "dx-fileuploader-files-container"; const FILEUPLOADER_FILE_CONTAINER_CLASS = "dx-fileuploader-file-container"; const FILEUPLOADER_FILE_INFO_CLASS = "dx-fileuploader-file-info"; const FILEUPLOADER_FILE_STATUS_MESSAGE_CLASS = "dx-fileuploader-file-status-message"; const FILEUPLOADER_FILE_CLASS = "dx-fileuploader-file"; const FILEUPLOADER_FILE_NAME_CLASS = "dx-fileuploader-file-name"; const FILEUPLOADER_FILE_SIZE_CLASS = "dx-fileuploader-file-size"; const FILEUPLOADER_BUTTON_CLASS = "dx-fileuploader-button"; const FILEUPLOADER_BUTTON_CONTAINER_CLASS = "dx-fileuploader-button-container"; const FILEUPLOADER_CANCEL_BUTTON_CLASS = "dx-fileuploader-cancel-button"; const FILEUPLOADER_UPLOAD_BUTTON_CLASS = "dx-fileuploader-upload-button"; const FILEUPLOADER_INVALID_CLASS = "dx-fileuploader-invalid"; const FILEUPLOADER_AFTER_LOAD_DELAY = 400; const FILEUPLOADER_CHUNK_META_DATA_NAME = "chunkMetadata"; const DRAG_EVENT_DELTA = 1; const DIALOG_TRIGGER_EVENT_NAMESPACE = "dxFileUploaderDialogTrigger"; const keyUpEventName = "keyup"; const nativeClickEvent = "click"; const ENTER_KEY = "enter"; const SPACE_KEY = "space"; let renderFileUploaderInput = () => (0, _renderer.default)("<input>").attr("type", "file"); const isFormDataSupported = () => !!window.FormData; class FileBlobReader { constructor(file, chunkSize) { this.file = file; this.chunkSize = chunkSize; this.index = 0 } read() { if (!this.file) { return null } const result = this.createBlobResult(this.file, this.index, this.chunkSize); if (result.isCompleted) { this.file = null } this.index += 1; return result } createBlobResult(file, index, chunkSize) { const 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 ("webkitSlice" in file && "function" === typeof file.webkitSlice) { return file.webkitSlice(startPos, startPos + length) } return null } } class FileUploadStrategyBase { constructor(fileUploader) { this.fileUploader = fileUploader } upload(file) { if (file.isInitialized && file.isAborted) { var _this$fileUploader; null === (_this$fileUploader = this.fileUploader) || void 0 === _this$fileUploader || _this$fileUploader._resetFileState(file) } if (file.isValid() && !file.uploadStarted) { this._prepareFileBeforeUpload(file); this._uploadCore(file) } } abortUpload(file) { var _file$request; if (file._isError || file._isLoaded || file.isAborted || !file.uploadStarted) { return } file.isAborted = true; null === (_file$request = file.request) || void 0 === _file$request || _file$request.abort(); if (this._isCustomCallback("abortUpload")) { const { abortUpload: abortUpload } = this.fileUploader.option(); const arg = this._createUploadArgument(file); let deferred = null; try { const result = null === abortUpload || void 0 === abortUpload ? void 0 : abortUpload(file.value, arg); deferred = (0, _m_deferred.fromPromise)(result) } catch (error) { deferred = (0, _deferred2.Deferred)().reject(error).promise() } if (deferred && "done" in deferred) { var _deferred; null === (_deferred = deferred) || void 0 === _deferred || _deferred.done((() => file.onAbort.fire())).fail((error => this._handleFileError(file, error))) } } } _beforeSend(xhr, file) { var _this$fileUploader$_b, _this$fileUploader2; const arg = this._createUploadArgument(file); null === (_this$fileUploader$_b = (_this$fileUploader2 = this.fileUploader)._beforeSendAction) || void 0 === _this$fileUploader$_b || _this$fileUploader$_b.call(_this$fileUploader2, { request: xhr, file: file.value, uploadInfo: arg }); file.request = xhr } _createUploadArgument(_file) { return { bytesUploaded: 0, chunkCount: 0, customData: {}, chunkBlob: new Blob, chunkIndex: 0 } } _uploadCore(_file) {} _isCustomCallback(name) { var _this$fileUploader3; const callback = null === (_this$fileUploader3 = this.fileUploader) || void 0 === _this$fileUploader3 ? void 0 : _this$fileUploader3.option(name); return callback && (0, _type.isFunction)(callback) } _handleProgress(file, e) { if (file._isError) { return } file._isProgressStarted = true; this._handleProgressCore(file, e) } _handleProgressCore(_file, _e) {} _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 || _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 } _shouldHandleError(file, e) { return (this._isStatusError(e.status) || !file._isProgressStarted) && !file.isAborted } _isStatusError(status) { return status >= 400 && status < 500 || status >= 500 && status < 600 } _onUploadStarted(file, e) { var _this$fileUploader4, _this$fileUploader4$_; file.uploadStarted = true; null === (_this$fileUploader4 = this.fileUploader) || void 0 === _this$fileUploader4 || null === (_this$fileUploader4$_ = _this$fileUploader4._uploadStartedAction) || void 0 === _this$fileUploader4$_ || _this$fileUploader4$_.call(_this$fileUploader4, { file: file.value, event: e, request: file.request }) } _onAbortHandler(file, e) { var _this$fileUploader5, _this$fileUploader$_u, _this$fileUploader6; const args = { file: file.value, event: e, request: file.request, message: null === (_this$fileUploader5 = this.fileUploader) || void 0 === _this$fileUploader5 ? void 0 : _this$fileUploader5._getUploadAbortedStatusMessage() }; null === (_this$fileUploader$_u = (_this$fileUploader6 = this.fileUploader)._uploadAbortedAction) || void 0 === _this$fileUploader$_u || _this$fileUploader$_u.call(_this$fileUploader6, args); this.fileUploader._setStatusMessage(file, args.message); this.fileUploader._handleAllFilesUploaded() } _onErrorHandler(file, error) { var _this$fileUploader$_u2, _this$fileUploader7, _this$fileUploader$_s, _this$fileUploader8; const { uploadFailedMessage: uploadFailedMessage } = this.fileUploader.option(); const args = { file: file.value, event: void 0, request: file.request, error: error, message: uploadFailedMessage }; null === (_this$fileUploader$_u2 = (_this$fileUploader7 = this.fileUploader)._uploadErrorAction) || void 0 === _this$fileUploader$_u2 || _this$fileUploader$_u2.call(_this$fileUploader7, args); null === (_this$fileUploader$_s = (_this$fileUploader8 = this.fileUploader)._setStatusMessage) || void 0 === _this$fileUploader$_s || _this$fileUploader$_s.call(_this$fileUploader8, file, args.message); this.fileUploader._handleAllFilesUploaded() } _onLoadedHandler(file, e) { var _this$fileUploader$_u3, _this$fileUploader9; const { uploadedMessage: uploadedMessage } = this.fileUploader.option(); const args = { file: file.value, event: e, request: file.request, message: uploadedMessage }; file._isLoaded = true; null === (_this$fileUploader$_u3 = (_this$fileUploader9 = this.fileUploader)._uploadedAction) || void 0 === _this$fileUploader$_u3 || _this$fileUploader$_u3.call(_this$fileUploader9, args); this.fileUploader._setStatusMessage(file, args.message); this.fileUploader._handleAllFilesUploaded() } _onProgressHandler(file, e) { if (file) { const totalFilesSize = this.fileUploader._getTotalFilesSize(); const totalLoadedFilesSize = this.fileUploader._getTotalLoadedFilesSize(); const loaded = e.loaded ?? 0; const loadedSize = Math.min(loaded, file.value.size); const 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) { const { uploadCustomData: formDataEntries } = this.fileUploader.option(); for (const entryName in formDataEntries) { if (Object.prototype.hasOwnProperty.call(formDataEntries, entryName) && (0, _type.isDefined)(formDataEntries[entryName])) { formData.append(entryName, formDataEntries[entryName]) } } } } class ChunksFileUploadStrategyBase extends FileUploadStrategyBase { constructor(fileUploader) { super(fileUploader); const { chunkSize: chunkSize } = this.fileUploader.option(); this.chunkSize = chunkSize ?? 0 } _uploadCore(file) { const realFile = file.value; const chunksData = { name: realFile.name, loadedBytes: 0, type: realFile.type, blobReader: new FileBlobReader(realFile, this.chunkSize), guid: new _guid.default, fileSize: realFile.size, count: this._getFileChunksCount(realFile), customData: {} }; file.chunksData = chunksData; this._sendChunk(file, chunksData) } _getFileChunksCount(jsFile) { return 0 === jsFile.size ? 1 : Math.ceil(jsFile.size / this.chunkSize) } _sendChunk(file, chunksData) { const chunk = chunksData.blobReader.read(); chunksData.currentChunk = chunk; if (chunk) { this._sendChunkCore(file, chunksData, chunk).done((() => { var _chunk$blob; if (file.isAborted) { return } chunksData.loadedBytes += (null === (_chunk$blob = chunk.blob) || void 0 === _chunk$blob ? void 0 : _chunk$blob.size) ?? 0; file.onProgress.fire({ loaded: chunksData.loadedBytes, total: file.value.size }); if (chunk.isCompleted) { file.onLoad.fire() } setTimeout((() => this._sendChunk(file, chunksData))) })).fail((error => { if (this._shouldHandleError(file, error)) { this._handleFileError(file, error) } })) } } _sendChunkCore(_file, _chunksData, _chunk) { return (0, _deferred2.Deferred)().reject() } _tryRaiseStartLoad(file) { if (!file.isStartLoad) { file.isStartLoad = true; file.onLoadStart.fire() } } _getEvent(_e) { return null } _createUploadArgument(file) { return this._createChunksInfo(file.chunksData) } _createChunksInfo(chunksData) { var _chunksData$currentCh, _chunksData$currentCh2; return { bytesUploaded: (null === chunksData || void 0 === chunksData ? void 0 : chunksData.loadedBytes) ?? 0, chunkCount: (null === chunksData || void 0 === chunksData ? void 0 : chunksData.count) ?? 0, customData: (null === chunksData || void 0 === chunksData ? void 0 : chunksData.customData) ?? {}, chunkBlob: (null === chunksData || void 0 === chunksData || null === (_chunksData$currentCh = chunksData.currentChunk) || void 0 === _chunksData$currentCh ? void 0 : _chunksData$currentCh.blob) ?? new Blob, chunkIndex: (null === chunksData || void 0 === chunksData || null === (_chunksData$currentCh2 = chunksData.currentChunk) || void 0 === _chunksData$currentCh2 ? void 0 : _chunksData$currentCh2.index) ?? 0 } } } class DefaultChunksFileUploadStrategy extends ChunksFileUploadStrategyBase { _sendChunkCore(file, chunksData, chunk) { const { uploadUrl: uploadUrl, uploadMethod: uploadMethod, uploadHeaders: uploadHeaders, name: name } = this.fileUploader.option(); return _ajax.default.sendRequest({ url: uploadUrl, method: uploadMethod, headers: uploadHeaders, beforeSend: xhr => this._beforeSend(xhr, file), upload: { onprogress: e => this._handleProgress(file, e), onloadstart: () => this._tryRaiseStartLoad(file), onabort: () => file.onAbort.fire() }, data: this._createFormData({ fileName: chunksData.name, blobName: name, blob: chunk.blob, index: chunk.index, count: chunksData.count, type: chunksData.type, guid: chunksData.guid, size: chunksData.fileSize }) }) } _createFormData(options) { const formData = new window.FormData; formData.append(options.blobName, options.blob); formData.append("chunkMetadata", JSON.stringify({ FileName: options.fileName, Index: options.index, TotalCount: options.count, FileSize: options.size, FileType: options.type, FileGuid: options.guid })); this._extendFormData(formData); return formData } } class CustomChunksFileUploadStrategy extends ChunksFileUploadStrategyBase { _sendChunkCore(file, chunksData) { this._tryRaiseStartLoad(file); const chunksInfo = this._createChunksInfo(chunksData); const { uploadChunk: uploadChunk } = this.fileUploader.option(); try { const result = null === uploadChunk || void 0 === uploadChunk ? void 0 : uploadChunk(file.value, chunksInfo); return (0, _m_deferred.fromPromise)(result) } catch (error) { return (0, _deferred2.Deferred)().reject(error).promise() } } _shouldHandleError(_file, _error) { return true } } class WholeFileUploadStrategyBase extends FileUploadStrategyBase { _uploadCore(file) { file.loadedSize = 0; const uploadFileDeferred = this._uploadFile(file); if ("done" in uploadFileDeferred) { uploadFileDeferred.done((() => { if (!file.isAborted) { file.onLoad.fire() } })).fail((error => { if (this._shouldHandleError(file, error)) { this._handleFileError(file, error) } })) } } _uploadFile(_file) { return (0, _deferred2.Deferred)().reject() } _handleProgressCore(file, e) { file.onProgress.fire(e) } _getLoadedData(loaded, total, segmentSize, event) { const result = super._getLoadedData(loaded, total, segmentSize, event); result.event = event; return result } } class DefaultWholeFileUploadStrategy extends WholeFileUploadStrategyBase { _uploadFile(file) { const { uploadUrl: uploadUrl, uploadMethod: uploadMethod, uploadHeaders: uploadHeaders, name: name } = this.fileUploader.option(); return _ajax.default.sendRequest({ url: uploadUrl, method: uploadMethod, headers: uploadHeaders, beforeSend: xhr => this._beforeSend(xhr, file), upload: { onprogress: e => this._handleProgress(file, e), onloadstart: () => file.onLoadStart.fire(), onabort: () => file.onAbort.fire() }, data: this._createFormData(name, file.value) }) } _createFormData(fieldName, fieldValue) { const formData = new window.FormData; formData.append(fieldName, fieldValue, null === fieldValue || void 0 === fieldValue ? void 0 : fieldValue.name); this._extendFormData(formData); return formData } } class CustomWholeFileUploadStrategy extends WholeFileUploadStrategyBase { _uploadFile(file) { file.onLoadStart.fire(); const progressCallback = loadedBytes => { const arg = { loaded: loadedBytes, total: file.value.size }; this._handleProgress(file, arg) }; const { uploadFile: uploadFile } = this.fileUploader.option(); try { const result = null === uploadFile || void 0 === uploadFile ? void 0 : uploadFile(file.value, progressCallback); return (0, _m_deferred.fromPromise)(result) } catch (error) { return (0, _deferred2.Deferred)().reject(error).promise() } } _shouldHandleError(_file, _e) { return true } } class FileUploader extends _editor.default { _supportedKeys() { const click = e => { e.preventDefault(); const $selectButton = this._selectButton.$element(); _events_engine.default.triggerHandler($selectButton, { type: "dxclick" }) }; return _extends({}, super._supportedKeys(), { space: click, enter: click }) } _setOptionsByReference() { super._setOptionsByReference(); (0, _extend.extend)(this._optionsByReference, { value: true }) } _getDefaultOptions() { return _extends({}, super._getDefaultOptions(), { chunkSize: 0, value: [], selectButtonText: _message.default.format("dxFileUploader-selectFile"), uploadButtonText: _message.default.format("dxFileUploader-upload"), labelText: _message.default.format("dxFileUploader-dropFile"), name: "files[]", multiple: false, accept: "", uploadUrl: "/", allowCanceling: true, showFileList: true, progress: 0, dialogTrigger: void 0, dropZone: void 0, readyToUploadMessage: _message.default.format("dxFileUploader-readyToUpload"), uploadedMessage: _message.default.format("dxFileUploader-uploaded"), uploadFailedMessage: _message.default.format("dxFileUploader-uploadFailedMessage"), uploadAbortedMessage: _message.default.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: _message.default.format("dxFileUploader-invalidFileExtension"), invalidMaxFileSizeMessage: _message.default.format("dxFileUploader-invalidMaxFileSize"), invalidMinFileSizeMessage: _message.default.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.default.real().deviceType && !_devices.default.isSimulator(), options: { focusStateEnabled: true } }, { device: [{ platform: "android" }], options: { validationMessageOffset: { v: 0 } } }, { device: () => "desktop" !== _devices.default.real().deviceType, options: { useDragOver: false, nativeDropSupported: false, labelText: "" } }, { device: () => !isFormDataSupported(), options: { uploadMode: "useForm" } }, { device: () => (0, _themes.isMaterial)((0, _themes.current)()), options: { _uploadButtonType: "default" } }, { device: () => (0, _themes.isFluent)((0, _themes.current)()), options: { _buttonStylingMode: "text" } }]) } _initOptions(options) { const 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() { const { chunkSize: chunkSize = 0 } = this.option(); if (chunkSize > 0) { const { uploadChunk: uploadChunk } = this.option(); this._uploadStrategy = uploadChunk && (0, _type.isFunction)(uploadChunk) ? new CustomChunksFileUploadStrategy(this) : new DefaultChunksFileUploadStrategy(this) } else { const { uploadFile: uploadFile } = this.option(); this._uploadStrategy = uploadFile && (0, _type.isFunction)(uploadFile) ? new CustomWholeFileUploadStrategy(this) : new DefaultWholeFileUploadStrategy(this) } } _initFileInput() { this._isCustomClickEvent = false; const { multiple: multiple, accept: accept, hint: hint } = this.option(); if (!this._$fileInput) { this._$fileInput = renderFileUploaderInput(); _events_engine.default.on(this._$fileInput, "change", this._inputChangeHandler.bind(this)); _events_engine.default.on(this._$fileInput, "click", (e => { e.stopPropagation(); this._resetInputValue(); const { useNativeInputClick: useNativeInputClick } = this.option(); return useNativeInputClick || this._isCustomClickEvent })) } const inputProps = { multiple: multiple, accept: accept, tabIndex: -1 }; if ((0, _type.isDefined)(hint)) { inputProps.title = hint } this._$fileInput.prop(inputProps) } _inputChangeHandler() { if (this._doPreventInputChange) { return } const fileName = this._$fileInput.val().replace(/^.*\\/, ""); const files = this._$fileInput.prop("files"); const { uploadMode: uploadMode } = this.option(); if (files && !files.length && "useForm" !== uploadMode) { return } const value = files ? this._getFiles(files) : [{ name: fileName }]; this._changeValue(value); if ("instantly" === uploadMode) { this._uploadFiles() } } _shouldFileListBeExtended() { const { uploadMode: uploadMode, extendSelection: extendSelection, multiple: multiple } = this.option(); return Boolean("useForm" !== uploadMode && extendSelection && multiple) } _changeValue(value) { const { value: currentValue } = this.option(); const files = this._shouldFileListBeExtended() ? null === currentValue || void 0 === currentValue ? void 0 : currentValue.slice() : []; this.option({ value: null === files || void 0 === files ? void 0 : files.concat(value) }) } _getFiles(fileList) { return [...fileList] } _getFile(fileData) { var _this$_files; const { value: value } = this.option(); const targetFileValue = (0, _type.isNumeric)(fileData) ? null === value || void 0 === value ? void 0 : value[fileData] : fileData; return null === (_this$_files = this._files) || void 0 === _this$_files ? void 0 : _this$_files.filter((file => file.value === targetFileValue))[0] } _initLabel() { if (!this._$inputLabel) { this._$inputLabel = (0, _renderer.default)("<div>") } this._updateInputLabelText() } _updateInputLabelText() { const { labelText: labelText } = this.option(); const correctedValue = this._isInteractionDisabled() ? "" : labelText; this._$inputLabel.text(correctedValue ?? "") } _focusTarget() { return this.$element().find(".dx-fileuploader-button") } _getSubmitElement() { return this._$fileInput } _initMarkup() { super._initMarkup(); this.$element().addClass("dx-fileuploader"); this._renderWrapper(); this._renderInputWrapper(); this._renderSelectButton(); this._renderInputContainer(); this._renderUploadButton(); this._preventRecreatingFiles = true; this._activeDropZone = null } _render() { const { dropZone: dropZone } = this.option(); this._preventRecreatingFiles = false; this._attachDragEventHandlers(this._$inputWrapper); this._attachDragEventHandlers(dropZone); this._renderFiles(); super._render() } _createFileProgressBar(file) { file.progressBar = this._createProgressBar(file.value.size); if (file.$file) { file.progressBar.$element().appendTo(file.$file) } this._initStatusMessage(file); this._ensureCancelButtonInitialized(file) } _setStatusMessage(file) { let message = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : ""; setTimeout((() => { const { showFileList: showFileList } = this.option(); if (showFileList) { if (file.$statusMessage) { var _file$progressBar2; file.$statusMessage.text(message); file.$statusMessage.css("display", ""); null === (_file$progressBar2 = file.progressBar) || void 0 === _file$progressBar2 || _file$progressBar2.$element().remove() } } }), 400) } _getUploadAbortedStatusMessage() { const { uploadMode: uploadMode, uploadAbortedMessage: uploadAbortedMessage, readyToUploadMessage: readyToUploadMessage } = this.option(); return "instantly" === uploadMode ? uploadAbortedMessage : readyToUploadMessage } _createFiles() { const { value: files } = this.option(); if (this._files && (0 === (null === files || void 0 === files ? void 0 : files.length) || !this._shouldFileListBeExtended())) { this._preventFilesUploading(this._files); this._files = null } if (!this._files) { this._files = [] } null === files || void 0 === files || files.slice(this._files.length).forEach((value => { var _this$_files2; const file = this._createFile(value); this._validateFile(file); null === (_this$_files2 = this._files) || void 0 === _this$_files2 || _this$_files2.push(file) })) } _preventFilesUploading(files) { null === files || void 0 === 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) { const { allowedFileExtensions: allowedFileExtensions } = this.option(); if (!(null !== allowedFileExtensions && void 0 !== allowedFileExtensions && allowedFileExtensions.length)) { return true } return this._isFileExtensionAllowed(file.value, allowedFileExtensions) } _validateMaxFileSize(file) { const fileSize = file.value.size; const { maxFileSize: maxFileSize = 0 } = this.option(); return maxFileSize > 0 ? fileSize <= maxFileSize : true } _validateMinFileSize(file) { const fileSize = file.value.size; const { minFileSize: minFileSize = 0 } = this.option(); return minFileSize > 0 ? fileSize >= minFileSize : true } _isFileExtensionAllowed(file, allowedExtensions) { for (let i = 0, n = allowedExtensions.length; i < n; i += 1) { let allowedExtension = allowedExtensions[i]; if (allowedExtension.startsWith(".")) { allowedExtension = allowedExtension.replace(".", "\\."); if (new RegExp(`${allowedExtension}$`, "i").exec(file.name)) { return true } } else { allowedExtension = allowedExtension.replace(new RegExp("\\*", "g"), ""); if (new RegExp(allowedExtension, "i").exec(file.type)) { return true } } } return false } _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: (0, _callbacks.default)(), onAbort: (0, _callbacks.default)(), onLoad: (0, _callbacks.default)(), onError: (0, _callbacks.default)(), onLoadStart: (0, _callbacks.default)(), isValidFileExtension: true, isValidMaxSize: true, isValidMinSize: true, isValid() { return Boolean(this.isValidFileExtension) && Boolean(this.isValidMaxSize) && Boolean(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; const { value: value, showFileList: showFileList } = this.option(); if (!this._$filesContainer) { this._$filesContainer = (0, _renderer.default)("<div>").addClass("dx-fileuploader-files-container").appendTo(this._$content) } else if (!this._shouldFileListBeExtended() || 0 === (null === value || void 0 === value ? void 0 : value.length)) { this._$filesContainer.empty() } if (showFileList) { var _this$_files3; null === (_this$_files3 = this._files) || void 0 === _this$_files3 || _this$_files3.forEach((file => { if (!file.$file) { this._renderFile(file) } })) } this.$element().toggleClass("dx-fileuploader-show-file-list", showFileList); this._toggleFileUploaderEmptyClassName(); this._updateFileNameMaxWidth(); null === (_this$_validationMess = this._validationMessage) || void 0 === _this$_validationMess || _this$_validationMess.repaint() } _renderFile(file) { const { value: value } = file; if (!this._$filesContainer) { return } const $fileContainer = (0, _renderer.default)("<div>").addClass("dx-fileuploader-file-container").appendTo(this._$filesContainer); this._renderFileButtons(file, $fileContainer); file.$file = (0, _renderer.default)("<div>").addClass("dx-fileuploader-file").appendTo($fileContainer); const $fileInfo = (0, _renderer.default)("<div>").addClass("dx-fileuploader-file-info").appendTo(file.$file); file.$statusMessage = (0, _renderer.default)("<div>").addClass("dx-fileuploader-file-status-message").appendTo(file.$file); (0, _renderer.default)("<div>").addClass("dx-fileuploader-file-name").text(value.name).appendTo($fileInfo); if ((0, _type.isDefined)(value.size)) { (0, _renderer.default)("<div>").addClass("dx-fileuploader-file-size").text(this._getFileSize(value.size)).appendTo($fileInfo) } if (file.isValid()) { const { readyToUploadMessage: readyToUploadMessage } = this.option(); file.$statusMessage.text(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("dx-fileuploader-invalid") } } _createValidationElement(key) { return (0, _renderer.default)("<span>").text(this.option()[key]) } _updateFileNameMaxWidth() { var _this$_$filesContaine, _this$_$filesContaine2, _this$_$filesContaine3, _this$_$filesContaine4; const { allowCanceling: allowCanceling, uploadMode: uploadMode } = this.option(); const cancelButtonsCount = allowCanceling && "useForm" !== uploadMode ? 1 : 0; const uploadButtonsCount = "useButtons" === uploadMode ? 1 : 0; const filesContainerWidth = (0, _size.getWidth)(null === (_this$_$filesContaine = this._$filesContainer) || void 0 === _this$_$filesContaine ? void 0 : _this$_$filesContaine.find(".dx-fileuploader-file-container").first()) || (0, _size.getWidth)(this._$filesContainer); const $buttonContainer = null === (_this$_$filesContaine2 = this._$filesContainer) || void 0 === _this$_$filesContaine2 ? void 0 : _this$_$filesContaine2.find(".dx-fileuploader-button-container").eq(0); const buttonsWidth = (0, _size.getWidth)($buttonContainer) * (cancelButtonsCount + uploadButtonsCount); const $fileSize = null === (_this$_$filesContaine3 = this._$filesContainer) || void 0 === _this$_$filesContaine3 ? void 0 : _this$_$filesContaine3.find(".dx-fileuploader-file-size").eq(0); const prevFileSize = null === $fileSize || void 0 === $fileSize ? void 0 : $fileSize.text(); null === $fileSize || void 0 === $fileSize || $fileSize.text("1000 Mb"); const fileSizeWidth = (0, _size.getWidth)($fileSize); null === $fileSize || void 0 === $fileSize || $fileSize.text(prevFileSize ?? ""); null === (_this$_$filesContaine4 = this._$filesContainer) || void 0 === _this$_$filesContaine4 || _this$_$filesContaine4.find(".dx-fileuploader-file-name").css("maxWidth", filesContainerWidth - buttonsWidth - fileSizeWidth) } _renderFileButtons(file, $container) { const $cancelButton = this._getCancelButton(file); if ($cancelButton) { $container.append($cancelButton) } const $uploadButton = this._getUploadButton(file); if ($uploadButton) { $container.append($uploadButton) } } _getCancelButton(file) { const { uploadMode: uploadMode } = this.option(); if ("useForm" === uploadMode) { return null } const { allowCanceling: allowCanceling, readOnly: readOnly, hoverStateEnabled: hoverStateEnabled, _buttonStylingMode: _buttonStylingMode } = this.option(); file.cancelButton = this._createComponent((0, _renderer.default)("<div>").addClass("dx-fileuploader-button dx-fileuploader-cancel-button"), _button.default, { onClick: () => { this._removeFile(file) }, icon: "close", visible: allowCanceling, disabled: readOnly, integrationOptions: {}, hoverStateEnabled: hoverStateEnabled, stylingMode: _buttonStylingMode }); return (0, _renderer.default)("<div>").addClass("dx-fileuploader-button-container").append(file.cancelButton.$element()) } _getUploadButton(file) { const { uploadMode: uploadMode } = this.option(); if (!file.isValid() || "useButtons" !== uploadMode) { return null } const { hoverStateEnabled: hoverStateEnabled, _buttonStylingMode: _buttonStylingMode } = this.option(); file.uploadButton = this._createComponent((0, _renderer.default)("<div>").addClass("dx-fileuploader-button dx-fileuploader-upload-button"), _button.default, { onClick: () => this._uploadFile(file), icon: "upload", hoverStateEnabled: hoverStateEnabled, stylingMode: _buttonStylingMode }); file.onLoadStart.add((() => { var _file$uploadButton; return null === (_file$uploadButton = file.uploadButton) || void 0 === _file$uploadButton ? void 0 : _file$uploadButton.option({ visible: false, disabled: true }) })); file.onAbort.add((() => { var _file$uploadButton2; return null === (_file$uploadButton2 = file.uploadButton) || void 0 === _file$uploadButton2 ? void 0 : _file$uploadButton2.option({ visible: true, disabled: false }) })); return (0, _renderer.default)("<div>").addClass("dx-fileuploader-button-container").append(file.uploadButton.$element()) } _removeFile(file) { var _file$$file, _this$_files4; null === (_file$$file = file.$file) || void 0 === _file$$file || _file$$file.parent().remove(); null === (_this$_files4 = this._files) || void 0 === _this$_files4 || _this$_files4.splice(this._files.indexOf(file), 1); const { value: value } = this.option(); const valueCopy = null === value || void 0 === value ? void 0 : value.slice(); null === valueCopy || void 0 === valueCopy || valueCopy.splice(valueCopy.indexOf(file.value), 1); this._preventRecreatingFiles = true; this.option({ value: valueCopy }); this._preventRecreatingFiles = false; this._toggleFileUploaderEmptyClassName(); this._resetInputValue(true) } removeFile(fileData) { const { uploadMode: uploadMode } = this.option(); if ("useForm" === uploadMode || !(0, _type.isDefined)(fileData)) { return } const file = this._getFile(fileData); if (file) { if (file.uploadStarted) { this._preventFilesUploading([file]) } this._removeFile(file) } } _toggleFileUploaderEmptyClassName() { var _this$_files5; this.$element().toggleClass("dx-fileuploader-empty", !(null !== (_this$_files5 = this._files) && void 0 !== _this$_files5 && _this$_files5.length) || this._hasInvalidFile(this._files)) } _hasInvalidFile(files) { return files.some((file => !file.isValid())) } _getFileSize(size) { const labels = [_message.default.format("dxFileUploader-bytes"), _message.default.format("dxFileUploader-kb"), _message.default.format("dxFileUploader-Mb"), _message.default.format("dxFileUploader-Gb")]; const count = labels.length - 1; let value = size; let i = 0; while (i < count && value >= 1024) { value /= 1024; i += 1 } return `${Math.round(value)} ${labels[i]}` } _renderSelectButton() { const $button = (0, _renderer.default)("<div>").addClass("dx-fileuploader-button").appendTo(this._$inputWrapper); const { selectButtonText: selectButtonText, readOnly: readOnly, hoverStateEnabled: hoverStateEnabled } = this.option(); this._selectButton = this._createComponent($button, _button.default, { text: selectButtonText, focusStateEnabled: false, integrationOptions: {}, disabled: readOnly, hoverStateEnabled: hoverStateEnabled }); if ("desktop" === _devices.default.real().deviceType) { this._selectButton.option({ onClick: () => this._selectFileDialogClickHandler() }) } else { this._attachSelectFileDialogHandlers(this._selectButton.$element()) } const { dialogTrigger: dialogTrigger } = this.option(); this._attachSelectFileDialogHandlers(dialogTrigger) } _selectFileDialogClickHandler() { const { useNativeInputClick: useNativeInputClick } = this.option(); if (useNativeInputClick || this._isInteractionDisabled()) { return } this._isCustomClickEvent = true; _events_engine.default.triggerHandler(this._$fileInput, { type: "click" }); this._isCustomClickEvent = false }