UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,169 lines (1,167 loc) • 65.7 kB
/** * DevExtreme (cjs/__internal/ui/m_file_uploader.js) * Version: 24.2.6 * Build date: Mon Mar 17 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 _click = require("../../common/core/events/click"); 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 _deferred = require("../../core/utils/deferred"); var _extend = require("../../core/utils/extend"); var _iterator = require("../../core/utils/iterator"); 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 _editor = _interopRequireDefault(require("../ui/editor/editor")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } 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 ENTER_KEY = "enter"; const SPACE_KEY = "space"; let renderFileUploaderInput = () => (0, _renderer.default)("<input>").attr("type", "file"); const isFormDataSupported = () => !!window.FormData; class FileUploader extends _editor.default { _supportedKeys() { const click = e => { e.preventDefault(); const $selectButton = this._selectButton.$element(); _events_engine.default.trigger($selectButton, _click.name) }; return (0, _extend.extend)(super._supportedKeys(), { space: click, enter: click }) } _setOptionsByReference() { super._setOptionsByReference(); (0, _extend.extend)(this._optionsByReference, { value: true }) } _getDefaultOptions() { return (0, _extend.extend)(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 } }, { device: () => !isFormDataSupported(), options: { uploadMode: "useForm" } }, { device: () => "desktop" !== _devices.default.real().deviceType, options: { nativeDropSupported: false } }, { device: () => (0, _themes.isMaterial)(), options: { _uploadButtonType: "default" } }, { device: () => (0, _themes.isFluent)(), 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() { if (this.option("chunkSize") > 0) { const uploadChunk = this.option("uploadChunk"); this._uploadStrategy = uploadChunk && (0, _type.isFunction)(uploadChunk) ? new CustomChunksFileUploadStrategy(this) : new DefaultChunksFileUploadStrategy(this) } else { const uploadFile = this.option("uploadFile"); 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(); return this.option("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"); if (files && !files.length && "useForm" !== this.option("uploadMode")) { return } const value = files ? this._getFiles(files) : [{ name: fileName }]; this._changeValue(value); if ("instantly" === this.option("uploadMode")) { this._uploadFiles() } } _shouldFileListBeExtended() { return "useForm" !== this.option("uploadMode") && this.option("extendSelection") && this.option("multiple") } _changeValue(value) { const files = this._shouldFileListBeExtended() ? this.option("value").slice() : []; this.option("value", files.concat(value)) } _getFiles(fileList) { const values = []; (0, _iterator.each)(fileList, ((_, value) => values.push(value))); return values } _getFile(fileData) { const targetFileValue = (0, _type.isNumeric)(fileData) ? this.option("value")[fileData] : fileData; return this._files.filter((file => file.value === targetFileValue))[0] } _initLabel() { if (!this._$inputLabel) { this._$inputLabel = (0, _renderer.default)("<div>") } this._updateInputLabelText() } _updateInputLabelText() { const correctedValue = this._isInteractionDisabled() ? "" : this.option("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() { this._preventRecreatingFiles = false; this._attachDragEventHandlers(this._$inputWrapper); this._attachDragEventHandlers(this.option("dropZone")); this._renderFiles(); super._render() } _createFileProgressBar(file) { file.progressBar = this._createProgressBar(file.value.size); file.progressBar.$element().appendTo(file.$file); this._initStatusMessage(file); this._ensureCancelButtonInitialized(file) } _setStatusMessage(file, message) { setTimeout((() => { if (this.option("showFileList")) { if (file.$statusMessage) { file.$statusMessage.text(message); file.$statusMessage.css("display", ""); file.progressBar.$element().remove() } } }), 400) } _getUploadAbortedStatusMessage() { return "instantly" === this.option("uploadMode") ? this.option("uploadAbortedMessage") : this.option("readyToUploadMessage") } _createFiles() { const value = this.option("value"); if (this._files && (0 === (null === value || void 0 === value ? void 0 : value.length) || !this._shouldFileListBeExtended())) { this._preventFilesUploading(this._files); this._files = null } if (!this._files) { this._files = [] }(0, _iterator.each)(null === value || void 0 === value ? void 0 : value.slice(this._files.length), ((_, value) => { const file = this._createFile(value); this._validateFile(file); this._files.push(file) })) } _preventFilesUploading(files) { files.forEach((file => this._uploadStrategy.abortUpload(file))) } _validateFile(file) { file.isValidFileExtension = this._validateFileExtension(file); file.isValidMinSize = this._validateMinFileSize(file); file.isValidMaxSize = this._validateMaxFileSize(file) } _validateFileExtension(file) { const allowedExtensions = this.option("allowedFileExtensions"); const accept = this.option("accept"); const allowedTypes = this._getAllowedFileTypes(accept); const fileExtension = file.value.name.substring(file.value.name.lastIndexOf(".")).toLowerCase(); if (0 !== (null === accept || void 0 === accept ? void 0 : accept.length) && !this._isFileTypeAllowed(file.value, allowedTypes)) { return false } if (0 === (null === allowedExtensions || void 0 === allowedExtensions ? void 0 : allowedExtensions.length)) { return true } for (let i = 0; i < allowedExtensions.length; i++) { if (fileExtension === allowedExtensions[i].toLowerCase()) { return true } } return false } _validateMaxFileSize(file) { const fileSize = file.value.size; const maxFileSize = this.option("maxFileSize"); return maxFileSize > 0 ? fileSize <= maxFileSize : true } _validateMinFileSize(file) { const fileSize = file.value.size; const minFileSize = this.option("minFileSize"); return minFileSize > 0 ? fileSize >= minFileSize : true } _createBeforeSendAction() { this._beforeSendAction = this._createActionByOption("onBeforeSend", { excludeValidators: ["readOnly"] }) } _createUploadStartedAction() { this._uploadStartedAction = this._createActionByOption("onUploadStarted", { excludeValidators: ["readOnly"] }) } _createUploadedAction() { this._uploadedAction = this._createActionByOption("onUploaded", { excludeValidators: ["readOnly"] }) } _createFilesUploadedAction() { this._filesUploadedAction = this._createActionByOption("onFilesUploaded", { excludeValidators: ["readOnly"] }) } _createProgressAction() { this._progressAction = this._createActionByOption("onProgress", { excludeValidators: ["readOnly"] }) } _createUploadAbortedAction() { this._uploadAbortedAction = this._createActionByOption("onUploadAborted", { excludeValidators: ["readOnly"] }) } _createUploadErrorAction() { this._uploadErrorAction = this._createActionByOption("onUploadError", { excludeValidators: ["readOnly"] }) } _createDropZoneEnterAction() { this._dropZoneEnterAction = this._createActionByOption("onDropZoneEnter") } _createDropZoneLeaveAction() { this._dropZoneLeaveAction = this._createActionByOption("onDropZoneLeave") } _createFile(value) { return { value: value, loadedSize: 0, onProgress: (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 this.isValidFileExtension && this.isValidMaxSize && this.isValidMinSize }, isInitialized: false } } _resetFileState(file) { file.isAborted = false; file.uploadStarted = false; file.isStartLoad = false; file.loadedSize = 0; file.chunksData = void 0; file.request = void 0 } _renderFiles() { var _this$_validationMess; const value = this.option("value"); 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() } const showFileList = this.option("showFileList"); if (showFileList) { (0, _iterator.each)(this._files, ((_, 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; 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()) { file.$statusMessage.text(this.option("readyToUploadMessage")) } else { if (!file.isValidFileExtension) { file.$statusMessage.append(this._createValidationElement("invalidFileExtensionMessage")) } if (!file.isValidMaxSize) { file.$statusMessage.append(this._createValidationElement("invalidMaxFileSizeMessage")) } if (!file.isValidMinSize) { file.$statusMessage.append(this._createValidationElement("invalidMinFileSizeMessage")) } $fileContainer.addClass("dx-fileuploader-invalid") } } _createValidationElement(key) { return (0, _renderer.default)("<span>").text(this.option(key)) } _updateFileNameMaxWidth() { const cancelButtonsCount = this.option("allowCanceling") && "useForm" !== this.option("uploadMode") ? 1 : 0; const uploadButtonsCount = "useButtons" === this.option("uploadMode") ? 1 : 0; const filesContainerWidth = (0, _size.getWidth)(this._$filesContainer.find(".dx-fileuploader-file-container").first()) || (0, _size.getWidth)(this._$filesContainer); const $buttonContainer = this._$filesContainer.find(".dx-fileuploader-button-container").eq(0); const buttonsWidth = (0, _size.getWidth)($buttonContainer) * (cancelButtonsCount + uploadButtonsCount); const $fileSize = this._$filesContainer.find(".dx-fileuploader-file-size").eq(0); const prevFileSize = $fileSize.text(); $fileSize.text("1000 Mb"); const fileSizeWidth = (0, _size.getWidth)($fileSize); $fileSize.text(prevFileSize); this._$filesContainer.find(".dx-fileuploader-file-name").css("maxWidth", filesContainerWidth - buttonsWidth - fileSizeWidth) } _renderFileButtons(file, $container) { const $cancelButton = this._getCancelButton(file); $cancelButton && $container.append($cancelButton); const $uploadButton = this._getUploadButton(file); $uploadButton && $container.append($uploadButton) } _getCancelButton(file) { if ("useForm" === this.option("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) { if (!file.isValid() || "useButtons" !== this.option("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((() => file.uploadButton.option({ visible: false, disabled: true }))); file.onAbort.add((() => file.uploadButton.option({ visible: true, disabled: false }))); return (0, _renderer.default)("<div>").addClass("dx-fileuploader-button-container").append(file.uploadButton.$element()) } _removeFile(file) { var _file$$file; null === (_file$$file = file.$file) || void 0 === _file$$file || _file$$file.parent().remove(); this._files.splice(this._files.indexOf(file), 1); const value = this.option("value").slice(); value.splice(value.indexOf(file.value), 1); this._preventRecreatingFiles = true; this.option("value", value); this._preventRecreatingFiles = false; this._toggleFileUploaderEmptyClassName(); this._resetInputValue(true) } removeFile(fileData) { if ("useForm" === this.option("uploadMode") || !(0, _type.isDefined)(fileData)) { return } const file = this._getFile(fileData); if (file) { if (file.uploadStarted) { this._preventFilesUploading([file]) } this._removeFile(file) } } _toggleFileUploaderEmptyClassName() { this.$element().toggleClass("dx-fileuploader-empty", !this._files.length || this._hasInvalidFile(this._files)) } _hasInvalidFile(files) { for (let i = 0; i < files.length; i++) { if (!files[i].isValid()) { return true } } return false } _getFileSize(size) { let i = 0; 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; while (i < count && size >= 1024) { size /= 1024; i++ } return `${Math.round(size)} ${labels[i]}` } _renderSelectButton() { const $button = (0, _renderer.default)("<div>").addClass("dx-fileuploader-button").appendTo(this._$inputWrapper); this._selectButton = this._createComponent($button, _button.default, { text: this.option("selectButtonText"), focusStateEnabled: false, integrationOptions: {}, disabled: this.option("readOnly"), hoverStateEnabled: this.option("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() { if (this.option("useNativeInputClick")) { return } if (this._isInteractionDisabled()) { return false } this._isCustomClickEvent = true; _events_engine.default.trigger(this._$fileInput, "click"); this._isCustomClickEvent = false } _attachSelectFileDialogHandlers(target) { if (!(0, _type.isDefined)(target)) { return } this._detachSelectFileDialogHandlers(target); const $target = (0, _renderer.default)(target); _events_engine.default.on($target, (0, _index.addNamespace)(_click.name, "dxFileUploaderDialogTrigger"), (() => { this._selectFileDialogClickHandler() })); _events_engine.default.on($target, (0, _index.addNamespace)("keyup", "dxFileUploaderDialogTrigger"), (e => { const normalizedKeyName = (0, _index.normalizeKeyName)(e); if ("enter" === normalizedKeyName || "space" === normalizedKeyName) { this._selectFileDialogClickHandler() } })) } _detachSelectFileDialogHandlers(target) { if (!(0, _type.isDefined)(target)) { return } const $target = (0, _renderer.default)(target); _events_engine.default.off($target, ".dxFileUploaderDialogTrigger") } _renderUploadButton() { if ("useButtons" !== this.option("uploadMode")) { return } const $uploadButton = (0, _renderer.default)("<div>").addClass("dx-fileuploader-button").addClass("dx-fileuploader-upload-button").appendTo(this._$content); this._uploadButton = this._createComponent($uploadButton, _button.default, { text: this.option("uploadButtonText"), onClick: this._uploadButtonClickHandler.bind(this), type: this.option("_uploadButtonType"), integrationOptions: {}, hoverStateEnabled: this.option("hoverStateEnabled") }) } _uploadButtonClickHandler() { this._uploadFiles() } _shouldDragOverBeRendered() { return !this.option("readOnly") && ("useForm" !== this.option("uploadMode") || this.option("nativeDropSupported")) } _isInteractionDisabled() { return this.option("readOnly") || this.option("disabled") } _renderInputContainer() { this._$inputContainer = (0, _renderer.default)("<div>").addClass("dx-fileuploader-input-container").appendTo(this._$inputWrapper); this._$fileInput.addClass("dx-fileuploader-input"); this._renderInput(); const labelId = `dx-fileuploader-input-label-${new _guid.default}`; this._$inputLabel.attr("id", labelId).addClass("dx-fileuploader-input-label").appendTo(this._$inputContainer); this.setAria("labelledby", labelId, this._$fileInput) } _renderInput() { if (this.option("useNativeInputClick")) { this._selectButton.option("template", this._selectButtonInputTemplate.bind(this)) } else { this._$fileInput.appendTo(this._$inputContainer); this._selectButton.option("template", "content") } this._applyInputAttributes(this.option("inputAttr")) } _selectButtonInputTemplate(data, content) { const $content = (0, _renderer.default)(content); const $text = (0, _renderer.default)("<span>").addClass("dx-button-text").text(data.text); $content.append($text).append(this._$fileInput); return $content } _renderInputWrapper() { this._$inputWrapper = (0, _renderer.default)("<div>").addClass("dx-fileuploader-input-wrapper").appendTo(this._$content) } _detachDragEventHandlers(target) { if (!(0, _type.isDefined)(target)) { return } _events_engine.default.off((0, _renderer.default)(target), (0, _index.addNamespace)("", this.NAME)) } _attachDragEventHandlers(target) { const isCustomTarget = target !== this._$inputWrapper; if (!(0, _type.isDefined)(target) || !this._shouldDragOverBeRendered()) { return } this._detachDragEventHandlers(target); target = (0, _renderer.default)(target); _events_engine.default.on(target, (0, _index.addNamespace)("dragenter", this.NAME), this._dragEnterHandler.bind(this, isCustomTarget)); _events_engine.default.on(target, (0, _index.addNamespace)("dragover", this.NAME), this._dragOverHandler.bind(this, isCustomTarget)); _events_engine.default.on(target, (0, _index.addNamespace)("dragleave", this.NAME), this._dragLeaveHandler.bind(this, isCustomTarget)); _events_engine.default.on(target, (0, _index.addNamespace)("drop", this.NAME), this._dropHandler.bind(this, isCustomTarget)) } _applyInputAttributes(customAttributes) { this._$fileInput.attr(customAttributes) } _useInputForDrop() { return this.option("nativeDropSupported") && "useForm" === this.option("uploadMode") } _getDropZoneElement(isCustomTarget, e) { let targetList = isCustomTarget ? Array.from((0, _renderer.default)(this.option("dropZone"))) : [this._$inputWrapper]; targetList = targetList.map((element => (0, _renderer.default)(element).get(0))); return targetList[targetList.indexOf(e.currentTarget)] } _dragEnterHandler(isCustomTarget, e) { if (this.option("disabled")) { return false } if (!this._useInputForDrop()) { e.preventDefault() } const dropZoneElement = this._getDropZoneElement(isCustomTarget, e); if ((0, _type.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) { const classAction = active ? "addClass" : "removeClass"; const mouseAction = active ? "_dropZoneEnterAction" : "_dropZoneLeaveAction"; this[mouseAction]({ 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); if (!this.option("multiple") && files.length > 1 || 0 === files.length) { return } this._changeValue(files); if ("instantly" === this.option("uploadMode")) { this._uploadFiles() } } _areAllFilesLoaded() { return this._files.every((file => !file.isValid() || file._isError || file._isLoaded || file.isAborted)) } _handleAllFilesUploaded() { this._recalculateProgress(); if (this._areAllFilesLoaded()) { this._filesUploadedAction() } } _getAllowedFileTypes(acceptSting) { if (!acceptSting.length) { return [] } return acceptSting.split(",").map((item => item.trim())) } _isFileTypeAllowed(file, allowedTypes) { for (let i = 0, n = allowedTypes.length; i < n; i++) { let allowedType = allowedTypes[i]; if ("." === allowedType[0]) { allowedType = allowedType.replace(".", "\\."); if (file.name.match(new RegExp(`${allowedType}$`, "i"))) { return true } } else { allowedType = allowedType.replace(new RegExp("\\*", "g"), ""); if (file.type.match(new RegExp(allowedType, "i"))) { return true } } } return false } _renderWrapper() { const $wrapper = (0, _renderer.default)("<div>").addClass("dx-fileuploader-wrapper").appendTo(this.$element()); const $container = (0, _renderer.default)("<div>").addClass("dx-fileuploader-container").appendTo($wrapper); this._$content = (0, _renderer.default)("<div>").addClass("dx-fileuploader-content").appendTo($container) } _clean() { this._$fileInput.detach(); delete this._$filesContainer; const { dialogTrigger: dialogTrigger } = this.option(); this._detachSelectFileDialogHandlers(dialogTrigger); this._detachDragEventHandlers(this.option("dropZone")); if (this._files) { this._files.forEach((file => { file.$file = null; file.$statusMessage = null })) } super._clean() } abortUpload(fileData) { if ("useForm" === this.option("uploadMode")) { return } if ((0, _type.isDefined)(fileData)) { const file = this._getFile(fileData); if (file) { this._preventFilesUploading([file]) } } else { this._preventFilesUploading(this._files) } } upload(fileData) { if ("useForm" === this.option("uploadMode")) { return } if ((0, _type.isDefined)(fileData)) { const file = this._getFile(fileData); if (file && isFormDataSupported()) { this._uploadFile(file) } } else { this._uploadFiles() } } _uploadFiles() { if (isFormDataSupported()) { (0, _iterator.each)(this._files, ((_, file) => this._uploadFile(file))) } } _uploadFile(file) { this._uploadStrategy.upload(file) } _updateProgressBar(file, loadedFileData) { file.progressBar && file.progressBar.option({ value: loadedFileData.loaded, showStatus: true }); this._progressAction({ file: file.value, segmentSize: loadedFileData.currentSegmentSize, bytesLoaded: loadedFileData.loaded, bytesTotal: loadedFileData.total, event: loadedFileData.event, request: file.request }) } _updateTotalProgress(totalFilesSize, totalLoadedFilesSize) { let progress = 0; if ((0, _type.isDefined)(totalFilesSize)) { if (this._files.length > 0 && this._areAllFilesLoaded() && 0 === totalFilesSize && 0 === totalLoadedFilesSize) { progress = this._getProgressValue(1) } else if (totalFilesSize) { progress = this._getProgressValue(totalLoadedFilesSize / totalFilesSize) } } this.option("progress", progress); this._setLoadedSize(totalLoadedFilesSize) } _getProgressValue(ratio) { return Math.floor(100 * ratio) } _initStatusMessage(file) { file.$statusMessage.css("display", "none") } _ensureCancelButtonInitialized(file) { if (file.isInitialized) { return } file.cancelButton.option("onClick", (() => { this._preventFilesUploading([file]); this._removeFile(file) })); const hideCancelButton = () => { setTimeout((() => { file.cancelButton.option({ visible: false }) }), 400) }; file.onLoad.add(hideCancelButton); file.onError.add(hideCancelButton) } _createProgressBar(fileSize) { return this._createComponent((0, _renderer.default)("<div>"), _progress_bar.default, { value: void 0, min: 0, max: fileSize, statusFormat: ratio => `${this._getProgressValue(ratio)}%`, showStatus: false, statusPosition: "right" }) } _getTotalFilesSize() { if (!this._totalFilesSize) { this._totalFilesSize = 0; (0, _iterator.each)(this._files, ((_, file) => { this._totalFilesSize += file.value.size })) } return this._totalFilesSize } _getTotalLoadedFilesSize() { if (!this._totalLoadedFilesSize) { this._totalLoadedFilesSize = 0; (0, _iterator.each)(this._files, ((_, file) => { this._totalLoadedFilesSize += file.loadedSize })) } return this._totalLoadedFilesSize } _setLoadedSize(value) { this._totalLoadedFilesSize = value } _recalculateProgress() { this._totalFilesSize = 0; this._totalLoadedFilesSize = 0; this._updateTotalProgress(this._getTotalFilesSize(), this._getTotalLoadedFilesSize()) } isMouseOverElement(mouseEvent, element, correctPseudoElements) { let dragEventDelta = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : 1; if (!element) { return false } const beforeHeight = correctPseudoElements ? parseFloat(window.getComputedStyle(element, ":before").height) : 0; const afterHeight = correctPseudoElements ? parseFloat(window.getComputedStyle(element, ":after").height) : 0; const x = (0, _size.getOffset)(element).left; const y = (0, _size.getOffset)(element).top + beforeHeight; const w = element.offsetWidth; const h = element.offsetHeight - beforeHeight - afterHeight; const eventX = this._getEventX(mouseEvent); const eventY = this._getEventY(mouseEvent); return eventX + dragEventDelta >= x && eventX - dragEventDelta < x + w && eventY + dragEventDelta >= y && eventY - dragEventDelta < y + h } _getEventX(e) { return (0, _index.isTouchEvent)(e) ? this._getTouchEventX(e) : e.clientX + this._getDocumentScrollLeft() } _getEventY(e) { return (0, _index.isTouchEvent)(e) ? this._getTouchEventY(e) : e.clientY + this._getDocumentScrollTop() } _getTouchEventX(e) { let touchPoint = null; if (e.changedTouches.length > 0) { touchPoint = e.changedTouches } else if (e.targetTouches.length > 0) { touchPoint = e.targetTouches } return touchPoint ? touchPoint[0].pageX : 0 } _getTouchEventY(e) { let touchPoint = null; if (e.changedTouches.length > 0) { touchPoint = e.changedTouches } else if (e.targetTouches.length > 0) { touchPoint = e.targetTouches } return touchPoint ? touchPoint[0].pageY : 0 } _getDocumentScrollTop() { const document = _dom_adapter.default.getDocument(); return document.documentElement.scrollTop || document.body.scrollTop } _getDocumentScrollLeft() { const document = _dom_adapter.default.getDocument(); return document.documentElement.scrollLeft || document.body.scrollLeft } _updateReadOnlyState() { const readOnly = this.option("readOnly"); this._selectButton.option("disabled", readOnly); this._files.forEach((file => { var _file$cancelButton; return null === (_file$cancelButton = file.cancelButton) || void 0 === _file$cancelButton ? void 0 : _file$cancelButton.option("disabled", readOnly) })); this._updateInputLabelText(); this._attachDragEventHandlers(this._$inputWrapper) } _updateHoverState() { var _this$_selectButton, _this$_uploadButton; const value = this.option("hoverStateEnabled"); null === (_this$_selectButton = this._selectButton) || void 0 === _this$_selectButton || _this$_selectButton.option("hoverStateEnabled", value); null === (_this$_uploadButton = this._uploadButton) || void 0 === _this$_uploadButton || _this$_uploadButton.option("hoverStateEnabled", value); this._files.forEach((file => { var _file$uploadButton, _file$cancelButton2; null === (_file$uploadButton = file.uploadButton) || void 0 === _file$uploadButton || _file$uploadButton.option("hoverStateEnabled", value); null === (_file$cancelButton2 = file.cancelButton) || void 0 === _file$cancelButton2 || _file$cancelButton2.option("hoverStateEnabled", value) })) } _optionChanged(args) { const { name: name, value: value, previousValue: previousValue } = args; switch (name) { case "height": case "width": this._updateFileNameMaxWidth(); super._optionChanged(args); break; case "value": !value.length && this._$fileInput.val(""); if (!this._preventRecreatingFiles) { this._createFiles(); this._renderFiles() } this._recalculateProgress(); super._optionChanged(args); break; case "name": case "hint": this._initFileInput(); super._optionChanged(args); break; case "accept": this._initFileInput(); break; case "multiple": this._initFileInput(); if (!args.value) { this.clear() } break; case "readOnly": this._updateReadOnlyState(); super._optionChanged(args); break; case "disabled": this._updateInputLabelText(); super._optionChanged(args); break; case "selectButtonText": this._selectButton.option("text", value); break; case "uploadButtonText": this._uploadButton && this._uploadButton.option("text", value); break; case "_uploadButtonType": this._uploadButton && this._uploadButton.option("type", value); break; case "_buttonStylingMode": this._files.forEach((file => { var _file$uploadButton2, _file$cancelButton3; null === (_file$uploadButton2 = file.uploadButton) || void 0 === _file$uploadButton2 || _file$uploadButton2.option("stylingMode", value); null === (_file$cancelButton3 = file.cancelButton) || void 0 === _file$cancelButton3 || _file$cancelButton3.option("stylingMode", value) })); break; case "dialogTrigger": this._detachSelectFileDialogHandlers(previousValue); this._attachSelectFileDialogHandlers(value); break; case "dropZone": this._detachDragEventHandlers(previousValue); this._attachDragEventHandlers(value); break; case "maxFileSize": case "minFileSize": case "allowedFileExtensions": case "invalidFileExtensionMessage": case "invalidMaxFileSizeMessage": case "invalidMinFileSizeMessage": case "readyToUploadMessage": case "uploadedMessage": case "uploadFailedMessage": case "uploadAbortedMessage": case "nativeDropSupported": this._invalidate(); break; case "labelText": this._updateInputLabelText(); break; case "showFileList": if (!this._preventRecreatingFiles) { this._renderFiles() } break; case "uploadFile": case "uploadChunk": case "chunkSize": this._setUploadStrategy(); break; case "abortUpload": case "uploadUrl": case "progress": case "uploadMethod": case "uploadHeaders": case "uploadCustomData": case "extendSelection": break; case "hoverStateEnabled": this._updateHoverState(); super._optionChanged(args); break; case "allowCanceling": case "uploadMode": this.clear(); this._invalidate(); break; case "onBeforeSend":