Browse Source

Upgrade to release v2.1.0

Kartik Visweswaran 10 năm trước cách đây
mục cha
commit
41a594fbd6
8 tập tin đã thay đổi với 314 bổ sung176 xóa
  1. 10 0
      CHANGE.md
  2. 54 0
      README.md
  3. 1 1
      bower.json
  4. 1 1
      css/fileinput.css
  5. 1 1
      css/fileinput.min.css
  6. 3 2
      examples/index.html
  7. 243 170
      js/fileinput.js
  8. 1 1
      js/fileinput.min.js

+ 10 - 0
CHANGE.md

@@ -1,3 +1,13 @@
+version 2.1.0
+=============
+**Date:** 08-Aug-2014
+
+1. (enh #16, #17): Added exception handling for trapping FileReader API errors
+2. New configuration property added: `maxFilesCount`. Defaults to `0` which means unlimited.
+3. New configurable error messages added: `msgFilesTooMany`, `msgFileNotFound`, `msgFileNotReadable`, `msgFilePreviewAborted`, and `msgFilePreviewError`.
+4. Enhanced plugin to improve browser performance when loading and previewing multiple image files.
+5. Other minor bug fixes
+
 version 2.0.0
 =============
 **Date:** 25-Jul-2014

+ 54 - 0
README.md

@@ -338,6 +338,9 @@ _string_ the URL for the upload processing action (typically for ajax based proc
 #### maxFileSize
 _float_ the maximum file size for upload in KB.  If set to `0`, it means size allowed is unlimited. Defaults to `0`.
 
+#### maxFilesCount
+_float_ the maximum number of files allowed for upload.  If set to `0`, it means size allowed is unlimited. Defaults to `0`.
+
 #### msgSizeTooLarge
 _string_ the message to be displayed when the file size exceeds maximum size. Defaults to:
 
@@ -350,6 +353,57 @@ where:
 `{size}`: will be replaced by the uploaded file size
 `{maxSize}`: will be replaced by the `maxFileSize` parameter.
 
+### msgFilesTooMany
+_string_ the message to be displayed when the file size exceeds maximum size. Defaults to:
+
+```
+Number of files selected for upload <b>({n})</b> exceeds maximum allowed limit of <b>{m}</b>. Please retry your upload!
+```
+
+where:
+`{n}`: will be replaced by number of files selected for upload
+`{m}`: will be replaced by the allowed maximum files as set in maxFilesCount
+
+#### msgFileNotFound
+_string_ the exception message to be displayed when the file selected is not found by the FileReader. Defaults to:
+
+```
+File "{name}" not found!
+```
+where:
+
+`{name}`: will be replaced by the file name being uploaded
+
+#### msgFileNotReadable
+_string_ the exception message to be displayed when the file selected is not readable by the FileReader API. Defaults to:
+
+```
+File "{name}" is not readable.
+```
+where:
+
+`{name}`: will be replaced by the file name being uploaded
+
+#### msgFilePreviewAborted
+_string_ the exception message to be displayed when the file preview upload is aborted. Defaults to:
+
+```
+File preview aborted for "{name}".
+```
+where:
+
+`{name}`: will be replaced by the file name being uploaded
+
+#### msgFilePreviewError
+_string_ the exception message to be displayed for any other error when previewing the file. Defaults to:
+
+```
+An error occurred while reading the file "{name}".
+```
+where:
+
+`{name}`: will be replaced by the file name being uploaded
+
 #### msgErrorClass
 _string_ the css class for the error message to be displayed in the preview window when the file size exceeds `maxSize`. Defaults to `file-error-message`.
 

+ 1 - 1
bower.json

@@ -1,6 +1,6 @@
 {
     "name": "bootstrap-fileinput",
-    "version": "2.0.0",
+    "version": "2.1.0",
     "homepage": "https://github.com/kartik-v/bootstrap-fileinput",
     "authors": [
         "Kartik Visweswaran <[email protected]>"

+ 1 - 1
css/fileinput.css

@@ -1,7 +1,7 @@
 /*!
  * @copyright Copyright &copy; Kartik Visweswaran, Krajee.com, 2014
  * @package bootstrap-fileinput
- * @version 2.0.0
+ * @version 2.1.0
  *
  * File input styling for Bootstrap 3.0
  * Built for Yii Framework 2.0

+ 1 - 1
css/fileinput.min.css

@@ -1,7 +1,7 @@
 /*!
  * @copyright Copyright &copy; Kartik Visweswaran, Krajee.com, 2014
  * @package bootstrap-fileinput
- * @version 2.0.0
+ * @version 2.1.0
  *
  * File input styling for Bootstrap 3.0
  * Built for Yii Framework 2.0

+ 3 - 2
examples/index.html

@@ -16,7 +16,7 @@
                     <input id="file-1" type="file" multiple=true data-preview-file-type="any">
                 </div>
                 <div class="form-group">
-                    <input id="file-1a" type="file" class="file" data-preview-file-type="any" data-initial-caption="Kartik" data-overwrite-initial="false">
+                    <input id="file-1a" type="file" multiple=true class="file" data-preview-file-type="any" data-initial-caption="Kartik" data-overwrite-initial="false">
                 </div>
                 <div class="form-group">
                     <input id="file-2" type="file" class="file" readonly=true>
@@ -39,7 +39,8 @@
     $("#file-1").fileinput({
         initialPreview: ["<img src='Desert.jpg' class='file-preview-image'>", "<img src='Jellyfish.jpg' class='file-preview-image'>"],
         overwriteInitial: false,
-        maxFileSize: 100
+        maxFileSize: 100,
+        maxFilesNum: 10
 	});
 	$("#file-3").fileinput({
 		showCaption: false,

+ 243 - 170
js/fileinput.js

@@ -1,6 +1,6 @@
 /*!
  * @copyright Copyright &copy; 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">&times;</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">&times;</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">&times;</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">&times;</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) : '&nbsp;';
             }
             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 &hellip;',
         msgProgress: 'Loaded {percent}% of {file}',

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
js/fileinput.min.js


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác