|
@@ -1,6 +1,6 @@
|
|
|
/*!
|
|
|
* @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014
|
|
|
- * @version 2.0.0
|
|
|
+ * @version 2.1.0
|
|
|
*
|
|
|
* File input styled for Bootstrap 3.0 that utilizes HTML5 File Input's advanced
|
|
|
* features including the FileReader API. This plugin is inspired by the blog article at
|
|
@@ -17,99 +17,117 @@
|
|
|
*/
|
|
|
(function ($) {
|
|
|
var MAIN_TEMPLATE_1 = '{preview}\n' +
|
|
|
- '<div class="input-group {class}">\n' +
|
|
|
- ' {caption}\n' +
|
|
|
- ' <div class="input-group-btn">\n' +
|
|
|
- ' {remove}\n' +
|
|
|
- ' {upload}\n' +
|
|
|
- ' {browse}\n' +
|
|
|
- ' </div>\n' +
|
|
|
- '</div>';
|
|
|
+ '<div class="input-group {class}">\n' +
|
|
|
+ ' {caption}\n' +
|
|
|
+ ' <div class="input-group-btn">\n' +
|
|
|
+ ' {remove}\n' +
|
|
|
+ ' {upload}\n' +
|
|
|
+ ' {browse}\n' +
|
|
|
+ ' </div>\n' +
|
|
|
+ '</div>',
|
|
|
|
|
|
- var MAIN_TEMPLATE_2 = '{preview}\n{remove}\n{upload}\n{browse}\n';
|
|
|
+ MAIN_TEMPLATE_2 = '{preview}\n{remove}\n{upload}\n{browse}\n',
|
|
|
|
|
|
- var PREVIEW_TEMPLATE = '<div class="file-preview {class}">\n' +
|
|
|
- ' <div class="file-preview-status text-center text-success"></div>\n' +
|
|
|
- ' <div class="close fileinput-remove text-right">×</div>\n' +
|
|
|
- ' <div class="file-preview-thumbnails"></div>\n' +
|
|
|
- ' <div class="clearfix"></div>' +
|
|
|
- '</div>';
|
|
|
+ PREVIEW_TEMPLATE = '<div class="file-preview {class}">\n' +
|
|
|
+ ' <div class="file-preview-status text-center text-success"></div>\n' +
|
|
|
+ ' <div class="close fileinput-remove text-right">×</div>\n' +
|
|
|
+ ' <div class="file-preview-thumbnails"></div>\n' +
|
|
|
+ ' <div class="clearfix"></div>' +
|
|
|
+ '</div>',
|
|
|
|
|
|
- var CAPTION_TEMPLATE = '<div class="form-control file-caption {class}">\n' +
|
|
|
- ' <span class="glyphicon glyphicon-file kv-caption-icon"></span><div class="file-caption-name"></div>\n' +
|
|
|
- '</div>';
|
|
|
+ CAPTION_TEMPLATE = '<div class="form-control file-caption {class}">\n' +
|
|
|
+ ' <span class="glyphicon glyphicon-file kv-caption-icon"></span><div class="file-caption-name"></div>\n' +
|
|
|
+ '</div>',
|
|
|
|
|
|
- var MODAL_TEMPLATE = '<div id="{id}" class="modal fade">\n' +
|
|
|
- ' <div class="modal-dialog modal-lg">\n' +
|
|
|
- ' <div class="modal-content">\n' +
|
|
|
- ' <div class="modal-header">\n' +
|
|
|
- ' <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>\n' +
|
|
|
- ' <h3 class="modal-title">Detailed Preview <small>{title}</small></h3>\n' +
|
|
|
- ' </div>\n' +
|
|
|
- ' <div class="modal-body">\n' +
|
|
|
- ' <textarea class="form-control" style="font-family:Monaco,Consolas,monospace; height: {height}px;" readonly>{body}</textarea>\n' +
|
|
|
- ' </div>\n' +
|
|
|
- ' </div>\n' +
|
|
|
- ' </div>\n' +
|
|
|
- '</div>\n';
|
|
|
+ MODAL_TEMPLATE = '<div id="{id}" class="modal fade">\n' +
|
|
|
+ ' <div class="modal-dialog modal-lg">\n' +
|
|
|
+ ' <div class="modal-content">\n' +
|
|
|
+ ' <div class="modal-header">\n' +
|
|
|
+ ' <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>\n' +
|
|
|
+ ' <h3 class="modal-title">Detailed Preview <small>{title}</small></h3>\n' +
|
|
|
+ ' </div>\n' +
|
|
|
+ ' <div class="modal-body">\n' +
|
|
|
+ ' <textarea class="form-control" style="font-family:Monaco,Consolas,monospace; height: {height}px;" readonly>{body}</textarea>\n' +
|
|
|
+ ' </div>\n' +
|
|
|
+ ' </div>\n' +
|
|
|
+ ' </div>\n' +
|
|
|
+ '</div>\n',
|
|
|
|
|
|
- var IMAGE_TEMPLATE = '<div class="file-preview-frame" id="{previewId}">\n' +
|
|
|
- ' {content}\n' +
|
|
|
- '</div>\n';
|
|
|
+ IMAGE_TEMPLATE = '<div class="file-preview-frame" id="{previewId}">\n' +
|
|
|
+ ' {content}\n' +
|
|
|
+ '</div>\n',
|
|
|
|
|
|
- var TEXT_TEMPLATE = '<div class="file-preview-frame" id="{previewId}">\n' +
|
|
|
- ' <div class="file-preview-text" title="{caption}">\n' +
|
|
|
- ' {strText}\n' +
|
|
|
- ' </div>\n' +
|
|
|
- '</div>\n';
|
|
|
+ TEXT_TEMPLATE = '<div class="file-preview-frame" id="{previewId}">\n' +
|
|
|
+ ' <div class="file-preview-text" title="{caption}">\n' +
|
|
|
+ ' {strText}\n' +
|
|
|
+ ' </div>\n' +
|
|
|
+ '</div>\n',
|
|
|
|
|
|
- var OTHER_TEMPLATE = '<div class="file-preview-frame" id="{previewId}">\n' +
|
|
|
- ' <div class="file-preview-other">\n' +
|
|
|
- ' <h2><i class="glyphicon glyphicon-file"></i></h2>\n' +
|
|
|
- ' {caption}\n' +
|
|
|
- ' </div>\n' +
|
|
|
- '</div>';
|
|
|
+ OTHER_TEMPLATE = '<div class="file-preview-frame" id="{previewId}">\n' +
|
|
|
+ ' <div class="file-preview-other">\n' +
|
|
|
+ ' <h2><i class="glyphicon glyphicon-file"></i></h2>\n' +
|
|
|
+ ' {caption}\n' +
|
|
|
+ ' </div>\n' +
|
|
|
+ '</div>',
|
|
|
+
|
|
|
+ isEmpty = function (value, trim) {
|
|
|
+ return value === null || value === undefined || value == []
|
|
|
+ || value === '' || trim && $.trim(value) === '';
|
|
|
+ },
|
|
|
+ isArray = Array.isArray || function (a) {
|
|
|
+ return Object.prototype.toString.call(a) === '[object Array]';
|
|
|
+ },
|
|
|
+ getValue = function (options, param, value) {
|
|
|
+ return (isEmpty(options) || isEmpty(options[param])) ? value : options[param];
|
|
|
+ },
|
|
|
+ getElement = function (options, param, value) {
|
|
|
+ return (isEmpty(options) || isEmpty(options[param])) ? value : $(options[param]);
|
|
|
+ },
|
|
|
+ isImageFile = function (type, name) {
|
|
|
+ return (typeof type !== "undefined") ? type.match('image.*') : name.match(/\.(gif|png|jpe?g)$/i);
|
|
|
+ },
|
|
|
+ isTextFile = function (type, name) {
|
|
|
+ return (typeof type !== "undefined") ? type.match('text.*') : name.match(/\.(txt|md|csv|htm|html|php|ini)$/i);
|
|
|
+ },
|
|
|
+ uniqId = function () {
|
|
|
+ return Math.round(new Date().getTime() + (Math.random() * 100));
|
|
|
+ },
|
|
|
+ hasFileAPISupport = function () {
|
|
|
+ return window.File && window.FileReader && window.FileList && window.Blob;
|
|
|
+ },
|
|
|
+ vUrl = window.URL || window.webkitURL;
|
|
|
|
|
|
- var isEmpty = function (value, trim) {
|
|
|
- return value === null || value === undefined || value == []
|
|
|
- || value === '' || trim && $.trim(value) === '';
|
|
|
- };
|
|
|
- var isArray = Array.isArray || function (a) {
|
|
|
- return Object.prototype.toString.call(a) === '[object Array]';
|
|
|
- };
|
|
|
- var getValue = function (options, param, value) {
|
|
|
- return (isEmpty(options) || isEmpty(options[param])) ? value : options[param];
|
|
|
- };
|
|
|
- var getElement = function (options, param, value) {
|
|
|
- return (isEmpty(options) || isEmpty(options[param])) ? value : $(options[param]);
|
|
|
- };
|
|
|
- var isImageFile = function (type, name) {
|
|
|
- return (typeof type !== "undefined") ? type.match('image.*') : name.match(/\.(gif|png|jpe?g)$/i);
|
|
|
- };
|
|
|
- var isTextFile = function (type, name) {
|
|
|
- return (typeof type !== "undefined") ? type.match('text.*') : name.match(/\.(txt|md|csv|htm|html|php|ini)$/i);
|
|
|
- };
|
|
|
- var uniqId = function () {
|
|
|
- return Math.round(new Date().getTime() + (Math.random() * 100));
|
|
|
- };
|
|
|
var FileInput = function (element, options) {
|
|
|
this.$element = $(element);
|
|
|
- this.init(options);
|
|
|
- this.listen();
|
|
|
+ if (hasFileAPISupport) {
|
|
|
+ this.init(options);
|
|
|
+ this.listen();
|
|
|
+ } else {
|
|
|
+ this.$element.removeClass('file-loading');
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
FileInput.prototype = {
|
|
|
constructor: FileInput,
|
|
|
init: function (options) {
|
|
|
var self = this;
|
|
|
+ self.reader = null;
|
|
|
self.showCaption = options.showCaption;
|
|
|
self.showPreview = options.showPreview;
|
|
|
self.maxFileSize = options.maxFileSize;
|
|
|
+ self.maxFilesCount = options.maxFilesCount;
|
|
|
self.msgSizeTooLarge = options.msgSizeTooLarge;
|
|
|
+ self.msgFilesTooMany = options.msgFilesTooMany;
|
|
|
+ self.msgFileNotFound = options.msgFileNotFound;
|
|
|
+ self.msgFileNotReadable = options.msgFileNotReadable;
|
|
|
+ self.msgFilePreviewAborted = options.msgFilePreviewAborted;
|
|
|
+ self.msgFilePreviewError = options.msgFilePreviewError;
|
|
|
self.msgErrorClass = options.msgErrorClass;
|
|
|
self.initialDelimiter = options.initialDelimiter;
|
|
|
self.initialPreview = options.initialPreview;
|
|
|
self.initialCaption = options.initialCaption;
|
|
|
+ self.initialPreviewCount = options.initialPreviewCount;
|
|
|
+ self.initialPreviewContent = options.initialPreviewContent;
|
|
|
self.overwriteInitial = options.overwriteInitial;
|
|
|
self.showRemove = options.showRemove;
|
|
|
self.showUpload = options.showUpload;
|
|
@@ -118,8 +136,7 @@
|
|
|
self.mainClass = options.mainClass;
|
|
|
if (isEmpty(options.mainTemplate)) {
|
|
|
self.mainTemplate = self.showCaption ? MAIN_TEMPLATE_1 : MAIN_TEMPLATE_2;
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
self.mainTemplate = options.mainTemplate;
|
|
|
}
|
|
|
self.previewTemplate = (self.showPreview) ? options.previewTemplate : '';
|
|
@@ -144,17 +161,16 @@
|
|
|
self.previewFileType = options.previewFileType;
|
|
|
self.wrapTextLength = options.wrapTextLength;
|
|
|
self.wrapIndicator = options.wrapIndicator;
|
|
|
+ self.isError = false;
|
|
|
self.isDisabled = self.$element.attr('disabled') || self.$element.attr('readonly');
|
|
|
if (isEmpty(self.$element.attr('id'))) {
|
|
|
self.$element.attr('id', uniqId());
|
|
|
}
|
|
|
if (typeof self.$container == 'undefined') {
|
|
|
self.$container = self.createContainer();
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
self.refreshContainer();
|
|
|
}
|
|
|
- /* Initialize plugin option parameters */
|
|
|
self.$captionContainer = getElement(options, 'elCaptionContainer', self.$container.find('.file-caption'));
|
|
|
self.$caption = getElement(options, 'elCaptionText', self.$container.find('.file-caption-name'));
|
|
|
self.$previewContainer = getElement(options, 'elPreviewContainer', self.$container.find('.file-preview'));
|
|
@@ -165,7 +181,7 @@
|
|
|
preview: self.$preview.html(),
|
|
|
caption: self.$caption.html()
|
|
|
};
|
|
|
- this.options = options;
|
|
|
+ self.options = options;
|
|
|
self.$element.removeClass('file-loading');
|
|
|
},
|
|
|
listen: function () {
|
|
@@ -192,8 +208,7 @@
|
|
|
if (len > 1 && cap == 0) {
|
|
|
caption = self.msgSelected.replace("{n}", len);
|
|
|
}
|
|
|
- }
|
|
|
- else if (len > 0) {
|
|
|
+ } else if (len > 0) {
|
|
|
var fileList = content.split(self.initialDelimiter);
|
|
|
for (var i = 0; i < len; i++) {
|
|
|
previewId += '-' + i;
|
|
@@ -202,14 +217,12 @@
|
|
|
if (len > 1 && cap == 0) {
|
|
|
caption = self.msgSelected.replace("{n}", len);
|
|
|
}
|
|
|
- }
|
|
|
- else if (cap > 0) {
|
|
|
+ } else if (cap > 0) {
|
|
|
self.$caption.html(caption);
|
|
|
self.$captionContainer.attr('title', caption);
|
|
|
self.$captionContainer.find('.kv-caption-icon').show();
|
|
|
return;
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
return;
|
|
|
}
|
|
|
self.initialPreviewCount = len;
|
|
@@ -224,8 +237,11 @@
|
|
|
if (e) {
|
|
|
e.preventDefault();
|
|
|
}
|
|
|
+ if (self.reader instanceof FileReader) {
|
|
|
+ self.reader.abort();
|
|
|
+ }
|
|
|
self.$element.val('');
|
|
|
- self.$previewContainer.find('.kv-fileinput-error').fadeOut('slow');
|
|
|
+ self.resetErrors(true);
|
|
|
if (e !== false) {
|
|
|
self.$element.trigger('change');
|
|
|
self.$element.trigger('fileclear');
|
|
@@ -234,8 +250,7 @@
|
|
|
self.$preview.html(self.original.preview);
|
|
|
self.$caption.html(self.original.caption);
|
|
|
self.$container.removeClass('file-input-new');
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
self.$preview.html('');
|
|
|
var cap = (!self.overwriteInitial && self.initialCaption.length > 0) ?
|
|
|
self.original.caption : '';
|
|
@@ -273,108 +288,161 @@
|
|
|
self.$container.find(".kv-fileinput-caption").removeClass("file-caption-disabled");
|
|
|
self.$container.find(".btn-file, .fileinput-remove, .kv-fileinput-upload").removeAttr("disabled");
|
|
|
},
|
|
|
- change: function (e) {
|
|
|
+ resetErrors: function (fade) {
|
|
|
+ var self = this, $error = self.$previewContainer.find('.kv-fileinput-error');
|
|
|
+ if (fade) {
|
|
|
+ $error.fadeOut('slow');
|
|
|
+ } else {
|
|
|
+ $error.remove();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ showError: function (msg, file, previewId) {
|
|
|
+ var self = this, $error = self.$previewContainer.find('.kv-fileinput-error');
|
|
|
+ if (isEmpty($error.attr('class'))) {
|
|
|
+ self.$previewContainer.append(
|
|
|
+ '<div class="kv-fileinput-error ' + self.msgErrorClass + '">' + msg + '</div>'
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ $error.html(msg);
|
|
|
+ }
|
|
|
+ $error.hide();
|
|
|
+ $error.fadeIn(800);
|
|
|
+ self.$element.trigger('fileerror', [file, previewId]);
|
|
|
+ self.$element.val('');
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ errorHandler: function (evt, caption) {
|
|
|
var self = this;
|
|
|
- var $el = self.$element, files = $el.get(0).files, numFiles = files ? (files.length + self.initialPreviewCount) : 1,
|
|
|
- label = $el.val().replace(/\\/g, '/').replace(/.*\//, ''), $preview = self.$preview,
|
|
|
+ switch (evt.target.error.code) {
|
|
|
+ case evt.target.error.NOT_FOUND_ERR:
|
|
|
+ self.addError(self.msgFileNotFound.replace('{name}', caption));
|
|
|
+ break;
|
|
|
+ case evt.target.error.NOT_READABLE_ERR:
|
|
|
+ self.addError(self.msgFileNotReadable.replace('{name}', caption));
|
|
|
+ break;
|
|
|
+ case evt.target.error.ABORT_ERR:
|
|
|
+ self.addError(self.msgFilePreviewAborted.replace('{name}', caption));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ self.addError(self.msgFilePreviewError.replace('{name}', caption));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ loadImage: function (file, caption) {
|
|
|
+ var self = this, $img = $(document.createElement("img"));
|
|
|
+ $img.attr({
|
|
|
+ src: vUrl.createObjectURL(file),
|
|
|
+ class: 'file-preview-image',
|
|
|
+ title: caption,
|
|
|
+ alt: caption,
|
|
|
+ onload: function (e) {
|
|
|
+ vUrl.revokeObjectURL($img.src);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // autosize if image width exceeds preview width
|
|
|
+ if ($img.width() >= self.$preview.width()) {
|
|
|
+ $img.attr({width: "100%", height: "auto"});
|
|
|
+ }
|
|
|
+ var $imgContent = $(document.createElement("div")).append($img);
|
|
|
+ return $imgContent.html();
|
|
|
+ },
|
|
|
+ readFiles: function (files) {
|
|
|
+ this.reader = new FileReader();
|
|
|
+ var self = this, $el = self.$element, $preview = self.$preview, reader = self.reader,
|
|
|
$container = self.$previewContainer, $status = self.$previewStatus, msgLoading = self.msgLoading,
|
|
|
- msgProgress = self.msgProgress, msgSelected = self.msgSelected, tfiles,
|
|
|
- fileType = self.previewFileType, wrapLen = parseInt(self.wrapTextLength),
|
|
|
- wrapInd = self.wrapIndicator, previewId = "preview-" + uniqId(), isError = false;
|
|
|
+ msgProgress = self.msgProgress, msgSelected = self.msgSelected, fileType = self.previewFileType,
|
|
|
+ wrapLen = parseInt(self.wrapTextLength), wrapInd = self.wrapIndicator,
|
|
|
+ previewId = "preview-" + uniqId();
|
|
|
|
|
|
+ function readFile(i) {
|
|
|
+ if (i >= files.length) {
|
|
|
+ $container.removeClass('loading');
|
|
|
+ $status.html('');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ previewId += "-" + i;
|
|
|
+ var file = files[i], caption = file.name, isImg = isImageFile(file.type, file.name),
|
|
|
+ isTxt = isTextFile(file.type, file.name), fileSize = (file.size ? file.size : 0) / 1000;
|
|
|
+ fileSize = fileSize.toFixed(2);
|
|
|
+ if (self.maxFileSize > 0 && fileSize > self.maxFileSize) {
|
|
|
+ var msg = self.msgSizeTooLarge.replace('{name}', caption).replace('{size}', fileSize).replace('{maxSize}', self.maxFileSize);
|
|
|
+ self.isError = self.showError(msg, file, previewId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if ($preview.length > 0 && (fileType == "any" ? (isImg || isTxt) : (fileType == "text" ? isTxt : isImg)) && typeof FileReader !== "undefined") {
|
|
|
+ $status.html(msgLoading);
|
|
|
+ $container.addClass('loading');
|
|
|
+ reader.onerror = function (evt) {
|
|
|
+ self.errorHandler(evt, caption);
|
|
|
+ };
|
|
|
+ reader.onload = function (theFile) {
|
|
|
+ var content = '', modal = '';
|
|
|
+ if (isTxt) {
|
|
|
+ var strText = theFile.target.result;
|
|
|
+ if (strText.length > wrapLen) {
|
|
|
+ var id = uniqId(), height = window.innerHeight * .75,
|
|
|
+ modal = MODAL_TEMPLATE.replace("{id}", id).replace("{title}", caption).replace("{body}", strText).replace("{height}", height);
|
|
|
+ wrapInd = wrapInd.replace("{title}", caption).replace("{dialog}", "$('#" + id + "').modal('show')");
|
|
|
+ strText = strText.substring(0, (wrapLen - 1)) + wrapInd;
|
|
|
+ }
|
|
|
+ content = self.previewTextTemplate.replace("{previewId}", previewId).replace("{caption}", caption).replace("{strText}", strText) + modal;
|
|
|
+ } else {
|
|
|
+ content = self.previewImageTemplate.replace("{previewId}", previewId).replace("{content}", self.loadImage(file, caption));
|
|
|
+ }
|
|
|
+ $preview.append("\n" + content);
|
|
|
+ };
|
|
|
+ reader.onloadend = function (e) {
|
|
|
+ setTimeout(readFile(i + 1), 1000);
|
|
|
+ $el.trigger('fileloaded', [file, previewId]);
|
|
|
+ };
|
|
|
+ reader.onprogress = function (data) {
|
|
|
+ if (data.lengthComputable) {
|
|
|
+ var progress = parseInt(((data.loaded / data.total) * 100), 10);
|
|
|
+ var msg = msgProgress.replace('{percent}', progress).replace('{file}', file.name);
|
|
|
+ $status.html(msg);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if (isTxt) {
|
|
|
+ reader.readAsText(file);
|
|
|
+ } else {
|
|
|
+ reader.readAsBinaryString(file);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ $preview.append("\n" + self.previewOtherTemplate.replace("{previewId}", previewId).replace("{caption}", caption));
|
|
|
+ $el.trigger('fileloaded', [file, previewId]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ readFile(0);
|
|
|
+ },
|
|
|
+ change: function (e) {
|
|
|
+ var self = this, $el = self.$element, label = $el.val().replace(/\\/g, '/').replace(/.*\//, ''),
|
|
|
+ total = 0, $preview = self.$preview, files = $el.get(0).files, msgSelected = self.msgSelected,
|
|
|
+ numFiles = !isEmpty(files) ? (files.length + self.initialPreviewCount) : 1, tfiles;
|
|
|
if (e.target.files === undefined) {
|
|
|
tfiles = e.target && e.target.value ? [
|
|
|
{name: e.target.value.replace(/^.+\\/, '')}
|
|
|
] : [];
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
tfiles = e.target.files;
|
|
|
}
|
|
|
if (tfiles.length === 0) {
|
|
|
return;
|
|
|
}
|
|
|
+ self.resetErrors();
|
|
|
$preview.html('');
|
|
|
if (!self.overwriteInitial) {
|
|
|
$preview.html(self.initialPreviewContent);
|
|
|
}
|
|
|
var total = tfiles.length;
|
|
|
- for (var i = 0; i < total; i++) {
|
|
|
- (function (file) {
|
|
|
- previewId += "-" + i;
|
|
|
- var caption = file.name, isImg = isImageFile(file.type, file.name), isTxt = isTextFile(file.type, file.name),
|
|
|
- fileSize = (file.size ? file.size : 0) / 1000;
|
|
|
- fileSize = fileSize.toFixed(2);
|
|
|
- self.$previewContainer.find('.kv-fileinput-error').remove();
|
|
|
- if (self.maxFileSize > 0 && fileSize > self.maxFileSize) {
|
|
|
- var msg = self.msgSizeTooLarge.replace('{name}', caption).replace('{size}', fileSize).replace('{maxSize}', self.maxFileSize),
|
|
|
- errorMsg = '<div class="kv-fileinput-error ' + self.msgErrorClass + '">' + msg + '</div>';
|
|
|
- self.$element.val('');
|
|
|
- self.$previewContainer.append(errorMsg);
|
|
|
- var $error = self.$previewContainer.find('.kv-fileinput-error');
|
|
|
- $error.hide();
|
|
|
- $error.fadeIn(800);
|
|
|
- self.$element.trigger('fileerror', [file, previewId]);
|
|
|
- isError = true;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if ($preview.length > 0 && (fileType == "any" ? (isImg || isTxt) : (fileType == "text" ? isTxt : isImg)) && typeof FileReader !== "undefined") {
|
|
|
- var reader = new FileReader();
|
|
|
- $status.html(msgLoading);
|
|
|
- $container.addClass('loading');
|
|
|
- reader.onload = function (theFile) {
|
|
|
- var content = '', modal = '';
|
|
|
- if (isTxt) {
|
|
|
- var strText = theFile.target.result;
|
|
|
- if (strText.length > wrapLen) {
|
|
|
- var id = uniqId(), height = window.innerHeight * .75,
|
|
|
- modal = MODAL_TEMPLATE.replace("{id}", id).replace("{title}", caption).replace("{body}", strText).replace("{height}", height);
|
|
|
- wrapInd = wrapInd.replace("{title}", caption).replace("{dialog}", "$('#" + id + "').modal('show')");
|
|
|
- strText = strText.substring(0, (wrapLen - 1)) + wrapInd;
|
|
|
- }
|
|
|
- content = self.previewTextTemplate.replace("{previewId}", previewId).replace("{caption}", caption).replace("{strText}", strText) + modal;
|
|
|
- }
|
|
|
- else {
|
|
|
- content = self.previewImageTemplate.replace("{previewId}", previewId).replace("{content}", '<img src="' + theFile.target.result + '" class="file-preview-image" title="' + caption + '" alt="' + caption + '">');
|
|
|
- }
|
|
|
- $preview.append("\n" + content);
|
|
|
-
|
|
|
- // we check here if the picture is not too wide. If it's the case, we
|
|
|
- // scale the picture on the width and not on the height.
|
|
|
- var $image = $preview.find("#" + previewId + " img");
|
|
|
- if ($image.width() > $preview.width()) {
|
|
|
- $image.width("100%");
|
|
|
- $image.height("auto");
|
|
|
- }
|
|
|
-
|
|
|
- if (i >= total - 1) {
|
|
|
- $container.removeClass('loading');
|
|
|
- $status.html('');
|
|
|
- }
|
|
|
- $el.trigger('fileloaded', [file, previewId]);
|
|
|
- };
|
|
|
- reader.onprogress = function (data) {
|
|
|
- if (data.lengthComputable) {
|
|
|
- var progress = parseInt(((data.loaded / data.total) * 100), 10);
|
|
|
- var msg = msgProgress.replace('{percent}', progress).replace('{file}', file.name);
|
|
|
- $status.html(msg);
|
|
|
- }
|
|
|
- };
|
|
|
- if (isTxt) {
|
|
|
- reader.readAsText(file);
|
|
|
- }
|
|
|
- else {
|
|
|
- reader.readAsDataURL(file);
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- $preview.append("\n" + self.previewOtherTemplate.replace("{previewId}", previewId).replace("{caption}", caption));
|
|
|
- $el.trigger('fileloaded', [file, previewId]);
|
|
|
- }
|
|
|
- })(tfiles[i]);
|
|
|
+ if (self.maxFilesCount > 0 && total > self.maxFilesCount) {
|
|
|
+ var msg = self.msgFilesTooMany.replace('{m}', self.maxFilesCount).replace('{n}', total);
|
|
|
+ self.isError = self.showError(msg, null, null);
|
|
|
+ self.$container.removeClass('file-input-new');
|
|
|
+ return;
|
|
|
}
|
|
|
+ self.readFiles(files);
|
|
|
+ self.reader = null;
|
|
|
var log = numFiles > 1 ? msgSelected.replace('{n}', numFiles) : label;
|
|
|
- if (isError) {
|
|
|
+ if (self.isError) {
|
|
|
log = numFiles > 1 ? msgSelected.replace('{n}', numFiles - 1) : ' ';
|
|
|
}
|
|
|
self.$caption.html(log);
|
|
@@ -434,8 +502,7 @@
|
|
|
}
|
|
|
if (isEmpty(self.uploadUrl)) {
|
|
|
content = '<button type="submit" class="' + css + '"' + status + '>' + self.uploadIcon + self.uploadLabel + '</button>';
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
content = '<a href="' + self.uploadUrl + '" class="' + self.uploadClass + '"' + status + '>' + self.uploadIcon + self.uploadLabel + '</a>';
|
|
|
}
|
|
|
return content;
|
|
@@ -505,7 +572,13 @@
|
|
|
uploadClass: 'btn btn-default',
|
|
|
uploadUrl: null,
|
|
|
maxFileSize: 0,
|
|
|
+ maxFilesCount: 0,
|
|
|
msgSizeTooLarge: 'File "{name}" (<b>{size} KB</b>) exceeds maximum allowed upload size of <b>{maxSize} KB</b>. Please retry your upload!',
|
|
|
+ msgFilesTooMany: 'Number of files selected for upload <b>({n})</b> exceeds maximum allowed limit of <b>{m}</b>. Please retry your upload!',
|
|
|
+ msgFileNotFound: 'File "{name}" not found!',
|
|
|
+ msgFileNotReadable: 'File "{name}" is not readable.',
|
|
|
+ msgFilePreviewAborted: 'File preview aborted for "{name}".',
|
|
|
+ msgFilePreviewError: 'An error occurred while reading the file "{name}".',
|
|
|
msgErrorClass: 'file-error-message',
|
|
|
msgLoading: 'Loading …',
|
|
|
msgProgress: 'Loaded {percent}% of {file}',
|