devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,325 lines (1,097 loc) • 43.1 kB
JavaScript
"use strict";
var $ = require("../core/renderer"),
window = require("../core/utils/window").getWindow(),
eventsEngine = require("../events/core/events_engine"),
registerComponent = require("../core/component_registrator"),
Callbacks = require("../core/utils/callbacks"),
isDefined = require("../core/utils/type").isDefined,
each = require("../core/utils/iterator").each,
extend = require("../core/utils/extend").extend,
inArray = require("../core/utils/array").inArray,
ajax = require("../core/utils/ajax"),
Editor = require("./editor/editor"),
Button = require("./button"),
ProgressBar = require("./progress_bar"),
browser = require("../core/utils/browser"),
devices = require("../core/devices"),
eventUtils = require("../events/utils"),
clickEvent = require("../events/click"),
messageLocalization = require("../localization/message"),
themes = require("./themes");
var FILEUPLOADER_CLASS = "dx-fileuploader",
FILEUPLOADER_EMPTY_CLASS = "dx-fileuploader-empty",
FILEUPLOADER_SHOW_FILE_LIST_CLASS = "dx-fileuploader-show-file-list",
FILEUPLOADER_DRAGOVER_CLASS = "dx-fileuploader-dragover",
FILEUPLOADER_WRAPPER_CLASS = "dx-fileuploader-wrapper",
FILEUPLOADER_CONTAINER_CLASS = "dx-fileuploader-container",
FILEUPLOADER_CONTENT_CLASS = "dx-fileuploader-content",
FILEUPLOADER_INPUT_WRAPPER_CLASS = "dx-fileuploader-input-wrapper",
FILEUPLOADER_INPUT_CONTAINER_CLASS = "dx-fileuploader-input-container",
FILEUPLOADER_INPUT_LABEL_CLASS = "dx-fileuploader-input-label",
FILEUPLOADER_INPUT_CLASS = "dx-fileuploader-input",
FILEUPLOADER_FILES_CONTAINER_CLASS = "dx-fileuploader-files-container",
FILEUPLOADER_FILE_CONTAINER_CLASS = "dx-fileuploader-file-container",
FILEUPLOADER_FILE_INFO_CLASS = "dx-fileuploader-file-info",
FILEUPLOADER_FILE_STATUS_MESSAGE_CLASS = "dx-fileuploader-file-status-message",
FILEUPLOADER_FILE_CLASS = "dx-fileuploader-file",
FILEUPLOADER_FILE_NAME_CLASS = "dx-fileuploader-file-name",
FILEUPLOADER_FILE_SIZE_CLASS = "dx-fileuploader-file-size",
FILEUPLOADER_BUTTON_CLASS = "dx-fileuploader-button",
FILEUPLOADER_BUTTON_CONTAINER_CLASS = "dx-fileuploader-button-container",
FILEUPLOADER_CANCEL_BUTTON_CLASS = "dx-fileuploader-cancel-button",
FILEUPLOADER_UPLOAD_BUTTON_CLASS = "dx-fileuploader-upload-button",
FILEUPLOADER_AFTER_LOAD_DELAY = 400;
var renderFileUploaderInput = function renderFileUploaderInput() {
return $("<input>").attr("type", "file");
};
var isFormDataSupported = function isFormDataSupported() {
return !!window.FormData;
};
/**
* @name dxFileUploader
* @publicName dxFileUploader
* @inherits Editor
* @module ui/file_uploader
* @export default
*/
var FileUploader = Editor.inherit({
_supportedKeys: function _supportedKeys() {
var click = function click(e) {
e.preventDefault();
var $selectButton = this._selectButton.$element();
eventsEngine.trigger($selectButton, clickEvent.name);
};
return extend(this.callBase(), {
space: click,
enter: click
});
},
_setOptionsByReference: function _setOptionsByReference() {
this.callBase();
extend(this._optionsByReference, {
value: true
});
},
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxFileUploaderOptions.value
* @publicName value
* @type Array<File>
* @default []
*/
value: [],
/**
* @name dxFileUploaderOptions.selectButtonText
* @publicName selectButtonText
* @type string
* @default "Select File"
*/
selectButtonText: messageLocalization.format("dxFileUploader-selectFile"),
/**
* @name dxFileUploaderOptions.uploadButtonText
* @publicName uploadButtonText
* @type string
* @default "Upload"
*/
uploadButtonText: messageLocalization.format("dxFileUploader-upload"),
/**
* @name dxFileUploaderOptions.labelText
* @publicName labelText
* @type string
* @default "or Drop file here"
*/
labelText: messageLocalization.format("dxFileUploader-dropFile"),
/**
* @name dxFileUploaderOptions.name
* @publicName name
* @type string
* @default ""
*/
name: "files[]",
/**
* @name dxFileUploaderOptions.multiple
* @publicName multiple
* @type boolean
* @default false
*/
multiple: false,
/**
* @name dxFileUploaderOptions.accept
* @publicName accept
* @type string
* @default ""
*/
accept: "",
/**
* @name dxFileUploaderOptions.uploadUrl
* @publicName uploadUrl
* @type string
* @default "/"
*/
uploadUrl: "/",
/**
* @name dxFileUploaderOptions.allowCanceling
* @publicName allowCanceling
* @type boolean
* @default true
*/
allowCanceling: true,
/**
* @name dxFileUploaderOptions.showFileList
* @publicName showFileList
* @type boolean
* @default true
*/
showFileList: true,
/**
* @name dxFileUploaderOptions.progress
* @publicName progress
* @type number
* @default 0
*/
progress: 0,
/**
* @name dxFileUploaderOptions.readyToUploadMessage
* @publicName readyToUploadMessage
* @type string
* @default "Ready to upload"
*/
readyToUploadMessage: messageLocalization.format("dxFileUploader-readyToUpload"),
/**
* @name dxFileUploaderOptions.uploadedMessage
* @publicName uploadedMessage
* @type string
* @default "Uploaded"
*/
uploadedMessage: messageLocalization.format("dxFileUploader-uploaded"),
/**
* @name dxFileUploaderOptions.uploadFailedMessage
* @publicName uploadFailedMessage
* @type string
* @default "Upload failed"
*/
uploadFailedMessage: messageLocalization.format("dxFileUploader-uploadFailedMessage"),
/**
* @name dxFileUploaderOptions.uploadMode
* @publicName uploadMode
* @type Enums.FileUploadMode
* @default "instantly"
*/
uploadMode: "instantly",
/**
* @name dxFileUploaderOptions.uploadMethod
* @publicName uploadMethod
* @type Enums.UploadHttpMethod
* @default "POST"
*/
uploadMethod: "POST",
/**
* @name dxFileUploaderOptions.uploadHeaders
* @publicName uploadHeaders
* @type object
* @default {}
*/
uploadHeaders: {},
/**
* @name dxFileUploaderOptions.onUploadStarted
* @publicName onUploadStarted
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 file:File
* @type_function_param1_field5 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field6 event:event
* @type_function_param1_field7 request:XMLHttpRequest
* @action
*/
onUploadStarted: null,
/**
* @name dxFileUploaderOptions.onUploaded
* @publicName onUploaded
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 file:File
* @type_function_param1_field5 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field6 event:event
* @type_function_param1_field7 request:XMLHttpRequest
* @action
*/
onUploaded: null,
/**
* @name dxFileUploaderOptions.onProgress
* @publicName onProgress
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 file:File
* @type_function_param1_field5 segmentSize:Number
* @type_function_param1_field6 bytesLoaded:Number
* @type_function_param1_field7 bytesTotal:Number
* @type_function_param1_field8 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field9 event:event
* @type_function_param1_field10 request:XMLHttpRequest
* @action
*/
onProgress: null,
/**
* @name dxFileUploaderOptions.onUploadError
* @publicName onUploadError
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 file:File
* @type_function_param1_field5 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field6 event:event
* @type_function_param1_field7 request:XMLHttpRequest
* @action
*/
onUploadError: null,
/**
* @name dxFileUploaderOptions.onUploadAborted
* @publicName onUploadAborted
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 file:object
* @type_function_param1_field5 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field6 event:event
* @type_function_param1_field7 request:XMLHttpRequest
* @action
*/
onUploadAborted: null,
/**
* @name dxFileUploaderOptions.extendSelection
* @publicName extendSelection
* @type boolean
* @default true
* @hidden
*/
extendSelection: true,
/**
* @name dxFileUploaderOptions.validationMessageMode
* @publicName validationMessageMode
* @hidden
* @inheritdoc
*/
validationMessageMode: "always",
/**
* @name dxFileUploaderOptions.onValueChanged
* @publicName onValueChanged
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 value:Array<File>
* @type_function_param1_field5 previousValue:Array<File>
* @type_function_param1_field6 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field7 event:event
* @action
*/
validationMessageOffset: { h: 0, v: 0 },
useNativeInputClick: false,
useDragOver: true,
nativeDropSupported: true,
_uploadButtonType: "normal"
});
},
_defaultOptionsRules: function _defaultOptionsRules() {
return this.callBase().concat([{
device: function device() {
return devices.real().deviceType === "desktop" && !devices.isSimulator();
},
options: {
/**
* @name dxFileUploaderOptions.focusStateEnabled
* @publicName focusStateEnabled
* @type boolean
* @default true @for desktop
* @inheritdoc
*/
focusStateEnabled: true
}
}, {
device: [{
platform: "android"
}, {
platform: "win"
}],
options: {
validationMessageOffset: { v: 0 }
}
}, {
device: function device() {
return browser.msie && browser.version <= 10;
},
options: {
uploadMode: "useForm",
useNativeInputClick: true
}
}, {
device: function device() {
return devices.real().platform !== "generic";
},
options: {
useDragOver: false
}
}, {
device: function device() {
return !isFormDataSupported();
},
options: {
uploadMode: "useForm"
}
}, {
device: function device() {
return browser.msie || devices.real().platform !== "generic";
},
options: {
nativeDropSupported: false
}
}, {
device: function device() {
return themes.isMaterial();
},
options: {
_uploadButtonType: "default"
}
}]);
},
_init: function _init() {
this.callBase.apply(this, arguments);
this._initFileInput();
this._initLabel();
this._createFiles();
this._createUploadStartedAction();
this._createUploadedAction();
this._createProgressAction();
this._createUploadErrorAction();
this._createUploadAbortedAction();
},
_initFileInput: function _initFileInput() {
this._isCustomClickEvent = false;
if (!this._$fileInput) {
this._$fileInput = renderFileUploaderInput();
eventsEngine.on(this._$fileInput, "change", this._inputChangeHandler.bind(this));
eventsEngine.on(this._$fileInput, "click", function (e) {
e.stopPropagation();
return this.option("useNativeInputClick") || this._isCustomClickEvent;
}.bind(this));
}
this._$fileInput.prop({
multiple: this.option("multiple"),
accept: this.option("accept"),
tabIndex: -1
});
},
_inputChangeHandler: function _inputChangeHandler() {
if (this._doPreventInputChange) {
return;
}
var fileName = this._$fileInput.val().replace(/^.*\\/, ''),
files = this._$fileInput.prop("files");
if (files && !files.length) {
return;
}
var value = files ? this._getFiles(files) : [{ name: fileName }];
this._changeValue(value);
if (this.option("uploadMode") === "instantly") {
this._uploadFiles();
}
},
_shouldFileListBeExtended: function _shouldFileListBeExtended() {
return this.option("uploadMode") !== "useForm" && this.option("extendSelection") && this.option("multiple");
},
_removeDuplicates: function _removeDuplicates(files, value) {
var result = [];
for (var i = 0; i < value.length; i++) {
if (!this._isFileInArray(files, value[i])) {
result.push(value[i]);
}
}
return result;
},
_isFileInArray: function _isFileInArray(files, file) {
for (var i = 0; i < files.length; i++) {
var item = files[i];
if (item.size === file.size && item.name === file.name) {
return true;
}
}
return false;
},
_changeValue: function _changeValue(value) {
var files = this._shouldFileListBeExtended() ? this.option("value").slice() : [];
if (this.option("uploadMode") !== "instantly") {
value = this._removeDuplicates(files, value);
}
this.option("value", files.concat(value));
},
_getFiles: function _getFiles(fileList) {
var values = [];
each(fileList, function (_, value) {
values.push(value);
});
return values;
},
_initLabel: function _initLabel() {
if (!this._$inputLabel) {
this._$inputLabel = $("<div>");
}
this._$inputLabel.text(this.option("labelText"));
},
_focusTarget: function _focusTarget() {
return this.$element().find("." + FILEUPLOADER_BUTTON_CLASS);
},
_getSubmitElement: function _getSubmitElement() {
return this._$fileInput;
},
_initMarkup: function _initMarkup() {
this.callBase();
this.$element().addClass(FILEUPLOADER_CLASS);
this._renderWrapper();
this._renderInputWrapper();
this._renderSelectButton();
this._renderInputContainer();
this._renderUploadButton();
this._preventRecreatingFiles = true;
},
_render: function _render() {
this._preventRecreatingFiles = false;
this._renderDragEvents();
this._renderFiles();
this.callBase();
},
_createFiles: function _createFiles() {
var value = this.option("value");
if (!this._files || value.length === 0 || !this._shouldFileListBeExtended()) {
this._files = [];
}
each(value.slice(this._files.length), function (_, value) {
this._files.push(this._createFile(value));
}.bind(this));
},
_createUploadStartedAction: function _createUploadStartedAction() {
this._uploadStartedAction = this._createActionByOption("onUploadStarted");
},
_createUploadedAction: function _createUploadedAction() {
this._uploadedAction = this._createActionByOption("onUploaded");
},
_createProgressAction: function _createProgressAction() {
this._progressAction = this._createActionByOption("onProgress");
},
_createUploadAbortedAction: function _createUploadAbortedAction() {
this._uploadAbortedAction = this._createActionByOption("onUploadAborted");
},
_createUploadErrorAction: function _createUploadErrorAction() {
this._uploadErrorAction = this._createActionByOption("onUploadError");
},
_createFile: function _createFile(value) {
return {
value: value,
loadedSize: 0,
onProgress: Callbacks(),
onAbort: Callbacks(),
onLoad: Callbacks(),
onError: Callbacks(),
onLoadStart: Callbacks()
};
},
_renderFiles: function _renderFiles() {
var value = this.option("value");
if (!this._$filesContainer) {
this._$filesContainer = $("<div>").addClass(FILEUPLOADER_FILES_CONTAINER_CLASS).appendTo(this._$content);
} else if (!this._shouldFileListBeExtended() || value.length === 0) {
this._$filesContainer.empty();
}
var showFileList = this.option("showFileList");
if (showFileList) {
var that = this;
each(this._files, function (_, file) {
if (!file.$file) {
that._renderFile(file);
}
});
}
this.$element().toggleClass(FILEUPLOADER_SHOW_FILE_LIST_CLASS, showFileList);
this.$element().toggleClass(FILEUPLOADER_EMPTY_CLASS, !this._files.length);
this._updateFileNameMaxWidth();
this._$validationMessage && this._$validationMessage.dxOverlay("instance").repaint();
},
_renderFile: function _renderFile(file) {
var value = file.value;
var $fileContainer = $("<div>").addClass(FILEUPLOADER_FILE_CONTAINER_CLASS).appendTo(this._$filesContainer);
this._renderFileButtons(file, $fileContainer);
file.$file = $("<div>").addClass(FILEUPLOADER_FILE_CLASS).appendTo($fileContainer);
var $fileInfo = $("<div>").addClass(FILEUPLOADER_FILE_INFO_CLASS).appendTo(file.$file);
file.$statusMessage = $("<div>").addClass(FILEUPLOADER_FILE_STATUS_MESSAGE_CLASS).text(this.option("readyToUploadMessage")).appendTo(file.$file);
$("<div>").addClass(FILEUPLOADER_FILE_NAME_CLASS).text(value.name).appendTo($fileInfo);
if (isDefined(value.size)) {
$("<div>").addClass(FILEUPLOADER_FILE_SIZE_CLASS).text(this._getFileSize(value.size)).appendTo($fileInfo);
}
},
_updateFileNameMaxWidth: function _updateFileNameMaxWidth() {
var cancelButtonsCount = this.option("allowCanceling") && this.option("uploadMode") !== "useForm" ? 1 : 0,
uploadButtonsCount = this.option("uploadMode") === "useButtons" ? 1 : 0,
filesContainerWidth = this._$filesContainer.find("." + FILEUPLOADER_FILE_CONTAINER_CLASS).first().width() || this._$filesContainer.width(),
$buttonContainer = this._$filesContainer.find("." + FILEUPLOADER_BUTTON_CONTAINER_CLASS).eq(0),
buttonsWidth = $buttonContainer.width() * (cancelButtonsCount + uploadButtonsCount),
$fileSize = this._$filesContainer.find("." + FILEUPLOADER_FILE_SIZE_CLASS).eq(0);
var prevFileSize = $fileSize.text();
$fileSize.text("1000 Mb");
var fileSizeWidth = $fileSize.width();
$fileSize.text(prevFileSize);
this._$filesContainer.find("." + FILEUPLOADER_FILE_NAME_CLASS).css("maxWidth", filesContainerWidth - buttonsWidth - fileSizeWidth);
},
_renderFileButtons: function _renderFileButtons(file, $container) {
var $cancelButton = this._getCancelButton(file);
$cancelButton && $container.append($cancelButton);
var $uploadButton = this._getUploadButton(file);
$uploadButton && $container.append($uploadButton);
},
_getCancelButton: function _getCancelButton(file) {
if (this.option("uploadMode") === "useForm") {
return null;
}
file.cancelButton = this._createComponent($("<div>").addClass(FILEUPLOADER_BUTTON_CLASS + " " + FILEUPLOADER_CANCEL_BUTTON_CLASS), Button, {
onClick: function () {
this._removeFile(file);
}.bind(this),
icon: "close",
visible: this.option("allowCanceling"),
integrationOptions: {}
});
return $("<div>").addClass(FILEUPLOADER_BUTTON_CONTAINER_CLASS).append(file.cancelButton.$element());
},
_getUploadButton: function _getUploadButton(file) {
if (this.option("uploadMode") !== "useButtons") {
return null;
}
file.uploadButton = this._createComponent($("<div>").addClass(FILEUPLOADER_BUTTON_CLASS + " " + FILEUPLOADER_UPLOAD_BUTTON_CLASS), Button, {
onClick: function () {
this._uploadFile(file);
}.bind(this),
icon: "upload"
});
file.onLoadStart.add(function () {
file.uploadButton.$element().remove();
}.bind(this));
return $("<div>").addClass(FILEUPLOADER_BUTTON_CONTAINER_CLASS).append(file.uploadButton.$element());
},
_removeFile: function _removeFile(file) {
file.$file.parent().remove();
this._files.splice(inArray(file, this._files), 1);
var value = this.option("value").slice();
value.splice(inArray(file.value, value), 1);
this._preventRecreatingFiles = true;
this.option("value", value);
this._preventRecreatingFiles = false;
this.$element().toggleClass(FILEUPLOADER_EMPTY_CLASS, !this._files.length);
this._doPreventInputChange = true;
this._$fileInput.val("");
this._doPreventInputChange = false;
},
_getFileSize: function _getFileSize(size) {
var i = 0,
labels = [messageLocalization.format("dxFileUploader-bytes"), messageLocalization.format("dxFileUploader-kb"), messageLocalization.format("dxFileUploader-Mb"), messageLocalization.format("dxFileUploader-Gb")],
count = labels.length - 1;
while (i < count && size >= 1024) {
size /= 1024;
i++;
}
return Math.round(size) + " " + labels[i];
},
_renderSelectButton: function _renderSelectButton() {
var $button = $("<div>").addClass(FILEUPLOADER_BUTTON_CLASS).appendTo(this._$inputWrapper);
this._selectButton = this._createComponent($button, Button, {
text: this.option("selectButtonText"),
focusStateEnabled: false,
integrationOptions: {}
});
// NOTE: click triggering on input 'file' works correctly only in native click handler when device is used
if (devices.real().deviceType === "desktop") {
this._selectButton.option("onClick", this._selectButtonClickHandler.bind(this));
} else {
eventsEngine.off($button, "click");
eventsEngine.on($button, "click", this._selectButtonClickHandler.bind(this));
}
},
_selectButtonClickHandler: function _selectButtonClickHandler() {
var that = this;
if (that.option("useNativeInputClick")) {
return;
}
if (that.option("disabled")) {
return false;
}
that._isCustomClickEvent = true;
eventsEngine.trigger(that._$fileInput, "click");
that._isCustomClickEvent = false;
},
_renderUploadButton: function _renderUploadButton() {
if (this.option("uploadMode") !== "useButtons") {
return;
}
var $uploadButton = $("<div>").addClass(FILEUPLOADER_BUTTON_CLASS).addClass(FILEUPLOADER_UPLOAD_BUTTON_CLASS).appendTo(this._$content);
this._uploadButton = this._createComponent($uploadButton, Button, {
text: this.option("uploadButtonText"),
onClick: this._uploadButtonClickHandler.bind(this),
type: this.option("_uploadButtonType"),
integrationOptions: {}
});
},
_uploadButtonClickHandler: function _uploadButtonClickHandler() {
this._uploadFiles();
},
_shouldDragOverBeRendered: function _shouldDragOverBeRendered() {
return this.option("uploadMode") !== "useForm" || this.option("nativeDropSupported");
},
_renderInputContainer: function _renderInputContainer() {
this._$inputContainer = $("<div>").addClass(FILEUPLOADER_INPUT_CONTAINER_CLASS).appendTo(this._$inputWrapper);
if (!this._shouldDragOverBeRendered()) {
this._$inputContainer.css("display", "none");
}
this._$fileInput.addClass(FILEUPLOADER_INPUT_CLASS);
this._renderInput();
this._$inputLabel.addClass(FILEUPLOADER_INPUT_LABEL_CLASS).appendTo(this._$inputContainer);
},
_renderInput: function _renderInput() {
if (this.option("useNativeInputClick")) {
this._selectButton.option("template", this._selectButtonInputTemplate.bind(this));
} else {
this._$fileInput.appendTo(this._$inputContainer);
this._selectButton.option("template", "content");
}
},
_selectButtonInputTemplate: function _selectButtonInputTemplate(data, content) {
var $content = $(content);
var $text = $("<span>").addClass("dx-button-text").text(data.text);
$content.append($text).append(this._$fileInput);
return $content;
},
_renderInputWrapper: function _renderInputWrapper() {
this._$inputWrapper = $("<div>").addClass(FILEUPLOADER_INPUT_WRAPPER_CLASS).appendTo(this._$content);
},
_renderDragEvents: function _renderDragEvents() {
eventsEngine.off(this._$inputWrapper, "." + this.NAME);
if (!this._shouldDragOverBeRendered()) {
return;
}
this._dragEventsCount = 0;
eventsEngine.on(this._$inputWrapper, eventUtils.addNamespace("dragenter", this.NAME), this._dragEnterHandler.bind(this));
eventsEngine.on(this._$inputWrapper, eventUtils.addNamespace("dragover", this.NAME), this._dragOverHandler.bind(this));
eventsEngine.on(this._$inputWrapper, eventUtils.addNamespace("dragleave", this.NAME), this._dragLeaveHandler.bind(this));
eventsEngine.on(this._$inputWrapper, eventUtils.addNamespace("drop", this.NAME), this._dropHandler.bind(this));
},
_useInputForDrop: function _useInputForDrop() {
return this.option("nativeDropSupported") && this.option("uploadMode") === "useForm";
},
_dragEnterHandler: function _dragEnterHandler(e) {
if (this.option("disabled")) {
return false;
}
if (!this._useInputForDrop()) {
e.preventDefault();
}
this._dragEventsCount++;
this.$element().addClass(FILEUPLOADER_DRAGOVER_CLASS);
},
_dragOverHandler: function _dragOverHandler(e) {
if (!this._useInputForDrop()) {
e.preventDefault();
}
},
_dragLeaveHandler: function _dragLeaveHandler(e) {
if (!this._useInputForDrop()) {
e.preventDefault();
}
this._dragEventsCount--;
if (this._dragEventsCount <= 0) {
this.$element().removeClass(FILEUPLOADER_DRAGOVER_CLASS);
}
},
_dropHandler: function _dropHandler(e) {
this._dragEventsCount = 0;
this.$element().removeClass(FILEUPLOADER_DRAGOVER_CLASS);
if (this._useInputForDrop()) {
return;
}
e.preventDefault();
var fileList = e.originalEvent.dataTransfer.files,
files = this._getFiles(fileList);
if (!this.option("multiple") && files.length > 1) {
return;
}
this._changeValue(this._filterFiles(files));
if (this.option("uploadMode") === "instantly") {
this._uploadFiles();
}
},
_filterFiles: function _filterFiles(files) {
if (!files.length) {
return files;
}
var accept = this.option("accept");
if (!accept.length) {
return files;
}
var result = [],
allowedTypes = this._getAllowedFileTypes(accept);
for (var i = 0, n = files.length; i < n; i++) {
if (this._isFileTypeAllowed(files[i], allowedTypes)) {
result.push(files[i]);
}
}
return result;
},
_getAllowedFileTypes: function _getAllowedFileTypes(acceptSting) {
if (!acceptSting.length) {
return [];
}
return acceptSting.split(',').map(function (item) {
return item.trim();
});
},
_isFileTypeAllowed: function _isFileTypeAllowed(file, allowedTypes) {
for (var i = 0, n = allowedTypes.length; i < n; i++) {
var allowedType = allowedTypes[i];
if (allowedType[0] === ".") {
allowedType = allowedType.replace(".", "\\.");
if (file.name.match(new RegExp(allowedType + "$", "i"))) {
return true;
}
} else {
allowedType = allowedType.replace("*", "");
if (file.type.match(new RegExp(allowedType, "i"))) {
return true;
}
}
}
return false;
},
_renderWrapper: function _renderWrapper() {
var $wrapper = $("<div>").addClass(FILEUPLOADER_WRAPPER_CLASS).appendTo(this.$element());
var $container = $("<div>").addClass(FILEUPLOADER_CONTAINER_CLASS).appendTo($wrapper);
this._$content = $("<div>").addClass(FILEUPLOADER_CONTENT_CLASS).appendTo($container);
},
_clean: function _clean() {
this._$fileInput.detach();
delete this._$filesContainer;
this.callBase.apply(this, arguments);
},
_uploadFiles: function _uploadFiles() {
if (!isFormDataSupported()) {
return;
}
each(this._files, function (_, file) {
this._uploadFile(file);
}.bind(this));
},
_uploadFile: function _uploadFile(file) {
if (file.uploadStarted) {
return;
}
var $file = file.$file,
value = file.value;
if ($file) {
file.progressBar = this._createProgressBar(value.size);
file.progressBar.$element().appendTo($file);
this._initStatusMessage(file);
this._initCancelButton(file);
}
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));
this._sendFileData(file, this._createFormData(this.option("name"), value));
},
_onUploadStarted: function _onUploadStarted(file, e) {
file.uploadStarted = true;
this._uploadStartedAction({
file: file.value,
event: e,
request: file.request
});
},
_onErrorHandler: function _onErrorHandler(file, e) {
var that = this;
setTimeout(function () {
if (that.option("showFileList")) {
file.$statusMessage.text(that.option("uploadFailedMessage"));
file.$statusMessage.css("display", "");
file.progressBar.$element().remove();
}
}, FILEUPLOADER_AFTER_LOAD_DELAY);
this._uploadErrorAction({
file: file.value,
event: e,
request: file.request
});
},
_onAbortHandler: function _onAbortHandler(file, e) {
this._uploadAbortedAction({
file: file.value,
event: e,
request: file.request
});
},
_onLoadedHandler: function _onLoadedHandler(file, e) {
var that = this;
setTimeout(function () {
if (that.option("showFileList")) {
file.$statusMessage.text(that.option("uploadedMessage"));
file.$statusMessage.css("display", "");
file.progressBar.$element().remove();
}
}, FILEUPLOADER_AFTER_LOAD_DELAY);
this._uploadedAction({
file: file.value,
event: e,
request: file.request
});
},
_onProgressHandler: function _onProgressHandler(file, e) {
var totalSize = this._getTotalSize(),
currentLoadedSize = 0,
loadedSize = this._getLoadedSize(),
progress = 0;
if (file) {
currentLoadedSize = Math.min(e.loaded, file.value.size);
var segmentSize = currentLoadedSize - file.loadedSize;
loadedSize += segmentSize;
file.progressBar && file.progressBar.option({
value: currentLoadedSize,
showStatus: true
});
this._progressAction({
file: file.value,
segmentSize: segmentSize,
bytesLoaded: e.loaded,
bytesTotal: e.total,
event: e,
request: file.request
});
file.loadedSize = currentLoadedSize;
}
if (totalSize) {
progress = Math.round(loadedSize / totalSize * 100);
}
this.option("progress", progress);
this._setLoadedSize(loadedSize);
},
_initStatusMessage: function _initStatusMessage(file) {
file.$statusMessage.css("display", "none");
},
_initCancelButton: function _initCancelButton(file) {
var cancelClickHandler = function () {
file.request.abort();
this._removeFile(file);
}.bind(this);
file.cancelButton.option("onClick", cancelClickHandler);
var hideCancelButton = function hideCancelButton() {
setTimeout(function () {
file.cancelButton.option({
visible: false
});
}, FILEUPLOADER_AFTER_LOAD_DELAY);
};
file.onLoad.add(hideCancelButton);
file.onError.add(hideCancelButton);
},
_sendFileData: function _sendFileData(file, data) {
var that = this;
file.loadedSize = 0;
ajax.sendRequest({
url: this.option("uploadUrl"),
method: this.option("uploadMethod"),
headers: this.option("uploadHeaders"),
beforeSend: function beforeSend(xhr) {
file.request = xhr;
},
upload: {
"onprogress": function onprogress(e) {
if (file._isError) {
return;
}
file._isProgressStarted = true;
file.onProgress.fire(e);
},
"onloadstart": function onloadstart() {
file.onLoadStart.fire();
},
"onabort": function onabort() {
file.onAbort.fire();
}
},
data: data
}).done(function () {
file.onLoad.fire();
}).fail(function (e) {
if (that._isStatusError(e.status) || !file._isProgressStarted) {
file._isError = true;
file.onError.fire();
}
});
},
_isStatusError: function _isStatusError(status) {
return 400 <= status && status < 500 || 500 <= status && status < 600;
},
_createFormData: function _createFormData(fieldName, fieldValue) {
var formData = new window.FormData();
formData.append(fieldName, fieldValue);
return formData;
},
_createProgressBar: function _createProgressBar(fileSize) {
return this._createComponent($("<div>"), ProgressBar, {
value: undefined,
min: 0,
max: fileSize,
statusFormat: function statusFormat(ratio) {
return Math.round(ratio * 100) + "%";
},
showStatus: false,
statusPosition: "right"
});
},
_getTotalSize: function _getTotalSize() {
if (!this._totalSize) {
var value = this.option("value"),
totalSize = 0;
each(value, function (_, file) {
totalSize += file.size;
});
this._totalSize = totalSize;
}
return this._totalSize;
},
_getLoadedSize: function _getLoadedSize() {
if (!this._loadedSize) {
var loadedSize = 0;
each(this._files, function (_, file) {
loadedSize += file.loadedSize;
});
this._loadedSize = loadedSize;
}
return this._loadedSize;
},
_setLoadedSize: function _setLoadedSize(value) {
this._loadedSize = value;
},
_recalculateProgress: function _recalculateProgress() {
delete this._totalSize;
delete this._loadedSize;
this._onProgressHandler();
},
_getValidationMessageTarget: function _getValidationMessageTarget() {
return this._$inputWrapper;
},
_optionChanged: function _optionChanged(args) {
var value = args.value;
switch (args.name) {
case "height":
case "width":
this._updateFileNameMaxWidth();
this.callBase(args);
break;
case "value":
!value.length && this._$fileInput.val("");
if (!this._preventRecreatingFiles) {
this._createFiles();
this._renderFiles();
}
this._recalculateProgress();
this.callBase(args);
break;
case "name":
this._initFileInput();
this.callBase(args);
break;
case "accept":
this._initFileInput();
break;
case "multiple":
this._initFileInput();
if (!args.value) {
this.reset();
}
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 "readyToUploadMessage":
case "uploadedMessage":
case "uploadFailedMessage":
this._invalidate();
break;
case "labelText":
this._$inputLabel.text(value);
break;
case "showFileList":
if (!this._preventRecreatingFiles) {
this._renderFiles();
}
break;
case "uploadUrl":
case "progress":
case "uploadMethod":
case "uploadHeaders":
case "extendSelection":
break;
case "allowCanceling":
case "uploadMode":
this.reset();
this._invalidate();
break;
case "onUploadStarted":
this._createUploadStartedAction();
break;
case "onUploaded":
this._createUploadedAction();
break;
case "onProgress":
this._createProgressAction();
break;
case "onUploadError":
this._createUploadErrorAction();
break;
case "onUploadAborted":
this._createUploadAbortedAction();
break;
case "useNativeInputClick":
this._renderInput();
break;
case "useDragOver":
this._renderDragEvents();
break;
case "nativeDropSupported":
this._invalidate();
break;
default:
this.callBase(args);
}
},
reset: function reset() {
this.option("value", []);
}
});
///#DEBUG
FileUploader.__internals = {
changeFileInputRenderer: function changeFileInputRenderer(renderer) {
renderFileUploaderInput = renderer;
},
resetFileInputTag: function resetFileInputTag() {
renderFileUploaderInput = function renderFileUploaderInput() {
return $("<input>").attr("type", "file");
};
}
};
///#ENDDEBUG
registerComponent("dxFileUploader", FileUploader);
module.exports = FileUploader;