devextreme
Version:
JavaScript/TypeScript Component Suite for Responsive Web Development
1,206 lines • 63.6 kB
JavaScript
/**
* DevExtreme (esm/__internal/ui/file_uploader/file_uploader.js)
* Version: 25.2.5
* Build date: Fri Feb 20 2026
*
* Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import eventsEngine from "../../../common/core/events/core/events_engine";
import {
addNamespace,
isTouchEvent,
normalizeKeyName
} from "../../../common/core/events/utils/index";
import messageLocalization from "../../../common/core/localization/message";
import registerComponent from "../../../core/component_registrator";
import devices from "../../../core/devices";
import domAdapter from "../../../core/dom_adapter";
import Guid from "../../../core/guid";
import $ from "../../../core/renderer";
import Callbacks from "../../../core/utils/callbacks";
import {
extend
} from "../../../core/utils/extend";
import {
getOffset,
getOuterWidth,
getWidth
} from "../../../core/utils/size";
import {
isDefined,
isFunction,
isNumeric
} from "../../../core/utils/type";
import {
getWindow
} from "../../../core/utils/window";
import Button from "../../../ui/button";
import ProgressBar from "../../../ui/progress_bar";
import {
current,
isFluent,
isMaterial
} from "../../../ui/themes";
import {
ICON_CLASS
} from "../../core/utils/m_icon";
import Editor from "../../ui/editor/editor";
import {
CustomChunksFileUploadStrategy
} from "../../ui/file_uploader/file_upload_strategy.chunks.custom";
import {
DefaultChunksFileUploadStrategy
} from "../../ui/file_uploader/file_upload_strategy.chunks.default";
import {
CustomWholeFileUploadStrategy
} from "../../ui/file_uploader/file_upload_strategy.whole.custom";
import {
DefaultWholeFileUploadStrategy
} from "../../ui/file_uploader/file_upload_strategy.whole.default";
import {
getFileIconName,
getFileSize
} from "../../ui/file_uploader/file_uploader.utils";
const window = getWindow();
export 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_FILE_ICON_CLASS = "dx-fileuploader-file-icon";
const FILEUPLOADER_BUTTON_CLASS = "dx-fileuploader-button";
const FILEUPLOADER_BUTTON_CONTAINER_CLASS = "dx-fileuploader-button-container";
export const FILEUPLOADER_CANCEL_BUTTON_CLASS = "dx-fileuploader-cancel-button";
export const FILEUPLOADER_CANCEL_BUTTON_POSITION_END_CLASS = "dx-fileuploader-cancel-button-position-end";
const FILEUPLOADER_UPLOAD_BUTTON_CLASS = "dx-fileuploader-upload-button";
const FILEUPLOADER_INVALID_CLASS = "dx-fileuploader-invalid";
const FILEUPLOADER_AFTER_LOAD_DELAY = 400;
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 = () => $("<input>").attr("type", "file");
const isFormDataSupported = () => !!window.FormData;
class FileUploader extends Editor {
_supportedKeys() {
const click = e => {
e.preventDefault();
const $selectButton = this._selectButton.$element();
eventsEngine.triggerHandler($selectButton, {
type: "dxclick"
})
};
return Object.assign({}, super._supportedKeys(), {
space: click,
enter: click
})
}
_setOptionsByReference() {
super._setOptionsByReference();
extend(this._optionsByReference, {
value: true
})
}
_getDefaultOptions() {
return Object.assign({}, 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,
onFileValidationError: null,
onProgress: null,
onUploadError: null,
onUploadAborted: null,
onDropZoneEnter: null,
onDropZoneLeave: null,
onCancelButtonClick: null,
onFileLimitReached: void 0,
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",
_hideCancelButtonOnUpload: true,
_showFileIcon: false,
_cancelButtonPosition: "start",
_maxFileCount: void 0
})
}
_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,
nativeDropSupported: false,
labelText: ""
}
}, {
device: () => !isFormDataSupported(),
options: {
uploadMode: "useForm"
}
}, {
device: () => isMaterial(current()),
options: {
_uploadButtonType: "default"
}
}, {
device: () => isFluent(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._createFileLimitReachedAction();
this._createFiles();
this._createBeforeSendAction();
this._createUploadStartedAction();
this._createUploadedAction();
this._createFilesUploadedAction();
this._createFileValidationErrorAction();
this._createProgressAction();
this._createUploadErrorAction();
this._createUploadAbortedAction();
this._createDropZoneEnterAction();
this._createDropZoneLeaveAction();
this._createCancelButtonClickAction()
}
_setUploadStrategy() {
const {
chunkSize: chunkSize = 0
} = this.option();
if (chunkSize > 0) {
const {
uploadChunk: uploadChunk
} = this.option();
this._uploadStrategy = uploadChunk && isFunction(uploadChunk) ? new CustomChunksFileUploadStrategy(this) : new DefaultChunksFileUploadStrategy(this)
} else {
const {
uploadFile: uploadFile
} = this.option();
this._uploadStrategy = uploadFile && 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();
eventsEngine.on(this._$fileInput, "change", (() => {
this._inputChangeHandler()
}));
eventsEngine.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 (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
}
if (this._isFileLimitReached(files)) {
var _this$_fileLimitReach;
null === (_this$_fileLimitReach = this._fileLimitReachedAction) || void 0 === _this$_fileLimitReach || _this$_fileLimitReach.call(this);
return
}
const value = files ? this._getFiles(files) : [{
name: fileName
}];
this._changeValue(value);
if ("instantly" === uploadMode) {
this._uploadFiles()
}
}
_isFileLimitReached() {
let files = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : [];
const {
_maxFileCount: _maxFileCount,
value: value
} = this.option();
if (void 0 === _maxFileCount) {
return false
}
const totalCount = files.length + ((null === value || void 0 === value ? void 0 : value.length) ?? 0);
const isFileLimitReached = totalCount > _maxFileCount;
return isFileLimitReached
}
_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 = 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 = $("<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$progressBar;
file.$statusMessage.text(message);
file.$statusMessage.css("display", "");
null === (_file$progressBar = file.progressBar) || void 0 === _file$progressBar || _file$progressBar.$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._isFileLimitReached()) {
var _this$_fileLimitReach2;
null === (_this$_fileLimitReach2 = this._fileLimitReachedAction) || void 0 === _this$_fileLimitReach2 || _this$_fileLimitReach2.call(this)
}
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"]
})
}
_createFileValidationErrorAction() {
this._fileValidationErrorAction = this._createActionByOption("onFileValidationError", {
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")
}
_createCancelButtonClickAction() {
this._cancelButtonClickAction = this._createActionByOption("onCancelButtonClick", {
excludeValidators: ["readOnly"]
})
}
_createFileLimitReachedAction() {
this._fileLimitReachedAction = this._createActionByOption("onFileLimitReached", {
excludeValidators: ["readOnly"]
})
}
_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 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$_files4, _this$_validationMess;
const {
value: value,
showFileList: showFileList
} = this.option();
if (!this._$filesContainer) {
this._$filesContainer = $("<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._toggleFileContainerAria(Boolean(showFileList && (null === (_this$_files4 = this._files) || void 0 === _this$_files4 ? void 0 : _this$_files4.length)));
this._toggleFileUploaderEmptyClassName();
this._updateFileNameMaxWidth();
null === (_this$_validationMess = this._validationMessage) || void 0 === _this$_validationMess || _this$_validationMess.repaint()
}
_toggleFileContainerAria(applyAria) {
var _this$_$filesContaine;
const aria = {
role: applyAria ? "list" : null,
"aria-label": applyAria ? messageLocalization.format("dxFileUploader-fileListLabel") : null
};
null === (_this$_$filesContaine = this._$filesContainer) || void 0 === _this$_$filesContaine || _this$_$filesContaine.attr(aria)
}
_renderFile(file) {
const {
value: value
} = file;
if (!this._$filesContainer) {
return
}
const $fileContainer = $("<div>").addClass("dx-fileuploader-file-container").appendTo(this._$filesContainer).attr("role", "listitem");
this._renderFileIcon(value.name, $fileContainer);
file.$file = $("<div>").addClass("dx-fileuploader-file").appendTo($fileContainer);
const $fileInfo = $("<div>").addClass("dx-fileuploader-file-info").appendTo(file.$file);
file.$statusMessage = $("<div>").addClass("dx-fileuploader-file-status-message").appendTo(file.$file);
$("<div>").addClass("dx-fileuploader-file-name").text(value.name).attr("title", value.name).appendTo($fileInfo);
if (isDefined(value.size)) {
$("<div>").addClass("dx-fileuploader-file-size").text(getFileSize(value.size)).appendTo($fileInfo)
}
this._renderFileButtons(file, $fileContainer);
if (file.isValid()) {
const {
readyToUploadMessage: readyToUploadMessage
} = this.option();
file.$statusMessage.text(readyToUploadMessage ?? "")
} else {
var _this$_fileValidation;
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"))
}
null === (_this$_fileValidation = this._fileValidationErrorAction) || void 0 === _this$_fileValidation || _this$_fileValidation.call(this, {
file: file.value
});
$fileContainer.addClass("dx-fileuploader-invalid")
}
}
_createValidationElement(key) {
return $("<span>").text(this.option()[key])
}
_updateFileNameMaxWidth() {
var _this$_$filesContaine2, _this$_$filesContaine3, _this$_$filesContaine4, _this$_$filesContaine5, _this$_$filesContaine6;
const {
allowCanceling: allowCanceling,
uploadMode: uploadMode,
_showFileIcon: _showFileIcon
} = this.option();
const cancelButtonsCount = allowCanceling && "useForm" !== uploadMode ? 1 : 0;
const uploadButtonsCount = "useButtons" === uploadMode ? 1 : 0;
const filesContainerWidth = getWidth(null === (_this$_$filesContaine2 = this._$filesContainer) || void 0 === _this$_$filesContaine2 ? void 0 : _this$_$filesContaine2.find(".dx-fileuploader-file-container").first()) || getWidth(this._$filesContainer);
const $buttonContainer = null === (_this$_$filesContaine3 = this._$filesContainer) || void 0 === _this$_$filesContaine3 ? void 0 : _this$_$filesContaine3.find(".dx-fileuploader-button-container").eq(0);
const buttonsWidth = getWidth($buttonContainer) * (cancelButtonsCount + uploadButtonsCount);
const $fileSize = null === (_this$_$filesContaine4 = this._$filesContainer) || void 0 === _this$_$filesContaine4 ? void 0 : _this$_$filesContaine4.find(".dx-fileuploader-file-size").eq(0);
const $icon = null === (_this$_$filesContaine5 = this._$filesContainer) || void 0 === _this$_$filesContaine5 ? void 0 : _this$_$filesContaine5.find(".dx-fileuploader-file-icon").eq(0);
const iconWidth = _showFileIcon ? getOuterWidth($icon) : 0;
const prevFileSize = null === $fileSize || void 0 === $fileSize ? void 0 : $fileSize.text();
null === $fileSize || void 0 === $fileSize || $fileSize.text("1000 Mb");
const fileSizeWidth = getWidth($fileSize);
null === $fileSize || void 0 === $fileSize || $fileSize.text(prevFileSize ?? "");
const maxWidth = filesContainerWidth - buttonsWidth - fileSizeWidth - iconWidth;
null === (_this$_$filesContaine6 = this._$filesContainer) || void 0 === _this$_$filesContaine6 || _this$_$filesContaine6.find(".dx-fileuploader-file-name").css("maxWidth", maxWidth)
}
_renderFileButtons(file, $container) {
const {
_cancelButtonPosition: _cancelButtonPosition
} = this.option();
const $uploadButton = this._getUploadButton(file);
if ($uploadButton) {
$container.prepend($uploadButton)
}
const $cancelButton = this._getCancelButton(file);
if ($cancelButton) {
if ("end" === _cancelButtonPosition) {
$container.append($cancelButton);
return
}
$container.prepend($cancelButton)
}
}
_renderFileIcon(fileName, $container) {
const {
_showFileIcon: _showFileIcon
} = this.option();
if (!_showFileIcon) {
return
}
$("<div>").addClass(`dx-fileuploader-file-icon ${ICON_CLASS} ${ICON_CLASS}-${getFileIconName(fileName)}`).appendTo($container)
}
_getCancelButton(file) {
var _file$value;
const {
uploadMode: uploadMode,
_cancelButtonPosition: _cancelButtonPosition
} = this.option();
if ("useForm" === uploadMode) {
return null
}
const {
allowCanceling: allowCanceling,
readOnly: readOnly,
hoverStateEnabled: hoverStateEnabled,
_buttonStylingMode: _buttonStylingMode
} = this.option();
file.cancelButton = this._createComponent($("<div>").addClass("dx-fileuploader-button dx-fileuploader-cancel-button"), Button, {
onClick: () => {
var _this$_cancelButtonCl;
this._removeFile(file);
null === (_this$_cancelButtonCl = this._cancelButtonClickAction) || void 0 === _this$_cancelButtonCl || _this$_cancelButtonCl.call(this, {
file: file.value
})
},
icon: "close",
visible: allowCanceling,
disabled: readOnly,
integrationOptions: {},
hoverStateEnabled: hoverStateEnabled,
stylingMode: _buttonStylingMode,
elementAttr: {
"aria-label": messageLocalization.format("dxFileUploader-removeFileButtonLabel", (null === file || void 0 === file || null === (_file$value = file.value) || void 0 === _file$value ? void 0 : _file$value.name) ?? "")
}
});
if ("end" === _cancelButtonPosition) {
file.cancelButton.$element().addClass("dx-fileuploader-cancel-button-position-end")
}
return $("<div>").addClass("dx-fileuploader-button-container").append(file.cancelButton.$element())
}
_getUploadButton(file) {
var _file$value2;
const {
uploadMode: uploadMode
} = this.option();
if (!file.isValid() || "useButtons" !== uploadMode) {
return null
}
const {
hoverStateEnabled: hoverStateEnabled,
_buttonStylingMode: _buttonStylingMode
} = this.option();
file.uploadButton = this._createComponent($("<div>").addClass("dx-fileuploader-button dx-fileuploader-upload-button"), Button, {
onClick: () => this._uploadFile(file),
icon: "upload",
hoverStateEnabled: hoverStateEnabled,
stylingMode: _buttonStylingMode,
elementAttr: {
"aria-label": messageLocalization.format("dxFileUploader-uploadFileButtonLabel", (null === file || void 0 === file || null === (_file$value2 = file.value) || void 0 === _file$value2 ? void 0 : _file$value2.name) ?? "")
}
});
file.onLoadStart.add((() => {
var _file$uploadButton;
null === (_file$uploadButton = file.uploadButton) || void 0 === _file$uploadButton || _file$uploadButton.option({
visible: false,
disabled: true
})
}));
file.onAbort.add((() => {
var _file$uploadButton2;
null === (_file$uploadButton2 = file.uploadButton) || void 0 === _file$uploadButton2 || _file$uploadButton2.option({
visible: true,
disabled: false
})
}));
return $("<div>").addClass("dx-fileuploader-button-container").append(file.uploadButton.$element())
}
_removeFile(file) {
var _file$$file, _this$_files5, _this$_files6;
null === (_file$$file = file.$file) || void 0 === _file$$file || _file$$file.parent().remove();
null === (_this$_files5 = this._files) || void 0 === _this$_files5 || _this$_files5.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;
if (0 === (null === (_this$_files6 = this._files) || void 0 === _this$_files6 ? void 0 : _this$_files6.length)) {
this._toggleFileContainerAria(false)
}
this._toggleFileUploaderEmptyClassName();
this._resetInputValue(true)
}
removeFile(fileData) {
const {
uploadMode: uploadMode
} = this.option();
if ("useForm" === uploadMode || !isDefined(fileData)) {
return
}
const file = this._getFile(fileData);
if (file) {
if (file.uploadStarted) {
this._preventFilesUploading([file])
}
this._removeFile(file)
}
}
_toggleFileUploaderEmptyClassName() {
var _this$_files7;
this.$element().toggleClass("dx-fileuploader-empty", !(null !== (_this$_files7 = this._files) && void 0 !== _this$_files7 && _this$_files7.length) || this._hasInvalidFile(this._files))
}
_hasInvalidFile(files) {
return files.some((file => !file.isValid()))
}
_renderSelectButton() {
const $button = $("<div>").addClass("dx-fileuploader-button").appendTo(this._$inputWrapper);
const {
selectButtonText: selectButtonText,
readOnly: readOnly,
hoverStateEnabled: hoverStateEnabled
} = this.option();
this._selectButton = this._createComponent($button, Button, {
text: selectButtonText,
focusStateEnabled: false,
integrationOptions: {},
disabled: readOnly,
hoverStateEnabled: hoverStateEnabled
});
if ("desktop" === devices.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;
eventsEngine.trigger(this._$fileInput, "click");
this._isCustomClickEvent = false
}
_attachSelectFileDialogHandlers(target) {
if (!isDefined(target)) {
return
}
this._detachSelectFileDialogHandlers(target);
const $target = $(target);
eventsEngine.on($target, addNamespace("click", "dxFileUploaderDialogTrigger"), (() => {
this._selectFileDialogClickHandler()
}));
eventsEngine.on($target, addNamespace("keyup", "dxFileUploaderDialogTrigger"), (e => {
const normalizedKeyName = normalizeKeyName(e);
if ("enter" === normalizedKeyName || "space" === normalizedKeyName) {
this._selectFileDialogClickHandler()
}
}))
}
_detachSelectFileDialogHandlers(target) {
if (!isDefined(target)) {
return
}
const $target = $(target);
eventsEngine.off($target, ".dxFileUploaderDialogTrigger")
}
_renderUploadButton() {
const {
uploadButtonText: uploadButtonText,
_uploadButtonType: _uploadButtonType,
hoverStateEnabled: hoverStateEnabled,
uploadMode: uploadMode
} = this.option();
if ("useButtons" !== uploadMode) {
return
}
const $uploadButton = $("<div>").addClass("dx-fileuploader-button").addClass("dx-fileuploader-upload-button").appendTo(this._$content);
this._uploadButton = this._createComponent($uploadButton, Button, {
text: uploadButtonText,
onClick: this._uploadButtonClickHandler.bind(this),
type: _uploadButtonType,
integrationOptions: {},
hoverStateEnabled: hoverStateEnabled
})
}
_uploadButtonClickHandler() {
this._uploadFiles()
}
_shouldDragOverBeRendered() {
const {
readOnly: readOnly,
uploadMode: uploadMode,
nativeDropSupported: nativeDropSupported
} = this.option();
return !readOnly && ("useForm" !== uploadMode || nativeDropSupported)
}
_isInteractionDisabled() {
const {
readOnly: readOnly,
disabled: disabled
} = this.option();
return Boolean(readOnly) || Boolean(disabled)
}
_renderInputContainer() {
this._$inputContainer = $("<div>").addClass("dx-fileuploader-input-container").appendTo(this._$inputWrapper);
this._renderInput();
this._$fileInput.addClass("dx-fileuploader-input");
const labelId = `dx-fileuploader-input-label-${new Guid}`;
this._$inputLabel.attr("id", labelId).addClass("dx-fileuploader-input-label").appendTo(this._$inputContainer);
this.setAria("labelledby", labelId, this._$fileInput)
}
_renderInput() {
const {
useNativeInputClick: useNativeInputClick,
inputAttr: inputAttr
} = this.option();
if (useNativeInputClick) {
this._selectButton.option({
template: this._selectButtonInputTemplate.bind(this)
})
} else {
this._$fileInput.appendTo(this._$inputContainer);
this._selectButton.option({
template: "content"
})
}
this._applyInputAttributes(inputAttr)
}
_selectButtonInputTemplate(data, content) {
const $content = $(content);
const $text = $("<span>").addClass("dx-button-text").text(data.text);
$content.append($text).append(this._$fileInput);
return $content
}
_renderInputWrapper() {
if (!this._$content) {
return
}
this._$inputWrapper = $("<div>").addClass("dx-fileuploader-input-wrapper").appendTo(this._$content)
}
_detachDragEventHandlers(target) {
if (!target) {
return
}
eventsEngine.off($(target), addNamespace("", this.NAME))
}
_attachDragEventHandlers(target) {
const isCustomTarget = target !== this._$inputWrapper;
if (!isDefined(target) || !this._shouldDragOverBeRendered()) {
return
}
this._detachDragEventHandlers(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() {
const {
uploadMode: uploadMode,
nativeDropSupported: nativeDropSupported
} = this.option();
return Boolean(nativeDropSupported) && "useForm" === uploadMode
}
_getDropZoneElement(isCustomTarget, e) {
if (!e.currentTarget) {
return
}
const {
dropZone: dropZone
} = this.option();
const targetList = isCustomTarget ? $(dropZone).toArray() : [this._$inputWrapper];
const targetListElements = targetList.map((element => $(element).get(0)));
const currentTargetIndex = targetListElements.indexOf(e.currentTarget);
return targetListElements[currentTargetIndex]
}
_dragEnterHandler(isCustomTarget, e) {
const {
disabled: disabled
} = this.option();
if (disabled) {
return false
}
if (!this._useInputForDrop()) {
e.preventDefault()
}
const 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) {
const 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, -1)
}
_tryToggleDropZoneActive(active, isCustom, event) {
var _this$mouseAction;
const classAction = active ? "addClass" : "removeClass";
const mouseAction = active ? "_dropZoneEnterAction" : "_dropZoneLeaveAction";
null === (_this$mouseAction = this[mouseAction]) || void 0 === _this$mouseAction || _this$mouseAction.call(this, {
event: event,
dropZoneElement: this._activeDropZone
});
if (!isCustom) {
this.$element()[classAction]("dx-fileuploader-dragover")
}
}
_dropHandler(isCustomTarget, e) {
this._activeDropZone = null;
if (!isCustomTarget) {
this.$element().removeClass("dx-fileuploader-dragover")
}
if (this._useInputForDrop() || isCustomTarget && this._isInteractionDisabled()) {
return
}
e.preventDefault();
const fileList = e.originalEvent.dataTransfer.files;
const files = this._getFiles(fileList);
const {
multiple: multiple,
uploadMode: uploadMode
} = this.option();
if (!multiple && files.length > 1 || 0 === files.length) {
return
}
if (this._isFileLimitReached(files)) {
var _this$_fileLimitReach3;
null === (_this$_fileLimitReach3 = this._fileLimitReachedAction) || void 0 === _this$_fileLimitReach3 || _this$_fileLimitReach3.call(this);
return
}
this._changeValue(files);
if ("instantly" === uploadMode) {
this._uploadFiles()
}
}
_areAllFilesLoaded() {
var _this$_files8;
return null === (_this$_files8 = this._files) || void 0 === _this$_files8 ? void 0 : _this$_files8.every((file => !file.isValid() || file._isError || file._isLoaded || file.isAborted))
}
_handleAllFilesUploaded() {
this._recalculateProgress();
if (this._areAllFilesLoaded()) {
var _this$_filesUploadedA;
null === (_this$_filesUploadedA = this._filesUploadedAction) || void 0 === _this$_filesUploadedA || _this$_filesUploadedA.call(this)
}
}
_renderWrapper() {
const $wrapper = $("<div>").addClass("dx-fileuploader-wrapper").appendTo(this.$element());
const $container = $("<div>").addClass("dx-fileuploader-container").appendTo($wrapper);
this._$content = $("<div>").addClass("dx-fileuploader-content").appendTo($container)
}
_clean() {
this._$fileInput.detach();
this._$filesContainer = null;
const {
dialogTrigger: dialogTrigger,
dropZone: dropZone
} = this.option();
this._detachSelectFileDialogHandlers(dialogTrigger);
this._detachDragEventHandlers(dropZone);
if (this._files) {
this._files.forEach((file => {
file.$file = null;
file.$statusMessage = null
}))
}
super._clean()
}
abortUpload(fileData) {
const {
uploadMode: uploadMode
} = this.option();
if ("useForm" === uploadMode) {
return
}
if (isDefined(fileData)) {
const file = this._getFile(fileData);
if (file) {
this._preventFilesUploading([file])
}
} else {
this._preventFilesUploading(this._files)
}
}
upload(fileData) {
const {
uploadMode: uploadMode
} = this.option();
if ("useForm" === uploadMode) {
return
}
if (isDefined(fileData)) {
const file = this._getFile(fileData);
if (file && isFormDataSupported()) {
this._uploadFile(file)
}
} else {
this._uploadFiles()
}
}
_uploadFiles() {
if (isFormDataSupported()) {
var _this$_files9;
null === (_this$_files9 = this._files) || void 0 === _this$_files9 || _this$_files9.forEach((file => this._uploadFile(file)))
}
}
_uploadFile(file) {
this._uploadStrategy.upload(file)
}
_updateProgressBar(file, loadedFileData) {
var _file$progressBar2, _this$_progressAction;
null === (_file$progressBar2 = file.progressBar) || void 0 === _file$progressBar2 || _file$progressBar2.option({
value: loadedFileData.loaded,
showStatus: true
});
null === (_this$_progressAction = this._progressAction) || void 0 === _this$_progressAction || _this$_progressAction.call(this, {
file: file.value,
segmentSize: loadedFileData.currentSegmentSize,
bytesLoaded: loadedFileData.loaded,
bytesTotal: loadedFileData.total,
event: loadedFileData.event,
request: file.request
})
}
_updateTotalProgress(totalFilesSize, totalLoadedFilesSize) {
let progress = 0;
if (isDefined(totalFilesSize)) {
if (this._files && 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) {
var _file$$statusMessage;
null === (_file$$statusMessage = file.$statusMessage) || void 0 === _file$$statusMessage || _file$$statusMessage.css("display", "none")
}
_ensureCancelButtonInitialized(file) {
var _file$cancelButton;
if (file.isInitialized) {
return
}
null === (_file$cancelButton = file.cancelButton) || void 0 === _file$cancelButton || _file$cancelButton.option({
onClick: () => {
var _this$_cancelButtonCl2;
this._preventFilesUploading([file]);
this._remove