devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,197 lines (1,195 loc) • 76.8 kB
JavaScript
/**
* 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
}