ソースを参照

Upgrade to release v2.5.0

Kartik Visweswaran 10 年 前
コミット
4ef7029c66
8 ファイル変更108 行追加27 行削除
  1. 11 2
      CHANGE.md
  2. 23 1
      README.md
  3. 1 1
      bower.json
  4. 1 1
      css/fileinput.css
  5. 1 1
      css/fileinput.min.css
  6. 4 3
      examples/index.html
  7. 66 17
      js/fileinput.js
  8. 1 1
      js/fileinput.min.js

+ 11 - 2
CHANGE.md

@@ -1,11 +1,20 @@
+version 2.5.0
+=============
+**Date:** 09-Oct-2014
+
+- (enh #36): Add validation routine for allowed file types and extensions.
+- (bug #37): HTML encode text content for preview in modal.
+- (enh #38): Highlight error CSS in file caption on validation error.
+- (bug #39): HTML encode caption hover title.
+
 version 2.4.0
 =============
 **Date:** 20-Sep-2014
 
 - (enh #30): Enhanced generic support for more preview formats (audio, video, html, flash, and other objects).
 - (enh #31): Better control and configuration of preview templates.
-- (enh #32): Added checks for file api support
-- (enh #33): Better text format validation and correct modal preview
+- (enh #32): Added checks for file api support.
+- (enh #33): Better text format validation and correct modal preview.
 
 > **Note:** There are BC Breaking Changes with release v2.4.0.
 

+ 23 - 1
README.md

@@ -7,7 +7,7 @@ wide variety of files i.e. images, text, html, video, audio, flash, and objects.
 
 ![File Input Screenshot](https://lh3.googleusercontent.com/-3FiEmc_okc4/VBw_d2LBAJI/AAAAAAAAAL8/KbVj5X9Dus0/w596-h454-no/FileInput.jpg)
 
-> NOTE: The latest version of the plugin v2.4.0 has been released. Refer the [CHANGE LOG](https://github.com/kartik-v/bootstrap-fileinput/blob/master/CHANGE.md) for details.
+> NOTE: The latest version of the plugin v2.5.0 has been released. Refer the [CHANGE LOG](https://github.com/kartik-v/bootstrap-fileinput/blob/master/CHANGE.md) for details.
 
 ## Features  
 
@@ -364,6 +364,28 @@ _array_ the list of allowed mime types for preview. This is set to null by defau
 with `allowedPreviewTypes` to filter only the needed file types allowed for preview. You can check this [list of allowed mime types](http://www.sitepoint.com/web-foundations/mime-types-complete-list/)
 to add to this list if needed.
 
+#### allowedFileTypes
+
+_array_ the list of allowed file types for upload. This by default is set to null which means the plugin supports all file types for upload. If an 
+invalid file type is found, then a validation error message as set in `msgInvalidFileType` will be raised. The following types as set in `fileTypeSettings` 
+are available for setup. 
+
+```js
+['image', 'html', 'text', 'video', 'audio', 'flash', 'object']
+```
+
+#### allowedFileExtensions
+
+_array_ the list of allowed file extensions for upload. This by default is set to null which means the plugin supports all file extensions for upload. If an 
+invalid file extension is found, then a validation error message as set in `msgInvalidFileExtension` will be raised. An example of setting this could be:
+
+```js
+['jpg', 'gif', 'png', 'txt']
+```
+
+> NOTE: You need to be careful in case you are setting both `allowedFileTypes` and `allowedFileExtensions`. In this case, the `allowedFileTypes` property 
+is validated first and generally precedes the `allowedFileExtensions` setting (and the latter validation maybe skipped).
+
 #### previewSettings
 
 _object_ the format settings (width and height) for rendering each preview file type. This is by default setup as following:

+ 1 - 1
bower.json

@@ -1,6 +1,6 @@
 {
     "name": "bootstrap-fileinput",
-    "version": "2.4.0",
+    "version": "2.5.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.4.0
+ * @version 2.5.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.4.0
+ * @version 2.5.0
  *
  * File input styling for Bootstrap 3.0
  * Built for Yii Framework 2.0

+ 4 - 3
examples/index.html

@@ -7,7 +7,7 @@
         <link href="http://netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css" rel="stylesheet">
         <link href="../css/fileinput.css" media="all" rel="stylesheet" type="text/css" />
         <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
-        <script src="../js/fileinput.min.js" type="text/javascript"></script>
+        <script src="../js/fileinput.js" type="text/javascript"></script>
         <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.0/js/bootstrap.min.js" type="text/javascript"></script>
     </head>
     <body>
@@ -41,8 +41,9 @@
     $("#file-1").fileinput({
         initialPreview: ["<img src='Desert.jpg' class='file-preview-image'>", "<img src='Jellyfish.jpg' class='file-preview-image'>"],
         overwriteInitial: false,
-        maxFileSize: 100,
-        maxFilesNum: 10
+        maxFileSize: 1000,
+        maxFilesNum: 10,
+        allowedFileTypes: ['image', 'video', 'flash']
 	});
 	$("#file-3").fileinput({
 		showUpload: false,

+ 66 - 17
js/fileinput.js

@@ -1,6 +1,6 @@
 /*!
  * @copyright Copyright &copy; Kartik Visweswaran, Krajee.com, 2014
- * @version 2.4.0
+ * @version 2.5.0
  *
  * File input styled for Bootstrap 3.0 that utilizes HTML5 File Input's advanced 
  * features including the FileReader API. 
@@ -164,6 +164,14 @@
         hasFileAPISupport = function () {
             return window.File && window.FileReader && window.FileList && window.Blob;
         },
+        htmlEncode = function(str) {
+            return String(str)
+                .replace(/&/g, '&amp;')
+                .replace(/"/g, '&quot;')
+                .replace(/'/g, '&#39;')
+                .replace(/</g, '&lt;')
+                .replace(/>/g, '&gt;');
+        },
         vUrl = window.URL || window.webkitURL;
 
     var FileInput = function (element, options) {
@@ -203,6 +211,8 @@
             self.previewTemplates = options.previewTemplates;
             self.allowedPreviewTypes = isEmpty(options.allowedPreviewTypes) ? defaultPreviewTypes : options.allowedPreviewTypes;
             self.allowedPreviewMimeTypes = options.allowedPreviewMimeTypes;
+            self.allowedFileTypes = options.allowedFileTypes;
+            self.allowedFileExtensions = options.allowedFileExtensions;
             self.previewSettings = options.previewSettings;
             self.fileTypeSettings = options.fileTypeSettings;
             self.showRemove = options.showRemove;
@@ -226,6 +236,8 @@
             self.msgLoading = options.msgLoading;
             self.msgProgress = options.msgProgress;
             self.msgSelected = options.msgSelected;
+            self.msgInvalidFileType = options.msgInvalidFileType;
+            self.msgInvalidFileExtension = options.msgInvalidFileExtension;
             self.previewFileType = options.previewFileType;
             self.wrapTextLength = options.wrapTextLength;
             self.wrapIndicator = options.wrapIndicator;
@@ -279,7 +291,8 @@
         initPreview: function () {
             var self = this, html = '', content = self.initialPreview, len = self.initialPreviewCount,
                 cap = self.initialCaption.length, previewId = "preview-" + uniqId(),
-                caption = (cap > 0) ? self.initialCaption : self.msgSelected.replace(/\{n\}/g, len);
+                caption = (cap > 0) ? self.initialCaption : self.msgSelected.replace(/\{n\}/g, len),
+                title = $(caption).text();
             if (isArray(content) && len > 0) {
                 for (var i = 0; i < len; i++) {
                     previewId += '-' + i;
@@ -303,7 +316,7 @@
                 } else {
                     if (cap > 0) {
                         self.$caption.html(caption);
-                        self.$captionContainer.attr('title', caption);
+                        self.$captionContainer.attr('title', title);
                         return;
                     } else {
                         return;
@@ -313,7 +326,7 @@
             self.initialPreviewContent = html;
             self.$preview.html(html);
             self.$caption.html(caption);
-            self.$captionContainer.attr('title', caption);
+            self.$captionContainer.attr('title', title);
             self.$container.removeClass('file-input-new');
         },
         clearObjects: function() {
@@ -399,6 +412,7 @@
         resetErrors: function (fade) {
             var self = this, $error = self.$previewContainer.find('.kv-fileinput-error');
             self.isError = false;
+            self.$container.removeClass('has-error');
             if (fade) {
                 $error.fadeOut('slow');
             } else {
@@ -418,6 +432,7 @@
             $error.fadeIn(800);
             self.$element.trigger('fileerror', [file, previewId, index]);
             self.$element.val('');
+            self.$container.removeClass('has-error').addClass('has-error');
             return true;
         },
         errorHandler: function (evt, caption) {
@@ -469,20 +484,24 @@
                 chkTypes = types.indexOf(cat) >=0, chkMimes = isEmpty(mimes) || (!isEmpty(mimes) && isSet(file.type, mimes));
             if (chkTypes && chkMimes) {
                 if (cat == 'text') {
-                    var strText = theFile.target.result;
+                    var strText = htmlEncode(theFile.target.result);
                     vUrl.revokeObjectURL(data);
                     if (strText.length > wrapLen) {
                         var id = 'text-' + uniqId(), height = window.innerHeight * .75,
-                            modal = self.getLayoutTemplate('modal').replace(/\{id\}/g, id).replace(/\{title\}/g,
-                                caption).replace(/\{body\}/g, strText).replace(/\{height\}/g, height);
-                        wrapInd = wrapInd.replace(/\{title\}/g, caption).replace(/\{dialog\}/g,
-                            "$('#" + id + "').modal('show')");
-                        strText = strText.substring(0, (wrapLen - 1)) + wrapInd;
+                            modal = self.getLayoutTemplate('modal')
+                                .replace(/\{id\}/g, id)
+                                .replace(/\{title\}/g, caption)
+                                .replace(/\{height\}/g, height)
+                                .replace(/\{body\}/g, strText);
+                            wrapInd = wrapInd
+                                .replace(/\{title\}/g, caption)
+                                .replace(/\{dialog\}/g, "$('#" + id + "').modal('show')");
+                            strText = strText.substring(0, (wrapLen - 1)) + wrapInd;
                     }
                     content = template
                         .replace(/\{previewId\}/g, previewId).replace(/\{caption\}/g, caption)
-                        .replace(/\{type\}/g, file.type).replace(/\{data\}/g, strText)
-                        .replace(/\{width\}/g, config.width).replace(/\{height\}/g, config.height) + modal;
+                        .replace(/\{type\}/g, file.type).replace(/\{width\}/g, config.width)
+                        .replace(/\{height\}/g, config.height).replace(/\{data\}/g, strText) + modal;
                 } else {
                     content = template
                         .replace(/\{previewId\}/g, previewId).replace(/\{caption\}/g, caption)
@@ -500,8 +519,8 @@
                 $container = self.$previewContainer, $status = self.$previewStatus, msgLoading = self.msgLoading,
                 msgProgress = self.msgProgress, msgSelected = self.msgSelected, fileType = self.previewFileType,
                 wrapLen = parseInt(self.wrapTextLength), wrapInd = self.wrapIndicator,
-                previewInitId = "preview-" + uniqId(), numFiles = files.length,
-                isText = isSet('text', self.fileTypeSettings) ? self.fileTypeSettings['text'] : defaultFileTypeSettings['text'];
+                previewInitId = "preview-" + uniqId(), numFiles = files.length, settings = self.fileTypeSettings,
+                isText = isSet('text', settings) ? settings['text'] : defaultFileTypeSettings['text'];
 
             function readFile(i) {
                 if (i >= numFiles) {
@@ -510,14 +529,40 @@
                     return;
                 }
                 var previewId = previewInitId + "-" + i, file = files[i], caption = file.name, 
-                    fileSize = (file.size ? file.size : 0) / 1000, previewData = vUrl.createObjectURL(file);
+                    fileSize = (file.size ? file.size : 0) / 1000, checkFile, 
+                    previewData = vUrl.createObjectURL(file), fileCount = 0, j, msg, typ, chk,
+                    fileTypes = self.allowedFileTypes, strTypes = isEmpty(fileTypes) ? '' : fileTypes.join(', '), 
+                    fileExt = self.allowedFileExtensions, strExt = isEmpty(fileExt) ? '' : fileExt.join(', '),
+                    fileExtExpr = isEmpty(fileExt) ? '' : new RegExp('\\.(' + fileExt.join('|') + ')$', 'i');
                 fileSize = fileSize.toFixed(2);
                 if (self.maxFileSize > 0 && fileSize > self.maxFileSize) {
-                    var msg = self.msgSizeTooLarge.replace(/\{name\}/g, caption).replace(/\{size\}/g,
+                    msg = self.msgSizeTooLarge.replace(/\{name\}/g, caption).replace(/\{size\}/g,
                         fileSize).replace(/\{maxSize\}/g, self.maxFileSize);
                     self.isError = self.showError(msg, file, previewId, i);
                     return;
                 }
+                if (!isEmpty(fileTypes) && isArray(fileTypes)) {
+                    for (j = 0; j < fileTypes.length; j++) {
+                        typ = fileTypes[j];
+                        checkFile = settings[typ];
+                        chk = (checkFile !== undefined && checkFile(file.type, caption));
+                        fileCount += isEmpty(chk) ? 0 : chk.length;
+                    }
+                    if (fileCount == 0) {
+                        msg = self.msgInvalidFileType.replace(/\{name\}/g, caption).replace(/\{types\}/g, strTypes);
+                        self.isError = self.showError(msg, file, previewId, i);
+                        return;
+                    }
+                }
+                if (fileCount == 0 && !isEmpty(fileExt) && isArray(fileExt) && !isEmpty(fileExtExpr)) {
+                    chk = caption.match(fileExtExpr);
+                    fileCount += isEmpty(chk) ? 0 : chk.length;
+                    if (fileCount == 0) {
+                        msg = self.msgInvalidFileExtension.replace(/\{name\}/g, caption).replace(/\{extensions\}/g, strExt);
+                        self.isError = self.showError(msg, file, previewId, i);
+                        return;
+                    }
+                }
                 if (!self.showPreview) {
                     setTimeout(readFile(i + 1), 1000);
                     return;
@@ -607,7 +652,7 @@
                 self.showFileIcon();
             }
             self.$caption.html(log);
-            self.$captionContainer.attr('title', log);
+            self.$captionContainer.attr('title', $(log).text());
             self.$container.removeClass('file-input-new');
             $el.trigger('fileselect', [numFiles, label]);
         },
@@ -718,6 +763,8 @@
         previewTemplates: defaultPreviewTemplates,
         allowedPreviewTypes: defaultPreviewTypes,
         allowedPreviewMimeTypes: null,
+        allowedFileTypes: null,
+        allowedFileExtensions: null,
         previewSettings: defaultPreviewSettings,
         fileTypeSettings: defaultFileTypeSettings,
         browseLabel: 'Browse &hellip;',
@@ -738,6 +785,8 @@
         msgFileNotReadable: 'File "{name}" is not readable.',
         msgFilePreviewAborted: 'File preview aborted for "{name}".',
         msgFilePreviewError: 'An error occurred while reading the file "{name}".',
+        msgInvalidFileType: 'Invalid type for file "{name}". Only "{types}" files are supported.',
+        msgInvalidFileExtension: 'Invalid extension for file "{name}". Only "{extensions}" files are supported.',
         msgValidationError: '<span class="text-danger"><i class="glyphicon glyphicon-exclamation-sign"></i> File Upload Error</span>',
         msgErrorClass: 'file-error-message',
         msgLoading: 'Loading  file {index} of {files} &hellip;',

ファイルの差分が大きいため隠しています
+ 1 - 1
js/fileinput.min.js


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません