1/*! 2 * @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 3 * @version 2.7.0 4 * 5 * File input styled for Bootstrap 3.0 that utilizes HTML5 File Input's advanced 6 * features including the FileReader API. 7 * 8 * The plugin drastically enhances the HTML file input to preview multiple files on the client before 9 * upload. In addition it provides the ability to preview content of images, text, videos, audio, html, 10 * flash and other objects. 11 * 12 * Author: Kartik Visweswaran 13 * Copyright: 2014, Kartik Visweswaran, Krajee.com 14 * For more JQuery plugins visit http://plugins.krajee.com 15 * For more Yii related demos visit http://demos.krajee.com 16 */ 17(function ($) { 18 var STYLE_SETTING = 'style="width:{width};height:{height};"'; 19 var PREVIEW_LABEL = ' <div class="text-center"><small>{caption}</small></div>\n'; 20 var OBJECT_PARAMS = ' <param name="controller" value="true" />\n' + 21 ' <param name="allowFullScreen" value="true" />\n' + 22 ' <param name="allowScriptAccess" value="always" />\n' + 23 ' <param name="autoPlay" value="false" />\n' + 24 ' <param name="autoStart" value="false" />\n'+ 25 ' <param name="quality" value="high" />\n'; 26 27 var DEFAULT_PREVIEW = '<div class="file-preview-other" ' + STYLE_SETTING + '>\n' + 28 ' <h2><i class="glyphicon glyphicon-file"></i></h2>\n' + 29 ' </div>'; 30 var defaultLayoutTemplates = { 31 main1: '{preview}\n' + 32 '<div class="input-group {class}">\n' + 33 ' {caption}\n' + 34 ' <div class="input-group-btn">\n' + 35 ' {remove}\n' + 36 ' {upload}\n' + 37 ' {browse}\n' + 38 ' </div>\n' + 39 '</div>', 40 main2: '{preview}\n{remove}\n{upload}\n{browse}\n', 41 preview: '<div class="file-preview {class}">\n' + 42 ' <div class="close fileinput-remove text-right">×</div>\n' + 43 ' <div class="file-preview-thumbnails"></div>\n' + 44 ' <div class="clearfix"></div>' + 45 ' <div class="file-preview-status text-center text-success"></div>\n' + 46 ' <div class="kv-fileinput-error"></div>\n' + 47 '</div>', 48 caption: '<div tabindex="-1" class="form-control file-caption {class}">\n' + 49 ' <span class="glyphicon glyphicon-file kv-caption-icon"></span><div class="file-caption-name"></div>\n' + 50 '</div>', 51 modal: '<div id="{id}" class="modal fade">\n' + 52 ' <div class="modal-dialog modal-lg">\n' + 53 ' <div class="modal-content">\n' + 54 ' <div class="modal-header">\n' + 55 ' <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>\n' + 56 ' <h3 class="modal-title">Detailed Preview <small>{title}</small></h3>\n' + 57 ' </div>\n' + 58 ' <div class="modal-body">\n' + 59 ' <textarea class="form-control" style="font-family:Monaco,Consolas,monospace; height: {height}px;" readonly>{body}</textarea>\n' + 60 ' </div>\n' + 61 ' </div>\n' + 62 ' </div>\n' + 63 '</div>\n' 64 }; 65 var defaultPreviewTypes = ['image', 'html', 'text', 'video', 'audio', 'flash', 'object']; 66 var defaultPreviewTemplates = { 67 generic: '<div class="file-preview-frame" id="{previewId}">\n' + 68 ' {content}\n' + 69 '</div>\n', 70 html: '<div class="file-preview-frame" id="{previewId}">\n' + 71 ' <object data="{data}" type="{type}" width="{width}" height="{height}">\n' + 72 ' ' + DEFAULT_PREVIEW + '\n' + 73 ' </object>\n' + PREVIEW_LABEL + 74 '</div>', 75 image: '<div class="file-preview-frame" id="{previewId}">\n' + 76 ' <img src="{data}" class="file-preview-image" title="{caption}" alt="{caption}" ' + STYLE_SETTING + '>\n' + 77 '</div>\n', 78 text: '<div class="file-preview-frame" id="{previewId}">\n' + 79 ' <div class="file-preview-text" title="{caption}" ' + STYLE_SETTING + '>\n' + 80 ' {data}\n' + 81 ' </div>\n' + 82 '</div>\n', 83 video: '<div class="file-preview-frame" id="{previewId}" title="{caption}" ' + STYLE_SETTING + '>\n' + 84 ' <video width="{width}" height="{height}" controls>\n' + 85 ' <source src="{data}" type="{type}">\n' + 86 ' ' + DEFAULT_PREVIEW + '\n' + 87 ' </video>\n' + PREVIEW_LABEL + 88 '</div>\n', 89 audio: '<div class="file-preview-frame" id="{previewId}" title="{caption}" ' + STYLE_SETTING + '>\n' + 90 ' <audio controls>\n' + 91 ' <source src="{data}" type="{type}">\n' + 92 ' ' + DEFAULT_PREVIEW + '\n' + 93 ' </audio>\n' + PREVIEW_LABEL + 94 '</div>\n', 95 flash: '<div class="file-preview-frame" id="{previewId}" title="{caption}" ' + STYLE_SETTING + '>\n' + 96 ' <object type="application/x-shockwave-flash" width="{width}" height="{height}" data="{data}">\n' + 97 OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n' + 98 ' </object>\n' + PREVIEW_LABEL + 99 '</div>\n', 100 object: '<div class="file-preview-frame" id="{previewId}" title="{caption}" ' + STYLE_SETTING + '>\n' + 101 ' <object data="{data}" type="{type}" width="{width}" height="{height}">\n' + 102 ' <param name="movie" value="{caption}" />\n' + 103 OBJECT_PARAMS + ' ' + DEFAULT_PREVIEW + '\n' + 104 ' </object>\n' + PREVIEW_LABEL + 105 '</div>', 106 other: '<div class="file-preview-frame" id="{previewId}" title="{caption}" ' + STYLE_SETTING + '>\n' + 107 ' ' + DEFAULT_PREVIEW + '\n' + PREVIEW_LABEL + 108 '</div>', 109 }; 110 var defaultPreviewSettings = { 111 image: {width: "100%", height: "160px"}, 112 html: {width: "320px", height: "180px"}, 113 text: {width: "160px", height: "160px"}, 114 video: {width: "320px", height: "240px"}, 115 audio: {width: "320px", height: "80px"}, 116 flash: {width: "320px", height: "240px"}, 117 object: {width: "320px", height: "300px"}, 118 other: {width: "160px", height: "120px"} 119 }; 120 var defaultFileTypeSettings = { 121 image: function(vType, vName) { 122 return (typeof vType !== "undefined") ? vType.match('image.*') : vName.match(/\.(gif|png|jpe?g)$/i); 123 }, 124 html: function(vType, vName) { 125 return (typeof vType !== "undefined") ? vType == 'text/html' : vName.match(/\.(htm|html)$/i); 126 }, 127 text: function(vType, vName) { 128 return typeof vType !== "undefined" && vType.match('text.*') || vName.match(/\.(txt|md|csv|nfo|php|ini)$/i); 129 }, 130 video: function (vType, vName) { 131 return typeof vType !== "undefined" && vType.match(/\.video\/(ogg|mp4|webm)$/i) || vName.match(/\.(og?|mp4|webm)$/i); 132 }, 133 audio: function (vType, vName) { 134 return typeof vType !== "undefined" && vType.match(/\.audio\/(ogg|mp3|wav)$/i) || vName.match(/\.(ogg|mp3|wav)$/i); 135 }, 136 flash: function (vType, vName) { 137 return typeof vType !== "undefined" && vType == 'application/x-shockwave-flash' || vName.match(/\.(swf)$/i); 138 }, 139 object: function (vType, vName) { 140 return true; 141 }, 142 other: function (vType, vName) { 143 return true; 144 }, 145 }; 146 var isEmpty = function (value, trim) { 147 return value === null || value === undefined || value == [] 148 || value === '' || trim && $.trim(value) === ''; 149 }, 150 isArray = function (a) { 151 return Array.isArray(a) || Object.prototype.toString.call(a) === '[object Array]'; 152 }, 153 isSet = function (needle, haystack) { 154 return (typeof haystack == 'object' && needle in haystack); 155 }, 156 getValue = function (options, param, value) { 157 return (isEmpty(options) || isEmpty(options[param])) ? value : options[param]; 158 }, 159 getElement = function (options, param, value) { 160 return (isEmpty(options) || isEmpty(options[param])) ? value : $(options[param]); 161 }, 162 uniqId = function () { 163 return Math.round(new Date().getTime() + (Math.random() * 100)); 164 }, 165 hasFileAPISupport = function () { 166 return window.File && window.FileReader && window.FileList && window.Blob; 167 }, 168 htmlEncode = function(str) { 169 return String(str) 170 .replace(/&/g, '&') 171 .replace(/"/g, '"') 172 .replace(/'/g, ''') 173 .replace(/</g, '<') 174 .replace(/>/g, '>'); 175 }, 176 vUrl = window.URL || window.webkitURL; 177 178 var FileInput = function (element, options) { 179 this.$element = $(element); 180 if (hasFileAPISupport()) { 181 this.init(options); 182 this.listen(); 183 } else { 184 this.$element.removeClass('file-loading'); 185 } 186 }; 187 188 FileInput.prototype = { 189 constructor: FileInput, 190 init: function (options) { 191 var self = this; 192 self.reader = null; 193 self.showCaption = options.showCaption; 194 self.showPreview = options.showPreview; 195 self.maxFileSize = options.maxFileSize;
196 self.maxFileCount = options.maxFileCount; 197 self.msgSizeTooLarge = options.msgSizeTooLarge; 198 self.msgFilesTooMany = options.msgFilesTooMany; 199 self.msgFileNotFound = options.msgFileNotFound; 200 self.msgFileNotReadable = options.msgFileNotReadable; 201 self.msgFilePreviewAborted = options.msgFilePreviewAborted; 202 self.msgFilePreviewError = options.msgFilePreviewError; 203 self.msgValidationError = options.msgValidationError; 204 self.msgErrorClass = options.msgErrorClass; 205 self.initialDelimiter = options.initialDelimiter; 206 self.initialPreview = options.initialPreview; 207 self.initialCaption = options.initialCaption; 208 self.initialPreviewCount = options.initialPreviewCount; 209 self.initialPreviewContent = options.initialPreviewContent; 210 self.overwriteInitial = options.overwriteInitial; 211 self.layoutTemplates = options.layoutTemplates; 212 self.previewTemplates = options.previewTemplates; 213 self.allowedPreviewTypes = isEmpty(options.allowedPreviewTypes) ? defaultPreviewTypes : options.allowedPreviewTypes; 214 self.allowedPreviewMimeTypes = options.allowedPreviewMimeTypes; 215 self.allowedFileTypes = options.allowedFileTypes; 216 self.allowedFileExtensions = options.allowedFileExtensions; 217 self.previewSettings = options.previewSettings; 218 self.fileTypeSettings = options.fileTypeSettings; 219 self.showRemove = options.showRemove; 220 self.showUpload = options.showUpload; 221 self.captionClass = options.captionClass; 222 self.previewClass = options.previewClass; 223 self.mainClass = options.mainClass; 224 self.mainTemplate = self.showCaption ? self.getLayoutTemplate('main1') : self.getLayoutTemplate('main2'); 225 self.captionTemplate = self.getLayoutTemplate('caption'); 226 self.previewGenericTemplate = self.getPreviewTemplate('generic'); 227 self.browseLabel = options.browseLabel; 228 self.browseIcon = options.browseIcon; 229 self.browseClass = options.browseClass; 230 self.removeLabel = options.removeLabel; 231 self.removeIcon = options.removeIcon; 232 self.removeClass = options.removeClass; 233 self.uploadLabel = options.uploadLabel; 234 self.uploadIcon = options.uploadIcon; 235 self.uploadClass = options.uploadClass; 236 self.uploadUrl = options.uploadUrl; 237 self.msgLoading = options.msgLoading; 238 self.msgProgress = options.msgProgress; 239 self.msgSelected = options.msgSelected; 240 self.msgInvalidFileType = options.msgInvalidFileType; 241 self.msgInvalidFileExtension = options.msgInvalidFileExtension; 242 self.previewFileType = options.previewFileType; 243 self.wrapTextLength = options.wrapTextLength; 244 self.wrapIndicator = options.wrapIndicator; 245 self.isError = false; 246 self.isDisabled = self.$element.attr('disabled') || self.$element.attr('readonly'); 247 if (isEmpty(self.$element.attr('id'))) { 248 self.$element.attr('id', uniqId()); 249 } 250 if (typeof self.$container == 'undefined') { 251 self.$container = self.createContainer(); 252 } else { 253 self.refreshContainer(); 254 } 255 self.$captionContainer = getElement(options, 'elCaptionContainer', self.$container.find('.file-caption')); 256 self.$caption = getElement(options, 'elCaptionText', self.$container.find('.file-caption-name')); 257 self.$previewContainer = getElement(options, 'elPreviewContainer', self.$container.find('.file-preview')); 258 self.$preview = getElement(options, 'elPreviewImage', self.$container.find('.file-preview-thumbnails')); 259 self.$previewStatus = getElement(options, 'elPreviewStatus', self.$container.find('.file-preview-status')); 260 self.$errorContainer = getElement(options, 'elErrorContainer', self.$previewContainer.find('.kv-fileinput-error')); 261 if (!isEmpty(self.msgErrorClass)) { 262 self.$errorContainer.removeClass(self.msgErrorClass).addClass(self.msgErrorClass); 263 } 264 self.$errorContainer.hide(); 265 var content = self.initialPreview;
266 self.initialPreviewCount = isArray(content) ? content.length : (content.length > 0 ? content.split(self.initialDelimiter).length : 0) 267 self.initPreview(); 268 self.original = { 269 preview: self.$preview.html(), 270 caption: self.$caption.html() 271 }; 272 self.options = options; 273 self.$element.removeClass('file-loading'); 274 }, 275 getLayoutTemplate: function(t) { 276 var self = this; 277 return isSet(t, self.layoutTemplates) ? self.layoutTemplates[t] : defaultLayoutTemplates[t]; 278 }, 279 getPreviewTemplate: function(t) { 280 var self = this; 281 return isSet(t, self.previewTemplates) ? self.previewTemplates[t] : defaultPreviewTemplates[t]; 282 }, 283 listen: function () { 284 var self = this, $el = self.$element, $cap = self.$captionContainer, $btnFile = self.$btnFile; 285 $el.on('change', $.proxy(self.change, self)); 286 $btnFile.on('click', function (ev) { 287 self.clear(false); 288 $cap.focus(); 289 }); 290 $el.closest('form').on('reset', $.proxy(self.reset, self)); 291 self.$container.on('click', '.fileinput-remove:not([disabled])', $.proxy(self.clear, self)); 292 }, 293 refresh: function (options) { 294 var self = this, params = (arguments.length) ? $.extend(self.options, options) : self.options; 295 self.$element.off(); 296 self.init(params); 297 }, 298 initPreview: function () { 299 var self = this, html = '', content = self.initialPreview, len = self.initialPreviewCount, 300 cap = self.initialCaption.length, previewId = "preview-" + uniqId(), 301 caption = (cap > 0) ? self.initialCaption : self.msgSelected.replace(/\{n\}/g, len), 302 title = $(caption).text(); 303 if (isArray(content) && len > 0) { 304 for (var i = 0; i < len; i++) { 305 previewId += '-' + i; 306 html += self.previewGenericTemplate.replace(/\{previewId\}/g, previewId).replace(/\{content\}/g, 307 content[i]); 308 } 309 if (len > 1 && cap == 0) { 310 caption = self.msgSelected.replace(/\{n\}/g, len); 311 } 312 } else { 313 if (len > 0) { 314 var fileList = content.split(self.initialDelimiter); 315 for (var i = 0; i < len; i++) { 316 previewId += '-' + i; 317 html += self.previewGenericTemplate.replace(/\{previewId\}/g, previewId).replace(/\{content\}/g, 318 fileList[i]); 319 } 320 if (len > 1 && cap == 0) { 321 caption = self.msgSelected.replace(/\{n\}/g, len); 322 } 323 } else { 324 if (cap > 0) { 325 self.$caption.html(caption); 326 self.$captionContainer.attr('title', title); 327 return; 328 } else { 329 return; 330 } 331 } 332 } 333 self.initialPreviewContent = html; 334 self.$preview.html(html); 335 self.$caption.html(caption); 336 self.$captionContainer.attr('title', title); 337 self.$container.removeClass('file-input-new'); 338 }, 339 clearObjects: function() { 340 var self = this, $preview = self.$preview; 341 $preview.find('video audio').each(function() { 342 this.pause(); 343 delete(this); 344 $(this).remove(); 345 }); 346 $preview.find('img object div').each(function() { 347 delete(this); 348 $(this).remove(); 349 }); 350 }, 351 clearFileInput: function() { 352 var self = this, $el = self.$element; 353 if (isEmpty($el.val())) { 354 return; 355 } 356 // Fix for IE ver < 11, that does not clear file inputs 357 // Requires a sequence of steps to prevent IE crashing but 358 // still allow clearing of the file input. 359 if (/MSIE/.test(navigator.userAgent)) { 360 var $frm1 = $el.closest('form'); 361 if ($frm1.length) { 362 $el.wrap('<form>'); 363 var $frm2 = $el.closest('form'), $tmpEl = $(document.createElement('div')); 364 $frm2.before($tmpEl).after($frm1).trigger('reset'); 365 $el.unwrap().appendTo($tmpEl).unwrap(); 366 } else { 367 $el.wrap('<form>').closest('form').trigger('reset').unwrap(); 368 } 369 } else { // normal input clear behavior for other sane browsers 370 $el.val(''); 371 } 372 }, 373 clear: function () { 374 var self = this, e = arguments.length && arguments[0]; 375 if (e) { 376 e.preventDefault(); 377 } 378 if (self.reader instanceof FileReader) { 379 self.reader.abort(); 380 } 381 self.clearFileInput(); 382 self.resetErrors(true); 383 384 if (e !== false) { 385 self.$element.trigger('change');
386 self.$element.trigger('fileclear'); 387 } 388 if (self.overwriteInitial) { 389 self.initialPreviewCount = 0; 390 } 391 if (!self.overwriteInitial && !isEmpty(self.initialPreviewContent)) { 392 self.showFileIcon(); 393 self.$preview.html(self.original.preview); 394 self.$caption.html(self.original.caption); 395 self.$container.removeClass('file-input-new'); 396 } else { 397 self.clearObjects(); 398 self.$preview.html(''); 399 var cap = (!self.overwriteInitial && self.initialCaption.length > 0) ? 400 self.original.caption : ''; 401 self.$caption.html(cap); 402 self.$captionContainer.attr('title', ''); 403 self.$container.removeClass('file-input-new').addClass('file-input-new'); 404 } 405 self.hideFileIcon(); 406 self.$element.trigger('filecleared'); 407 self.$captionContainer.focus(); 408 }, 409 reset: function (e) { 410 var self = this; 411 self.clear(false); 412 self.$preview.html(self.original.preview); 413 self.$caption.html(self.original.caption); 414 self.$container.find('.fileinput-filename').text(''); 415 self.$element.trigger('filereset'); 416 if (self.initialPreview.length > 0) { 417 self.$container.removeClass('file-input-new'); 418 } 419 }, 420 disable: function (e) { 421 var self = this; 422 self.isDisabled = true; 423 self.$element.attr('disabled', 'disabled'); 424 self.$container.find(".kv-fileinput-caption").addClass("file-caption-disabled"); 425 self.$container.find(".btn-file, .fileinput-remove, .kv-fileinput-upload").attr("disabled", true); 426 }, 427 enable: function (e) { 428 var self = this; 429 self.isDisabled = false; 430 self.$element.removeAttr('disabled'); 431 self.$container.find(".kv-fileinput-caption").removeClass("file-caption-disabled"); 432 self.$container.find(".btn-file, .fileinput-remove, .kv-fileinput-upload").removeAttr("disabled"); 433 }, 434 hideFileIcon: function () { 435 if (this.overwriteInitial) { 436 this.$captionContainer.find('.kv-caption-icon').hide(); 437 } 438 }, 439 showFileIcon: function () { 440 this.$captionContainer.find('.kv-caption-icon').show(); 441 }, 442 resetErrors: function (fade) { 443 var self = this, $error = self.$errorContainer; 444 self.isError = false; 445 self.$container.removeClass('has-error'); 446 if (fade) { 447 $error.fadeOut('slow'); 448 } else { 449 $error.hide(); 450 } 451 }, 452 showError: function (msg, file, previewId, index) { 453 var self = this, $error = self.$errorContainer, $el = self.$element; 454 $error.html(msg); 455 $error.fadeIn(800); 456 $el.trigger('fileerror', [file, previewId, index]); 457 self.clearFileInput(); 458 self.$container.removeClass('has-error').addClass('has-error'); 459 return true; 460 }, 461 errorHandler: function (evt, caption) { 462 var self = this; 463 switch (evt.target.error.code) { 464 case evt.target.error.NOT_FOUND_ERR: 465 self.addError(self.msgFileNotFound.replace(/\{name\}/g, caption)); 466 break; 467 case evt.target.error.NOT_READABLE_ERR: 468 self.addError(self.msgFileNotReadable.replace(/\{name\}/g, caption)); 469 break; 470 case evt.target.error.ABORT_ERR: 471 self.addError(self.msgFilePreviewAborted.replace(/\{name\}/g, caption)); 472 break; 473 default: 474 self.addError(self.msgFilePreviewError.replace(/\{name\}/g, caption)); 475 } 476 }, 477 parseFileType: function(file) { 478 var isValid, vType; 479 for (var i = 0;
479 i < defaultPreviewTypes.length; i++) { 480 cat = defaultPreviewTypes[i]; 481 isValid = isSet(cat, self.fileTypeSettings) ? self.fileTypeSettings[cat] : defaultFileTypeSettings[cat]; 482 vType = isValid(file.type, file.name) ? cat : ''; 483 if (vType != '') { 484 return vType; 485 } 486 } 487 return 'other'; 488 }, 489 previewDefault: function(file, previewId) { 490 var self = this, data = vUrl.createObjectURL(file), $obj = $('#' + previewId), 491 previewOtherTemplate = isSet('other', self.previewTemplates) ? self.previewTemplates['other'] : defaultPreviewTemplates['other']; 492 self.$preview.append("\n" + previewOtherTemplate 493 .replace(/\{previewId\}/g, previewId) 494 .replace(/\{caption\}/g, self.slug(file.name)) 495 .replace(/\{type\}/g, file.type) 496 .replace(/\{data\}/g, data)); 497 $obj.on('load', function(e) { 498 vUrl.revokeObjectURL($obj.attr('data')); 499 }); 500 }, 501 previewFile: function(file, theFile, previewId, data) { 502 var self = this, i, cat = self.parseFileType(file), caption = self.slug(file.name), data, obj, content, 503 types = self.allowedPreviewTypes, mimes = self.allowedPreviewMimeTypes, fType = file.type, 504 template = isSet(cat, self.previewTemplates) ? self.previewTemplates[cat] : defaultPreviewTemplates[cat], 505 config = isSet(cat, self.previewSettings) ? self.previewSettings[cat] : defaultPreviewSettings[cat], 506 wrapLen = parseInt(self.wrapTextLength), wrapInd = self.wrapIndicator, $preview = self.$preview, 507 chkTypes = types.indexOf(cat) >=0, chkMimes = isEmpty(mimes) || (!isEmpty(mimes) && isSet(file.type, mimes)); 508 if (chkTypes && chkMimes) { 509 if (cat == 'text') { 510 var strText = htmlEncode(theFile.target.result); 511 vUrl.revokeObjectURL(data); 512 if (strText.length > wrapLen) { 513 var id = 'text-' + uniqId(), height = window.innerHeight * .75, 514 modal = self.getLayoutTemplate('modal') 515 .replace(/\{id\}/g, id) 516 .replace(/\{title\}/g, caption) 517 .replace(/\{height\}/g, height) 518 .replace(/\{body\}/g, strText); 519 wrapInd = wrapInd 520 .replace(/\{title\}/g, caption) 521 .replace(/\{dialog\}/g, "$('#" + id + "').modal('show')"); 522 strText = strText.substring(0, (wrapLen - 1)) + wrapInd; 523 } 524 content = template 525 .replace(/\{previewId\}/g, previewId).replace(/\{caption\}/g, caption) 526 .replace(/\{type\}/g, file.type).replace(/\{width\}/g, config.width) 527 .replace(/\{height\}/g, config.height).replace(/\{data\}/g, strText) + modal; 528 } else { 529 content = template 530 .replace(/\{previewId\}/g, previewId).replace(/\{caption\}/g, caption) 531 .replace(/\{type\}/g, file.type).replace(/\{data\}/g, data) 532 .replace(/\{width\}/g, config.width).replace(/\{height\}/g, config.height); 533 } 534 $preview.append("\n" + content); 535 } else { 536 self.previewDefault(file, previewId); 537 } 538 }, 539 readFiles: function (files) { 540 this.reader = new FileReader(); 541 var self = this, $el = self.$element, $preview = self.$preview, reader = self.reader, 542 $container = self.$previewContainer, $status = self.$previewStatus, msgLoading = self.msgLoading, 543 msgProgress = self.msgProgress, msgSelected = self.msgSelected, fileType = self.previewFileType, 544 wrapLen = parseInt(self.wrapTextLength), wrapInd = self.wrapIndicator, 545 previewInitId = "preview-" + uniqId(), numFiles = files.length, settings = self.fileTypeSettings, 546 isText = isSet('text', settings) ? settings['text'] : defaultFileTypeSettings['text']; 547 548 function readFile(i) { 549 if (i >= numFiles) { 550 $container.removeClass('loading'); 551 $status.html(''); 552 return; 553 } 554 var previewId = previewInitId + "-" + i, file = files[i], caption = self.slug(file.name), 555 fileSize = (file.size ? file.size : 0) / 1000, checkFile, 556 previewData = vUrl.createObjectURL(file), fileCount = 0, j, msg, typ, chk, 557 fileTypes = self.allowedFileTypes, strTypes = isEmpty(fileTypes) ? '' : fileTypes.join(', '), 558 fileExt = self.allowedFileExtensions, strExt = isEmpty(fileExt) ? '' : fileExt.join(', '), 559 fileExtExpr = isEmpty(fileExt) ? '' : new RegExp('\\.(' + fileExt.join('|') + ')$', 'i'); 560 fileSize = fileSize.toFixed(2); 561 if (self.maxFileSize > 0 && fileSize > self.maxFileSize) { 562 msg = self.msgSizeTooLarge.replace(/\{name\}/g, caption).replace(/\{size\}/g, 563 fileSize).replace(/\{maxSize\}/g, self.maxFileSize); 564 self.isError = self.showError(msg, file, previewId, i); 565 return; 566 } 567 if (!isEmpty(fileTypes) && isArray(fileTypes)) { 568 for (j = 0; j < fileTypes.length; j++) { 569 typ = fileTypes[j]; 570 checkFile = settings[typ]; 571 chk = (checkFile !== undefined && checkFile(file.type, caption)); 572 fileCount += isEmpty(chk) ? 0 : chk.length; 573 } 574 if (fileCount == 0) { 575 msg = self.msgInvalidFileType.replace(/\{name\}/g, caption).replace(/\{types\}/g, strTypes); 576 self.isError = self.showError(msg, file, previewId, i); 577 return; 578 } 579 } 580 if (fileCount == 0 && !isEmpty(fileExt) && isArray(fileExt) && !isEmpty(fileExtExpr)) { 581 chk = caption.match(fileExtExpr); 582 fileCount += isEmpty(chk) ? 0 : chk.length; 583 if (fileCount == 0) { 584 msg = self.msgInvalidFileExtension.replace(/\{name\}/g, caption).replace(/\{extensions\}/g, strExt); 585 self.isError = self.showError(msg, file, previewId, i); 586 return; 587 } 588 } 589 if (!self.showPreview) { 590 $el.trigger('fileloaded', [file, previewId, i]); 591 setTimeout(readFile(i + 1), 1000); 592 return; 593 } 594 if ($preview.length > 0 && typeof FileReader !== "undefined") { 595 $status.html(msgLoading.replace(/\{index\}/g, i + 1).replace(/\{files\}/g, numFiles)); 596 $container.addClass('loading'); 597 reader.onerror = function (evt) { 598 self.errorHandler(evt, caption); 599 }; 600 reader.onload = function (theFile) { 601 self.previewFile(file, theFile, previewId, previewData); 602 }; 603 reader.onloadend = function (e) { 604 var msg = msgProgress 605 .replace(/\{index\}/g, i + 1).replace(/\{files\}/g, numFiles) 606 .replace(/\{percent\}/g, 100).replace(/\{name\}/g, caption); 607 setTimeout(function () { 608 $status.html(msg); 609 vUrl.revokeObjectURL(previewData); 610 }, 1000); 611 setTimeout(function () { 612 readFile(i + 1); 613 }, 1500); 614 $el.trigger('fileloaded', [file, previewId, i]); 615 }; 616 reader.onprogress = function (data) { 617 if (data.lengthComputable) { 618 var progress = parseInt(((data.loaded / data.total) * 100), 10); 619 var msg = msgProgress 620 .replace(/\{index\}/g, i + 1).replace(/\{files\}/g, numFiles) 621 .replace(/\{percent\}/g, progress).replace(/\{name\}/g, caption); 622 setTimeout(function () { 623 $status.html(msg); 624 }, 1000); 625 } 626 }; 627 if (isText(file.type, caption)) { 628 reader.readAsText(file); 629 } else { 630 reader.readAsArrayBuffer(file); 631 } 632 } else { 633 self.previewDefault(file, previewId); 634 $el.trigger('fileloaded', [file, previewId, i]); 635 setTimeout(readFile(i + 1), 1000); 636 } 637 } 638 readFile(0); 639 }, 640 slug: function (text) { 641 return isEmpty(text) ? '' : text.split(/(\\|\/)/g).pop().replace(/[^\w-.\\\/ ]+/g,''); 642 }, 643 change: function (e) { 644 var self = this, $el = self.$element, label = self.slug($el.val()), 645 total = 0, $preview = self.$preview, files = $el.get(0).files, msgSelected = self.msgSelected, 646 numFiles = !isEmpty(files) ? (files.length + self.initialPreviewCount) : 1, tfiles; 647 self.hideFileIcon(); 648 if (e.target.files === undefined) { 649 tfiles = e.target && e.target.value ? [ 650 {name: e.target.value.replace(/^.+\\/, '')} 651 ] : []; 652 } else { 653 tfiles = e.target.files; 654 } 655 if (tfiles.length === 0) { 656 return; 657 } 658 self.resetErrors(); 659 $preview.html(''); 660 if (!self.overwriteInitial) { 661 $preview.html(self.initialPreviewContent); 662 } 663 var total = tfiles.length;
664 if (self.maxFileCount > 0 && total > self.maxFileCount) { 665 var msg = self.msgFilesTooMany.replace(/\{m\}/g, self.maxFileCount).replace(/\{n\}/g, total); 666 self.isError = self.showError(msg, null, null, null); 667 self.$captionContainer.find('.kv-caption-icon').hide(); 668 self.$caption.html(self.msgValidationError); 669 self.$container.removeClass('file-input-new'); 670 return; 671 } 672 self.readFiles(files); 673 self.reader = null; 674 var log = numFiles > 1 ? msgSelected.replace(/\{n\}/g, numFiles) : label; 675 if (self.isError) { 676 self.$captionContainer.find('.kv-caption-icon').hide(); 677 log = self.msgValidationError; 678 } else { 679 self.showFileIcon(); 680 } 681 self.$caption.html(log); 682 self.$captionContainer.attr('title', $(log).text()); 683 self.$container.removeClass('file-input-new'); 684 $el.trigger('fileselect', [numFiles, label]); 685 }, 686 initBrowse: function ($container) { 687 var self = this; 688 self.$btnFile = $container.find('.btn-file'); 689 self.$btnFile.append(self.$element); 690 }, 691 createContainer: function () { 692 var self = this; 693 var $container = $(document.createElement("span")).attr({"class": 'file-input file-input-new'}).html(self.renderMain()); 694 self.$element.before($container); 695 self.initBrowse($container); 696 return $container; 697 }, 698 refreshContainer: function () { 699 var self = this, $container = self.$container; 700 $container.before(self.$element); 701 $container.html(self.renderMain()); 702 self.initBrowse($container); 703 }, 704 renderMain: function () { 705 var self = this; 706 var preview = self.showPreview ? self.getLayoutTemplate('preview').replace(/\{class\}/g, self.previewClass) : ''; 707 var css = self.isDisabled ? self.captionClass + ' file-caption-disabled' : self.captionClass; 708 var caption = self.captionTemplate.replace(/\{class\}/g, css + ' kv-fileinput-caption'); 709 return self.mainTemplate.replace(/\{class\}/g, self.mainClass). 710 replace(/\{preview\}/g, preview). 711 replace(/\{caption\}/g, caption). 712 replace(/\{upload\}/g, self.renderUpload()). 713 replace(/\{remove\}/g, self.renderRemove()). 714 replace(/\{browse\}/g, self.renderBrowse()); 715 }, 716 renderBrowse: function () { 717 var self = this, css = self.browseClass + ' btn-file', status = ''; 718 if (self.isDisabled) { 719 status = ' disabled '; 720 } 721 return '<div class="' + css + '"' + status + '> ' + self.browseIcon + self.browseLabel + ' </div>'; 722 }, 723 renderRemove: function () { 724 var self = this, css = self.removeClass + ' fileinput-remove fileinput-remove-button', status = ''; 725 if (!self.showRemove) { 726 return ''; 727 } 728 if (self.isDisabled) { 729 status = ' disabled '; 730 } 731 return '<button type="button" class="' + css + '"' + status + '>' + self.removeIcon + self.removeLabel + '</button>'; 732 }, 733 renderUpload: function () { 734 var self = this, css = self.uploadClass + ' kv-fileinput-upload', content = '', status = ''; 735 if (!self.showUpload) { 736 return ''; 737 } 738 if (self.isDisabled) { 739 status = ' disabled '; 740 } 741 if (isEmpty(self.uploadUrl)) { 742 content = '<button type="submit" class="' + css + '"' + status + '>' + self.uploadIcon + self.uploadLabel + '</button>'; 743 } else { 744 content = '<a href="' + self.uploadUrl + '" class="' + self.uploadClass + '"' + status + '>' + self.uploadIcon + self.uploadLabel + '</a>'; 745 } 746 return content; 747 } 748 } 749 750 //FileInput plugin definition 751 $.fn.fileinput = function (option) { 752 if (!hasFileAPISupport()) { 753 return; 754 } 755 756 var args = Array.apply(null, arguments); 757 args.shift(); 758 return this.each(function () { 759 var $this = $(this), 760 data = $this.data('fileinput'), 761 options = typeof option === 'object' && option; 762 763 if (!data) { 764 $this.data('fileinput', 765 (data = new FileInput(this, $.extend({}, $.fn.fileinput.defaults, options, $(this).data())))); 766 } 767 768 if (typeof option === 'string') { 769 data[option].apply(data, args); 770 } 771 }); 772 }; 773 774 $.fn.fileinput.defaults = { 775 showCaption: true, 776 showPreview: true, 777 showRemove: true, 778 showUpload: true, 779 mainClass: '', 780 previewClass: '', 781 captionClass: '', 782 mainTemplate: null, 783 initialDelimiter: '*$$*', 784 initialPreview: '', 785 initialCaption: '', 786 initialPreviewCount: 0, 787 initialPreviewContent: '', 788 overwriteInitial: true, 789 layoutTemplates: defaultLayoutTemplates, 790 previewTemplates: defaultPreviewTemplates,
791 allowedPreviewTypes: defaultPreviewTypes, 792 allowedPreviewMimeTypes: null, 793 allowedFileTypes: null, 794 allowedFileExtensions: null, 795 previewSettings: defaultPreviewSettings, 796 fileTypeSettings: defaultFileTypeSettings, 797 browseLabel: 'Browse …', 798 browseIcon: '<i class="glyphicon glyphicon-folder-open"></i> ', 799 browseClass: 'btn btn-primary', 800 removeLabel: 'Remove', 801 removeIcon: '<i class="glyphicon glyphicon-ban-circle"></i> ', 802 removeClass: 'btn btn-default', 803 uploadLabel: 'Upload', 804 uploadIcon: '<i class="glyphicon glyphicon-upload"></i> ', 805 uploadClass: 'btn btn-default', 806 uploadUrl: null, 807 maxFileSize: 0, 808 maxFileCount: 0, 809 msgSizeTooLarge: 'File "{name}" (<b>{size} KB</b>) exceeds maximum allowed upload size of <b>{maxSize} KB</b>. Please retry your upload!', 810 msgFilesTooMany: 'Number of files selected for upload <b>({n})</b> exceeds maximum allowed limit of <b>{m}</b>. Please retry your upload!', 811 msgFileNotFound: 'File "{name}" not found!', 812 msgFileNotReadable: 'File "{name}" is not readable.', 813 msgFilePreviewAborted: 'File preview aborted for "{name}".', 814 msgFilePreviewError: 'An error occurred while reading the file "{name}".', 815 msgInvalidFileType: 'Invalid type for file "{name}". Only "{types}" files are supported.', 816 msgInvalidFileExtension: 'Invalid extension for file "{name}". Only "{extensions}" files are supported.', 817 msgValidationError: '<span class="text-danger"><i class="glyphicon glyphicon-exclamation-sign"></i> File Upload Error</span>', 818 msgErrorClass: 'file-error-message', 819 msgLoading: 'Loading file {index} of {files} …', 820 msgProgress: 'Loading file {index} of {files} - {name} - {percent}% completed.', 821 msgSelected: '{n} files selected', 822 previewFileType: 'image', 823 wrapTextLength: 250, 824 wrapIndicator: ' <span class="wrap-indicator" title="{title}" onclick="{dialog}">[…]</span>', 825 elCaptionContainer: null, 826 elCaptionText: null, 827 elPreviewContainer: null, 828 elPreviewImage: null, 829 elPreviewStatus: null, 830 elErrorContainer: null 831 }; 832 833 /** 834 * Convert automatically file inputs with class 'file' 835 * into a bootstrap fileinput control. 836 */ 837 $(document).ready(function () { 838 var $input = $('input.file[type=file]'), count = $input.attr('type') != null ? $input.length : 0; 839 if (count > 0) { 840 $input.fileinput(); 841 } 842 }); 843 844})(window.jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.